Merge branch 'ver/1.21.11' into feat/lightning-affects-blocks

This commit is contained in:
granny
2025-12-17 20:22:53 -08:00
796 changed files with 38036 additions and 49832 deletions

View File

@@ -0,0 +1,85 @@
package org.purpurmc.purpur.gui.util;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
import org.apache.logging.log4j.core.pattern.PatternFormatter;
import org.apache.logging.log4j.core.pattern.PatternParser;
import org.apache.logging.log4j.util.PerformanceSensitive;
import java.util.List;
@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY)
@ConverterKeys({"highlightGUIError"})
@PerformanceSensitive("allocation")
public final class HighlightErrorConverter extends LogEventPatternConverter {
private static final String ERROR = "\u00A74\u00A7l"; // Bold Red
private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow
private final List<PatternFormatter> formatters;
private HighlightErrorConverter(List<PatternFormatter> formatters) {
super("highlightGUIError", null);
this.formatters = formatters;
}
@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
Level level = event.getLevel();
if (level.isMoreSpecificThan(Level.ERROR)) {
format(ERROR, event, toAppendTo);
return;
} else if (level.isMoreSpecificThan(Level.WARN)) {
format(WARN, event, toAppendTo);
return;
}
for (PatternFormatter formatter : formatters) {
formatter.format(event, toAppendTo);
}
}
private void format(String style, LogEvent event, StringBuilder toAppendTo) {
int start = toAppendTo.length();
toAppendTo.append(style);
int end = toAppendTo.length();
for (PatternFormatter formatter : formatters) {
formatter.format(event, toAppendTo);
}
if (toAppendTo.length() == end) {
toAppendTo.setLength(start);
}
}
@Override
public boolean handlesThrowable() {
for (final PatternFormatter formatter : formatters) {
if (formatter.handlesThrowable()) {
return true;
}
}
return false;
}
public static HighlightErrorConverter newInstance(Configuration config, String[] options) {
if (options.length != 1) {
LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length);
return null;
}
if (options[0] == null) {
LOGGER.error("No pattern supplied on highlightGUIError");
return null;
}
PatternParser parser = PatternLayout.createPatternParser(config);
List<PatternFormatter> formatters = parser.parse(options[0]);
return new HighlightErrorConverter(formatters);
}
}

View File

