1
0
mirror of https://github.com/nkomarn/harbor.git synced 2024-09-27 22:17:31 +02:00

🌈 Getting closer to full release!

This commit is contained in:
Mykyta 2019-09-19 20:13:47 -07:00
parent 78ec4c54e6
commit 8cb28d96be
No known key found for this signature in database
GPG Key ID: C147E30C19EA3570
9 changed files with 573 additions and 54 deletions

24
pom.xml
View File

@ -16,6 +16,14 @@
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>ess-repo</id>
<url>https://ci.ender.zone/plugin/repository/everything/</url>
</repository>
<repository>
<id>cavetale-repo</id>
<url>https://cavetale.com/jenkins/plugin/repository/everything/</url>
</repository>
</repositories>
<dependencies>
@ -25,6 +33,22 @@
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>EssentialsX</artifactId>
<version>2.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry</artifactId>
<version>1.7.27</version>
</dependency>
<dependency>
<groupId>com.rylinaux</groupId>
<artifactId>PlugMan</artifactId>
<version>2.1.5</version>
</dependency>
</dependencies>
<build>

View File

@ -1,22 +1,33 @@
package xyz.nkomarn.Harbor;
import com.earth2me.essentials.Essentials;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import xyz.nkomarn.Harbor.command.HarborCommand;
import xyz.nkomarn.Harbor.task.Checker;
import xyz.nkomarn.Harbor.util.Config;
import xyz.nkomarn.Harbor.util.Metrics;
public class Harbor extends JavaPlugin {
public static Harbor instance;
public static String version = "1.6";
public static boolean debug = false;
public static Essentials essentials;
public void onEnable() {
instance = this;
saveDefaultConfig();
Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(this,
new Checker(), 0L, Config.getInteger("values.check") * 20);
new Checker(), 0L, Config.getInteger("values.timer") * 20);
getCommand("harbor").setExecutor(new HarborCommand());
// bStats
Metrics metrics = new Metrics(this);
// Essentials hook
essentials = (Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials");
}
public void onDisable() {

View File

@ -1,12 +1,11 @@
package xyz.nkomarn.Harbor.command;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import xyz.nkomarn.Harbor.Harbor;
import xyz.nkomarn.Harbor.util.Updater;
public class HarborCommand implements CommandExecutor {
@ -17,19 +16,9 @@ public class HarborCommand implements CommandExecutor {
Player player = (Player) commandSender;
World world = player.getWorld();
changeTimeTask = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(Harbor.instance, new Runnable() {
Updater.check();
Updater.upgrade();
@Override
public void run() {
if (!(world.getTime() >= 450 && world.getTime() <= 1000)) {
world.setTime(world.getTime() + 60);
}
else {
Bukkit.getScheduler().cancelTask(changeTimeTask);
System.out.println("Stopped time change");
}
}
}, 0L, 1L);
return true;
}
}

View File

@ -1,8 +1,8 @@
package xyz.nkomarn.Harbor.task;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.World;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import xyz.nkomarn.Harbor.Harbor;
@ -23,9 +23,21 @@ public class Checker implements Runnable {
if (Config.getList("blacklist").contains(world.getName())) return;
// Check if the night is already being skipped
System.out.println("Contains world? " + skippingWorlds.contains(world));
if (skippingWorlds.contains(world)) return;
int sleeping = getSleeping(world).size();
int needed = getNeeded(world);
// Send actionbar notification
if (getSleeping(world).size() > 0 && getNeeded(world) > 0) {
for (Player player : world.getPlayers()) {
sendActionBar(player, Config.getString("messages.actionbar.sleeping")
.replace("[sleeping]", String.valueOf(sleeping))
.replace("[online]", String.valueOf(world.getPlayers().size()))
.replace("[needed]", String.valueOf(needed)));
}
}
// Check if world is applicable for skipping
if (getNeeded(world) == 0 && getSleeping(world).size() > 0) {
@ -37,6 +49,11 @@ public class Checker implements Runnable {
}
}
private void sendActionBar(Player player, String message) {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(
ChatColor.translateAlternateColorCodes('&', message)));
}
private List<Player> getSleeping(World world) {
List<Player> sleeping = new ArrayList<>();
for (Player player : world.getPlayers()) {
@ -68,12 +85,17 @@ public class Checker implements Runnable {
boolean s = false;
if (Config.getBoolean("features.ignore")) if (p.getGameMode() == GameMode.SURVIVAL) s = false; else s = true;
if (Config.getBoolean("features.bypass")) if (p.hasPermission("harbor.bypass")) s = true; else s = false;
// TODO AFK DETECTION if (afk.contains(p)) s = true;
// Essentials AFK detection
if (Harbor.essentials != null) {
if (Harbor.essentials.getUser(p).isAfk()) s = true;
}
return s;
}
private void accelerateNight(World world) {
Bukkit.broadcastMessage("Accelerating time.");
Bukkit.broadcastMessage("Harbor - Accelerating time.");
new BukkitRunnable() {
@ -84,8 +106,16 @@ public class Checker implements Runnable {
world.setTime(time + 60);
}
else {
System.out.println("Stopped time change " + time);
Bukkit.broadcastMessage("Harbor - Stopped time change (" + time + ").");
skippingWorlds.remove(world);
// Reset sleep statistic if phantoms are disabled TODO move out of here
if (!Config.getBoolean("features.phantoms")) {
for (Player player : world.getPlayers()) {
player.setStatistic(Statistic.TIME_SINCE_REST, 0);
}
}
this.cancel();
}
}

View File

@ -4,6 +4,7 @@ import xyz.nkomarn.Harbor.Harbor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Config {
/**
@ -19,8 +20,12 @@ public class Config {
* @param location Configuration location of the boolean
*/
public static boolean getBoolean(String location) {
try {return Harbor.instance.getConfig().getBoolean(location);}
catch (Exception e) {error(e); return false;}
try {
return Harbor.instance.getConfig().getBoolean(location);
}
catch (Exception e) {
error(e); return false;
}
}
/**
@ -28,8 +33,13 @@ public class Config {
* @param location Configuration location of the string
*/
public static String getString(String location) {
try {return Harbor.instance.getConfig().getString(location);}
catch (Exception e) {error(e); return "";}
try {
return Harbor.instance.getConfig().getString(location);
}
catch (Exception e) {
error(e);
return "";
}
}
/**
@ -37,8 +47,13 @@ public class Config {
* @param location Configuration location of the integer
*/
public static int getInteger(String location) {
try {return Harbor.instance.getConfig().getInt(location);}
catch (Exception e) {error(e); return 0;}
try {
return Harbor.instance.getConfig().getInt(location);
}
catch (Exception e) {
error(e);
return 0;
}
}
/**
@ -46,8 +61,13 @@ public class Config {
* @param location Configuration location of the double
*/
public static double getDouble(String location) {
try {return Double.parseDouble(Harbor.instance.getConfig().getString(location));}
catch (Exception e) {error(e); return 0.0;}
try {
return Double.parseDouble(Objects.requireNonNull(Harbor.instance.getConfig().getString(location)));
}
catch (Exception e) {
error(e);
return 0.0;
}
}
/**
@ -55,7 +75,12 @@ public class Config {
* @param location Configuration location of the double
*/
public static List<String> getList(String location) {
try {return Harbor.instance.getConfig().getStringList(location);}
catch (Exception e) {error(e); return new ArrayList<>();}
try {
return Harbor.instance.getConfig().getStringList(location);
}
catch (Exception e) {
error(e);
return new ArrayList<>();
}
}
}

View File

@ -0,0 +1,366 @@
package xyz.nkomarn.Harbor.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class Metrics {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
final String defaultPackage = new String(
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
*/
public Metrics(Plugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
"To honor their work, you should not disable it.\n" +
"This has nearly no effect on the server performance!\n" +
"Check out https://bStats.org/ to learn more :)"
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) { }
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
enabled = config.getBoolean("enabled", true);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) { }
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
data.add("customCharts", new JsonArray());
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
}
continue; // continue looping since we cannot do any other thing.
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
}
}
} catch (NoSuchFieldException ignored) { }
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data.toString());
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
bufferedReader.close();
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
}

