Initial RS-MobStand commit

This commit is contained in:
BigPappa committed 2026-06-07 12:11:50 -05:00
commit 73f9d3a155
27 files changed
+5203

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
# IntelliJ IDEA
.idea/
*.iml
# Maven build output
target/
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
logs/
+82
View File
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ========================================================= -->
<!-- RSScripting -->
<!-- RS MobStand -->
<!-- ========================================================= -->
<groupId>com.rsscripting</groupId>
<artifactId>rs-mobstand</artifactId>
<version>1.0.0</version>
<name>RS-MobStand</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- ========================================================= -->
<!-- Repositories -->
<!-- ========================================================= -->
<repositories>
<!-- PaperMC -->
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<!-- ========================================================= -->
<!-- Dependencies -->
<!-- ========================================================= -->
<dependencies>
<!-- Paper API -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ========================================================= -->
<!-- Build -->
<!-- ========================================================= -->
<build>
<!-- Final Jar Name -->
<finalName>RS-MobStand-1.0.0</finalName>
<plugins>
<!-- Java Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,94 @@
package com.rsscripting.mobstand;
import com.rsscripting.mobstand.commands.RSMSCommand;
import com.rsscripting.mobstand.commands.RSMSTabCompleter;
import com.rsscripting.mobstand.tasks.SlaughterTask;
import com.rsscripting.mobstand.config.ConfigManager;
import com.rsscripting.mobstand.listeners.ChunkLoadListener;
import com.rsscripting.mobstand.listeners.ArmorStandBreakListener;
import com.rsscripting.mobstand.listeners.ArmorStandListener;
import com.rsscripting.mobstand.listeners.MenuListener;
import com.rsscripting.mobstand.managers.StandManager;
import com.rsscripting.mobstand.utils.VersionChecker;
import org.bukkit.plugin.java.JavaPlugin;
public class RSMobStandPlugin extends JavaPlugin {
private StandManager standManager;
private ConfigManager configManager;
@Override
public void onEnable() {
saveDefaultConfig();
this.standManager = new StandManager(this);
this.configManager = new ConfigManager(this);
getCommand("rsms").setExecutor(
new RSMSCommand(this)
);
getCommand("rsms").setTabCompleter(
new RSMSTabCompleter()
);
getServer().getPluginManager().registerEvents(
new ArmorStandListener(
this,
standManager
),
this
);
getServer().getPluginManager().registerEvents(
new ArmorStandBreakListener(this),
this
);
getServer().getPluginManager().registerEvents(
new MenuListener(this),
this
);
long scanInterval = getConfig().getLong("scan-interval");
new SlaughterTask(this).runTaskTimer(
this,
20L,
scanInterval
);
getServer().getPluginManager().registerEvents(
new ChunkLoadListener(standManager),
this
);
new VersionChecker(this).checkVersion();
getLogger().info("RS Mob Stand enabled.");
}
@Override
public void onDisable() {
getLogger().info("RS Mob Stand disabled.");
}
public StandManager getStandManager() {
return standManager;
}
public ConfigManager getConfigManager() {
return configManager;
}
}
@@ -0,0 +1,81 @@
package com.rsscripting.mobstand.commands;
import com.rsscripting.mobstand.RSMobStandPlugin;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class RSMSCommand implements CommandExecutor {
private final RSMobStandPlugin plugin;
public RSMSCommand(
RSMobStandPlugin plugin
) {
this.plugin = plugin;
}
@Override
public boolean onCommand(
CommandSender sender,
Command command,
String label,
String[] args
) {
if (!sender.hasPermission(
"rsmobstand.admin"
)) {
sender.sendMessage(
ChatColor.RED
+ "You do not have permission."
);
return true;
}
if (args.length == 0) {
sender.sendMessage(
ChatColor.YELLOW
+ "/rsms reload"
);
return true;
}
if (args[0].equalsIgnoreCase(
"reload"
)) {
plugin.reloadConfig();
plugin.getConfigManager()
.loadConfigs();
sender.sendMessage(
ChatColor.GREEN
+ "RS MobStand configuration reloaded."
);
return true;
}
sender.sendMessage(
ChatColor.RED
+ "Unknown subcommand."
);
return true;
}
}
@@ -0,0 +1,33 @@
package com.rsscripting.mobstand.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import java.util.ArrayList;
import java.util.List;
public class RSMSTabCompleter implements TabCompleter {
@Override
public List<String> onTabComplete(
CommandSender sender,
Command command,
String alias,
String[] args
) {
List<String> completions =
new ArrayList<>();
if (args.length == 1) {
completions.add("reload");
}
return completions;
}
}
@@ -0,0 +1,278 @@
package com.rsscripting.mobstand.config;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.data.MobFilterData;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import java.io.File;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
public class ConfigManager {
private final RSMobStandPlugin plugin;
private final Map<String, MobFilterData> adultMobData =
new HashMap<>();
private final Map<String, MobFilterData> babyMobData =
new HashMap<>();
private FileConfiguration adultConfig;
private FileConfiguration babyConfig;
public ConfigManager(RSMobStandPlugin plugin) {
this.plugin = plugin;
loadConfigs();
}
// Loads all plugin configs.
public void loadConfigs() {
File configFolder = new File(
plugin.getDataFolder(),
"configs"
);
if (!configFolder.exists()) {
if (!configFolder.mkdirs()) {
plugin.getLogger().warning(
"Failed to create configs folder."
);
}
}
loadAdultConfig();
loadAdultMobData();
loadBabyConfig();
loadBabyMobData();
}
// Loads adult mob config.
private void loadAdultConfig() {
File file = new File(
plugin.getDataFolder(),
"configs/adult-mobs.yml"
);
if (!file.exists()) {
plugin.saveResource("configs/adult-mobs.yml", false);
}
adultConfig = YamlConfiguration.loadConfiguration(file);
}
//Loads baby mob config.
private void loadBabyConfig() {
File file = new File(
plugin.getDataFolder(),
"configs/baby-mobs.yml"
);
if (!file.exists()) {
plugin.saveResource("configs/baby-mobs.yml", false);
}
babyConfig = YamlConfiguration.loadConfiguration(file);
}
// Gets allowed adult mobs.
public void loadBabyMobData() {
babyMobData.clear();
ConfigurationSection section =
babyConfig.getConfigurationSection(
"baby-mobs"
);
if (section == null) {
return;
}
for (String mobName : section.getKeys(false)) {
String path =
"baby-mobs." + mobName;
String iconName =
babyConfig.getString(
path + ".icon"
);
boolean enabled =
babyConfig.getBoolean(
path + ".enabled-by-default"
);
boolean killAll =
babyConfig.getBoolean(
path + ".kill-all",
false
);
if (iconName == null) {
plugin.getLogger().warning(
"Missing icon for mob: "
+ mobName
);
continue;
}
Material icon =
Material.matchMaterial(iconName);
if (icon == null) {
plugin.getLogger().warning(
"Invalid icon material for mob: "
+ mobName
);
continue;
}
babyMobData.put(
mobName,
new MobFilterData(
icon,
enabled,
killAll
)
);
}
}
// Gets allowed adult mobs.
public void loadAdultMobData() {
adultMobData.clear();
ConfigurationSection section =
adultConfig.getConfigurationSection(
"adult-mobs"
);
if (section == null) {
return;
}
for (String mobName : section.getKeys(false)) {
String path =
"adult-mobs." + mobName;
String iconName =
adultConfig.getString(
path + ".icon"
);
boolean enabled =
adultConfig.getBoolean(
path + ".enabled-by-default"
);
boolean killAll =
adultConfig.getBoolean(
path + ".kill-all",
false
);
if (iconName == null) {
plugin.getLogger().warning(
"Missing icon for mob: "
+ mobName
);
continue;
}
Material icon =
Material.matchMaterial(iconName);
if (icon == null) {
plugin.getLogger().warning(
"Invalid icon material for mob: "
+ mobName
);
continue;
}
adultMobData.put(
mobName,
new MobFilterData(
icon,
enabled,
killAll
)
);
}
}
// Gets allowed baby mobs.
public List<String> getBabyMobs() {
return babyConfig.getStringList("baby-mobs");
}
public Map<String, MobFilterData> getAdultMobData() {
return adultMobData;
}
public Map<String, MobFilterData> getBabyMobData() {
return babyMobData;
}
}
@@ -0,0 +1,45 @@
package com.rsscripting.mobstand.data;
import org.bukkit.Material;
public class MobFilterData {
private final Material icon;
private final boolean enabledByDefault;
private final boolean killAll;
public MobFilterData(
Material icon,
boolean enabledByDefault,
boolean killAll
) {
this.icon = icon;
this.enabledByDefault = enabledByDefault;
this.killAll = killAll;
}
public Material getIcon() {
return icon;
}
public boolean isKillAll() {
return killAll;
}
public boolean isEnabledByDefault() {
return enabledByDefault;
}
}
@@ -0,0 +1,326 @@
package com.rsscripting.mobstand.gui;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.data.MobFilterData;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AdultFilterMenu {
public static final String MENU_TITLE =
ChatColor.BLUE + "Adult Filters";
private static final int ITEMS_PER_PAGE = 45;
public static void open(
RSMobStandPlugin plugin,
Player player,
ArmorStand armorStand,
int page
) {
Map<String, MobFilterData> mobData =
plugin.getConfigManager()
.getAdultMobData();
List<String> mobs =
new ArrayList<>(mobData.keySet());
Collections.sort(mobs);
List<String> disabledMobs =
plugin.getStandManager()
.getDisabledAdultMobs(
armorStand
);
int startIndex =
page * ITEMS_PER_PAGE;
int endIndex =
Math.min(
startIndex + ITEMS_PER_PAGE,
mobs.size()
);
int visibleItemCount =
endIndex - startIndex;
int contentRows =
(int) Math.ceil(
visibleItemCount / 9.0
);
contentRows =
Math.max(contentRows, 1);
int totalRows =
contentRows + 1;
int inventorySize =
totalRows * 9;
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"ADULTFILTER"
),
inventorySize,
MENU_TITLE
+ " (Page "
+ (page + 1)
+ ")"
);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
List<ItemStack> filterItems =
new ArrayList<>();
for (int i = startIndex; i < endIndex; i++) {
String mobName = mobs.get(i);
MobFilterData data =
mobData.get(mobName);
boolean forcedEnabled =
data.isKillAll();
boolean enabled =
forcedEnabled
|| !disabledMobs.contains(
mobName
);
ItemStack item =
new ItemStack(
enabled
? data.getIcon()
: Material.RED_STAINED_GLASS_PANE
);
ItemMeta meta =
item.getItemMeta();
if (meta != null) {
meta.setDisplayName(
enabled
? ChatColor.GREEN + mobName
: ChatColor.YELLOW
+ mobName
+ " (Disabled)"
);
java.util.List<String> lore =
new java.util.ArrayList<>();
if (forcedEnabled) {
lore.add(
ChatColor.YELLOW
+ "Overridden by Server Admin"
);
}
meta.setLore(lore);
item.setItemMeta(meta);
}
filterItems.add(item);
}
int currentRow = 0;
int rowStart = 0;
while (rowStart < filterItems.size()) {
int remainingItems =
filterItems.size() - rowStart;
int itemsThisRow =
Math.min(9, remainingItems);
boolean isLastPartialRow =
itemsThisRow < 9;
int rowBaseSlot =
currentRow * 9;
List<Integer> rowSlots =
new ArrayList<>();
if (isLastPartialRow) {
if (itemsThisRow % 2 == 0) {
switch (itemsThisRow) {
case 2 -> rowSlots = List.of(3, 5);
case 4 -> rowSlots = List.of(2, 3, 5, 6);
case 6 -> rowSlots = List.of(1, 2, 3, 5, 6, 7);
case 8 -> rowSlots = List.of(0, 1, 2, 3, 5, 6, 7, 8);
}
} else {
int startSlot =
(9 - itemsThisRow) / 2;
for (int i = 0; i < itemsThisRow; i++) {
rowSlots.add(startSlot + i);
}
}
} else {
for (int i = 0; i < 9; i++) {
rowSlots.add(i);
}
}
for (int i = 0; i < itemsThisRow; i++) {
inventory.setItem(
rowBaseSlot + rowSlots.get(i),
filterItems.get(rowStart + i)
);
}
rowStart += itemsThisRow;
currentRow++;
}
int navigationRow =
inventorySize - 9;
// Previous page
if (page > 0) {
ItemStack previous =
new ItemStack(Material.ARROW);
ItemMeta previousMeta =
previous.getItemMeta();
if (previousMeta != null) {
previousMeta.setDisplayName(
ChatColor.YELLOW
+ "Previous Page"
);
previous.setItemMeta(previousMeta);
}
inventory.setItem(
navigationRow,
previous
);
}
// Back button
ItemStack back =
new ItemStack(Material.OAK_DOOR);
ItemMeta backMeta =
back.getItemMeta();
if (backMeta != null) {
backMeta.setDisplayName(
ChatColor.YELLOW + "Back"
);
back.setItemMeta(backMeta);
}
inventory.setItem(
navigationRow + 4,
back
);
// Next page
if (endIndex < mobs.size()) {
ItemStack next =
new ItemStack(Material.ARROW);
ItemMeta nextMeta =
next.getItemMeta();
if (nextMeta != null) {
nextMeta.setDisplayName(
ChatColor.YELLOW
+ "Next Page"
);
next.setItemMeta(nextMeta);
}
inventory.setItem(
navigationRow + 8,
next
);
}
player.openInventory(inventory);
}
}
@@ -0,0 +1,326 @@
package com.rsscripting.mobstand.gui;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.data.MobFilterData;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class BabyFilterMenu {
public static final String MENU_TITLE =
ChatColor.BLUE + "Baby Filters";
private static final int ITEMS_PER_PAGE = 45;
public static void open(
RSMobStandPlugin plugin,
Player player,
ArmorStand armorStand,
int page
) {
Map<String, MobFilterData> mobData =
plugin.getConfigManager()
.getBabyMobData();
List<String> mobs =
new ArrayList<>(mobData.keySet());
Collections.sort(mobs);
List<String> disabledMobs =
plugin.getStandManager()
.getDisabledBabyMobs(
armorStand
);
int startIndex =
page * ITEMS_PER_PAGE;
int endIndex =
Math.min(
startIndex + ITEMS_PER_PAGE,
mobs.size()
);
int visibleItemCount =
endIndex - startIndex;
int contentRows =
(int) Math.ceil(
visibleItemCount / 9.0
);
contentRows =
Math.max(contentRows, 1);
int totalRows =
contentRows + 1;
int inventorySize =
totalRows * 9;
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"BABYFILTER"
),
inventorySize,
MENU_TITLE
+ " (Page "
+ (page + 1)
+ ")"
);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
List<ItemStack> filterItems =
new ArrayList<>();
for (int i = startIndex; i < endIndex; i++) {
String mobName = mobs.get(i);
MobFilterData data =
mobData.get(mobName);
boolean forcedEnabled =
data.isKillAll();
boolean enabled =
forcedEnabled
|| !disabledMobs.contains(
mobName
);
ItemStack item =
new ItemStack(
enabled
? data.getIcon()
: Material.RED_STAINED_GLASS_PANE
);
ItemMeta meta =
item.getItemMeta();
if (meta != null) {
meta.setDisplayName(
enabled
? ChatColor.GREEN + mobName
: ChatColor.YELLOW
+ mobName
+ " (Disabled)"
);
java.util.List<String> lore =
new java.util.ArrayList<>();
if (forcedEnabled) {
lore.add(
ChatColor.YELLOW
+ "Overridden by Server Admin"
);
}
meta.setLore(lore);
item.setItemMeta(meta);
}
filterItems.add(item);
}
int currentRow = 0;
int rowStart = 0;
while (rowStart < filterItems.size()) {
int remainingItems =
filterItems.size() - rowStart;
int itemsThisRow =
Math.min(9, remainingItems);
boolean isLastPartialRow =
itemsThisRow < 9;
int rowBaseSlot =
currentRow * 9;
List<Integer> rowSlots =
new ArrayList<>();
if (isLastPartialRow) {
if (itemsThisRow % 2 == 0) {
switch (itemsThisRow) {
case 2 -> rowSlots = List.of(3, 5);
case 4 -> rowSlots = List.of(2, 3, 5, 6);
case 6 -> rowSlots = List.of(1, 2, 3, 5, 6, 7);
case 8 -> rowSlots = List.of(0, 1, 2, 3, 5, 6, 7, 8);
}
} else {
int startSlot =
(9 - itemsThisRow) / 2;
for (int i = 0; i < itemsThisRow; i++) {
rowSlots.add(startSlot + i);
}
}
} else {
for (int i = 0; i < 9; i++) {
rowSlots.add(i);
}
}
for (int i = 0; i < itemsThisRow; i++) {
inventory.setItem(
rowBaseSlot + rowSlots.get(i),
filterItems.get(rowStart + i)
);
}
rowStart += itemsThisRow;
currentRow++;
}
int navigationRow =
inventorySize - 9;
// Previous page
if (page > 0) {
ItemStack previous =
new ItemStack(Material.ARROW);
ItemMeta previousMeta =
previous.getItemMeta();
if (previousMeta != null) {
previousMeta.setDisplayName(
ChatColor.YELLOW
+ "Previous Page"
);
previous.setItemMeta(previousMeta);
}
inventory.setItem(
navigationRow,
previous
);
}
// Back button
ItemStack back =
new ItemStack(Material.OAK_DOOR);
ItemMeta backMeta =
back.getItemMeta();
if (backMeta != null) {
backMeta.setDisplayName(
ChatColor.YELLOW + "Back"
);
back.setItemMeta(backMeta);
}
inventory.setItem(
navigationRow + 4,
back
);
// Next page
if (endIndex < mobs.size()) {
ItemStack next =
new ItemStack(Material.ARROW);
ItemMeta nextMeta =
next.getItemMeta();
if (nextMeta != null) {
nextMeta.setDisplayName(
ChatColor.YELLOW
+ "Next Page"
);
next.setItemMeta(nextMeta);
}
inventory.setItem(
navigationRow + 8,
next
);
}
player.openInventory(inventory);
}
}
@@ -0,0 +1,77 @@
package com.rsscripting.mobstand.gui;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Collections;
public class ConversionMenu {
public static final String MENU_TITLE =
ChatColor.BLUE
+ "Convert to RS-MobStand";
public static void open(Player player) {
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"CONVERT"
),
9,
MENU_TITLE
);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
ItemStack convertItem = new ItemStack(Material.CRAFTING_TABLE);
ItemMeta meta = convertItem.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.GREEN + "Convert to RS MobStand");
meta.setLore(Collections.singletonList(
ChatColor.YELLOW + "Convert this armor stand into an RS MobStand."
));
meta.addItemFlags(
ItemFlag.HIDE_ATTRIBUTES
);
convertItem.setItemMeta(meta);
}
inventory.setItem(4, convertItem);
player.openInventory(inventory);
}
}
@@ -0,0 +1,181 @@
package com.rsscripting.mobstand.gui;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.List;
public class LeaveAliveMenu {
public static final String MENU_TITLE =
ChatColor.BLUE
+ "Leave Alive: ";
public void open(
Player player,
int leaveAlive
) {
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"LEAVEALIVE"
),
18,
MENU_TITLE
+ leaveAlive
);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
inventory.setItem(
4,
createItem(
Material.OAK_DOOR,
"§eBack",
List.of(
"§7Return to previous menu."
),
1
)
);
inventory.setItem(
10,
createItem(
Material.RED_WOOL,
"§cRemove 10",
List.of(
"§7Decrease by 10."
),
10
)
);
inventory.setItem(
11,
createItem(
Material.RED_WOOL,
"§cRemove 5",
List.of(
"§7Decrease by 5."
),
5
)
);
inventory.setItem(
12,
createItem(
Material.RED_WOOL,
"§cRemove 1",
List.of(
"§7Decrease by 1."
),
1
)
);
inventory.setItem(
13,
createItem(
Material.GRAY_STAINED_GLASS_PANE,
" ",
List.of(),
1
)
);
inventory.setItem(
14,
createItem(
Material.GREEN_WOOL,
"§aAdd 1",
List.of(
"§7Increase by 1."
),
1
)
);
inventory.setItem(
15,
createItem(
Material.GREEN_WOOL,
"§aAdd 5",
List.of(
"§7Increase by 5."
),
5
)
);
inventory.setItem(
16,
createItem(
Material.GREEN_WOOL,
"§aAdd 10",
List.of(
"§7Increase by 10."
),
10
)
);
player.openInventory(inventory);
}
private ItemStack createItem(
Material material,
String name,
List<String> lore,
int amount
) {
ItemStack item =
new ItemStack(
material,
amount
);
ItemMeta meta =
item.getItemMeta();
if (meta == null) {
return item;
}
meta.setDisplayName(name);
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
}
@@ -0,0 +1,225 @@
package com.rsscripting.mobstand.gui;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.utils.VersionChecker;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Collections;
public class RSAdminMenu {
public static final String MENU_TITLE =
ChatColor.GOLD + "Admin";
public static void open(
Player player
) {
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"ADMIN"
),
18,
MENU_TITLE
);
/*
|--------------------------------------------------------------------------
| FILLER
|--------------------------------------------------------------------------
*/
ItemStack filler =
new ItemStack(
Material.WHITE_STAINED_GLASS_PANE
);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
/*
|--------------------------------------------------------------------------
| RELOAD CONFIG
|--------------------------------------------------------------------------
*/
ItemStack reloadItem =
new ItemStack(
Material.ENCHANTED_BOOK
);
ItemMeta reloadMeta =
reloadItem.getItemMeta();
if (reloadMeta != null) {
reloadMeta.setDisplayName(
ChatColor.GREEN
+ "Reload Config"
);
reloadMeta.setLore(
Collections.singletonList(
ChatColor.GRAY
+ "Reload plugin configuration."
)
);
reloadMeta.addItemFlags(
ItemFlag.HIDE_ENCHANTS
);
reloadItem.setItemMeta(
reloadMeta
);
}
inventory.setItem(
12,
reloadItem
);
/*
|--------------------------------------------------------------------------
| Back Button
|--------------------------------------------------------------------------
*/
ItemStack backItem =
new ItemStack(
Material.OAK_DOOR
);
ItemMeta backMeta =
backItem.getItemMeta();
if (backMeta != null) {
backMeta.setDisplayName(
ChatColor.YELLOW
+ "Back"
);
backItem.setItemMeta(
backMeta
);
}
inventory.setItem(
4,
backItem
);
/*
|--------------------------------------------------------------------------
| VERSION
|--------------------------------------------------------------------------
*/
ItemStack versionItem =
new ItemStack(
Material.BOOK
);
ItemMeta versionMeta =
versionItem.getItemMeta();
if (versionMeta != null) {
versionMeta.setDisplayName(
ChatColor.AQUA
+ "Plugin Version"
);
String currentVersion =
Bukkit.getPluginManager()
.getPlugin("RS-MobStand")
.getDescription() .getVersion();
String latestVersion =
VersionChecker.getLatestVersion();
java.util.List<String> lore =
new java.util.ArrayList<>();
lore.add(
ChatColor.GRAY
+ "Current Version: "
+ ChatColor.WHITE
+ currentVersion
);
if (!currentVersion.equalsIgnoreCase(
latestVersion
)) {
lore.add(
ChatColor.GRAY
+ "Latest Version: "
+ ChatColor.WHITE
+ latestVersion
);
lore.add("");
lore.add(
ChatColor.GREEN
+ "Update Available!"
);
}
else {
lore.add(
ChatColor.YELLOW
+ "Plugin Up To Date"
);
}
versionMeta.setLore(lore);
versionItem.setItemMeta(
versionMeta
);
}
inventory.setItem(
14,
versionItem
);
player.openInventory(
inventory
);
}
}
@@ -0,0 +1,32 @@
package com.rsscripting.mobstand.gui;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
public class RSMenuHolder
implements InventoryHolder {
private final String menuId;
public RSMenuHolder(
String menuId
) {
this.menuId = menuId;
}
public String getMenuId() {
return menuId;
}
@Override
public Inventory getInventory() {
return null;
}
}
@@ -0,0 +1,179 @@
package com.rsscripting.mobstand.gui;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.ChatColor;
import java.util.List;
public class RadiusMenu {
public static final String MENU_TITLE =
ChatColor.BLUE
+ "Radius: ";
public void open(
Player player,
int radius
) {
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"RADIUS"
),
18,
MENU_TITLE + radius
);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
inventory.setItem(
4,
createItem(
Material.OAK_DOOR,
"§eBack",
List.of(
"§7Return to previous menu."
),
1
)
);
inventory.setItem(
10,
createItem(
Material.RED_WOOL,
"§cRemove 10",
List.of(
"§7Decrease radius by 10."
),
10
)
);
inventory.setItem(
11,
createItem(
Material.RED_WOOL,
"§cRemove 5",
List.of(
"§7Decrease radius by 5."
),
5
)
);
inventory.setItem(
12,
createItem(
Material.RED_WOOL,
"§cRemove 1",
List.of(
"§7Decrease radius by 1."
),
1
)
);
inventory.setItem(
13,
createItem(
Material.GRAY_STAINED_GLASS_PANE,
" ",
List.of(),
1
)
);
inventory.setItem(
14,
createItem(
Material.GREEN_WOOL,
"§aAdd 1",
List.of(
"§7Increase radius by 1."
),
1
)
);
inventory.setItem(
15,
createItem(
Material.GREEN_WOOL,
"§aAdd 5",
List.of(
"§7Increase radius by 5."
),
5
)
);
inventory.setItem(
16,
createItem(
Material.GREEN_WOOL,
"§aAdd 10",
List.of(
"§7Increase radius by 10."
),
10
)
);
player.openInventory(inventory);
}
private ItemStack createItem(
Material material,
String name,
List<String> lore,
int amount
) {
ItemStack item =
new ItemStack(
material,
amount
);
ItemMeta meta =
item.getItemMeta();
if (meta == null) {
return item;
}
meta.setDisplayName(name);
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
}
@@ -0,0 +1,369 @@
package com.rsscripting.mobstand.gui;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.managers.StandManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import java.util.Collections;
import java.util.List;
public class StandMenu {
public static final String MENU_TITLE =
ChatColor.BLUE + "RS MobStand";
public static void open(
RSMobStandPlugin plugin,
Player player,
ArmorStand armorStand
) {
Inventory inventory =
Bukkit.createInventory(
new RSMenuHolder(
"MAIN"
),
18,
MENU_TITLE
);
StandManager standManager =
plugin.getStandManager();
double radius =
standManager.getRadius(armorStand);
int maxRadius =
plugin.getConfig()
.getInt(
"max-player-scan-radius"
);
radius =
Math.min(
radius,
maxRadius
);
standManager.setRadius(
armorStand,
radius
);
boolean gravity =
standManager.hasGravity(armorStand);
boolean paused =
standManager.isPaused(armorStand);
ItemStack filler =
new ItemStack(Material.WHITE_STAINED_GLASS_PANE);
ItemMeta fillerMeta =
filler.getItemMeta();
if (fillerMeta != null) {
fillerMeta.setDisplayName(" ");
filler.setItemMeta(fillerMeta);
}
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, filler);
}
// Convert back item
ItemStack revertItem =
new ItemStack(Material.COMPOSTER);
ItemMeta revertMeta =
revertItem.getItemMeta();
if (revertMeta != null) {
revertMeta.setDisplayName(
ChatColor.RED
+ "Convert Back to Armor Stand"
);
revertMeta.setLore(
Collections.singletonList(
ChatColor.GRAY
+ "Remove MobStand functionality."
)
);
revertItem.setItemMeta(revertMeta);
}
inventory.setItem(2, revertItem);
/*
|--------------------------------------------------------------------------
| ADMIN MENU
|--------------------------------------------------------------------------
*/
if (player.isOp()) {
ItemStack adminItem =
new ItemStack(
Material.COMMAND_BLOCK
);
ItemMeta adminMeta =
adminItem.getItemMeta();
if (adminMeta != null) {
adminMeta.setDisplayName(
ChatColor.GOLD
+ "Admin"
);
adminMeta.setLore(
java.util.Collections.singletonList(
"§7Administrative options."
)
);
adminItem.setItemMeta(
adminMeta
);
}
inventory.setItem(
4,
adminItem
);
}
// Pause item
ItemStack pauseItem =
new ItemStack(
paused
? Material.RED_DYE
: Material.GREEN_DYE
);
ItemMeta pauseMeta =
pauseItem.getItemMeta();
if (pauseMeta != null) {
pauseMeta.setDisplayName(
paused
? ChatColor.GREEN + "Enable Processing"
: ChatColor.RED + "Disable Processing"
);
pauseMeta.setLore(List.of(
paused
? ChatColor.GRAY
+ "Click to resume processing."
: ChatColor.GRAY
+ "Click to pause processing."
));
pauseMeta.addItemFlags(
ItemFlag.HIDE_ATTRIBUTES
);
pauseItem.setItemMeta(pauseMeta);
}
inventory.setItem(6, pauseItem);
// Adult filters item
ItemStack adultFilters =
new ItemStack(Material.BEEF);
ItemMeta adultMeta =
adultFilters.getItemMeta();
if (adultMeta != null) {
adultMeta.setDisplayName(
ChatColor.YELLOW
+ "Adult Filters"
);
adultMeta.setLore(List.of(
ChatColor.YELLOW
+ "Configure adult mob filters."
));
adultFilters.setItemMeta(adultMeta);
}
inventory.setItem(15, adultFilters);
// Baby filters item
ItemStack babyFilters =
new ItemStack(Material.BEE_SPAWN_EGG);
ItemMeta babyMeta =
babyFilters.getItemMeta();
if (babyMeta != null) {
babyMeta.setDisplayName(
ChatColor.YELLOW
+ "Baby Filters"
);
babyMeta.setLore(List.of(
ChatColor.YELLOW
+ "Configure baby mob filters."
));
babyFilters.setItemMeta(babyMeta);
}
inventory.setItem(17, babyFilters);
// Leave Alive item
int leaveAlive =
standManager.getLeaveAlive(
armorStand
);
ItemStack leaveAliveItem =
new ItemStack(
Material.TOTEM_OF_UNDYING
);
ItemMeta leaveAliveMeta =
leaveAliveItem.getItemMeta();
if (leaveAliveMeta != null) {
leaveAliveMeta.setDisplayName(
ChatColor.YELLOW
+ "Leave Alive:"
);
leaveAliveMeta.setLore(List.of(
ChatColor.GRAY
+ "Current: "
+ ChatColor.WHITE
+ leaveAlive,
"",
ChatColor.YELLOW
+ "Click to adjust."
));
leaveAliveItem.setItemMeta(leaveAliveMeta);
}
inventory.setItem(13, leaveAliveItem);
// Radius item
ItemStack radiusItem =
new ItemStack(
Material.COMPASS
);
ItemMeta radiusMeta =
radiusItem.getItemMeta();
if (radiusMeta != null) {
radiusMeta.setDisplayName(
ChatColor.YELLOW
+ "Scan Radius"
);
radiusMeta.setLore(List.of(
ChatColor.GRAY
+ "Current: "
+ ChatColor.WHITE
+ (int) radius,
"",
ChatColor.YELLOW
+ "Click to adjust radius."
));
radiusItem.setItemMeta(radiusMeta);
}
inventory.setItem(9, radiusItem);
ItemStack gravityItem =
new ItemStack(Material.FEATHER);
ItemMeta gravityMeta =
gravityItem.getItemMeta();
if (gravityMeta != null) {
gravityMeta.setDisplayName(
(gravity
? ChatColor.GREEN
: ChatColor.RED)
+ "Gravity"
);
gravityMeta.setLore(List.of(
ChatColor.GRAY + "Current: "
+ (gravity
? ChatColor.GREEN + "Enabled"
: ChatColor.RED + "Disabled"),
"",
ChatColor.YELLOW + "Left Click: Toggle"
));
// Add enchant glow if gravity enabled
if (gravity) {
gravityMeta.addEnchant(
Enchantment.UNBREAKING,
1,
true
);
gravityMeta.addItemFlags(
ItemFlag.HIDE_ENCHANTS
);
}
gravityItem.setItemMeta(gravityMeta);
}
inventory.setItem(11, gravityItem);
player.openInventory(inventory);
}
}
@@ -0,0 +1,39 @@
package com.rsscripting.mobstand.listeners;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.managers.StandManager;
import org.bukkit.entity.ArmorStand;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
public class ArmorStandBreakListener implements Listener {
private final StandManager standManager;
public ArmorStandBreakListener(RSMobStandPlugin plugin) {
this.standManager = plugin.getStandManager();
}
@EventHandler
public void onArmorStandDamage(EntityDamageEvent event) {
// Must be armor stand
if (!(event.getEntity() instanceof ArmorStand armorStand)) {
return;
}
// Only manage MobStands
if (!standManager.isMobStand(armorStand)) {
return;
}
// Completely block ALL vanilla damage
event.setCancelled(true);
}
}
@@ -0,0 +1,113 @@
package com.rsscripting.mobstand.listeners;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.gui.ConversionMenu;
import com.rsscripting.mobstand.gui.StandMenu;
import com.rsscripting.mobstand.managers.StandManager;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.inventory.ItemStack;
public class ArmorStandListener implements Listener {
private final StandManager standManager;
private final RSMobStandPlugin plugin;
public ArmorStandListener(
RSMobStandPlugin plugin,
StandManager standManager
) {
this.plugin = plugin;
this.standManager = standManager;
}
@EventHandler
public void onArmorStandInteract(PlayerInteractAtEntityEvent event) {
// Only armor stands
if (!(event.getRightClicked() instanceof ArmorStand armorStand)) {
return;
}
Player player = event.getPlayer();
// Must be sneaking
if (!player.isSneaking()) {
return;
}
ItemStack item =
player.getInventory()
.getItemInMainHand();
if (item == null ||
item.getType() != Material.STICK) {
return;
}
// Existing MobStand
if (standManager.isMobStand(armorStand)) {
// Track selected stand
standManager.setSelectedArmorStand(
player.getUniqueId(),
armorStand.getUniqueId()
);
// Owner check
if (!standManager.isOwner(
armorStand,
player
)
&& !player.hasPermission(
"rsmobstand.admin"
)) {
player.sendMessage(
ChatColor.RED
+ "You do not own this MobStand."
);
return;
}
// Cancel normal interaction
event.setCancelled(true);
StandMenu.open(
plugin,
player,
armorStand
);
return;
}
// Track selected stand
standManager.setSelectedArmorStand(
player.getUniqueId(),
armorStand.getUniqueId()
);
// Cancel normal interaction
event.setCancelled(true);
// Open conversion menu
ConversionMenu.open(player);
}
}
@@ -0,0 +1,47 @@
package com.rsscripting.mobstand.listeners;
import com.rsscripting.mobstand.managers.StandManager;
import org.bukkit.Chunk;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
public class ChunkLoadListener implements Listener {
private final StandManager standManager;
public ChunkLoadListener(StandManager standManager) {
this.standManager = standManager;
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
for (Entity entity : chunk.getEntities()) {
if (!(entity instanceof ArmorStand armorStand)) {
continue;
}
if (!standManager.isMobStand(armorStand)) {
continue;
}
armorStand.setInvulnerable(true);
standManager.getActiveStands().add(
armorStand.getUniqueId()
);
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,794 @@
package com.rsscripting.mobstand.managers;
import com.rsscripting.mobstand.RSMobStandPlugin;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.ArmorStand;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Set;
import java.util.HashSet;
public class StandManager {
private final RSMobStandPlugin plugin;
private final NamespacedKey mobStandKey;
private final NamespacedKey ownerKey;
private final NamespacedKey radiusKey;
private final NamespacedKey gravityKey;
private final NamespacedKey pausedKey;
private final NamespacedKey disabledAdultMobsKey;
private final NamespacedKey disabledBabyMobsKey;
private final Map<UUID, UUID> selectedArmorStands = new HashMap<>();
private final NamespacedKey leaveAliveKey;
private final Set<UUID> activeStands = new HashSet<>();
public StandManager(RSMobStandPlugin plugin) {
this.plugin = plugin;
this.mobStandKey = new NamespacedKey(plugin, "mobstand");
this.ownerKey = new NamespacedKey(plugin, "owner");
this.radiusKey = new NamespacedKey(plugin, "radius");
this.leaveAliveKey = new NamespacedKey(plugin, "leaveAlive");
this.gravityKey = new NamespacedKey(plugin, "gravity");
this.pausedKey = new NamespacedKey(plugin, "paused");
this.disabledAdultMobsKey =
new NamespacedKey(
plugin,
"disabled_adult_mobs"
);
this.disabledBabyMobsKey =
new NamespacedKey(
plugin,
"disabled_baby_mobs"
);
}
// counts stands on server.
public int getPlayerStandCount(UUID ownerUUID) {
int count = 0;
for (UUID standUUID : activeStands) {
Entity entity =
Bukkit.getEntity(standUUID);
if (!(entity instanceof ArmorStand armorStand)) {
continue;
}
UUID standOwner =
getOwnerUUID(armorStand);
if (ownerUUID.equals(standOwner)) {
count++;
}
}
return count;
}
// Converts an armor stand into a MobStand.
public boolean convertToMobStand(ArmorStand armorStand, Player owner) {
if (!isArmorStandEmpty(armorStand)) {
owner.sendMessage(
ChatColor.RED
+ "Remove all equipment first."
);
return false;
}
if (!owner.hasPermission(
"rsmobstand.bypasslimits"
)) {
// Server limit
int maxTotalStands =
plugin.getConfig()
.getInt(
"max-total-stands"
);
if (maxTotalStands > 0 &&
activeStands.size()
>= maxTotalStands) {
owner.sendMessage(
ChatColor.RED
+ "Server mob stand limit reached."
);
return false;
}
// Player limit
int maxPlayerStands =
plugin.getConfig()
.getInt(
"max-stands-per-player"
);
int playerStandCount =
getPlayerStandCount(
owner.getUniqueId()
);
if (maxPlayerStands > 0 &&
playerStandCount
>= maxPlayerStands) {
owner.sendMessage(
ChatColor.RED
+ "You have reached your mob stand limit."
);
return false;
}
}
armorStand.getPersistentDataContainer().set(
pausedKey,
PersistentDataType.BYTE,
(byte) 0
);
armorStand.getPersistentDataContainer().set(
mobStandKey,
PersistentDataType.BYTE,
(byte) 1
);
armorStand.getPersistentDataContainer().set(
ownerKey,
PersistentDataType.STRING,
owner.getUniqueId().toString()
);
double defaultRadius = plugin.getConfig().getDouble(
"default-scan-radius"
);
armorStand.getPersistentDataContainer().set(
radiusKey,
PersistentDataType.DOUBLE,
defaultRadius
);
boolean defaultGravity = plugin.getConfig().getBoolean(
"default-gravity"
);
int defaultLeaveAlive =
plugin.getConfig().getInt(
"default-leave-alive"
);
armorStand.getPersistentDataContainer().set(
leaveAliveKey,
PersistentDataType.INTEGER,
defaultLeaveAlive
);
armorStand.getPersistentDataContainer().set(
gravityKey,
PersistentDataType.BYTE,
(byte) (defaultGravity ? 1 : 0)
);
armorStand.setGravity(defaultGravity);
armorStand.setCustomName("§cRS MobStand");
armorStand.setCustomNameVisible(true);
armorStand.setInvulnerable(true);
activeStands.add(armorStand.getUniqueId());
// Initialize adult disabled filters
List<String> disabledAdultMobs =
new ArrayList<>();
plugin.getConfigManager()
.getAdultMobData()
.forEach((mobName, data) -> {
if (!data.isEnabledByDefault()) {
disabledAdultMobs.add(
mobName
);
}
});
setDisabledAdultMobs(
armorStand,
disabledAdultMobs
);
// Initialize baby disabled filters
List<String> disabledBabyMobs =
new ArrayList<>();
plugin.getConfigManager()
.getBabyMobData()
.forEach((mobName, data) -> {
if (!data.isEnabledByDefault()) {
disabledBabyMobs.add(
mobName
);
}
});
setDisabledBabyMobs(
armorStand,
disabledBabyMobs
);
return true;
}
// Removes MobStand data.
public void revertToNormalStand(ArmorStand armorStand) {
armorStand.getPersistentDataContainer().remove(mobStandKey);
armorStand.getPersistentDataContainer().remove(radiusKey);
armorStand.getPersistentDataContainer().remove(gravityKey);
armorStand.getPersistentDataContainer().remove(pausedKey);
armorStand.getPersistentDataContainer().remove(leaveAliveKey);
armorStand.getPersistentDataContainer().remove(disabledAdultMobsKey);
armorStand.getPersistentDataContainer().remove(disabledBabyMobsKey);
clearOwnership(armorStand);
activeStands.remove(armorStand.getUniqueId());
armorStand.setInvulnerable(false);
armorStand.setCustomName(null);
armorStand.setCustomNameVisible(false);
armorStand.setGravity(true);
}
// Checks if armor stand is a MobStand.
public boolean isMobStand(ArmorStand armorStand) {
Byte value = armorStand.getPersistentDataContainer().get(
mobStandKey,
PersistentDataType.BYTE
);
return value != null && value == (byte) 1;
}
// Stores selected armor stand for player.
public void setSelectedArmorStand(UUID playerUUID, UUID armorStandUUID) {
selectedArmorStands.put(playerUUID, armorStandUUID);
}
// Gets selected armor stand UUID.
public UUID getSelectedArmorStand(UUID playerUUID) {
return selectedArmorStands.get(playerUUID);
}
// Removes selected armor stand tracking.
public void clearSelectedArmorStand(UUID playerUUID) {
selectedArmorStands.remove(playerUUID);
}
// Checks if player owns this stand.
public boolean isOwner(ArmorStand armorStand, Player player) {
String ownerUUID = armorStand.getPersistentDataContainer().get(
ownerKey,
PersistentDataType.STRING
);
if (ownerUUID == null) {
return false;
}
return ownerUUID.equals(player.getUniqueId().toString());
}
public UUID getOwnerUUID(
ArmorStand armorStand
) {
String ownerUUID =
armorStand
.getPersistentDataContainer()
.get(
ownerKey,
PersistentDataType.STRING
);
if (ownerUUID == null) {
return null;
}
return UUID.fromString(ownerUUID);
}
// Removes ownership data.
public void clearOwnership(ArmorStand armorStand) {
armorStand.getPersistentDataContainer().remove(ownerKey);
}
// Gets all active Mob stands.
public Set<UUID> getActiveStands() {
return activeStands;
}
// Reloads all MobStands from loaded worlds.
public void loadExistingStands() {
activeStands.clear();
for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntities()) {
// Must be armor stand
if (!(entity instanceof ArmorStand armorStand)) {
continue;
}
Byte storedValue = armorStand.getPersistentDataContainer().get(
mobStandKey,
PersistentDataType.BYTE
);
// Must be MobStand
if (!isMobStand(armorStand)) {
continue;
}
// Restore invulnerability
armorStand.setInvulnerable(true);
// Re-register stand
activeStands.add(armorStand.getUniqueId());
}
}
plugin.getLogger().info(
"Recovered "
+ activeStands.size()
+ " MobStands."
);
}
public int getLeaveAlive(
ArmorStand armorStand
) {
Integer leaveAlive =
armorStand
.getPersistentDataContainer()
.get(
leaveAliveKey,
PersistentDataType.INTEGER
);
if (leaveAlive == null) {
return plugin.getConfig().getInt(
"default-leave-alive"
);
}
return leaveAlive;
}
public double getRadius(ArmorStand armorStand) {
Double radius = armorStand.getPersistentDataContainer().get(
radiusKey,
PersistentDataType.DOUBLE
);
if (radius == null) {
return plugin.getConfig().getDouble(
"default-scan-radius"
);
}
return radius;
}
public void setRadius(
ArmorStand armorStand,
double radius
) {
double maxRadius = plugin.getConfig().getDouble(
"max-player-scan-radius"
);
// Clamp radius
if (radius < 0) {
radius = 0;
}
if (radius > maxRadius) {
radius = maxRadius;
}
armorStand.getPersistentDataContainer().set(
radiusKey,
PersistentDataType.DOUBLE,
radius
);
}
public void setLeaveAlive(
ArmorStand armorStand,
int leaveAlive
) {
int minLeaveAlive =
plugin.getConfig()
.getInt(
"min-leave-alive"
);
int maxLeaveAlive =
plugin.getConfig()
.getInt(
"max-leave-alive"
);
// Clamp leaveAlive
if (leaveAlive < minLeaveAlive) {
leaveAlive = minLeaveAlive;
}
if (leaveAlive > maxLeaveAlive) {
leaveAlive = maxLeaveAlive;
}
armorStand.getPersistentDataContainer().set(
leaveAliveKey,
PersistentDataType.INTEGER,
leaveAlive
);
}
public boolean hasGravity(ArmorStand armorStand) {
Byte value = armorStand.getPersistentDataContainer().get(
gravityKey,
PersistentDataType.BYTE
);
if (value == null) {
return plugin.getConfig().getBoolean(
"default-gravity"
);
}
return value == (byte) 1;
}
public void setGravity(
ArmorStand armorStand,
boolean gravity
) {
armorStand.getPersistentDataContainer().set(
gravityKey,
PersistentDataType.BYTE,
(byte) (gravity ? 1 : 0)
);
armorStand.setGravity(gravity);
}
public boolean isPaused(ArmorStand armorStand) {
Byte value = armorStand.getPersistentDataContainer().get(
pausedKey,
PersistentDataType.BYTE
);
if (value == null) {
return false;
}
return value == (byte) 1;
}
public void setPaused(
ArmorStand armorStand,
boolean paused
) {
armorStand.getPersistentDataContainer().set(
pausedKey,
PersistentDataType.BYTE,
(byte) (paused ? 1 : 0)
);
}
// Get disabled Adult Mobs
public List<String> getDisabledAdultMobs(
ArmorStand armorStand
) {
String stored =
armorStand
.getPersistentDataContainer()
.get(
disabledAdultMobsKey,
PersistentDataType.STRING
);
if (stored == null || stored.isEmpty()) {
return new ArrayList<>();
}
return new ArrayList<>(
Arrays.asList(
stored.split(",")
)
);
}
public List<String> getDisabledBabyMobs(
ArmorStand armorStand
) {
String stored =
armorStand
.getPersistentDataContainer()
.get(
disabledBabyMobsKey,
PersistentDataType.STRING
);
if (stored == null || stored.isEmpty()) {
return new ArrayList<>();
}
return new ArrayList<>(
Arrays.asList(
stored.split(",")
)
);
}
public void setDisabledAdultMobs(
ArmorStand armorStand,
List<String> mobs
) {
String stored =
String.join(",", mobs);
armorStand
.getPersistentDataContainer()
.set(
disabledAdultMobsKey,
PersistentDataType.STRING,
stored
);
}
public void setDisabledBabyMobs(
ArmorStand armorStand,
List<String> mobs
) {
String stored =
String.join(",", mobs);
armorStand
.getPersistentDataContainer()
.set(
disabledBabyMobsKey,
PersistentDataType.STRING,
stored
);
}
public void toggleAdultMobFilter(
ArmorStand armorStand,
String mobName
) {
List<String> disabled =
getDisabledAdultMobs(
armorStand
);
if (disabled.contains(mobName)) {
disabled.remove(mobName);
} else {
disabled.add(mobName);
}
setDisabledAdultMobs(
armorStand,
disabled
);
}
private boolean isArmorStandEmpty(
ArmorStand armorStand
) {
return armorStand.getEquipment()
.getHelmet()
.getType()
.isAir()
&& armorStand.getEquipment()
.getChestplate()
.getType()
.isAir()
&& armorStand.getEquipment()
.getLeggings()
.getType()
.isAir()
&& armorStand.getEquipment()
.getBoots()
.getType()
.isAir()
&& armorStand.getEquipment()
.getItemInMainHand()
.getType()
.isAir()
&& armorStand.getEquipment()
.getItemInOffHand()
.getType()
.isAir();
}
public void toggleBabyMobFilter(
ArmorStand armorStand,
String mobName
) {
List<String> disabled =
getDisabledBabyMobs(
armorStand
);
if (disabled.contains(mobName)) {
disabled.remove(mobName);
} else {
disabled.add(mobName);
}
setDisabledBabyMobs(
armorStand,
disabled
);
}
}
@@ -0,0 +1,380 @@
package com.rsscripting.mobstand.tasks;
import com.rsscripting.mobstand.RSMobStandPlugin;
import com.rsscripting.mobstand.managers.StandManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.EntityType;
import org.bukkit.Location;
import java.util.UUID;
import java.util.List;
public class SlaughterTask extends BukkitRunnable {
private final RSMobStandPlugin plugin;
private final StandManager standManager;
public SlaughterTask(RSMobStandPlugin plugin) {
this.plugin = plugin;
this.standManager = plugin.getStandManager();
}
@Override
public void run() {
long start = System.nanoTime();
boolean debugEnabled =
plugin.getConfig().getBoolean("debug.enabled");
boolean performanceLogging =
plugin.getConfig().getBoolean(
"debug.logging.performance"
);
boolean entityKillLogging =
plugin.getConfig().getBoolean(
"debug.logging.entity-kills"
);
Integer killedThisCycle =
entityKillLogging ? 0 : null;
Location lastStandLocation = null;
var configManager = plugin.getConfigManager();
var adultMobs =
configManager
.getAdultMobData()
.keySet();
var babyMobs =
configManager
.getBabyMobData()
.keySet();
for (UUID uuid : standManager.getActiveStands()) {
Entity entity = Bukkit.getEntity(uuid);
// Stand no longer exists
if (!(entity instanceof ArmorStand armorStand)) {
continue;
}
// Skip paused stands
if (standManager.isPaused(armorStand)) {
continue;
}
// Scan nearby entities
double radius = standManager.getRadius(armorStand);
int leaveAlive =
standManager.getLeaveAlive(
armorStand
);
lastStandLocation =
armorStand.getLocation();
java.util.Map<String, List<LivingEntity>> adultTargets =
new java.util.HashMap<>();
java.util.Map<String, List<LivingEntity>> babyTargets =
new java.util.HashMap<>();
List<LivingEntity> killAllTargets =
new java.util.ArrayList<>();
for (Entity nearby : armorStand.getNearbyEntities(
radius,
radius,
radius
)) {
// Ignore players
if (nearby instanceof Player) {
continue;
}
// Ignore armor stands
if (nearby instanceof ArmorStand) {
continue;
}
// Must be living entity
if (!(nearby instanceof LivingEntity livingEntity)) {
continue;
}
EntityType entityType = livingEntity.getType();
String entityName = entityType.name();
boolean killAll = false;
boolean shouldKill;
// Ageable mobs
if (livingEntity instanceof Ageable ageable) {
// Adult mob
if (ageable.isAdult()) {
List<String> disabledAdultMobs =
standManager.getDisabledAdultMobs(
armorStand
);
shouldKill =
!disabledAdultMobs.contains(
entityName
);
if (configManager
.getAdultMobData()
.containsKey(entityName)) {
killAll =
configManager
.getAdultMobData()
.get(entityName)
.isKillAll();
}
}
// Baby mob
else {
List<String> disabledBabyMobs =
standManager.getDisabledBabyMobs(
armorStand
);
shouldKill =
!disabledBabyMobs.contains(
entityName
);
if (configManager
.getBabyMobData()
.containsKey(entityName)) {
killAll =
configManager
.getBabyMobData()
.get(entityName)
.isKillAll();
}
}
}
// Non-ageable mobs
else {
List<String> disabledAdultMobs =
standManager.getDisabledAdultMobs(
armorStand
);
shouldKill =
!disabledAdultMobs.contains(
entityName
);
if (configManager
.getAdultMobData()
.containsKey(entityName)) {
killAll =
configManager
.getAdultMobData()
.get(entityName)
.isKillAll();
}
}
// Not allowed
if (!shouldKill) {
continue;
}
// Ignore named mobs
if (livingEntity.customName() != null) {
continue;
}
if (killAll) {
killAllTargets.add(livingEntity);
} else {
if (livingEntity instanceof Ageable ageable) {
if (ageable.isAdult()) {
adultTargets
.computeIfAbsent(
entityName,
k -> new java.util.ArrayList<>()
)
.add(livingEntity);
} else {
babyTargets
.computeIfAbsent(
entityName,
k -> new java.util.ArrayList<>()
)
.add(livingEntity);
}
} else {
adultTargets
.computeIfAbsent(
entityName,
k -> new java.util.ArrayList<>()
)
.add(livingEntity);
}
}
// Damage amount
double damage =
plugin.getConfig()
.getDouble(
"damage-amount"
);
// Kill all targets first
for (LivingEntity target : killAllTargets) {
target.damage(damage);
if (entityKillLogging) {
killedThisCycle++;
}
}
// Leave alive logic
// Adult targets
for (List<LivingEntity> mobList : adultTargets.values()) {
int allowedKills =
mobList.size() - leaveAlive;
if (allowedKills > 0) {
for (int i = 0; i < allowedKills; i++) {
LivingEntity target =
mobList.get(i);
target.damage(damage);
if (entityKillLogging) {
killedThisCycle++;
}
}
}
}
// Baby targets
for (List<LivingEntity> mobList : babyTargets.values()) {
int allowedKills =
mobList.size() - leaveAlive;
if (allowedKills > 0) {
for (int i = 0; i < allowedKills; i++) {
LivingEntity target =
mobList.get(i);
target.damage(damage);
if (entityKillLogging) {
killedThisCycle++;
}
}
}
}
}
}
long duration = System.nanoTime() - start;
double ms = duration / 1_000_000.0;
if (debugEnabled && performanceLogging) {
if (ms >= 5.0) {
Location location =
lastStandLocation;
if (location == null) {
return;
}
plugin.getLogger().warning(
"Slow cycle: "
+ String.format("%.2f", ms)
+ "ms"
+ (
entityKillLogging
? " | Killed: " + killedThisCycle
: ""
)
+ " | "
+ location.getWorld().getName()
+ " "
+ location.getBlockX()
+ " "
+ location.getBlockY()
+ " "
+ location.getBlockZ()
);
}
}
}
}
@@ -0,0 +1,148 @@
package com.rsscripting.mobstand.utils;
import com.rsscripting.mobstand.RSMobStandPlugin;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
public class VersionChecker {
private final RSMobStandPlugin plugin;
private static String latestVersion =
"Unknown";
private static final String VERSION_URL =
"https://raw.githubusercontent.com/russelburgraymond/RS-MobStand/main/version.txt";
public VersionChecker(
RSMobStandPlugin plugin
) {
this.plugin = plugin;
}
public void checkVersion() {
plugin.getServer()
.getScheduler()
.runTaskAsynchronously(
plugin,
() -> {
try {
/*
|--------------------------------------------------------------------------
| READ VERSION FILE
|--------------------------------------------------------------------------
*/
URL url =
new URL(
VERSION_URL
);
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
url.openStream()
)
);
latestVersion =
reader.readLine();
reader.close();
/*
|--------------------------------------------------------------------------
| CURRENT VERSION
|--------------------------------------------------------------------------
*/
String currentVersion =
plugin.getDescription()
.getVersion();
/*
|--------------------------------------------------------------------------
| UPDATE AVAILABLE
|--------------------------------------------------------------------------
*/
if (!currentVersion.equalsIgnoreCase(
latestVersion
)) {
plugin.getLogger().warning(
"================================================="
);
plugin.getLogger().warning(
"A new version of RS-MobStand is available!"
);
plugin.getLogger().warning(
"Current Version: "
+ currentVersion
);
plugin.getLogger().warning(
"Latest Version: "
+ latestVersion
);
plugin.getLogger().warning(
"================================================="
);
}
/*
|--------------------------------------------------------------------------
| UP TO DATE
|--------------------------------------------------------------------------
*/
else {
plugin.getLogger().info(
"RS-MobStand is up to date."
);
}
}
/*
|--------------------------------------------------------------------------
| FAILED
|--------------------------------------------------------------------------
*/
catch (Exception exception) {
plugin.getLogger().warning(
"Could not check for updates."
);
}
}
);
}
public static String getLatestVersion() {
return latestVersion;
}
}
+91
View File
@@ -0,0 +1,91 @@
# =========================================
# RS MobStand Configuration
# RS MobStand by Big_Pappa
# =========================================
config-version: 1
##########################################################
# Plugin Settings #
##########################################################
# How often MobStands scan for mobs. Measured in ticks.
# 20 ticks = 1 second
scan-interval: 1200
# Default gravity state for new MobStands.
# true = Stand will fall until sitting on top of a block
# false = Stand will not fall if block below it is broken.
default-gravity: true
# Damage dealt to mobs.
damage-amount: 1000.0
##########################################################
# Scan Settings #
# Recommended maximum radius: #
# 32 for normal servers #
# 48 for large/high-performance servers #
##########################################################
# Default radius new MobStands begin with.
default-scan-radius: 10
# Maximum radius players may set their MobStand to.
# Recommended maximum radius:
# 32 for normal servers
# 48 for large/high-performance servers
# Excessive values may impact performance heavily.
max-player-scan-radius: 32
##########################################################
# Stand Count Settings #
##########################################################
# Maximum number of stands a player may place at one time.
# 0 = Unlimited
max-stands-per-player: 5
# Maximum number of stands that may be placed on the server at one time.
# 0 = Unlimited
max-total-stands: 200
##########################################################
# Settings below are for leaving mobs alive by default. #
##########################################################
# Default setting when new stands are created.
default-leave-alive: 2
# Minimum amount users may set their stands to leave alive each
# scan and kill cycle. NOTE: Named mobs DO NOT count as the
# RS Mob Stand ignores those completely.
min-leave-alive: 0
# Minimum amount users may set their stands to leave alive each
# scan and kill cycle. NOTE: Named mobs DO NOT count as the
# RS Mob Stand ignores those completely.
max-leave-alive: 30
##########################################################
# Debugging #
# Debugging options below this line are intended for #
# development/testing and should be left disabled on #
# production servers. #
##########################################################
debug:
enabled: false # Override all options below.
logging:
performance: true # Enables performance log entries
entity-kills: true # Adds kill count to performance entries (Requires performance: true)
performance:
warning-ms: 100.0
summary-interval-seconds: 300
+131
View File
@@ -0,0 +1,131 @@
# =========================================
# RS MobStand Adult Mob Settings
# =========================================
# Add as many as you like. The menus will
# Center, add rows, and pagenate them
# automatically.
# =========================================
#==========================================
# available items
# icon: icon that will show for that mob in the menu
# enabled-by-default: wether or not will be killed by default
# kill-all: true Kills all these mob types regardless of user settings
# - Named mobs will still be ignored
#================================================================
# Kills all these mob types regardless of user settings
# kill-all: true
# - Named mobs will still be ignored
#================================================================
adult-mobs:
BLAZE:
icon: BLAZE_ROD
enabled-by-default: true
# true: Stand will kill all regardless of settings.
# false: (or not existing for mob) will follow players setting.
# Ignores per-stand settings.
kill-all: false
CAVE_SPIDER:
icon: SPIDER_EYE
enabled-by-default: true
CHICKEN:
icon: FEATHER
enabled-by-default: true
COW:
icon: LEATHER
enabled-by-default: true
CREEPER:
icon: GUNPOWDER
enabled-by-default: true
DROWNED:
icon: COPPER_INGOT
enabled-by-default: true
ENDERMAN:
icon: ENDER_PEARL
enabled-by-default: true
GHAST:
icon: GHAST_TEAR
enabled-by-default: true
GUARDIAN:
icon: PRISMARINE_SHARD
enabled-by-default: true
HOGLIN:
icon: PORKCHOP
enabled-by-default: true
HUSK:
icon: ROTTEN_FLESH
enabled-by-default: true
MAGMA_CUBE:
icon: MAGMA_CREAM
enabled-by-default: true
PHANTOM:
icon: PHANTOM_MEMBRANE
enabled-by-default: true
PIG:
icon: PORKCHOP
enabled-by-default: true
PIGLIN:
icon: GOLD_NUGGET
enabled-by-default: true
RABBIT:
icon: RABBIT_HIDE
enabled-by-default: true
SHEEP:
icon: WHITE_WOOL
enabled-by-default: true
SHULKER:
icon: SHULKER_SHELL
enabled-by-default: true
SILVERFISH:
icon: STONE
enabled-by-default: true
SKELETON:
icon: BONE
enabled-by-default: true
SLIME:
icon: SLIME_BALL
enabled-by-default: true
SPIDER:
icon: STRING
enabled-by-default: true
WITCH:
icon: GLASS_BOTTLE
enabled-by-default: true
ZOGLIN:
icon: ROTTEN_FLESH
enabled-by-default: true
ZOMBIE:
icon: ROTTEN_FLESH
enabled-by-default: true
ZOMBIE_VILLAGER:
icon: EMERALD
enabled-by-default: true
ZOMBIFIED_PIGLIN:
icon: GOLD_INGOT
enabled-by-default: true
+73
View File
@@ -0,0 +1,73 @@
# =========================================
# RS MobStand baby Mob Settings
# =========================================
# Add as many as you like. The menus will
# Center, add rows, and pagenate them
# automatically.
# =========================================
#==========================================
# available items
# icon: icon that will show for that mob in the menu
# enabled-by-default: wether or not will be killed by default
# kill-all: Kills all these mob types regardless of user settings
# - Named mobs will still be ignored
#================================================================
baby-mobs:
CHICKEN:
icon: FEATHER
enabled-by-default: false
# true: Stand will kill all regardless of players settings.
# false: (or not existing for mob) will follow players setting.
# Ignores per-stand settings.
kill-all: false
COW:
icon: LEATHER
enabled-by-default: false
DROWNED:
icon: COPPER_INGOT
enabled-by-default: true
HOGLIN:
icon: PORKCHOP
enabled-by-default: true
HUSK:
icon: ROTTEN_FLESH
enabled-by-default: true
PIG:
icon: PORKCHOP
enabled-by-default: false
PIGLIN:
icon: GOLD_NUGGET
enabled-by-default: true
RABBIT:
icon: RABBIT_HIDE
enabled-by-default: false
SHEEP:
icon: WHITE_WOOL
enabled-by-default: false
ZOGLIN:
icon: ROTTEN_FLESH
enabled-by-default: true
ZOMBIE:
icon: ROTTEN_FLESH
enabled-by-default: true
ZOMBIE_VILLAGER:
icon: EMERALD
enabled-by-default: true
ZOMBIFIED_PIGLIN:
icon: GOLD_INGOT
enabled-by-default: true
+27
View File
@@ -0,0 +1,27 @@
name: RS-MobStand
prefix: RS-MobStand
version: 1.0.0
main: com.rsscripting.mobstand.RSMobStandPlugin
api-version: '1.21'
author: RSScripting
commands:
rsms:
description: RS MobStand main command
usage: /rsms reload
permission: rsmobstand.admin
permissions:
rsmobstand.use:
description: Allows use of RS MobStand
default: true
rsmobstand.admin:
description: Admin access for RS MobStand
default: op
rsmobstand.bypasslimits:
description: Bypass MobStand placement limits
default: op
+15
View File
@@ -0,0 +1,15 @@
=========================================
1.0.0 (05/28/2026)
=========================================
[ADDED]
- Added kill-all support to adult and baby configs.
[FIXED]
- Fixed admin reload not reloading mob configs.
- Fixed adult/baby filters toggle bug.
- Fixed kill-all mobs appearing disabled in menus.
[CHANGED]
- Kill-all mobs now remain visually enabled.
- Added admin override lore to forced-enabled mobs.