@@ -0,0 +1,618 @@
package org.purpurmc.purpur;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.purpurmc.purpur.command.PurpurCommand;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.purpurmc.purpur.task.TPSBarTask;
@SuppressWarnings("unused")
public class PurpurConfig {
private static final String HEADER = "This is the main configuration file for Purpur.\n"
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
+ "with caution, and make sure you know what each option does before configuring.\n"
+ "\n"
+ "If you need help with the configuration or have any questions related to Purpur,\n"
+ "join us in our Discord guild.\n"
+ "\n"
+ "Website: https://purpurmc.org \n"
+ "Docs: https://purpurmc.org/docs \n";
private static File CONFIG_FILE;
public static YamlConfiguration config;
private static Map<String, Command> commands;
public static int version;
static boolean verbose;
public static void init(File configFile) {
CONFIG_FILE = configFile;
config = new YamlConfiguration();
try {
config.load(CONFIG_FILE);
} catch (IOException ignore) {
} catch (InvalidConfigurationException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
config.options().copyDefaults(true);
verbose = getBoolean("verbose", false);
commands = new HashMap<>();
commands.put("purpur", new PurpurCommand("purpur"));
version = getInt("config-version", 46);
set("config-version", 46);
readConfig(PurpurConfig.class, null);
Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache);
}
protected static void log(String s) {
if (verbose) {
log(Level.INFO, s);
}
}
protected static void log(Level level, String s) {
Bukkit.getLogger().log(level, s);
}
public static void registerCommands() {
for (Map.Entry<String, Command> entry : commands.entrySet()) {
MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue());
}
}
static void readConfig(Class<?> clazz, Object instance) {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
}
}
}
}
try {
config.save(CONFIG_FILE);
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
}
}
private static void set(String path, Object val) {
config.addDefault(path, val);
config.set(path, val);
}
private static String getString(String path, String def) {
config.addDefault(path, def);
return config.getString(path, config.getString(path));
}
private static boolean getBoolean(String path, boolean def) {
config.addDefault(path, def);
return config.getBoolean(path, config.getBoolean(path));
}
private static double getDouble(String path, double def) {
config.addDefault(path, def);
return config.getDouble(path, config.getDouble(path));
}
private static int getInt(String path, int def) {
config.addDefault(path, def);
return config.getInt(path, config.getInt(path));
}
private static <T> List<?> getList(String path, T def) {
config.addDefault(path, def);
return config.getList(path, config.getList(path));
}
static Map<String, Object> getMap(String path, Map<String, Object> def) {
if (def != null && config.getConfigurationSection(path) == null) {
config.addDefault(path, def);
return def;
}
return toMap(config.getConfigurationSection(path));
}
private static Map<String, Object> toMap(ConfigurationSection section) {
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
if (section != null) {
for (String key : section.getKeys(false)) {
Object obj = section.get(key);
if (obj != null) {
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
}
}
}
return builder.build();
}
public static String cannotRideMob = "<red>You cannot mount that mob";
public static String afkBroadcastAway = "<yellow><italic>%s is now AFK";
public static String afkBroadcastBack = "<yellow><italic>%s is no longer AFK";
public static boolean afkBroadcastUseDisplayName = false;
public static String afkTabListPrefix = "[AFK] ";
public static String afkTabListSuffix = "";
public static String creditsCommandOutput = "<green>%s has been shown the end credits";
public static String demoCommandOutput = "<green>%s has been shown the demo screen";
public static String pingCommandOutput = "<green>%s's ping is %sms";
public static String ramCommandOutput = "<green>Ram Usage: <used>/<xmx> (<percent>)";
public static String rambarCommandOutput = "<green>Rambar toggled <onoff> for <target>";
public static String tpsbarCommandOutput = "<green>Tpsbar toggled <onoff> for <target>";
public static String dontRunWithScissors = "<red><italic>Don't run with scissors!";
public static String uptimeCommandOutput = "<green>Server uptime is <uptime>";
public static String unverifiedUsername = "default";
public static String sleepSkippingNight = "default";
public static String sleepingPlayersPercent = "default";
public static String sleepNotPossible = "default";
private static void messages() {
cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway);
afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack);
afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName);
afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix)));
afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix)));
creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput);
demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput);
pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput);
ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput);
rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput);
tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput);
dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors);
uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput);
unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername);
sleepSkippingNight = getString("settings.messages.sleep-skipping-night", sleepSkippingNight);
sleepingPlayersPercent = getString("settings.messages.sleeping-players-percent", sleepingPlayersPercent);
sleepNotPossible = getString("settings.messages.sleep-not-possible", sleepNotPossible);
}
public static String deathMsgRunWithScissors = "<player> slipped and fell on their shears";
public static String deathMsgStonecutter = "<player> has sawed themself in half";
private static void deathMessages() {
deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors);
deathMsgStonecutter = getString("settings.messages.death-message.stonecutter", deathMsgStonecutter);
}
public static boolean advancementOnlyBroadcastToAffectedPlayer = false;
public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false;
private static void broadcastSettings() {
if (version < 13) {
boolean oldValue = getBoolean("settings.advancement.only-broadcast-to-affected-player", false);
set("settings.broadcasts.advancement.only-broadcast-to-affected-player", oldValue);
set("settings.advancement.only-broadcast-to-affected-player", null);
}
advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer);
deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer);
}
public static String serverModName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName();
private static void serverModName() {
serverModName = getString("settings.server-mod-name", serverModName);
}
public static double laggingThreshold = 19.0D;
private static void tickLoopSettings() {
laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold);
}
public static boolean useAlternateKeepAlive = false;
private static void useAlternateKeepAlive() {
useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive);
}
public static boolean disableGiveCommandDrops = false;
private static void disableGiveCommandDrops() {
disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops);
}
public static String commandRamBarTitle = "<gray>Ram<yellow>:</yellow> <used>/<xmx> (<percent>)";
public static BossBar.Overlay commandRamBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
public static BossBar.Color commandRamBarProgressColorGood = BossBar.Color.GREEN;
public static BossBar.Color commandRamBarProgressColorMedium = BossBar.Color.YELLOW;
public static BossBar.Color commandRamBarProgressColorLow = BossBar.Color.RED;
public static String commandRamBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
public static String commandRamBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
public static String commandRamBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
public static int commandRamBarTickInterval = 20;
public static String commandTPSBarTitle = "<gray>TPS<yellow>:</yellow> <tps> MSPT<yellow>:</yellow> <mspt> Ping<yellow>:</yellow> <ping>ms";
public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT;
public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN;
public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW;
public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED;
public static String commandTPSBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
public static String commandTPSBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
public static String commandTPSBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
public static int commandTPSBarTickInterval = 20;
public static String commandCompassBarTitle = "S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 ";
public static BossBar.Overlay commandCompassBarProgressOverlay = BossBar.Overlay.PROGRESS;
public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE;
public static float commandCompassBarProgressPercent = 1.0F;
public static int commandCompassBarTickInterval = 5;
public static boolean commandGamemodeRequiresPermission = false;
public static boolean hideHiddenPlayersFromEntitySelector = false;
public static String uptimeFormat = "<days><hours><minutes><seconds>";
public static String uptimeDay = "%02d day, ";
public static String uptimeDays = "%02d days, ";
public static String uptimeHour = "%02d hour, ";
public static String uptimeHours = "%02d hours, ";
public static String uptimeMinute = "%02d minute, and ";
public static String uptimeMinutes = "%02d minutes, and ";
public static String uptimeSecond = "%02d second";
public static String uptimeSeconds = "%02d seconds";
private static void commandSettings() {
commandRamBarTitle = getString("settings.command.rambar.title", commandRamBarTitle);
commandRamBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.rambar.overlay", commandRamBarProgressOverlay.name()));
commandRamBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.good", commandRamBarProgressColorGood.name()));
commandRamBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.medium", commandRamBarProgressColorMedium.name()));
commandRamBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.low", commandRamBarProgressColorLow.name()));
commandRamBarTextColorGood = getString("settings.command.rambar.text-color.good", commandRamBarTextColorGood);
commandRamBarTextColorMedium = getString("settings.command.rambar.text-color.medium", commandRamBarTextColorMedium);
commandRamBarTextColorLow = getString("settings.command.rambar.text-color.low", commandRamBarTextColorLow);
commandRamBarTickInterval = getInt("settings.command.rambar.tick-interval", commandRamBarTickInterval);
commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle);
commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name()));
commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name()));
commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name()));
commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name()));
commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name()));
commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood);
commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium);
commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow);
commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval);
commandCompassBarTitle = getString("settings.command.compass.title", commandCompassBarTitle);
commandCompassBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.compass.overlay", commandCompassBarProgressOverlay.name()));
commandCompassBarProgressColor = BossBar.Color.valueOf(getString("settings.command.compass.progress-color", commandCompassBarProgressColor.name()));
commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent);
commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval);
commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission);
hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector);
uptimeFormat = getString("settings.command.uptime.format", uptimeFormat);
uptimeDay = getString("settings.command.uptime.day", uptimeDay);
uptimeDays = getString("settings.command.uptime.days", uptimeDays);
uptimeHour = getString("settings.command.uptime.hour", uptimeHour);
uptimeHours = getString("settings.command.uptime.hours", uptimeHours);
uptimeMinute = getString("settings.command.uptime.minute", uptimeMinute);
uptimeMinutes = getString("settings.command.uptime.minutes", uptimeMinutes);
uptimeSecond = getString("settings.command.uptime.second", uptimeSecond);
uptimeSeconds = getString("settings.command.uptime.seconds", uptimeSeconds);
}
public static int barrelRows = 3;
public static boolean enderChestSixRows = false;
public static boolean enderChestPermissionRows = false;
public static boolean cryingObsidianValidForPortalFrame = false;
public static int beeInsideBeeHive = 3;
public static boolean anvilCumulativeCost = true;
public static int smoothSnowAccumulationStep = 0;
public static int lightningRodRange = 128;
public static Set<Enchantment> grindstoneIgnoredEnchants = new HashSet<>();
public static boolean grindstoneRemoveAttributes = false;
public static boolean grindstoneRemoveDisplay = false;
public static int caveVinesMaxGrowthAge = 25;
public static int kelpMaxGrowthAge = 25;
public static int twistingVinesMaxGrowthAge = 25;
public static int weepingVinesMaxGrowthAge = 25;
public static boolean magmaBlockReverseBubbleColumnFlow = false;
public static boolean soulSandBlockReverseBubbleColumnFlow = false;
private static void blockSettings() {
if (version < 3) {
boolean oldValue = getBoolean("settings.barrel.packed-barrels", true);
set("settings.blocks.barrel.six-rows", oldValue);
set("settings.packed-barrels", null);
oldValue = getBoolean("settings.large-ender-chests", true);
set("settings.blocks.ender_chest.six-rows", oldValue);
set("settings.large-ender-chests", null);
}
if (version < 20) {
boolean oldValue = getBoolean("settings.blocks.barrel.six-rows", false);
set("settings.blocks.barrel.rows", oldValue ? 6 : 3);
set("settings.blocks.barrel.six-rows", null);
}
barrelRows = getInt("settings.blocks.barrel.rows", barrelRows);
if (barrelRows < 1 || barrelRows > 6) {
Bukkit.getLogger().severe("settings.blocks.barrel.rows must be 1-6, resetting to default");
barrelRows = 3;
}
org.bukkit.event.inventory.InventoryType.BARREL.setDefaultSize(switch (barrelRows) {
case 6 -> 54;
case 5 -> 45;
case 4 -> 36;
case 2 -> 18;
case 1 -> 9;
default -> 27;
});
enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows);
org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27);
enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows);
cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame);
beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive);
anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost);
smoothSnowAccumulationStep = getInt("settings.blocks.snow.smooth-accumulation-step", smoothSnowAccumulationStep);
if (smoothSnowAccumulationStep > 7) {
smoothSnowAccumulationStep = 7;
log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to above maximum allowed value of 7");
log(Level.WARNING, "Using value of 7 to prevent issues");
} else if (smoothSnowAccumulationStep < 0) {
smoothSnowAccumulationStep = 0;
log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to below minimum allowed value of 0");
log(Level.WARNING, "Using value of 0 to prevent issues");
}
lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange);
ArrayList<String> defaultCurses = new ArrayList<>(){{
add("minecraft:binding_curse");
add("minecraft:vanishing_curse");
}};
if (version < 24 && !getBoolean("settings.blocks.grindstone.ignore-curses", true)) {
defaultCurses.clear();
}
getList("settings.blocks.grindstone.ignored-enchants", defaultCurses).forEach(key -> {
Registry<Enchantment> registry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT);
Enchantment enchantment = registry.getValue(Identifier.parse(key.toString()));
if (enchantment == null) return;
grindstoneIgnoredEnchants.add(enchantment);
});
grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes);
grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay);
caveVinesMaxGrowthAge = getInt("settings.blocks.cave_vines.max-growth-age", caveVinesMaxGrowthAge);
if (caveVinesMaxGrowthAge > 25) {
caveVinesMaxGrowthAge = 25;
log(Level.WARNING, "blocks.cave_vines.max-growth-age is set to above maximum allowed value of 25");
log(Level.WARNING, "Using value of 25 to prevent issues");
}
kelpMaxGrowthAge = getInt("settings.blocks.kelp.max-growth-age", kelpMaxGrowthAge);
if (kelpMaxGrowthAge > 25) {
kelpMaxGrowthAge = 25;
log(Level.WARNING, "blocks.kelp.max-growth-age is set to above maximum allowed value of 25");
log(Level.WARNING, "Using value of 25 to prevent issues");
}
twistingVinesMaxGrowthAge = getInt("settings.blocks.twisting_vines.max-growth-age", twistingVinesMaxGrowthAge);
if (twistingVinesMaxGrowthAge > 25) {
twistingVinesMaxGrowthAge = 25;
log(Level.WARNING, "blocks.twisting_vines.max-growth-age is set to above maximum allowed value of 25");
log(Level.WARNING, "Using value of 25 to prevent issues");
}
weepingVinesMaxGrowthAge = getInt("settings.blocks.weeping_vines.max-growth-age", weepingVinesMaxGrowthAge);
if (weepingVinesMaxGrowthAge > 25) {
weepingVinesMaxGrowthAge = 25;
log(Level.WARNING, "blocks.weeping_vines.max-growth-age is set to above maximum allowed value of 25");
log(Level.WARNING, "Using value of 25 to prevent issues");
}
magmaBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.magma-block.reverse-bubble-column-flow", magmaBlockReverseBubbleColumnFlow);
soulSandBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.soul-sand.reverse-bubble-column-flow", soulSandBlockReverseBubbleColumnFlow);
}
public static boolean allowInapplicableEnchants = false;
public static boolean allowIncompatibleEnchants = false;
public static boolean allowHigherEnchantsLevels = false;
public static boolean allowUnsafeEnchantCommand = false;
public static boolean replaceIncompatibleEnchants = false;
public static boolean clampEnchantLevels = true;
private static void enchantmentSettings() {
if (version < 30) {
boolean oldValue = getBoolean("settings.enchantment.allow-unsafe-enchants", false);
set("settings.enchantment.anvil.allow-unsafe-enchants", oldValue);
set("settings.enchantment.anvil.allow-inapplicable-enchants", true);
set("settings.enchantment.anvil.allow-incompatible-enchants", true);
set("settings.enchantment.anvil.allow-higher-enchants-levels", true);
set("settings.enchantment.allow-unsafe-enchants", null);
}
if (version < 37) {
boolean allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", false);
if (!allowUnsafeEnchants) {
set("settings.enchantment.anvil.allow-inapplicable-enchants", false);
set("settings.enchantment.anvil.allow-incompatible-enchants", false);
set("settings.enchantment.anvil.allow-higher-enchants-levels", false);
}
set("settings.enchantment.anvil.allow-unsafe-enchants", null);
}
allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants);
allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants);
allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels);
allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchantCommand);
replaceIncompatibleEnchants = getBoolean("settings.enchantment.anvil.replace-incompatible-enchants", replaceIncompatibleEnchants);
clampEnchantLevels = getBoolean("settings.enchantment.clamp-levels", clampEnchantLevels);
}
public static boolean endermanShortHeight = false;
private static void entitySettings() {
endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight);
if (endermanShortHeight) EntityType.ENDERMAN.dimensions = EntityDimensions.scalable(0.6F, 1.9F);
}
public static boolean allowWaterPlacementInTheEnd = true;
private static void allowWaterPlacementInEnd() {
allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd);
}
public static boolean beeCountPayload = false;
private static void beeCountPayload() {
beeCountPayload = getBoolean("settings.bee-count-payload", beeCountPayload);
}
public static boolean loggerSuppressInitLegacyMaterialError = false;
public static boolean loggerSuppressIgnoredAdvancementWarnings = false;
public static boolean loggerSuppressUnrecognizedRecipeErrors = false;
public static boolean loggerSuppressSetBlockFarChunk = false;
public static boolean loggerSuppressLibraryLoader = false;
private static void loggerSettings() {
loggerSuppressInitLegacyMaterialError = getBoolean("settings.logger.suppress-init-legacy-material-errors", loggerSuppressInitLegacyMaterialError);
loggerSuppressIgnoredAdvancementWarnings = getBoolean("settings.logger.suppress-ignored-advancement-warnings", loggerSuppressIgnoredAdvancementWarnings);
loggerSuppressUnrecognizedRecipeErrors = getBoolean("settings.logger.suppress-unrecognized-recipe-errors", loggerSuppressUnrecognizedRecipeErrors);
loggerSuppressSetBlockFarChunk = getBoolean("settings.logger.suppress-setblock-in-far-chunk-errors", loggerSuppressSetBlockFarChunk);
loggerSuppressLibraryLoader = getBoolean("settings.logger.suppress-library-loader", loggerSuppressLibraryLoader);
org.bukkit.plugin.java.JavaPluginLoader.SuppressLibraryLoaderLogger = loggerSuppressLibraryLoader;
}
public static boolean tpsCatchup = true;
private static void tpsCatchup() {
tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup);
}
public static boolean useUPnP = false;
public static boolean maxJoinsPerSecond = false;
public static boolean kickForOutOfOrderChat = true;
private static void networkSettings() {
useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP);
maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond);
kickForOutOfOrderChat = getBoolean("settings.network.kick-for-out-of-order-chat", kickForOutOfOrderChat);
}
public static Pattern usernameValidCharactersPattern;
private static void usernameValidationSettings() {
String defaultPattern = "^[a-zA-Z0-9_.]*$";
String setPattern = getString("settings.username-valid-characters", defaultPattern);
usernameValidCharactersPattern = Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern);
}
public static boolean fixProjectileLootingTransfer = false;
private static void fixProjectileLootingTransfer() {
fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer);
}
public static boolean clampAttributes = true;
private static void clampAttributes() {
clampAttributes = getBoolean("settings.clamp-attributes", clampAttributes);
}
public static boolean limitArmor = true;
private static void limitArmor() {
limitArmor = getBoolean("settings.limit-armor", limitArmor);
}
private static void blastResistanceSettings() {
getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> {
Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId));
if (block == Blocks.AIR) {
log(Level.SEVERE, "Invalid block for `settings.blast-resistance-overrides`: " + blockId);
return;
}
if (!(value instanceof Number blastResistance)) {
log(Level.SEVERE, "Invalid blast resistance for `settings.blast-resistance-overrides." + blockId + "`: " + value);
return;
}
block.explosionResistance = blastResistance.floatValue();
});
}
private static void blockFallMultiplierSettings() {
getMap("settings.block-fall-multipliers", Map.ofEntries(
Map.entry("minecraft:hay_block", Map.of("damage", 0.2F)),
Map.entry("minecraft:white_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:light_gray_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:gray_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:black_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:brown_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:pink_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:red_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:orange_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:yellow_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:green_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:lime_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:cyan_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:light_blue_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:blue_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:purple_bed", Map.of("distance", 0.5F)),
Map.entry("minecraft:magenta_bed", Map.of("distance", 0.5F))
)).forEach((blockId, value) -> {
Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId));
if (block == Blocks.AIR) {
log(Level.SEVERE, "Invalid block for `settings.block-fall-multipliers`: " + blockId);
return;
}
if (!(value instanceof Map<?, ?> map)) {
log(Level.SEVERE, "Invalid fall multiplier for `settings.block-fall-multipliers." + blockId + "`: " + value
+ ", expected a map with keys `damage` and `distance` to floats.");
return;
}
Object rawFallDamageMultiplier = map.get("damage");
if (rawFallDamageMultiplier == null) rawFallDamageMultiplier = 1F;
if (!(rawFallDamageMultiplier instanceof Number fallDamageMultiplier)) {
log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".damage`: " + map.get("damage"));
return;
}
Object rawFallDistanceMultiplier = map.get("distance");
if (rawFallDistanceMultiplier == null) rawFallDistanceMultiplier = 1F;
if (!(rawFallDistanceMultiplier instanceof Number fallDistanceMultiplier)) {
log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".distance`: " + map.get("distance"));
return;
}
block.fallDamageMultiplier = fallDamageMultiplier.floatValue();
block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue();
});
}
public static boolean playerDeathsAlwaysShowItem = false;
private static void playerDeathsAlwaysShowItem() {
playerDeathsAlwaysShowItem = getBoolean("settings.player-deaths-always-show-item", playerDeathsAlwaysShowItem);
}
public static boolean registerMinecraftDebugCommands = false;
private static void registerMinecraftDebugCommands() {
registerMinecraftDebugCommands = getBoolean("settings.register-minecraft-debug-commands", registerMinecraftDebugCommands);
}
public static boolean registerMinecraftDisabledCommands = false;
private static void registerMinecraftDisabledCommands() {
registerMinecraftDisabledCommands = getBoolean("settings.register-minecraft-disabled-commands", registerMinecraftDebugCommands);
}
public static List<String> startupCommands = new ArrayList<>();
private static void startupCommands() {
startupCommands.clear();
getList("settings.startup-commands", new ArrayList<String>()).forEach(line -> {
String command = line.toString();
if (command.startsWith("/")) {
command = command.substring(1);
}
startupCommands.add(command);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.task.CompassTask;
public class CompassCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("compass")
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.compass"))
.executes(context -> {
ServerPlayer player = context.getSource().getPlayerOrException();
CompassTask task = CompassTask.instance();
if (player.compassBar()) {
task.removePlayer(player.getBukkitEntity());
player.compassBar(false);
} else {
task.addPlayer(player.getBukkitEntity());
player.compassBar(true);
}
return 1;
})
);
}
}

