Initial Development
- Add Stop Complete
This commit is contained in:
commit
3566555893
31 files changed
+1018
No files matched your search
@@ -0,0 +1,72 @@
|
||||
|
||||
package com.rsscripting.rstaxi;
|
||||
|
||||
import com.rsscripting.rstaxi.commands.CabCommand;
|
||||
import com.rsscripting.rstaxi.language.LanguageManager;
|
||||
import com.rsscripting.rstaxi.database.DatabaseManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class RSTaxi extends JavaPlugin {
|
||||
|
||||
private LanguageManager languageManager;
|
||||
private DatabaseManager databaseManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
saveDefaultConfig();
|
||||
|
||||
if (!new java.io.File(getDataFolder(), "language/en_us.yml").exists()) {
|
||||
saveResource("language/en_us.yml", false);
|
||||
}
|
||||
|
||||
languageManager = new LanguageManager(this);
|
||||
languageManager.load();
|
||||
|
||||
databaseManager = new DatabaseManager(this);
|
||||
|
||||
try {
|
||||
databaseManager.connect();
|
||||
databaseManager.initialize();
|
||||
} catch (java.sql.SQLException e) {
|
||||
getLogger().severe("Failed to initialize SQLite database!");
|
||||
getLogger().severe(e.getMessage());
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getCommand("cab") == null) {
|
||||
getLogger().severe("Command 'cab' is missing from plugin.yml!");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
getCommand("cab").setExecutor(new CabCommand(this));
|
||||
|
||||
getLogger().info("RS-Taxi has been enabled!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
if (databaseManager != null) {
|
||||
databaseManager.close();
|
||||
}
|
||||
|
||||
getLogger().info("RS-Taxi has been disabled!");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public LanguageManager getLanguageManager() {
|
||||
return languageManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public DatabaseManager getDatabaseManager() {
|
||||
return databaseManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
|
||||
package com.rsscripting.rstaxi.commands;
|
||||
|
||||
import com.rsscripting.rstaxi.RSTaxi;
|
||||
import com.rsscripting.rstaxi.platform.PlatformValidator;
|
||||
import com.rsscripting.rstaxi.stops.StopManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class CabCommand implements CommandExecutor {
|
||||
|
||||
private final RSTaxi plugin;
|
||||
private final PlatformValidator platformValidator;
|
||||
private final StopManager stopManager;
|
||||
|
||||
public CabCommand(RSTaxi plugin) {
|
||||
this.plugin = plugin;
|
||||
this.platformValidator = new PlatformValidator(plugin);
|
||||
this.stopManager = new StopManager(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String label,
|
||||
String[] args
|
||||
) {
|
||||
if (args.length == 0) {
|
||||
return handleTravel(sender, args);
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
return handleAdd(sender, args);
|
||||
}
|
||||
case "update" -> {
|
||||
return handleUpdate(sender, args);
|
||||
}
|
||||
case "edit" -> {
|
||||
return handleEdit(sender, args);
|
||||
}
|
||||
case "remove" -> {
|
||||
return handleRemove(sender, args);
|
||||
}
|
||||
case "list" -> {
|
||||
return handleList(sender);
|
||||
}
|
||||
default -> {
|
||||
return handleTravel(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleTravel(
|
||||
CommandSender sender,
|
||||
String[] args
|
||||
) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(
|
||||
plugin.getLanguageManager()
|
||||
.getMessage("general.player-only")
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!player.hasPermission("rs-cab.use")) {
|
||||
player.sendMessage(
|
||||
plugin.getLanguageManager()
|
||||
.getMessage("general.no-permission")
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Travel behavior will be implemented in Phase 2.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean handleAdd(
|
||||
CommandSender sender,
|
||||
String[] args
|
||||
) {
|
||||
if (!sender.hasPermission("rs-cab.add")) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("general.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("general.player-only"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String arguments = String.join(" ", args)
|
||||
.substring("add".length()).trim();
|
||||
|
||||
String name;
|
||||
String icon;
|
||||
|
||||
if (arguments.startsWith("'")) {
|
||||
int closingQuote = arguments.indexOf('\'', 1);
|
||||
|
||||
if (closingQuote < 2) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("general.invalid-usage"));
|
||||
return true;
|
||||
}
|
||||
|
||||
name = arguments.substring(1, closingQuote);
|
||||
icon = arguments.substring(closingQuote + 1).trim();
|
||||
} else {
|
||||
String[] parts = arguments.split("\\s+");
|
||||
|
||||
if (parts.length != 2) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("general.invalid-usage"));
|
||||
return true;
|
||||
}
|
||||
|
||||
name = parts[0];
|
||||
icon = parts[1];
|
||||
}
|
||||
|
||||
if (name.isBlank() || icon.isBlank()
|
||||
|| icon.contains(" ")) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("general.invalid-usage"));
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (stopManager.exists(name)) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("stop.already-exists",
|
||||
Map.of("stop", name)));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!platformValidator.isValid(player)) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("platform.invalid"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stopManager.add(name, icon, player.getLocation())) {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("stop.added",
|
||||
Map.of("stop", name)));
|
||||
} else {
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("stop.already-exists",
|
||||
Map.of("stop", name)));
|
||||
}
|
||||
|
||||
} catch (SQLException | IllegalArgumentException e) {
|
||||
plugin.getLogger().severe(
|
||||
"Failed to create taxi stop: " + e.getMessage()
|
||||
);
|
||||
sender.sendMessage(plugin.getLanguageManager()
|
||||
.getMessage("stop.creation-error"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean handleUpdate(
|
||||
CommandSender sender,
|
||||
String[] args
|
||||
) {
|
||||
return checkPermission(sender, "rs-cab.edit");
|
||||
}
|
||||
|
||||
private boolean handleEdit(
|
||||
CommandSender sender,
|
||||
String[] args
|
||||
) {
|
||||
return checkPermission(sender, "rs-cab.edit");
|
||||
}
|
||||
|
||||
private boolean handleRemove(
|
||||
CommandSender sender,
|
||||
String[] args
|
||||
) {
|
||||
return checkPermission(sender, "rs-cab.delete");
|
||||
}
|
||||
|
||||
private boolean handleList(CommandSender sender) {
|
||||
return checkPermission(sender, "rs-cab.use");
|
||||
}
|
||||
|
||||
private boolean checkPermission(
|
||||
CommandSender sender,
|
||||
String permission
|
||||
) {
|
||||
if (!sender.hasPermission(permission)) {
|
||||
sender.sendMessage(
|
||||
plugin.getLanguageManager()
|
||||
.getMessage("general.no-permission")
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
package com.rsscripting.rstaxi.database;
|
||||
|
||||
import com.rsscripting.rstaxi.RSTaxi;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public final class DatabaseManager {
|
||||
|
||||
private final RSTaxi plugin;
|
||||
private Connection connection;
|
||||
|
||||
public DatabaseManager(RSTaxi plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void connect() throws SQLException {
|
||||
File folder = plugin.getDataFolder();
|
||||
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
throw new SQLException(
|
||||
"Could not create plugin data folder."
|
||||
);
|
||||
}
|
||||
|
||||
File databaseFile = new File(folder, "taxi.db");
|
||||
String url = "jdbc:sqlite:" + databaseFile.getAbsolutePath();
|
||||
|
||||
connection = DriverManager.getConnection(url);
|
||||
|
||||
plugin.getLogger().info("SQLite database connected.");
|
||||
}
|
||||
|
||||
public void initialize() throws SQLException {
|
||||
if (connection == null || connection.isClosed()) {
|
||||
throw new SQLException("Database is not connected.");
|
||||
}
|
||||
|
||||
String sql = """
|
||||
CREATE TABLE IF NOT EXISTS stops (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL COLLATE NOCASE UNIQUE,
|
||||
icon TEXT NOT NULL,
|
||||
world TEXT NOT NULL,
|
||||
x REAL NOT NULL,
|
||||
y REAL NOT NULL,
|
||||
z REAL NOT NULL,
|
||||
yaw REAL NOT NULL,
|
||||
pitch REAL NOT NULL
|
||||
);
|
||||
""";
|
||||
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute(sql);
|
||||
}
|
||||
|
||||
plugin.getLogger().info("SQLite database initialized.");
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
connection.close();
|
||||
plugin.getLogger().info("SQLite database connection closed.");
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().severe(
|
||||
"Failed to close SQLite database: "
|
||||
+ e.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
package com.rsscripting.rstaxi.language;
|
||||
|
||||
import com.rsscripting.rstaxi.RSTaxi;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
public final class LanguageManager {
|
||||
|
||||
private final RSTaxi plugin;
|
||||
private YamlConfiguration language;
|
||||
private YamlConfiguration english;
|
||||
|
||||
public LanguageManager(RSTaxi plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
File languageFolder =
|
||||
new File(plugin.getDataFolder(), "language");
|
||||
|
||||
if (!languageFolder.exists() && !languageFolder.mkdirs()) {
|
||||
plugin.getLogger().severe(
|
||||
"Could not create the language folder."
|
||||
);
|
||||
}
|
||||
|
||||
File englishFile =
|
||||
new File(languageFolder, "en_us.yml");
|
||||
|
||||
if (!englishFile.exists()) {
|
||||
plugin.saveResource("language/en_us.yml", false);
|
||||
}
|
||||
|
||||
english = YamlConfiguration.loadConfiguration(englishFile);
|
||||
|
||||
String selected = plugin.getConfig()
|
||||
.getString("language", "en_us")
|
||||
.trim();
|
||||
|
||||
if (!selected.matches("[a-zA-Z0-9_-]+")) {
|
||||
plugin.getLogger().warning(
|
||||
"Invalid language setting. Using en_us."
|
||||
);
|
||||
selected = "en_us";
|
||||
}
|
||||
|
||||
File selectedFile =
|
||||
new File(languageFolder, selected + ".yml");
|
||||
|
||||
if (!selectedFile.isFile()) {
|
||||
plugin.getLogger().warning(
|
||||
"Language file '" + selected
|
||||
+ ".yml' was not found. Using en_us."
|
||||
);
|
||||
language = english;
|
||||
return;
|
||||
}
|
||||
|
||||
language = YamlConfiguration.loadConfiguration(selectedFile);
|
||||
plugin.getLogger().info(
|
||||
"Loaded language: " + selected
|
||||
);
|
||||
}
|
||||
|
||||
public String getMessage(String path) {
|
||||
return getMessage(path, Map.of());
|
||||
}
|
||||
|
||||
public String getMessage(
|
||||
String path,
|
||||
Map<String, String> placeholders
|
||||
) {
|
||||
String message = language == null
|
||||
? null
|
||||
: language.getString(path);
|
||||
|
||||
if (message == null && english != null) {
|
||||
message = english.getString(path);
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
plugin.getLogger().warning(
|
||||
"Missing language message: " + path
|
||||
);
|
||||
message = "&cMissing message: " + path;
|
||||
}
|
||||
|
||||
String prefix = language == null
|
||||
? ""
|
||||
: language.getString("prefix", "");
|
||||
|
||||
if (!path.equals("prefix") && !prefix.isEmpty()) {
|
||||
message = prefix + message;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry
|
||||
: placeholders.entrySet()) {
|
||||
message = message.replace(
|
||||
"%" + entry.getKey() + "%",
|
||||
entry.getValue()
|
||||
);
|
||||
}
|
||||
|
||||
return ChatColor.translateAlternateColorCodes('&', message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
package com.rsscripting.rstaxi.platform;
|
||||
|
||||
import com.rsscripting.rstaxi.RSTaxi;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class PlatformValidator {
|
||||
|
||||
private final RSTaxi plugin;
|
||||
|
||||
private static final Map<String, int[]> OFFSETS = Map.of(
|
||||
"north", new int[]{0, -1},
|
||||
"northeast", new int[]{1, -1},
|
||||
"east", new int[]{1, 0},
|
||||
"southeast", new int[]{1, 1},
|
||||
"south", new int[]{0, 1},
|
||||
"southwest", new int[]{-1, 1},
|
||||
"west", new int[]{-1, 0},
|
||||
"northwest", new int[]{-1, -1}
|
||||
);
|
||||
|
||||
public PlatformValidator(RSTaxi plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean isValid(Player player) {
|
||||
if (!plugin.getConfig().getBoolean("platform.enabled", true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Block center = player.getLocation()
|
||||
.subtract(0, 1, 0)
|
||||
.getBlock();
|
||||
|
||||
Material target = getConfiguredMaterial(
|
||||
"platform.target-block"
|
||||
);
|
||||
|
||||
if (center.getType() != target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, int[]> entry : OFFSETS.entrySet()) {
|
||||
String direction = entry.getKey();
|
||||
int[] offset = entry.getValue();
|
||||
|
||||
Material required = getConfiguredMaterial(
|
||||
"platform.layout." + direction
|
||||
);
|
||||
|
||||
Block surrounding = center.getRelative(
|
||||
offset[0], 0, offset[1]
|
||||
);
|
||||
|
||||
if (surrounding.getType() != required) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Material getConfiguredMaterial(String path) {
|
||||
String name = plugin.getConfig().getString(path, "");
|
||||
Material material = Material.matchMaterial(name);
|
||||
|
||||
if (material == null || !material.isBlock()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid platform material at " + path + ": " + name
|
||||
);
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
package com.rsscripting.rstaxi.stops;
|
||||
|
||||
import com.rsscripting.rstaxi.RSTaxi;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public final class StopManager {
|
||||
|
||||
private final RSTaxi plugin;
|
||||
|
||||
public StopManager(RSTaxi plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean exists(String name) throws SQLException {
|
||||
String sql = "SELECT 1 FROM stops WHERE name = ?";
|
||||
|
||||
try (PreparedStatement statement = connection()
|
||||
.prepareStatement(sql)) {
|
||||
|
||||
statement.setString(1, name);
|
||||
|
||||
try (ResultSet results = statement.executeQuery()) {
|
||||
return results.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(
|
||||
String name,
|
||||
String icon,
|
||||
Location location
|
||||
) throws SQLException {
|
||||
|
||||
String sql = """
|
||||
INSERT OR IGNORE INTO stops
|
||||
(name, icon, world, x, y, z, yaw, pitch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
try (PreparedStatement statement = connection()
|
||||
.prepareStatement(sql)) {
|
||||
|
||||
statement.setString(1, name);
|
||||
statement.setString(2, icon);
|
||||
statement.setString(
|
||||
3, location.getWorld().getName()
|
||||
);
|
||||
statement.setDouble(4, location.getX());
|
||||
statement.setDouble(5, location.getY());
|
||||
statement.setDouble(6, location.getZ());
|
||||
statement.setFloat(7, location.getYaw());
|
||||
statement.setFloat(8, location.getPitch());
|
||||
|
||||
return statement.executeUpdate() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
private Connection connection() {
|
||||
return plugin.getDatabaseManager().getConnection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
# RS-Taxi Configuration
|
||||
|
||||
# Language file for your server.
|
||||
# If plugin cannot find needed message in selected file,
|
||||
# it will default to english for that message.
|
||||
# Example: en_us, de_de, fr_fr
|
||||
language: "en_us"
|
||||
|
||||
# Platform Settings.
|
||||
platform:
|
||||
# Determines wether platforms are required before adding a destination
|
||||
# true: A platform is required to be built following the "layout" defined in config.
|
||||
# false: A platform is not required.
|
||||
enabled: true
|
||||
|
||||
# The center block of the platform if required.
|
||||
target-block: "TARGET"
|
||||
|
||||
# Protect the platform blocks from destruction.
|
||||
# true: platforms cannot be broken.
|
||||
# false: platforms can be broken.
|
||||
protect-platforms: true
|
||||
|
||||
#Layout requirement for add a destination if enabled is set to true.
|
||||
layout:
|
||||
north: "OAK_PLANKS"
|
||||
northeast: "OAK_PLANKS"
|
||||
east: "OAK_PLANKS"
|
||||
southeast: "OAK_PLANKS"
|
||||
south: "OAK_PLANKS"
|
||||
southwest: "OAK_PLANKS"
|
||||
west: "OAK_PLANKS"
|
||||
northwest: "OAK_PLANKS"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
prefix: "&8[&6RS-Taxi&8] "
|
||||
|
||||
general:
|
||||
no-permission: "&cYou don't have permission to do that."
|
||||
player-only: "&cThis command can only be used by a player."
|
||||
player-not-found: "&cPlayer not found."
|
||||
invalid-usage: "&cInvalid command usage."
|
||||
unknown-stop: "&cThe stop &e%stop% &cdoes not exist."
|
||||
no-stops: "&eNo taxi stops have been created yet."
|
||||
|
||||
database:
|
||||
connection-success: "&aDatabase connected successfully."
|
||||
connection-error: "&cCould not connect to the database."
|
||||
|
||||
stop:
|
||||
added: "&aStop &e%stop% &ahas been created."
|
||||
updated: "&aStop &e%stop% &ahas been moved to your current location."
|
||||
edited: "&aStop &e%stop% &ahas been updated."
|
||||
deleted: "&aStop &e%stop% &ahas been removed."
|
||||
already-exists: "&cA stop named &e%stop% &calready exists."
|
||||
invalid-icon: "&cThe icon &e%icon% &cis invalid."
|
||||
creation-error: "&cUnable to create the taxi stop. Check the server logs."
|
||||
|
||||
teleport:
|
||||
success: "&aYou have arrived at &e%stop%&a."
|
||||
|
||||
confirmation:
|
||||
update: "&eAre you sure you want to move stop &6%stop%&e? Confirm the command to proceed."
|
||||
delete: "&eAre you sure you want to delete stop &6%stop%&e? Confirm the command to proceed."
|
||||
expired: "&cThat confirmation has expired."
|
||||
|
||||
list:
|
||||
header: "&6Available Taxi Stops:"
|
||||
entry: "&e%icon% &f%stop%"
|
||||
|
||||
platform:
|
||||
invalid: "&cYou must stand on a valid taxi platform to create a stop."
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
name: RS-Taxi
|
||||
version: '${project.version}'
|
||||
main: com.rsscripting.rstaxi.RSTaxi
|
||||
api-version: '26.1.2'
|
||||
description: Travel between named taxi stops.
|
||||
author: RSScripting
|
||||
|
||||
commands:
|
||||
cab:
|
||||
description: Travel between registered taxi stops.
|
||||
usage: /cab <stop|add|update|edit|remove|list>
|
||||
|
||||
permissions:
|
||||
rs-cab.use:
|
||||
description: Allows players to travel to taxi stops.
|
||||
default: true
|
||||
|
||||
rs-cab.add:
|
||||
description: Allows players to create taxi stops.
|
||||
default: op
|
||||
|
||||
rs-cab.edit:
|
||||
description: Allows players to update and edit taxi stops.
|
||||
default: op
|
||||
|
||||
rs-cab.delete:
|
||||
description: Allows players to delete taxi stops.
|
||||
default: op
|
||||
Reference in new issue
Block a user