mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-18 08:57:44 +01:00
Upstream has released updates that appears to apply and compile correctly Tuinity Changes: 3b008f5 Optimisations 200f825 Actually unload POI data db64f14 Make sure to despawn entities if they are outside the player general 89276ac Fix villagers aggressively looking at people 8830cef Remove streams for poi searching in some zombie pathfinding a17dc2c Attempt to fix incorrect nearest village distance tracker updating ef8cd34 Fix NPE 3e45700Do not return complex parts for entity by class lookup 2110847 Rewrite getClosestEntity 460581d Fix getClosestEntity not working 2cb36ca Optimise non-flush packet sending 784b838 Some fixes e2dcdd1 Correct return value for ChunkCache#getCubes 968512b Add Velocity natives for encryption and compression (#188) 102d60b Rebuild patches 57fed71 Fix decompression with Velocity natives 442890b Fix decompression with Velocity natives (#191) 0179ea8 Re-Add region manager and notify patch cbffdcc Do not mark entities in unloaded chunks as being in blocks f2eef4a Fixup dev branch patches and store reverted patches in revert folder
412 lines
20 KiB
Diff
412 lines
20 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: William Blake Galbreath <blake.galbreath@gmail.com>
|
|
Date: Thu, 9 May 2019 18:09:43 -0500
|
|
Subject: [PATCH] Purpur config files
|
|
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
index 8a19c96499..de75e33a68 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
@@ -581,7 +581,7 @@ public class Metrics {
|
|
boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
|
|
// Only start Metrics, if it's enabled in the config
|
|
if (config.getBoolean("enabled", true)) {
|
|
- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
|
|
+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Purpur
|
|
|
|
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
|
String minecraftVersion = Bukkit.getVersion();
|
|
@@ -590,8 +590,8 @@ public class Metrics {
|
|
}));
|
|
|
|
metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
|
- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() || PaperConfig.isProxyOnlineMode() ? "online" : "offline"));
|
|
- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"));
|
|
+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (PaperConfig.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur
|
|
+ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); // Purpur
|
|
|
|
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
|
diff --git a/src/main/java/net/minecraft/server/DedicatedServer.java b/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
index fcba187bbd..c1eb8e8679 100644
|
|
--- a/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
@@ -167,6 +167,15 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
|
|
return false;
|
|
}
|
|
com.destroystokyo.paper.PaperConfig.registerCommands();
|
|
+ // Purpur start
|
|
+ try {
|
|
+ net.pl3x.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings"));
|
|
+ } catch (Exception e) {
|
|
+ DedicatedServer.LOGGER.error("Unable to load server configuration", e);
|
|
+ return false;
|
|
+ }
|
|
+ net.pl3x.purpur.PurpurConfig.registerCommands();
|
|
+ // Purpur end
|
|
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
|
|
// Paper end
|
|
com.tuinity.tuinity.config.TuinityConfig.init((java.io.File) options.valueOf("tuinity-settings")); // Tuinity - Server Config
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index f7103ec2a9..ab8fad689a 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -95,6 +95,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
|
|
|
public final com.tuinity.tuinity.config.TuinityConfig.WorldConfig tuinityConfig; // Tuinity - Server Config
|
|
+ public final net.pl3x.purpur.PurpurWorldConfig purpurConfig; // Purpur
|
|
|
|
public final co.aikar.timings.WorldTimingsHandler timings; // Paper
|
|
public static BlockPosition lastPhysicsProblem; // Spigot
|
|
@@ -154,8 +155,9 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
protected World(WorldDataMutable worlddatamutable, ResourceKey<World> resourcekey, final DimensionManager dimensionmanager, Supplier<GameProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((WorldDataServer) worlddatamutable).getName()); // Spigot
|
|
this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig((((WorldDataServer)worlddatamutable).getName()), this.spigotConfig); // Paper
|
|
- this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this, executor) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
|
|
this.tuinityConfig = new com.tuinity.tuinity.config.TuinityConfig.WorldConfig(((WorldDataServer)worlddatamutable).getName()); // Tuinity - Server Config
|
|
+ this.purpurConfig = new net.pl3x.purpur.PurpurWorldConfig((((WorldDataServer)worlddatamutable).getName())); // Purpur
|
|
+ this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this, executor) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
|
|
this.generator = gen;
|
|
this.world = new CraftWorld((WorldServer) this, gen, env);
|
|
this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit
|
|
diff --git a/src/main/java/net/pl3x/purpur/PurpurConfig.java b/src/main/java/net/pl3x/purpur/PurpurConfig.java
|
|
new file mode 100644
|
|
index 0000000000..1d00832bff
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/PurpurConfig.java
|
|
@@ -0,0 +1,130 @@
|
|
+package net.pl3x.purpur;
|
|
+
|
|
+import com.google.common.base.Throwables;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.pl3x.purpur.command.PurpurCommand;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.configuration.InvalidConfigurationException;
|
|
+import org.bukkit.configuration.file.YamlConfiguration;
|
|
+
|
|
+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;
|
|
+
|
|
+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://github.com/pl3xgaming/Purpur \n"
|
|
+ + "Wiki: https://github.com/pl3xgaming/Purpur/wiki \n";
|
|
+ private static File CONFIG_FILE;
|
|
+ public static YamlConfiguration config;
|
|
+
|
|
+ private static Map<String, Command> commands;
|
|
+
|
|
+ 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", 4);
|
|
+ set("config-version", 4);
|
|
+
|
|
+ readConfig(PurpurConfig.class, null);
|
|
+ }
|
|
+
|
|
+ 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 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));
|
|
+ }
|
|
+
|
|
+ private static String getString(String path, String def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getString(path, config.getString(path));
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
|
|
new file mode 100644
|
|
index 0000000000..361f7857e4
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
|
|
@@ -0,0 +1,59 @@
|
|
+package net.pl3x.purpur;
|
|
+
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import java.util.List;
|
|
+import static net.pl3x.purpur.PurpurConfig.log;
|
|
+
|
|
+public class PurpurWorldConfig {
|
|
+
|
|
+ private final String worldName;
|
|
+
|
|
+ public PurpurWorldConfig(String worldName) {
|
|
+ this.worldName = worldName;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ public void init() {
|
|
+ log("-------- World Settings For [" + worldName + "] --------");
|
|
+ PurpurConfig.readConfig(PurpurWorldConfig.class, this);
|
|
+ }
|
|
+
|
|
+ private void set(String path, Object val) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, val);
|
|
+ PurpurConfig.config.set("world-settings.default." + path, val);
|
|
+ if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) {
|
|
+ PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val);
|
|
+ PurpurConfig.config.set("world-settings." + worldName + "." + path, val);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private ConfigurationSection getConfigurationSection(String path) {
|
|
+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
|
|
+ return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path);
|
|
+ }
|
|
+
|
|
+ private boolean getBoolean(String path, boolean def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private double getDouble(String path, double def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private int getInt(String path, int def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private <T> List<?> getList(String path, T def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private String getString(String path, String def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path));
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/command/PurpurCommand.java b/src/main/java/net/pl3x/purpur/command/PurpurCommand.java
|
|
new file mode 100644
|
|
index 0000000000..4904be939c
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/command/PurpurCommand.java
|
|
@@ -0,0 +1,65 @@
|
|
+package net.pl3x.purpur.command;
|
|
+
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.WorldServer;
|
|
+import net.pl3x.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 (WorldServer world : console.getWorlds()) {
|
|
+ world.purpurConfig.init();
|
|
+ }
|
|
+ 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;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 260d3513ec..51ef4ab09e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -857,6 +857,7 @@ public final class CraftServer implements Server {
|
|
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
|
|
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper
|
|
com.tuinity.tuinity.config.TuinityConfig.init((File) console.options.valueOf("tuinity-settings")); // Tuinity - Server Config
|
|
+ net.pl3x.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur
|
|
for (WorldServer world : console.getWorlds()) {
|
|
world.worldDataServer.setDifficulty(config.difficulty);
|
|
world.setSpawnFlags(config.spawnMonsters, config.spawnAnimals);
|
|
@@ -892,6 +893,7 @@ public final class CraftServer implements Server {
|
|
world.spigotConfig.init(); // Spigot
|
|
world.paperConfig.init(); // Paper
|
|
world.tuinityConfig.init(); // Tuinity - Server Config
|
|
+ world.purpurConfig.init(); // Purpur
|
|
}
|
|
|
|
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
|
|
@@ -910,6 +912,7 @@ public final class CraftServer implements Server {
|
|
reloadData();
|
|
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
|
|
com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper
|
|
+ net.pl3x.purpur.PurpurConfig.registerCommands(); // Purpur
|
|
overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
|
ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
|
|
|
@@ -2285,6 +2288,18 @@ public final class CraftServer implements Server {
|
|
}
|
|
// Tuinity end - add config to timings report
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public YamlConfiguration getPurpurConfig() {
|
|
+ return net.pl3x.purpur.PurpurConfig.config;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Properties getServerProperties() {
|
|
+ return getProperties().properties;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void restart() {
|
|
org.spigotmc.RestartCommand.restart();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
index a9c96d45c6..b65629cac7 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
@@ -146,6 +146,14 @@ public class Main {
|
|
.describedAs("Yml file");
|
|
/* Conctete End - Server Config */
|
|
|
|
+ // Purpur Start
|
|
+ acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings")
|
|
+ .withRequiredArg()
|
|
+ .ofType(File.class)
|
|
+ .defaultsTo(new File("purpur.yml"))
|
|
+ .describedAs("Yml file");
|
|
+ // Purpur end
|
|
+
|
|
// Paper start
|
|
acceptsAll(asList("server-name"), "Name of the server")
|
|
.withRequiredArg()
|