View File

@@ -0,0 +1,36 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import java.util.Collection;
import java.util.Collections;
public class CreditsCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("credits")
.requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.credits"))
.executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
.then(Commands.argument("targets", EntityArgument.players())
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.credits.other"))
.executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
)
);
}
private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
for (ServerPlayer player : targets) {
ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, 1F);
player.connection.send(packet);
String output = String.format(PurpurConfig.creditsCommandOutput, player.getGameProfile().name());
sender.sendSuccess(output, false);
}
return targets.size();
}
}

View File

@@ -0,0 +1,36 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import java.util.Collection;
import java.util.Collections;
public class DemoCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("demo")
.requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.demo"))
.executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
.then(Commands.argument("targets", EntityArgument.players())
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.demo.other"))
.executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
)
);
}
private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
for (ServerPlayer player : targets) {
ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.DEMO_EVENT, 0);
player.connection.send(packet);
String output = String.format(PurpurConfig.demoCommandOutput, player.getGameProfile().name());
sender.sendSuccess(output, false);
}
return targets.size();
}
}

View File

@@ -0,0 +1,33 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import java.util.Collection;
import java.util.Collections;
public class PingCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("ping")
.requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ping"))
.executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
.then(Commands.argument("targets", EntityArgument.players())
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ping.other"))
.executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
)
);
}
private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
for (ServerPlayer player : targets) {
String output = String.format(PurpurConfig.pingCommandOutput, player.getGameProfile().name(), player.connection.latency());
sender.sendSuccess(output, false);
}
return targets.size();
}
}

View File

@@ -0,0 +1,66 @@
package org.purpurmc.purpur.command;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import org.purpurmc.purpur.PurpurConfig;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PurpurCommand extends Command {
public PurpurCommand(String name) {
super(name);
this.description = "Purpur related commands";
this.usageMessage = "/purpur [reload | version]";
this.setPermission("bukkit.command.purpur");
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
if (args.length == 1) {
return Stream.of("reload", "version")
.filter(arg -> arg.startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
}
return Collections.emptyList();
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!testPermission(sender)) return true;
if (args.length != 1) {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return false;
}
if (args[0].equalsIgnoreCase("reload")) {
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
MinecraftServer console = MinecraftServer.getServer();
PurpurConfig.init((File) console.options.valueOf("purpur-settings"));
for (ServerLevel level : console.getAllLevels()) {
level.purpurConfig.init();
level.resetBreedingCooldowns(); // Purpur - Add adjustable breeding cooldown to config
}
console.server.reloadCount++;
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete.");
} else if (args[0].equalsIgnoreCase("version")) {
Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
if (verCmd != null) {
return verCmd.execute(sender, commandLabel, new String[0]);
}
}
return true;
}
}

View File

@@ -0,0 +1,45 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import org.purpurmc.purpur.task.RamBarTask;
import java.util.Collection;
import java.util.Collections;
public class RamBarCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("rambar")
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.rambar"))
.executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
.then(Commands.argument("targets", EntityArgument.players())
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.rambar.other"))
.executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
)
);
}
private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
for (ServerPlayer player : targets) {
boolean result = RamBarTask.instance().togglePlayer(player.getBukkitEntity());
player.ramBar(result);
Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.rambarCommandOutput,
Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
.color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
Placeholder.parsed("target", player.getGameProfile().name()));
sender.sendSuccess(output, false);
}
return targets.size();
}
}

View File

@@ -0,0 +1,31 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import org.purpurmc.purpur.task.RamBarTask;
public class RamCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("ram")
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ram"))
.executes(context -> {
CommandSourceStack sender = context.getSource();
RamBarTask ramBar = RamBarTask.instance();
sender.sendSuccess(() -> PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(PurpurConfig.ramCommandOutput,
Placeholder.component("allocated", ramBar.format(ramBar.getAllocated())),
Placeholder.component("used", ramBar.format(ramBar.getUsed())),
Placeholder.component("xmx", ramBar.format(ramBar.getXmx())),
Placeholder.component("xms", ramBar.format(ramBar.getXms())),
Placeholder.unparsed("percent", ((int) (ramBar.getPercent() * 100)) + "%")
)), false);
return 1;
})
);
}
}

View File

@@ -0,0 +1,45 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import org.purpurmc.purpur.task.TPSBarTask;
import java.util.Collection;
import java.util.Collections;
public class TPSBarCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("tpsbar")
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.tpsbar"))
.executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
.then(Commands.argument("targets", EntityArgument.players())
.requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.tpsbar.other"))
.executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
)
);
}
private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
for (ServerPlayer player : targets) {
boolean result = TPSBarTask.instance().togglePlayer(player.getBukkitEntity());
player.tpsBar(result);
Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.tpsbarCommandOutput,
Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
.color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
Placeholder.parsed("target", player.getGameProfile().name()));
sender.sendSuccess(output, false);
}
return targets.size();
}
}

View File

@@ -0,0 +1,56 @@
package org.purpurmc.purpur.command;
import com.mojang.brigadier.CommandDispatcher;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.permissions.Permissions;
import org.purpurmc.purpur.PurpurConfig;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
public class UptimeCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("uptime")
.requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.uptime"))
.executes((context) -> execute(context.getSource()))
);
}
private static int execute(CommandSourceStack sender) {
Data data = new Data();
data.format = PurpurConfig.uptimeFormat;
data.hide = true;
data.millis = System.currentTimeMillis() - MinecraftServer.startTimeMillis;
process(data, "<days>", PurpurConfig.uptimeDay, PurpurConfig.uptimeDays, TimeUnit.DAYS, TimeUnit.MILLISECONDS::toDays);
process(data, "<hours>", PurpurConfig.uptimeHour, PurpurConfig.uptimeHours, TimeUnit.HOURS, TimeUnit.MILLISECONDS::toHours);
process(data, "<minutes>", PurpurConfig.uptimeMinute, PurpurConfig.uptimeMinutes, TimeUnit.MINUTES, TimeUnit.MILLISECONDS::toMinutes);
data.hide = false; // never hide seconds
process(data, "<seconds>", PurpurConfig.uptimeSecond, PurpurConfig.uptimeSeconds, TimeUnit.SECONDS, TimeUnit.MILLISECONDS::toSeconds);
Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.uptimeCommandOutput, Placeholder.unparsed("uptime", data.format));
sender.sendSuccess(output, false);
return 1;
}
private static void process(Data data, String replace, String singular, String plural, TimeUnit unit, Function<Long, Long> func) {
if (data.format.contains(replace)) {
long val = func.apply(data.millis);
if (data.hide) data.hide = val == 0;
if (!data.hide) data.millis -= unit.toMillis(val);
data.format = data.format.replace(replace, data.hide ? "" : String.format(val == 1 ? singular : plural, val));
}
}
private static class Data {
String format;
boolean hide;
long millis;
}
}

View File

@@ -0,0 +1,41 @@
package org.purpurmc.purpur.configuration.transformation;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.purpurmc.purpur.PurpurConfig;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.NodePath;
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
import org.spongepowered.configurate.transformation.TransformAction;
import static org.spongepowered.configurate.NodePath.path;
public class FarEndTerrainGenerationMigration implements TransformAction {
public static boolean HAS_BEEN_REGISTERED = false;
public static final String MISC_KEY = "misc";
public static final String FIX_FAR_END_TERRAIN_GENERATION_KEY = "fix-far-end-terrain-generation";
@Override
public Object @Nullable [] visitPath(final NodePath path, final ConfigurationNode value) throws ConfigurateException {
String purpurGenerateEndVoidRingsPath = "settings.generate-end-void-rings";
ConfigurationNode fixFarEndTerrainGenerationNode = value.node(MISC_KEY, FIX_FAR_END_TERRAIN_GENERATION_KEY);
if (PurpurConfig.config.contains(purpurGenerateEndVoidRingsPath)) {
boolean purpurGenerateEndVoidRings = PurpurConfig.config.getBoolean(purpurGenerateEndVoidRingsPath);
if (purpurGenerateEndVoidRings) {
fixFarEndTerrainGenerationNode.set(false);
}
PurpurConfig.config.set(purpurGenerateEndVoidRingsPath, null);
}
return null;
}
public static void apply(final ConfigurationTransformation.Builder builder) {
if (PurpurConfig.version < 46) {
HAS_BEEN_REGISTERED = true;
builder.addAction(path(), new FarEndTerrainGenerationMigration());
}
}
}

View File

@@ -0,0 +1,74 @@
package org.purpurmc.purpur.controller;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Input;
import net.minecraft.world.entity.player.Player;
public class FlyingMoveControllerWASD extends MoveControllerWASD {
protected final float groundSpeedModifier;
protected final float flyingSpeedModifier;
protected int tooHighCooldown = 0;
protected boolean setNoGravityFlag;
public FlyingMoveControllerWASD(Mob entity) {
this(entity, 1.0F);
}
public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) {
this(entity, groundSpeedModifier, 1.0F, true);
}
public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) {
this(entity, groundSpeedModifier, flyingSpeedModifier, true);
}
public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) {
super(entity);
this.groundSpeedModifier = groundSpeedModifier;
this.flyingSpeedModifier = flyingSpeedModifier;
this.setNoGravityFlag = setNoGravityFlag;
}
@Override
public void purpurTick(Player rider) {
Input lastClientInput = ((ServerPlayer) rider).getLastClientInput();
float forward = lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : 0.0F;
float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F);
float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F);
if (lastClientInput.jump() && spacebarEvent(entity)) {
entity.onSpacebar();
}
if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
if (tooHighCooldown <= 0) {
tooHighCooldown = 20;
}
entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D));
vertical = 0.0F;
}
setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED));
float speed = (float) getSpeedModifier();
if (entity.onGround) {
speed *= groundSpeedModifier; // TODO = fix this!
} else {
speed *= flyingSpeedModifier;
}
if (setNoGravityFlag) {
entity.setNoGravity(forward > 0);
}
entity.setSpeed(speed);
entity.setVerticalMot(vertical);
entity.setStrafeMot(strafe);
entity.setForwardMot(forward);
setForward(entity.getForwardMot());
setStrafe(entity.getStrafeMot());
}
}

View File

@@ -0,0 +1,66 @@
package org.purpurmc.purpur.controller;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Input;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD {
public FlyingWithSpacebarMoveControllerWASD(Mob entity) {
super(entity);
}
public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) {
super(entity, groundSpeedModifier);
}
@Override
public void purpurTick(Player rider) {
Input lastClientInput = ((ServerPlayer) rider).getLastClientInput();
float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F);
float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.5F;
float vertical = 0;
if (forward < 0.0F) {
forward *= 0.5F;
strafe *= 0.5F;
}
float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED);
if (entity.onGround) {
speed *= groundSpeedModifier;
}
if (lastClientInput.jump() && spacebarEvent(entity) && !entity.onSpacebar()) {
entity.setNoGravity(true);
vertical = 1.0F;
} else {
entity.setNoGravity(false);
}
if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
if (tooHighCooldown <= 0) {
tooHighCooldown = 20;
}
entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D));
vertical = 0.0F;
}
setSpeedModifier(speed);
entity.setSpeed((float) getSpeedModifier());
entity.setVerticalMot(vertical);
entity.setStrafeMot(strafe);
entity.setForwardMot(forward);
setForward(entity.getForwardMot());
setStrafe(entity.getStrafeMot());
Vec3 mot = entity.getDeltaMovement();
if (mot.y > 0.2D) {
entity.setDeltaMovement(mot.x, 0.2D, mot.z);
}
}
}

View File

@@ -0,0 +1,79 @@
package org.purpurmc.purpur.controller;
import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.control.LookControl;
import net.minecraft.world.entity.player.Player;
public class LookControllerWASD extends LookControl {
protected final Mob entity;
private float yOffset = 0;
private float xOffset = 0;
public LookControllerWASD(Mob entity) {
super(entity);
this.entity = entity;
}
// tick
@Override
public void tick() {
if (entity.getRider() != null && entity.isControllable()) {
purpurTick(entity.getRider());
} else {
vanillaTick();
}
}
protected void purpurTick(Player rider) {
setYawPitch(rider.getYRot(), rider.getXRot());
}
public void vanillaTick() {
super.tick();
}
public void setYawPitch(float yRot, float xRot) {
entity.setXRot(normalizePitch(xRot + xOffset));
entity.setYRot(normalizeYaw(yRot + yOffset));
entity.setYHeadRot(entity.getYRot());
entity.xRotO = entity.getXRot();
entity.yRotO = entity.getYRot();
ClientboundMoveEntityPacket.PosRot entityPacket = new ClientboundMoveEntityPacket.PosRot(
entity.getId(),
(short) 0, (short) 0, (short) 0,
(byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F),
(byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F),
entity.onGround
);
((ServerLevel) entity.level()).getChunkSource().sendToTrackingPlayers(entity, entityPacket);
}
public void setOffsets(float yaw, float pitch) {
yOffset = yaw;
xOffset = pitch;
}
public float normalizeYaw(float yaw) {
yaw %= 360.0f;
if (yaw >= 180.0f) {
yaw -= 360.0f;
} else if (yaw < -180.0f) {
yaw += 360.0f;
}
return yaw;
}
public float normalizePitch(float pitch) {
if (pitch > 90.0f) {
pitch = 90.0f;
} else if (pitch < -90.0f) {
pitch = -90.0f;
}
return pitch;
}
}