View File

@ -0,0 +1,89 @@
package xyz.nkomarn.Harbor.util;
import com.rylinaux.plugman.util.PluginUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.InvalidDescriptionException;
import org.bukkit.plugin.InvalidPluginException;
import xyz.nkomarn.Harbor.Harbor;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class Updater {
public static String latest;
// Checks if an update is available
public static boolean check() {
try {
URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=60088");
URLConnection request = url.openConnection();
request.addRequestProperty("User-Agent", "Harbor");
request.connect();
InputStream inputStream = (InputStream) request.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
latest = reader.lines().collect(Collectors.joining(System.lineSeparator()));
System.out.println(latest);
if (Harbor.version.equals(latest)) return false;
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
// Actually update the Harbor JAR
public static boolean upgrade() {
Harbor.instance.getLogger().log(Level.INFO, "Downloading Harbor version " + latest + ".");
try {
String jar = new File(Updater.class.getProtectionDomain().getCodeSource()
.getLocation().getPath()).getName();
URL url = new URL("http://aqua.api.spiget.org/v2/resources/60088/download");
File jarFile = new File("plugins" + File.separator + jar);
InputStream inputStream = url.openStream();
// If Plugman is loaded, hot reload the plugin
if (Bukkit.getServer().getPluginManager().isPluginEnabled("PlugMan")) {
Bukkit.getServer().broadcastMessage("Using plugman");
PluginUtil.disable(Harbor.instance);
Files.copy(inputStream, Paths.get(jarFile.toURI()), StandardCopyOption.REPLACE_EXISTING);
PluginUtil.load("Harbor");
Bukkit.getServer().broadcastMessage("Boom done and updated");
}
// Unload plugin and copy new JAR
//Bukkit.getServer().getPluginManager().disablePlugin(Harbor.instance);
//URLClassLoader classLoader = (URLClassLoader) Harbor.instance.getClass().getClassLoader();
//classLoader.close();
//System.gc();
//Files.copy(inputStream, Paths.get(jarFile.toURI()), StandardCopyOption.REPLACE_EXISTING);
// Load the new version
//Bukkit.getServer().getPluginManager().loadPlugin(jarFile);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}

View File

@ -16,26 +16,22 @@
values:
# How often to run the clock task (used to detect sleep, AFK players, time actionbar, etc.)
# Lower this value for relatively slow servers
check: 2
timer: 2
# Percent of players that need to sleep to skip night (must be between 0 to 100)
percent: 35
# Time that it takes to be considered AFK (in minutes)
timeout: 15
percent: 100
features:
# Toggle night skipping feature. Configure amount of players needed to skip above (percent)
skip: true
# Clear weather when skipping night
weather: true
# Toggle the spawning of phantoms (false = no phantom spawning)
# Reset the sleep statistic (practically disables phantom spawns - false = no phantoms)
phantoms: false
# Toggle exclusion of operators/players with permission "harbor.bypass" from sleep count
bypass: true
# Toggle exclusion of players in creative and spectator mode
ignore: true
# Prevent all players from entering a bed
block: false
# Detect AFK players and remove them from the sleep count
# Detect AFK players and remove them from the sleep count (Essentials/API used for detection)
afk: true
# Displays a notification when a new update is released
notifier: true
@ -53,14 +49,8 @@ messages:
sleeping: "&e[player] is now sleeping ([sleeping]/[online], [needed] more needed to skip)."
# Display when a player left their bed
left: "&e[player] got out of bed ([sleeping]/[online], [needed] more needed to skip)."
# Shown when a player tries to sleep and sleeping is blocked
blocked: "&cSleeping is disabled."
# Sent to player when they sleep with "harbor.bypass" permission
bypass: "&eYou've been excluded from the sleep count."
# Shown when a player goes AFK
afk: "&7[player] has gone AFK."
# Shown when a player comes back after being AFK
unafk: "&7[player] is no longer AFK."
actionbar:
# Enable/Disable actionbar message
actionbar: true
@ -68,24 +58,18 @@ messages:
sleeping: "&e[sleeping] out of [online] players are sleeping ([needed] more needed to skip)."
# Shown when all players are in bed
everyone: "&eEveryone is sleeping. Sweet dreams!"
# Shown when a player tries to sleep and sleeping is blocked
blocked: "&cSleeping is disabled."
miscellaneous:
# Prefix for Harbor command/miscellaneous messages
prefix: "&8&l(&6&lHarbor&8&l)&r "
# Prefix for player list names when a player is AFK
afkprefix: "&8&l(&7AFK&8&l)&7 "
# Display server version in console
running: "&7Running on version [version]."
# Sent in console when the plugin isn't compatible with the server
incompatible: "&7Spigot [version] isn't compatible with Harbor."
# Harbor reload message
reloaded: "&7Reloaded Harbor."
reloaderror: "&7Error reloading Harbor."
# Sent when player doesn't have permissions to run command
permission: "&7Insufficient permissions."
# Sent when command argument isn't recognized
unrecognized: "&7Urecognized command."
unrecognized: "&7Unrecognized command."
blacklist:
- "world_nether"
@ -96,5 +80,5 @@ gui:
sleeping: "Sleeping Players | Page [page]"
# Spooky controls (don't change)
version: 1.5.3
version: 1.5.4
debug: false

View File

@ -4,6 +4,7 @@ main: xyz.nkomarn.${name}.${name}
version: ${version}
author: TechToolbox (@nkomarn)
website: https://nkomarn.xyz
softdepend: [Essentials]
api-version: 1.13
commands: