mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-15 23:20:44 +00:00
Merge branch 'railswitch' into mc/1.20
# Conflicts: # settings.gradle.kts
This commit is contained in:
16
plugins/railswitch-paper/.editorconfig
Normal file
16
plugins/railswitch-paper/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editorconfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
continuation_indent_size = 8
|
||||
|
||||
[{*.xml, *.yml}]
|
||||
indent_size = 2
|
||||
22
plugins/railswitch-paper/LICENSE.txt
Normal file
22
plugins/railswitch-paper/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 okx-code
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
26
plugins/railswitch-paper/README.md
Normal file
26
plugins/railswitch-paper/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# RailSwitch
|
||||
|
||||
## How to use
|
||||
|
||||
### Setup
|
||||
|
||||
1. Place a sign in the block above a detector rail. **It must be reinforced on the same group as the detector rail.**
|
||||
2. The first line must be `[destination]` (case insensitive)
|
||||
3. The second, third and fourth lines can be destination names.
|
||||
4. When a player passes over the detector rail, it will only activate and emit redstone if a player set their destination as any of the three on the sign.
|
||||
|
||||
You may also use `[!destination]`, which will activate the rail if a player's destination is **not** on the signs.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Type `/dest <destination>`
|
||||
2. Go AFK
|
||||
3. Arrive
|
||||
|
||||
You can also use spaces in a `/dest` command as an "or" operator, like `/dest <destination1> <destination2>`. In this command, RailSwitch will route players towards signs that contain either `<destination1>` or `<destination2>`.
|
||||
|
||||
Another way to think about it is that `/dest` takes multiple arguments. Each argument is a destination that will be checked as you pass a sign.
|
||||
|
||||
### Example Video
|
||||
|
||||
[](https://www.youtube.com/watch?v=GKku2fcB-wY)
|
||||
13
plugins/railswitch-paper/build.gradle.kts
Normal file
13
plugins/railswitch-paper/build.gradle.kts
Normal file
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev")
|
||||
}
|
||||
|
||||
version = "2.0.0-SNAPSHOT"
|
||||
|
||||
dependencies {
|
||||
paperDevBundle("1.18.2-R0.1-SNAPSHOT")
|
||||
|
||||
compileOnly(project(":plugins:civmodcore-paper"))
|
||||
compileOnly(project(":plugins:namelayer-paper"))
|
||||
compileOnly(project(":plugins:citadel-paper"))
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package sh.okx.railswitch;
|
||||
|
||||
import org.bukkit.event.Listener;
|
||||
import sh.okx.railswitch.commands.DestinationCommand;
|
||||
import sh.okx.railswitch.glue.CitadelGlue;
|
||||
import sh.okx.railswitch.settings.SettingsManager;
|
||||
import sh.okx.railswitch.switches.SwitchListener;
|
||||
import vg.civcraft.mc.civmodcore.ACivMod;
|
||||
import vg.civcraft.mc.civmodcore.commands.CommandManager;
|
||||
|
||||
/**
|
||||
* The Rail Switch plugin class
|
||||
*/
|
||||
public final class RailSwitchPlugin extends ACivMod implements Listener {
|
||||
|
||||
private CommandManager commandManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
super.onEnable();
|
||||
SettingsManager.init(this);
|
||||
registerListener(new CitadelGlue(this));
|
||||
registerListener(new SwitchListener());
|
||||
commandManager = new CommandManager(this);
|
||||
commandManager.init();
|
||||
commandManager.registerCommand(new DestinationCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
SettingsManager.reset();
|
||||
if (commandManager != null) {
|
||||
commandManager.reset();
|
||||
commandManager = null;
|
||||
}
|
||||
super.onDisable();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package sh.okx.railswitch.commands;
|
||||
|
||||
import co.aikar.commands.BaseCommand;
|
||||
import co.aikar.commands.annotation.CommandAlias;
|
||||
import co.aikar.commands.annotation.Description;
|
||||
import co.aikar.commands.annotation.Optional;
|
||||
import co.aikar.commands.annotation.Syntax;
|
||||
import org.bukkit.entity.Player;
|
||||
import sh.okx.railswitch.settings.SettingsManager;
|
||||
|
||||
/**
|
||||
* Continued support for setting and resetting your destination via a command.
|
||||
*/
|
||||
public final class DestinationCommand extends BaseCommand {
|
||||
|
||||
@CommandAlias("dest|destination|setdestination|switch|setswitch|setsw")
|
||||
@Description("Set your rail destination(s)")
|
||||
@Syntax("[destination]")
|
||||
public void onSetDestination(Player player, @Optional String destination) {
|
||||
SettingsManager.setDestination(player, destination);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package sh.okx.railswitch.glue;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import vg.civcraft.mc.citadel.Citadel;
|
||||
import vg.civcraft.mc.citadel.ReinforcementManager;
|
||||
import vg.civcraft.mc.citadel.model.Reinforcement;
|
||||
import vg.civcraft.mc.civmodcore.utilities.DependencyGlue;
|
||||
import vg.civcraft.mc.civmodcore.utilities.NullUtils;
|
||||
import vg.civcraft.mc.civmodcore.world.WorldUtils;
|
||||
|
||||
/**
|
||||
* Glue for Citadel.
|
||||
*/
|
||||
public final class CitadelGlue extends DependencyGlue {
|
||||
|
||||
private ReinforcementManager manager;
|
||||
|
||||
public CitadelGlue(Plugin plugin) {
|
||||
super(plugin, "Citadel");
|
||||
}
|
||||
|
||||
public boolean isSafeToUse() {
|
||||
if (!super.isDependencyEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (this.manager == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do two blocks, representing the sign and the rail, have the same reinforcement group?
|
||||
*
|
||||
* @param sign The block representing the sign.
|
||||
* @param rail The block representing the rail.
|
||||
* @return Returns true both blocks share the same reinforcement group, or if both are un-reinforced.
|
||||
*/
|
||||
public boolean doSignAndRailHaveSameReinforcement(Block sign, Block rail) {
|
||||
if (!isSafeToUse() || !WorldUtils.isValidBlock(sign) || !WorldUtils.isValidBlock(rail)) {
|
||||
return false;
|
||||
}
|
||||
Reinforcement signReinforcement = this.manager.getReinforcement(sign);
|
||||
Reinforcement railReinforcement = this.manager.getReinforcement(rail);
|
||||
if (signReinforcement == null && railReinforcement == null) {
|
||||
return true;
|
||||
}
|
||||
if (signReinforcement == null || railReinforcement == null) {
|
||||
return false;
|
||||
}
|
||||
return NullUtils.equalsNotNull(
|
||||
signReinforcement.getGroup(),
|
||||
railReinforcement.getGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDependencyEnabled() {
|
||||
this.manager = Citadel.getInstance().getReinforcementManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDependencyDisabled() {
|
||||
this.manager = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package sh.okx.railswitch.settings;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import vg.civcraft.mc.civmodcore.players.settings.impl.StringSetting;
|
||||
|
||||
/**
|
||||
* Setting representing a player's destination.
|
||||
*/
|
||||
public final class DestinationSetting extends StringSetting {
|
||||
|
||||
public DestinationSetting(JavaPlugin plugin) {
|
||||
super(plugin, "", "Destination", "dest", new ItemStack(Material.MINECART),
|
||||
"The destination(s) that will be used to route you at rail junctions.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toText(String value) {
|
||||
if (Strings.isNullOrEmpty(value)) {
|
||||
return "<empty>";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package sh.okx.railswitch.settings;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import vg.civcraft.mc.civmodcore.players.settings.PlayerSettingAPI;
|
||||
import vg.civcraft.mc.civmodcore.players.settings.gui.MenuSection;
|
||||
|
||||
/**
|
||||
* The menu for RailSwitch. This is what all RailSwitch settings will be registered to.
|
||||
*/
|
||||
public final class RailSwitchMenu extends MenuSection {
|
||||
|
||||
public RailSwitchMenu() {
|
||||
super("RailSwitch", "Settings relating to RailSwitch", PlayerSettingAPI.getMainMenu(),
|
||||
new ItemStack(Material.RAIL));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package sh.okx.railswitch.settings;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import vg.civcraft.mc.civmodcore.players.settings.gui.MenuSection;
|
||||
import vg.civcraft.mc.civmodcore.players.settings.impl.StringSetting;
|
||||
|
||||
/**
|
||||
* ResetSetting, a setting that functions more like a GUI button than an actual setting.
|
||||
*/
|
||||
public final class ResetSetting extends StringSetting {
|
||||
|
||||
private final DestinationSetting destinationSetting;
|
||||
|
||||
public ResetSetting(JavaPlugin plugin, DestinationSetting destinationSetting) {
|
||||
super(plugin, "", "Clear Destination", "dest", new ItemStack(Material.BARRIER), "Clears your destination.");
|
||||
this.destinationSetting = destinationSetting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMenuClick(Player player, MenuSection menu) {
|
||||
resetPlayerDestination(player);
|
||||
menu.showScreen(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a player's destination value.
|
||||
*
|
||||
* @param player The player to reset the destination for.
|
||||
*/
|
||||
public void resetPlayerDestination(Player player) {
|
||||
this.destinationSetting.setValue(player, "");
|
||||
player.sendMessage(ChatColor.GREEN + "Your destination has been reset.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(UUID player) {
|
||||
return this.destinationSetting.getValue(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toText(String value) {
|
||||
return this.destinationSetting.toText(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package sh.okx.railswitch.settings;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import sh.okx.railswitch.RailSwitchPlugin;
|
||||
|
||||
/**
|
||||
* Manages the initialisation and registration of menu settings.
|
||||
*/
|
||||
public final class SettingsManager {
|
||||
|
||||
private static RailSwitchMenu menu;
|
||||
|
||||
private static DestinationSetting destSetting;
|
||||
|
||||
private static ResetSetting resetSetting;
|
||||
|
||||
/**
|
||||
* Initialise the settings manager. This should only be called within RailSwitch onEnable().
|
||||
*
|
||||
* @param plugin The enabled RailSwitch plugin instance.
|
||||
*/
|
||||
public static void init(RailSwitchPlugin plugin) {
|
||||
// Create the menu elements
|
||||
menu = new RailSwitchMenu();
|
||||
destSetting = new DestinationSetting(plugin);
|
||||
resetSetting = new ResetSetting(plugin, destSetting);
|
||||
// Register those elements
|
||||
menu.registerToParentMenu();
|
||||
menu.registerSetting(destSetting);
|
||||
menu.registerSetting(resetSetting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully resets the settings manager. This should only be called within RailSwitch onDisable().
|
||||
*/
|
||||
public static void reset() {
|
||||
// TODO: Deregister and unload all the menu elements once PlayerSettingAPI becomes reload safe
|
||||
menu = null;
|
||||
destSetting = null;
|
||||
resetSetting = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's destination. This is for when the player settings don't themselves provide a way to do so.
|
||||
*
|
||||
* @param player The player to set the destination to.
|
||||
* @param destination The destination to set.
|
||||
*/
|
||||
public static void setDestination(Player player, String destination) {
|
||||
Preconditions.checkArgument(player != null);
|
||||
if (Strings.isNullOrEmpty(destination)) {
|
||||
if (resetSetting != null) {
|
||||
resetSetting.resetPlayerDestination(player);
|
||||
// Do not put a message here since the message is sent in the method above.
|
||||
}
|
||||
else {
|
||||
player.sendMessage(ChatColor.RED + "Could not reset your destination.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (destSetting != null) {
|
||||
destSetting.setValue(player, destination);
|
||||
player.sendMessage(ChatColor.GREEN + "Destination set to: " + destination);
|
||||
}
|
||||
else {
|
||||
player.sendMessage(ChatColor.RED + "Could not set your destination.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a player's destination.
|
||||
*
|
||||
* @param player The player to get the destination for.
|
||||
* @return Returns the player's destination, which will never be null.
|
||||
*/
|
||||
public static String getDestination(Player player) {
|
||||
String value = destSetting.getValue(player);
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package sh.okx.railswitch.switches;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Arrays;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Minecart;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||
import sh.okx.railswitch.RailSwitchPlugin;
|
||||
import sh.okx.railswitch.glue.CitadelGlue;
|
||||
import sh.okx.railswitch.settings.SettingsManager;
|
||||
import vg.civcraft.mc.civmodcore.world.WorldUtils;
|
||||
|
||||
/**
|
||||
* Switch listener that implements switch functionality.
|
||||
*/
|
||||
public class SwitchListener implements Listener {
|
||||
|
||||
public static final String WILDCARD = "*";
|
||||
|
||||
public static final CitadelGlue CITADEL_GLUE = new CitadelGlue(RailSwitchPlugin.getPlugin(RailSwitchPlugin.class));
|
||||
|
||||
/**
|
||||
* Event handler for rail switches. Will determine if a switch exists at the target location, and if so will process
|
||||
* it accordingly, allowing it to trigger or not trigger depending on the rider's set destination, the listed
|
||||
* destinations on the switch, and the switch type.
|
||||
*
|
||||
* @param event The block redstone event to base the switch's existence on.
|
||||
*/
|
||||
@EventHandler
|
||||
public void onSwitchTrigger(BlockRedstoneEvent event) {
|
||||
Block block = event.getBlock();
|
||||
// Block must be a detector rail being triggered
|
||||
if (!WorldUtils.isValidBlock(block)
|
||||
|| block.getType() != Material.DETECTOR_RAIL
|
||||
|| event.getNewCurrent() != 15) {
|
||||
return;
|
||||
}
|
||||
// Check that the block above the rail is a sign
|
||||
Block above = block.getRelative(BlockFace.UP);
|
||||
if (!Tag.SIGNS.isTagged(above.getType())
|
||||
|| !(above.getState() instanceof Sign)) {
|
||||
return;
|
||||
}
|
||||
// Check that the sign has a valid switch type
|
||||
String[] lines = ((Sign) above.getState()).getLines();
|
||||
SwitchType type = SwitchType.find(lines[0]);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
// Check that a player is triggering the switch
|
||||
// NOTE: The event doesn't provide the information and so the next best thing is searching for a
|
||||
// player who is nearby and riding a minecart.
|
||||
Player player = null; {
|
||||
double searchDistance = Double.MAX_VALUE;
|
||||
for (Entity entity : block.getWorld().getNearbyEntities(block.getLocation(), 3, 3, 3)) {
|
||||
if (!(entity instanceof Player)) {
|
||||
continue;
|
||||
}
|
||||
Entity vehicle = entity.getVehicle();
|
||||
// TODO: This should be abstracted into CivModCore
|
||||
if (vehicle == null
|
||||
|| vehicle.getType() != EntityType.MINECART
|
||||
|| !(vehicle instanceof Minecart)) {
|
||||
continue;
|
||||
}
|
||||
double distance = block.getLocation().distanceSquared(entity.getLocation());
|
||||
if (distance < searchDistance) {
|
||||
searchDistance = distance;
|
||||
player = (Player) entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
// If Citadel is enabled, check that the sign and the rail are on the same group
|
||||
if (CITADEL_GLUE.isSafeToUse()) {
|
||||
if (!CITADEL_GLUE.doSignAndRailHaveSameReinforcement(above, block)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Determine whether a player has a destination that matches one of the destinations
|
||||
// listed on the switch signs, or match if there's a wildcard.
|
||||
boolean matched = false;
|
||||
String setDest = SettingsManager.getDestination(player);
|
||||
if (!Strings.isNullOrEmpty(setDest)) {
|
||||
String[] playerDestinations = setDest.split(" ");
|
||||
String[] switchDestinations = Arrays.copyOfRange(lines, 1, lines.length);
|
||||
matcher:
|
||||
for (String playerDestination : playerDestinations) {
|
||||
if (Strings.isNullOrEmpty(playerDestination)) {
|
||||
continue;
|
||||
}
|
||||
if (playerDestination.equals(WILDCARD)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
for (String switchDestination : switchDestinations) {
|
||||
if (Strings.isNullOrEmpty(switchDestination)) {
|
||||
continue;
|
||||
}
|
||||
if (switchDestination.equals(WILDCARD)
|
||||
|| playerDestination.equalsIgnoreCase(switchDestination)) {
|
||||
matched = true;
|
||||
break matcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case NORMAL:
|
||||
event.setNewCurrent(matched ? 15 : 0);
|
||||
break;
|
||||
case INVERTED:
|
||||
event.setNewCurrent(matched ? 0 : 15);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package sh.okx.railswitch.switches;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Switch type matcher, will match a type to a tag.
|
||||
*/
|
||||
public enum SwitchType {
|
||||
|
||||
NORMAL("[destination]"),
|
||||
INVERTED("[!destination]");
|
||||
|
||||
private final String tag;
|
||||
|
||||
/**
|
||||
* Defines a new switch type by its tag. Should there be a duplicate tag, the {@link SwitchType#find(String) find()}
|
||||
* function will return the first value found.
|
||||
*
|
||||
* @param tag The tag to associate with this type.
|
||||
*/
|
||||
SwitchType(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match a switch type to a switch tag.
|
||||
*
|
||||
* @param tag The tag to match with a type.
|
||||
* @return Returns a match switch type, or null if none are found.
|
||||
*/
|
||||
public static SwitchType find(String tag) {
|
||||
if (tag == null || tag.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (SwitchType type : values()) {
|
||||
if (StringUtils.equalsIgnoreCase(tag, type.tag)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
10
plugins/railswitch-paper/src/main/resources/plugin.yml
Normal file
10
plugins/railswitch-paper/src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
name: RailSwitch
|
||||
version: ${version}
|
||||
main: sh.okx.railswitch.RailSwitchPlugin
|
||||
depend: [CivModCore]
|
||||
softdepend: [Citadel, NameLayer]
|
||||
api-version: 1.17
|
||||
authors: [Okx, Protonull]
|
||||
description: Easily make rail switches for automated destination selection
|
||||
commands:
|
||||
dest: {}
|
||||
@@ -28,3 +28,4 @@ include(":plugins:essenceglue-paper")
|
||||
include(":plugins:hiddenore-paper")
|
||||
include(":plugins:kirabukkitgateway-paper")
|
||||
include(":plugins:namecolors-paper")
|
||||
include(":plugins:railswitch-paper")
|
||||
|
||||
Reference in New Issue
Block a user