View File

@@ -0,0 +1,92 @@
package org.purpurmc.purpur.controller;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.MoveControl;
import net.minecraft.world.entity.player.Input;
import net.minecraft.world.entity.player.Player;
import org.purpurmc.purpur.event.entity.RidableSpacebarEvent;
public class MoveControllerWASD extends MoveControl {
protected final Mob entity;
private final double speedModifier;
public MoveControllerWASD(Mob entity) {
this(entity, 1.0D);
}
public MoveControllerWASD(Mob entity, double speedModifier) {
super(entity);
this.entity = entity;
this.speedModifier = speedModifier;
}
@Override
public boolean hasWanted() {
return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted();
}
@Override
public void tick() {
if (entity.getRider() != null && entity.isControllable()) {
purpurTick(entity.getRider());
} else {
vanillaTick();
}
}
public void vanillaTick() {
super.tick();
}
public void purpurTick(Player rider) {
Input lastClientInput = ((ServerPlayer) rider).getLastClientInput();
float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F) * 0.5F;
float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.25F;
if (forward <= 0.0F) {
forward *= 0.5F;
}
float yawOffset = 0;
if (strafe != 0) {
if (forward == 0) {
yawOffset += strafe > 0 ? -90 : 90;
forward = Math.abs(strafe * 2);
} else {
yawOffset += strafe > 0 ? -30 : 30;
strafe /= 2;
if (forward < 0) {
yawOffset += strafe > 0 ? -110 : 110;
forward *= -1;
}
}
} else if (forward < 0) {
yawOffset -= 180;
forward *= -1;
}
((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0);
if (lastClientInput.jump() && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) {
entity.jumpFromGround();
}
setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
entity.setSpeed((float) getSpeedModifier());
entity.setForwardMot(forward);
setForward(entity.getForwardMot());
setStrafe(entity.getStrafeMot());
}
public static boolean spacebarEvent(Mob entity) {
if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) {
return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent();
} else {
return true;
}
}
}

View File

@@ -0,0 +1,53 @@
package org.purpurmc.purpur.controller;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Input;
import net.minecraft.world.entity.player.Player;
public class WaterMoveControllerWASD extends MoveControllerWASD {
private final double speedModifier;
public WaterMoveControllerWASD(Mob entity) {
this(entity, 1.0D);
}
public WaterMoveControllerWASD(Mob entity, double speedModifier) {
super(entity);
this.speedModifier = speedModifier;
}
@Override
public void purpurTick(Player rider) {
Input lastClientInput = ((ServerPlayer) rider).getLastClientInput();
float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F);
float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.5F; // strafe slower by default
float vertical = -(rider.xRotO / 90);
if (forward == 0.0F) {
// strafe slower if not moving forward
strafe *= 0.5F;
// do not move vertically if not moving forward
vertical = 0.0F;
} else if (forward < 0.0F) {
// water animals can't swim backwards
forward = 0.0F;
vertical = 0.0F;
}
if (rider.jumping && spacebarEvent(entity)) {
entity.onSpacebar();
}
setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
entity.setSpeed((float) getSpeedModifier() * 0.1F);
entity.setForwardMot(forward * (float) speedModifier);
entity.setStrafeMot(strafe * (float) speedModifier);
entity.setVerticalMot(vertical * (float) speedModifier);
setForward(entity.getForwardMot());
setStrafe(entity.getStrafeMot());
}
}

View File

@@ -0,0 +1,116 @@
package org.purpurmc.purpur.entity;
import com.mojang.logging.LogUtils;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.level.storage.ValueInput;
import org.bukkit.block.EntityBlockStorage;
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.entity.Bee;
import org.bukkit.entity.EntityType;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.Locale;
public class PurpurStoredBee implements StoredEntity<Bee> {
static final Logger LOGGER = LogUtils.getLogger();
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
private final EntityBlockStorage<Bee> blockStorage;
private final BeehiveBlockEntity.BeeData handle;
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(PurpurStoredBee.DATA_TYPE_REGISTRY);
private Component customName;
public PurpurStoredBee(BeehiveBlockEntity.BeeData data, EntityBlockStorage<Bee> blockStorage, final BeehiveBlockEntity blockEntity) {
this.handle = data;
this.blockStorage = blockStorage;
CompoundTag customData = handle.occupant.entityData().copyTagWithEntityId();
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(blockEntity.problemPath(), LOGGER)) {
ValueInput valueInput = TagValueInput.create(scopedCollector, RegistryAccess.EMPTY, customData);
net.minecraft.network.chat.Component customNameMinecraft = BlockEntity.parseCustomNameSafe(valueInput, "CustomName");
this.customName = customNameMinecraft == null ? null : PaperAdventure.asAdventure(customNameMinecraft);
if (customData.get("BukkitValues") instanceof CompoundTag compoundTag) {
this.persistentDataContainer.putAll(compoundTag);
}
}
}
public BeehiveBlockEntity.BeeData getHandle() {
return handle;
}
@Override
public @Nullable Component customName() {
return customName;
}
@Override
public void customName(@Nullable Component customName) {
this.customName = customName;
}
@Override
public @Nullable String getCustomName() {
return PaperAdventure.asPlain(customName, Locale.US);
}
@Override
public void setCustomName(@Nullable String name) {
customName(name != null ? Component.text(name) : null);
}
@Override
public @NotNull PersistentDataContainer getPersistentDataContainer() {
return persistentDataContainer;
}
@Override
public boolean hasBeenReleased() {
return !blockStorage.getEntities().contains(this);
}
@Override
public @Nullable Bee release() {
return blockStorage.releaseEntity(this);
}
@Override
public @Nullable EntityBlockStorage<Bee> getBlockStorage() {
if(hasBeenReleased()) {
return null;
}
return blockStorage;
}
@Override
public @NotNull EntityType getType() {
return EntityType.BEE;
}
@Override
public void update() {
handle.occupant.entityData().copyTagWithEntityId().put("BukkitValues", this.persistentDataContainer.toTagCompound());
if(customName == null) {
handle.occupant.entityData().copyTagWithEntityId().remove("CustomName");
} else {
handle.occupant.entityData().copyTagWithEntityId().putString("CustomName", CraftChatMessage.toJSON(PaperAdventure.asVanilla(customName)));
}
}
}

View File

@@ -0,0 +1,20 @@
package org.purpurmc.purpur.entity.ai;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.Goal;
import java.util.EnumSet;
public class HasRider extends Goal {
public final Mob entity;
public HasRider(Mob entity) {
this.entity = entity;
setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR));
}
@Override
public boolean canUse() {
return entity.getRider() != null && entity.isControllable();
}
}

View File

@@ -0,0 +1,17 @@
package org.purpurmc.purpur.entity.ai;
import net.minecraft.world.entity.animal.equine.AbstractHorse;
public class HorseHasRider extends HasRider {
public final AbstractHorse horse;
public HorseHasRider(AbstractHorse entity) {
super(entity);
this.horse = entity;
}
@Override
public boolean canUse() {
return super.canUse() && horse.isSaddled();
}
}

View File

@@ -0,0 +1,17 @@
package org.purpurmc.purpur.entity.ai;
import net.minecraft.world.entity.animal.equine.Llama;
public class LlamaHasRider extends HasRider {
public final Llama llama;
public LlamaHasRider(Llama entity) {
super(entity);
this.llama = entity;
}
@Override
public boolean canUse() {
return super.canUse() && llama.isSaddled() && llama.isControllable();
}
}

View File

@@ -0,0 +1,95 @@
package org.purpurmc.purpur.entity.ai;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.animal.golem.IronGolem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks;
import java.util.EnumSet;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
public class ReceiveFlower extends Goal {
private final IronGolem irongolem;
private ServerPlayer target;
private int cooldown;
public ReceiveFlower(IronGolem entity) {
this.irongolem = entity;
setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
}
@Override
public boolean canUse() {
if (this.irongolem.getOfferFlowerTick() > 0) {
return false;
}
if (!this.irongolem.isAngry()) {
return false;
}
EntityReference<LivingEntity> persistentAngerTarget = this.irongolem.getPersistentAngerTarget();
if (persistentAngerTarget == null) {
return false;
}
LivingEntity target = EntityReference.getLivingEntity(persistentAngerTarget, this.irongolem.level());
if (!(target instanceof ServerPlayer player)) {
return false;
}
InteractionHand hand = getPoppyHand(player);
if (hand == null) {
return false;
}
removeFlower(player, hand);
this.target = player;
return true;
}
@Override
public boolean canContinueToUse() {
return this.cooldown > 0;
}
@Override
public void start() {
this.cooldown = 100;
this.irongolem.stopBeingAngry();
this.irongolem.offerFlower(true);
}
@Override
public void stop() {
this.irongolem.offerFlower(false);
this.target = null;
}
@Override
public void tick() {
this.irongolem.getLookControl().setLookAt(this.target, 30.0F, 30.0F);
--this.cooldown;
}
private InteractionHand getPoppyHand(ServerPlayer player) {
if (isPoppy(player.getMainHandItem())) {
return InteractionHand.MAIN_HAND;
}
if (isPoppy(player.getOffhandItem())) {
return InteractionHand.OFF_HAND;
}
return null;
}
private void removeFlower(ServerPlayer player, InteractionHand hand) {
player.setItemInHand(hand, ItemStack.EMPTY);
}
private boolean isPoppy(ItemStack item) {
return item.getItem() == Blocks.POPPY.asItem();
}
}

View File

@@ -0,0 +1,105 @@
package org.purpurmc.purpur.entity.projectile;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.dolphin.Dolphin;
import net.minecraft.world.entity.projectile.LlamaSpit;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.event.entity.EntityRemoveEvent;
public class DolphinSpit extends LlamaSpit {
public LivingEntity dolphin;
public int ticksLived;
public DolphinSpit(EntityType<? extends LlamaSpit> type, Level world) {
super(type, world);
}
public DolphinSpit(Level world, Dolphin dolphin) {
this(EntityType.LLAMA_SPIT, world);
this.setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin);
this.dolphin = dolphin;
this.setPos(
dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5 * (double) Mth.sin(dolphin.yBodyRot * (float) (Math.PI / 180.0)),
dolphin.getEyeY() - 0.1F,
dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5 * (double) Mth.cos(dolphin.yBodyRot * (float) (Math.PI / 180.0)));
}
@Override
public boolean canSaveToDisk() {
return false;
}
public void tick() {
projectileTick();
Vec3 mot = this.getDeltaMovement();
HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
this.preHitTargetOrDeflectSelf(hitResult);
double x = this.getX() + mot.x;
double y = this.getY() + mot.y;
double z = this.getZ() + mot.z;
this.updateRotation();
Vec3 motDouble = mot.scale(2.0);
for (int i = 0; i < 5; i++) {
((ServerLevel) level()).sendParticlesSource(null, ParticleTypes.BUBBLE,
true, false,
getX() + random.nextFloat() / 2 - 0.25F,
getY() + random.nextFloat() / 2 - 0.25F,
getZ() + random.nextFloat() / 2 - 0.25F,
0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D);
}
if (++ticksLived > 20) {
this.discard(EntityRemoveEvent.Cause.DISCARD);
} else {
this.setDeltaMovement(mot.scale(0.99D));
if (!this.isNoGravity()) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
}
this.setPos(x, y, z);
}
}
@Override
public void shoot(double x, double y, double z, float speed, float inaccuracy) {
setDeltaMovement(new Vec3(x, y, z).normalize().add(
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
.scale(speed));
}
@Override
protected void onHitEntity(EntityHitResult entityHitResult) {
Entity shooter = this.getOwner();
if (shooter instanceof LivingEntity) {
entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.dolphinSpitDamage);
}
}
@Override
protected void onHitBlock(BlockHitResult blockHitResult) {
if (this.hitCancelled) {
return;
}
BlockState state = this.level().getBlockState(blockHitResult.getBlockPos());
state.onProjectileHit(this.level(), state, blockHitResult, this);
this.discard(EntityRemoveEvent.Cause.DISCARD);
}
}

View File

@@ -0,0 +1,127 @@
package org.purpurmc.purpur.entity.projectile;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.monster.Phantom;
import net.minecraft.world.entity.projectile.LlamaSpit;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class PhantomFlames extends LlamaSpit {
public Phantom phantom;
public int ticksLived;
public boolean canGrief = false;
public PhantomFlames(EntityType<? extends LlamaSpit> type, Level world) {
super(type, world);
}
public PhantomFlames(Level world, Phantom phantom) {
this(EntityType.LLAMA_SPIT, world);
setOwner(phantom.getRider() != null ? phantom.getRider() : phantom);
this.phantom = phantom;
this.setPos(
phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * (float) (Math.PI / 180.0)),
phantom.getEyeY() - 0.10000000149011612D,
phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * (float) (Math.PI / 180.0)));
}
@Override
public boolean canSaveToDisk() {
return false;
}
public void tick() {
projectileTick();
Vec3 mot = this.getDeltaMovement();
HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
this.preHitTargetOrDeflectSelf(hitResult);
double x = this.getX() + mot.x;
double y = this.getY() + mot.y;
double z = this.getZ() + mot.z;
this.updateRotation();
Vec3 motDouble = mot.scale(2.0);
for (int i = 0; i < 5; i++) {
((ServerLevel) level()).sendParticlesSource(null, ParticleTypes.FLAME,
true, false,
getX() + random.nextFloat() / 2 - 0.25F,
getY() + random.nextFloat() / 2 - 0.25F,
getZ() + random.nextFloat() / 2 - 0.25F,
0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D);
}
if (++ticksLived > 20) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
} else if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
} else if (this.isInWater()) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
} else {
this.setDeltaMovement(mot.scale(0.99D));
if (!this.isNoGravity()) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
}
this.setPos(x, y, z);
}
}
@Override
public void shoot(double x, double y, double z, float speed, float inaccuracy) {
setDeltaMovement(new Vec3(x, y, z).normalize().add(
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
.scale(speed));
}
@Override
protected void onHitEntity(EntityHitResult entityHitResult) {
Level world = this.level();
if (world instanceof ServerLevel worldserver) {
Entity shooter = this.getOwner();
if (shooter instanceof LivingEntity) {
Entity target = entityHitResult.getEntity();
if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) {
boolean hurt = target.hurtServer(worldserver, target.damageSources().mobProjectile(this, (LivingEntity) shooter), worldserver.purpurConfig.phantomFlameDamage);
if (hurt && worldserver.purpurConfig.phantomFlameFireTime > 0) {
target.igniteForSeconds(worldserver.purpurConfig.phantomFlameFireTime);
}
}
}
}
}
@Override
protected void onHitBlock(BlockHitResult blockHitResult) {
Level world = this.level();
if (world instanceof ServerLevel worldserver) {
if (this.hitCancelled) {
return;
}
if (this.canGrief) {
BlockState state = worldserver.getBlockState(blockHitResult.getBlockPos());
state.onProjectileHit(worldserver, state, blockHitResult, this);
}
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
}
}
}

View File

@@ -0,0 +1,58 @@
package org.purpurmc.purpur.gui;
import net.md_5.bungee.api.ChatColor;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
public enum GUIColor {
BLACK(ChatColor.BLACK, new Color(0x000000)),
DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)),
DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)),
DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)),
DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)),
DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)),
GOLD(ChatColor.GOLD, new Color(0xBB8800)),
GRAY(ChatColor.GRAY, new Color(0x888888)),
DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)),
BLUE(ChatColor.BLUE, new Color(0x5555FF)),
GREEN(ChatColor.GREEN, new Color(0x55FF55)),
AQUA(ChatColor.AQUA, new Color(0x55DDDD)),
RED(ChatColor.RED, new Color(0xFF5555)),
LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)),
YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)),
WHITE(ChatColor.WHITE, new Color(0xBBBBBB));
private final ChatColor chat;
private final Color color;
private static final Map<ChatColor, GUIColor> BY_CHAT = new HashMap<>();
GUIColor(ChatColor chat, Color color) {
this.chat = chat;
this.color = color;
}
public Color getColor() {
return color;
}
public ChatColor getChatColor() {
return chat;
}
public String getCode() {
return chat.toString();
}
public static GUIColor getColor(ChatColor chat) {
return BY_CHAT.get(chat);
}
static {
for (GUIColor color : values()) {
BY_CHAT.put(color.chat, color);
}
}
}

View File

@@ -0,0 +1,83 @@
package org.purpurmc.purpur.gui;
import com.google.common.collect.Sets;
import javax.swing.UIManager;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import javax.swing.JTextPane;
import javax.swing.Timer;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import java.util.Set;
public class JColorTextPane extends JTextPane {
private static final GUIColor DEFAULT_COLOR;
static {
DEFAULT_COLOR = UIManager.getSystemLookAndFeelClassName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")
? GUIColor.WHITE : GUIColor.BLACK;
}
public void append(String msg) {
// TODO: update to use adventure instead
BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, DEFAULT_COLOR.getChatColor());
for (BaseComponent component : components) {
String text = component.toPlainText();
if (text == null || text.isEmpty()) {
continue;
}
GUIColor guiColor = GUIColor.getColor(component.getColor());
StyleContext context = StyleContext.getDefaultStyleContext();
AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor());
attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR);
attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic());
attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined());
attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough());
//attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly
try {
int pos = getDocument().getLength();
getDocument().insertString(pos, text, attr);
if (component.isObfuscated()) {
// dirty hack to blink some text
Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground()));
BLINKS.add(blink);
}
} catch (BadLocationException ignore) {
}
}
}
private static final Set<Blink> BLINKS = Sets.newHashSet();
private static boolean SYNC_BLINK;
static {
new Timer(500, e -> {
SYNC_BLINK = !SYNC_BLINK;
BLINKS.forEach(Blink::blink);
}).start();
}
public class Blink {
private final int start, length;
private final AttributeSet attr1, attr2;
private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) {
this.start = start;
this.length = length;
this.attr1 = attr1;
this.attr2 = attr2;
}
private void blink() {
getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true);
}
}
}

View File

@@ -0,0 +1,26 @@
package org.purpurmc.purpur.item;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import org.bukkit.event.entity.EntityPotionEffectEvent;
public class GlowBerryItem extends BlockItem {
public GlowBerryItem(Block block, Properties settings) {
super(block, settings);
}
@Override
public ItemStack finishUsingItem(ItemStack stack, Level world, LivingEntity user) {
ItemStack result = super.finishUsingItem(stack, world, user);
if (world.purpurConfig.glowBerriesEatGlowDuration > 0 && user instanceof ServerPlayer player) {
player.addEffect(new MobEffectInstance(MobEffects.GLOWING, world.purpurConfig.glowBerriesEatGlowDuration), EntityPotionEffectEvent.Cause.FOOD);
}
return result;
}
}

View File

@@ -0,0 +1,43 @@
package org.purpurmc.purpur.item;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class SpawnerItem extends BlockItem {
public SpawnerItem(Block block, Properties settings) {
super(block, settings);
}
@Override
protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player, ItemStack stack, BlockState state) {
boolean handled = super.updateCustomBlockEntityTag(pos, level, player, stack, state);
if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.place.spawners")) {
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof SpawnerBlockEntity spawner) {
CompoundTag customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
Optional<String> mobTypeStringOptional = customData.getString("Purpur.mob_type");
if (mobTypeStringOptional.isPresent()) {
EntityType.byString(mobTypeStringOptional.get()).ifPresent(type -> spawner.getSpawner().setEntityId(type, level, level.random, pos));
} else if (customData.contains("Purpur.SpawnData")) {
customData.getCompound("Purpur.SpawnData")
.flatMap(spawnerData -> spawnerData.read("SpawnData", net.minecraft.world.level.SpawnData.CODEC))
.ifPresent(spawnData -> spawner.getSpawner().nextSpawnData = spawnData);
}
}
}
return handled;
}
}

View File

@@ -0,0 +1,27 @@
package org.purpurmc.purpur.network;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.Identifier;
import org.jetbrains.annotations.NotNull;
public record ClientboundBeehivePayload(BlockPos pos, int numOfBees) implements CustomPacketPayload {
public static final StreamCodec<FriendlyByteBuf, ClientboundBeehivePayload> STREAM_CODEC = CustomPacketPayload.codec(ClientboundBeehivePayload::write, ClientboundBeehivePayload::new);
public static final Type<ClientboundBeehivePayload> TYPE = new Type<>(Identifier.fromNamespaceAndPath("purpur", "beehive_s2c"));
public ClientboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) {
this(friendlyByteBuf.readBlockPos(), friendlyByteBuf.readInt());
}
private void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeBlockPos(this.pos);
friendlyByteBuf.writeInt(this.numOfBees);
}
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@@ -0,0 +1,26 @@
package org.purpurmc.purpur.network;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.Identifier;
import org.jetbrains.annotations.NotNull;
public record ServerboundBeehivePayload(BlockPos pos) implements CustomPacketPayload {
public static final StreamCodec<FriendlyByteBuf, ServerboundBeehivePayload> STREAM_CODEC = CustomPacketPayload.codec(ServerboundBeehivePayload::write, ServerboundBeehivePayload::new);
public static final Type<ServerboundBeehivePayload> TYPE = new Type<>(Identifier.fromNamespaceAndPath("purpur", "beehive_c2s"));
public ServerboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) {
this(friendlyByteBuf.readBlockPos());
}
private void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeBlockPos(this.pos);
}
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@@ -0,0 +1,67 @@
package org.purpurmc.purpur.task;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import org.purpurmc.purpur.network.ClientboundBeehivePayload;
import org.purpurmc.purpur.network.ServerboundBeehivePayload;
import org.purpurmc.purpur.util.MinecraftInternalPlugin;
public class BeehiveTask implements PluginMessageListener {
private static BeehiveTask instance;
public static BeehiveTask instance() {
if (instance == null) {
instance = new BeehiveTask();
}
return instance;
}
private final PluginBase plugin = new MinecraftInternalPlugin();
private BeehiveTask() {
}
public void register() {
Bukkit.getMessenger().registerOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString());
Bukkit.getMessenger().registerIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString(), this);
}
public void unregister() {
Bukkit.getMessenger().unregisterOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString());
Bukkit.getMessenger().unregisterIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString());
}
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] bytes) {
FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.copiedBuffer(bytes));
ServerboundBeehivePayload payload = ServerboundBeehivePayload.STREAM_CODEC.decode(byteBuf);
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
// targeted block info max range specified in client at net.minecraft.client.gui.hud.DebugHud#render
if (!payload.pos().getCenter().closerThan(serverPlayer.position(), 20)) return; // Targeted Block info max range is 20
if (serverPlayer.level().getChunkIfLoaded(payload.pos()) == null) return;
BlockEntity blockEntity = serverPlayer.level().getBlockEntity(payload.pos());
if (!(blockEntity instanceof BeehiveBlockEntity beehive)) {
return;
}
ClientboundBeehivePayload customPacketPayload = new ClientboundBeehivePayload(payload.pos(), beehive.getOccupantCount());
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
ClientboundBeehivePayload.STREAM_CODEC.encode(friendlyByteBuf, customPacketPayload);
byte[] byteArray = new byte[friendlyByteBuf.readableBytes()];
friendlyByteBuf.readBytes(byteArray);
player.sendPluginMessage(this.plugin, customPacketPayload.type().id().toString(), byteArray);
}
}

View File

@@ -0,0 +1,121 @@
package org.purpurmc.purpur.task;
import net.kyori.adventure.bossbar.BossBar;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.purpurmc.purpur.util.MinecraftInternalPlugin;
public abstract class BossBarTask extends BukkitRunnable {
private final Map<UUID, BossBar> bossbars = new HashMap<>();
private boolean started;
abstract BossBar createBossBar();
abstract void updateBossBar(BossBar bossbar, Player player);
@Override
public void run() {
Iterator<Map.Entry<UUID, BossBar>> iter = bossbars.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<UUID, BossBar> entry = iter.next();
Player player = Bukkit.getPlayer(entry.getKey());
if (player == null) {
iter.remove();
continue;
}
updateBossBar(entry.getValue(), player);
}
}
@Override
public void cancel() {
super.cancel();
new HashSet<>(this.bossbars.keySet()).forEach(uuid -> {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
removePlayer(player);
}
});
this.bossbars.clear();
}
public boolean removePlayer(Player player) {
BossBar bossbar = this.bossbars.remove(player.getUniqueId());
if (bossbar != null) {
player.hideBossBar(bossbar);
return true;
}
return false;
}
public void addPlayer(Player player) {
removePlayer(player);
BossBar bossbar = createBossBar();
this.bossbars.put(player.getUniqueId(), bossbar);
this.updateBossBar(bossbar, player);
player.showBossBar(bossbar);
}
public boolean hasPlayer(UUID uuid) {
return this.bossbars.containsKey(uuid);
}
public boolean togglePlayer(Player player) {
if (removePlayer(player)) {
return false;
}
addPlayer(player);
return true;
}
public void start() {
stop();
this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1);
started = true;
}
public void stop() {
if (started) {
cancel();
}
}
public static void startAll() {
RamBarTask.instance().start();
TPSBarTask.instance().start();
CompassTask.instance().start();
}
public static void stopAll() {
RamBarTask.instance().stop();
TPSBarTask.instance().stop();
CompassTask.instance().stop();
}
public static void addToAll(ServerPlayer player) {
Player bukkit = player.getBukkitEntity();
if (player.ramBar()) {
RamBarTask.instance().addPlayer(bukkit);
}
if (player.tpsBar()) {
TPSBarTask.instance().addPlayer(bukkit);
}
if (player.compassBar()) {
CompassTask.instance().addPlayer(bukkit);
}
}
public static void removeFromAll(Player player) {
RamBarTask.instance().removePlayer(player);
TPSBarTask.instance().removePlayer(player);
CompassTask.instance().removePlayer(player);
}
}

View File

@@ -0,0 +1,68 @@
package org.purpurmc.purpur.task;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.item.Items;
import org.bukkit.entity.Player;
import org.purpurmc.purpur.PurpurConfig;
public class CompassTask extends BossBarTask {
private static CompassTask instance;
private int tick = 0;
public static CompassTask instance() {
if (instance == null) {
instance = new CompassTask();
}
return instance;
}
@Override
public void run() {
if (++tick < PurpurConfig.commandCompassBarTickInterval) {
return;
}
tick = 0;
MinecraftServer.getServer().getAllLevels().forEach((level) -> {
if (level.purpurConfig.compassItemShowsBossBar) {
level.players().forEach(player -> {
if (!player.compassBar()) {
if (player.getMainHandItem().getItem() != Items.COMPASS && player.getOffhandItem().getItem() != Items.COMPASS) {
removePlayer(player.getBukkitEntity());
} else if (!hasPlayer(player.getUUID())) {
addPlayer(player.getBukkitEntity());
}
}
});
}
});
super.run();
}
@Override
BossBar createBossBar() {
return BossBar.bossBar(Component.text(""), PurpurConfig.commandCompassBarProgressPercent, PurpurConfig.commandCompassBarProgressColor, PurpurConfig.commandCompassBarProgressOverlay);
}
@Override
void updateBossBar(BossBar bossbar, Player player) {
float yaw = player.getLocation().getYaw();
int length = PurpurConfig.commandCompassBarTitle.length();
int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F));
bossbar.name(Component.text(PurpurConfig.commandCompassBarTitle.substring(pos - 25, pos + 25)));
}
private float normalize(float yaw) {
while (yaw < -180.0F) {
yaw += 360.0F;
}
while (yaw > 180.0F) {
yaw -= 360.0F;
}
return yaw;
}
}

View File

@@ -0,0 +1,117 @@
package org.purpurmc.purpur.task;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.entity.Player;
import org.purpurmc.purpur.PurpurConfig;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
public class RamBarTask extends BossBarTask {
private static RamBarTask instance;
private long allocated = 0L;
private long used = 0L;
private long xmx = 0L;
private long xms = 0L;
private float percent = 0F;
private int tick = 0;
public static RamBarTask instance() {
if (instance == null) {
instance = new RamBarTask();
}
return instance;
}
@Override
BossBar createBossBar() {
return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandRamBarProgressOverlay);
}
@Override
void updateBossBar(BossBar bossbar, Player player) {
bossbar.progress(getBossBarProgress());
bossbar.color(getBossBarColor());
bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandRamBarTitle,
Placeholder.component("allocated", format(this.allocated)),
Placeholder.component("used", format(this.used)),
Placeholder.component("xmx", format(this.xmx)),
Placeholder.component("xms", format(this.xms)),
Placeholder.unparsed("percent", ((int) (this.percent * 100)) + "%")
));
}
@Override
public void run() {
if (++this.tick < PurpurConfig.commandRamBarTickInterval) {
return;
}
this.tick = 0;
MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
this.allocated = heap.getCommitted();
this.used = heap.getUsed();
this.xmx = heap.getMax();
this.xms = heap.getInit();
this.percent = Math.max(Math.min((float) this.used / this.xmx, 1.0F), 0.0F);
super.run();
}
private float getBossBarProgress() {
return this.percent;
}
private BossBar.Color getBossBarColor() {
if (this.percent < 0.5F) {
return PurpurConfig.commandRamBarProgressColorGood;
} else if (this.percent < 0.75F) {
return PurpurConfig.commandRamBarProgressColorMedium;
} else {
return PurpurConfig.commandRamBarProgressColorLow;
}
}
public Component format(long v) {
String color;
if (this.percent < 0.60F) {
color = PurpurConfig.commandRamBarTextColorGood;
} else if (this.percent < 0.85F) {
color = PurpurConfig.commandRamBarTextColorMedium;
} else {
color = PurpurConfig.commandRamBarTextColorLow;
}
String value;
if (v < 1024) {
value = v + "B";
} else {
int z = (63 - Long.numberOfLeadingZeros(v)) / 10;
value = String.format("%.1f%s", (double) v / (1L << (z * 10)), "BKMGTPE".charAt(z));
}
return MiniMessage.miniMessage().deserialize(color, Placeholder.unparsed("text", value));
}
public long getAllocated() {
return this.allocated;
}
public long getUsed() {
return this.used;
}
public long getXmx() {
return this.xmx;
}
public long getXms() {
return this.xms;
}
public float getPercent() {
return this.percent;
}
}

View File

@@ -0,0 +1,142 @@
package org.purpurmc.purpur.task;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.purpurmc.purpur.PurpurConfig;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class TPSBarTask extends BossBarTask {
private static TPSBarTask instance;
private double tps = 20.0D;
private double mspt = 0.0D;
private int tick = 0;
public static TPSBarTask instance() {
if (instance == null) {
instance = new TPSBarTask();
}
return instance;
}
@Override
BossBar createBossBar() {
return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandTPSBarProgressOverlay);
}
@Override
void updateBossBar(BossBar bossbar, Player player) {
bossbar.progress(getBossBarProgress());
bossbar.color(getBossBarColor());
bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle,
Placeholder.component("tps", getTPSColor()),
Placeholder.component("mspt", getMSPTColor()),
Placeholder.component("ping", getPingColor(player.getPing()))
));
}
@Override
public void run() {
if (++tick < PurpurConfig.commandTPSBarTickInterval) {
return;
}
tick = 0;
this.tps = Math.max(Math.min(Bukkit.getTPS()[0], 20.0D), 0.0D);
this.mspt = Bukkit.getAverageTickTime();
super.run();
}
private float getBossBarProgress() {
if (PurpurConfig.commandTPSBarProgressFillMode == FillMode.MSPT) {
return Math.max(Math.min((float) mspt / 50.0F, 1.0F), 0.0F);
} else {
return Math.max(Math.min((float) tps / 20.0F, 1.0F), 0.0F);
}
}
private BossBar.Color getBossBarColor() {
if (isGood(PurpurConfig.commandTPSBarProgressFillMode)) {
return PurpurConfig.commandTPSBarProgressColorGood;
} else if (isMedium(PurpurConfig.commandTPSBarProgressFillMode)) {
return PurpurConfig.commandTPSBarProgressColorMedium;
} else {
return PurpurConfig.commandTPSBarProgressColorLow;
}
}
private boolean isGood(FillMode mode) {
return isGood(mode, 0);
}
private boolean isGood(FillMode mode, int ping) {
if (mode == FillMode.MSPT) {
return mspt < 40;
} else if (mode == FillMode.TPS) {
return tps >= 19;
} else if (mode == FillMode.PING) {
return ping < 100;
} else {
return false;
}
}
private boolean isMedium(FillMode mode) {
return isMedium(mode, 0);
}
private boolean isMedium(FillMode mode, int ping) {
if (mode == FillMode.MSPT) {
return mspt < 50;
} else if (mode == FillMode.TPS) {
return tps >= 15;
} else if (mode == FillMode.PING) {
return ping < 200;
} else {
return false;
}
}
private Component getTPSColor() {
String color;
if (isGood(FillMode.TPS)) {
color = PurpurConfig.commandTPSBarTextColorGood;
} else if (isMedium(FillMode.TPS)) {
color = PurpurConfig.commandTPSBarTextColorMedium;
} else {
color = PurpurConfig.commandTPSBarTextColorLow;
}
return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps)));
}
private Component getMSPTColor() {
String color;
if (isGood(FillMode.MSPT)) {
color = PurpurConfig.commandTPSBarTextColorGood;
} else if (isMedium(FillMode.MSPT)) {
color = PurpurConfig.commandTPSBarTextColorMedium;
} else {
color = PurpurConfig.commandTPSBarTextColorLow;
}
return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", mspt)));
}
private Component getPingColor(int ping) {
String color;
if (isGood(FillMode.PING, ping)) {
color = PurpurConfig.commandTPSBarTextColorGood;
} else if (isMedium(FillMode.PING, ping)) {
color = PurpurConfig.commandTPSBarTextColorMedium;
} else {
color = PurpurConfig.commandTPSBarTextColorLow;
}
return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping)));
}
public enum FillMode {
TPS, MSPT, PING
}
}

View File

@@ -0,0 +1,24 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Map;
public abstract class Actionable {
private final Block into;
private final Map<Item, Double> drops;
public Actionable(Block into, Map<Item, Double> drops) {
this.into = into;
this.drops = drops;
}
public Block into() {
return into;
}
public Map<Item, Double> drops() {
return drops;
}
}

View File

@@ -0,0 +1,12 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Map;
public class Flattenable extends Actionable {
public Flattenable(Block into, Map<Item, Double> drops) {
super(into, drops);
}
}

View File

@@ -0,0 +1,12 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Map;
public class Strippable extends Actionable {
public Strippable(Block into, Map<Item, Double> drops) {
super(into, drops);
}
}

View File

@@ -0,0 +1,50 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.HoeItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.block.Block;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
public class Tillable extends Actionable {
private final Condition condition;
public Tillable(Condition condition, Block into, Map<Item, Double> drops) {
super(into, drops);
this.condition = condition;
}
public Condition condition() {
return condition;
}
public enum Condition {
AIR_ABOVE(HoeItem::onlyIfAirAbove),
ALWAYS((useOnContext) -> true);
private final Predicate<UseOnContext> predicate;
Condition(Predicate<UseOnContext> predicate) {
this.predicate = predicate;
}
public Predicate<UseOnContext> predicate() {
return predicate;
}
private static final Map<String, Condition> BY_NAME = new HashMap<>();
static {
for (Condition condition : values()) {
BY_NAME.put(condition.name(), condition);
}
}
public static Condition get(String name) {
return BY_NAME.get(name.toUpperCase(java.util.Locale.ROOT));
}
}
}

View File

@@ -0,0 +1,12 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Map;
public class Waxable extends Actionable {
public Waxable(Block into, Map<Item, Double> drops) {
super(into, drops);
}
}

View File

@@ -0,0 +1,12 @@
package org.purpurmc.purpur.tool;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Map;
public class Weatherable extends Actionable {
public Weatherable(Block into, Map<Item, Double> drops) {
super(into, drops);
}
}

View File

@@ -0,0 +1,149 @@
package org.purpurmc.purpur.util;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.PluginLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.InputStream;
import java.util.List;
public class MinecraftInternalPlugin extends PluginBase {
private boolean enabled = true;
private final String pluginName;
private PluginDescriptionFile pdf;
public MinecraftInternalPlugin() {
this.pluginName = "Minecraft";
pdf = new PluginDescriptionFile(pluginName, "1.0", "nms");
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public File getDataFolder() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public PluginDescriptionFile getDescription() {
return pdf;
}
@Override
public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
return pdf;
}
@Override
public FileConfiguration getConfig() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public InputStream getResource(String filename) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void saveConfig() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void saveDefaultConfig() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void saveResource(String resourcePath, boolean replace) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void reloadConfig() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public PluginLogger getLogger() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public PluginLoader getPluginLoader() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Server getServer() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void onDisable() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void onLoad() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void onEnable() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean isNaggable() {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void setNaggable(boolean canNag) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public @NotNull LifecycleEventManager<Plugin> getLifecycleManager() {
throw new UnsupportedOperationException("Not supported.");
}
}