12 Commits

17 changed files with 203 additions and 572 deletions

View File

@ -1,6 +1,6 @@
![SimplifyTools logo](docs/img/SimplifyTools.png) ![SimplifyTools logo](docs/img/SimplifyTools.png)
### An 'All-in-one' plugin for MC servers. ### An 'All-in-one' plugin for MC servers.
#### TabList Customizing, Custom Advancement, Connect messages, AutoSave, Logger #### TabList Customizing, Server Password, Clear Dropped Items, Custom Advancement, Connect messages, AutoSave, Logger
[![Build Status](https://ci.ditservices.hu/job/SimplifyTools/badge/icon)](https://ci.ditservices.hu/job/SimplifyTools/) ![ MC Version](https://ci.ditservices.hu/job/SimplifyTools/badge/icon?subject=MC&status=1.12%20-%201.21.4&color=darkblue) ![Git latest release](https://img.shields.io/github/v/release/LabodiDavid/SimplifyTools) [![bStats](https://ci.ditservices.hu/job/SimplifyTools/badge/icon?subject=bStats&status=3.0&color=brightgreen)](https://bstats.org/plugin/bukkit/SimplifyTools/15108) ![Lines of code](https://tokei.rs/b1/github/LabodiDavid/SimplifyTools?category=code) [![Build Status](https://ci.ditservices.hu/job/SimplifyTools/badge/icon)](https://ci.ditservices.hu/job/SimplifyTools/) ![ MC Version](https://ci.ditservices.hu/job/SimplifyTools/badge/icon?subject=MC&status=1.12%20-%201.21.4&color=darkblue) ![Git latest release](https://img.shields.io/github/v/release/LabodiDavid/SimplifyTools) [![bStats](https://ci.ditservices.hu/job/SimplifyTools/badge/icon?subject=bStats&status=3.0&color=brightgreen)](https://bstats.org/plugin/bukkit/SimplifyTools/15108) ![Lines of code](https://tokei.rs/b1/github/LabodiDavid/SimplifyTools?category=code)
<br>[Changelog](docs/ChangeLog.md) <br>[Changelog](docs/ChangeLog.md)
<hr> <hr>
@ -12,13 +12,15 @@
- **Tab Manager** - Allows you to specify colored texts, with animations on the tab window. - **Tab Manager** - Allows you to specify colored texts, with animations on the tab window.
Also you can display the average ping, Server RAM statistics. Also you can display the average ping, Server RAM statistics.
![Tab manager preview](docs/img/1.gif) ![Tab manager preview](docs/img/1.gif)
- **Automatic/command saving** - Allows you to schedule auto saving of the world/player data on your server, or you can initiate a save by a command.
- **Custom Connect/Disconnect messages** - Allows you to customize the message that is broadcasted when someone joins or leaves the server.
![Connect messages preview](docs/img/2.gif)
- **Plugin Manager** - Allows you to unload/load plugins without a server restart. (Disabled by default since v1.2.1)
- **Gameplay statistics** - You can check your gameplay statistics such as player/mob kills, etc. - **Gameplay statistics** - You can check your gameplay statistics such as player/mob kills, etc.
(Note: The plugin just shows the stats not recording itself, so stats before installing this plugin are counted too.) (Note: The plugin just shows the stats not recording itself, so stats before installing this plugin are counted too.)
![Stats preview](docs/img/3.gif) ![Stats preview](docs/img/3.gif)
- **Server Password** - Allows you to set a server password *(same password for all players)*, ability to: skip auth for OP players, hide inventory, disable moving, building, until providing correct password `/slogin {PASSWORD}`
- **Clearing Dropped Items** - Set an interval in seconds to remove dropped items. Ability to broadcast clearing before 1min, 10sec, skip items and/or worlds.
- **Custom Connect/Disconnect messages** - Allows you to customize the message that is broadcasted when someone joins or leaves the server.
![Connect messages preview](docs/img/2.gif)
- **Automatic/command saving** - Allows you to schedule auto saving of the world/player data on your server, or you can initiate a save by a command.
- **Plugin Manager** - Allows you to unload/load plugins without a server restart. (Disabled by default since v1.2.1)
- **Custom Advancement Messages** - You can customize the advancement messages in three categories: advancement, goal, challenge. - **Custom Advancement Messages** - You can customize the advancement messages in three categories: advancement, goal, challenge.
(Note: The advancement names are currently only can be displayed in english. In future versions there will be a feature to translate to any languages.) (Note: The advancement names are currently only can be displayed in english. In future versions there will be a feature to translate to any languages.)
![Advancement Messages preview](docs/img/4.gif) ![Advancement Messages preview](docs/img/4.gif)
@ -32,6 +34,12 @@ _________________
_________________ _________________
### [Commands & Permissions](docs/cmd_perms.md) ### [Commands & Permissions](docs/cmd_perms.md)
_________________ _________________
### Translation
Under the plugin's directory inside the lang folder you can add your own translation. <br>
Just copy-paste [en.yml](src/main/resources/en.yml) and translate the strings to your language. <br>
In the config.yml you can set the newly created language (for example: de.yml) to like this: `language: 'de'` and your translation will be used. <br>
Feel free to open a pull request and your language can be added to the plugin.
_________________
### [Building from source](docs/BUILDING.md) ### [Building from source](docs/BUILDING.md)
_________________ _________________
### If you have an idea or bug report [Create an issue](https://github.com/LabodiDavid/SimplifyTools/issues/new/choose) or [Create a pull request](https://github.com/LabodiDavid/SimplifyTools/compare) ### If you have an idea or bug report [Create an issue](https://github.com/LabodiDavid/SimplifyTools/issues/new/choose) or [Create a pull request](https://github.com/LabodiDavid/SimplifyTools/compare)

View File

@ -1,5 +1,13 @@
# SimplifyTools - Changelog # SimplifyTools - Changelog
- **1.2.4**
- Added *Server password feature*
- Added *Clearing dropped items feature*
- Implemented customizable translation
- Added total play time to stats command
- Tab completer optimalizations
- Minor refactors/optimalizations
- Prepared support for 1.21.5 (Not tested yet)
- From now running newer untested server versions is allowed
- **1.2.3** - **1.2.3**
- Added support for version up to 1.21.4 - Added support for version up to 1.21.4
- The plugin now depends on, <b>requires ProtocolLib</b> (https://github.com/dmulloy2/ProtocolLib/) - The plugin now depends on, <b>requires ProtocolLib</b> (https://github.com/dmulloy2/ProtocolLib/)

View File

@ -5,12 +5,14 @@ You can use the following commands to use the features of SimplifyTools.
- **/st ping**: Displays your ping to the current server.<br />Requires `st.ping` - **/st ping**: Displays your ping to the current server.<br />Requires `st.ping`
- **/st tps**: Displays plugin calculated TPS.<br />Requires `st.tps` - **/st tps**: Displays plugin calculated TPS.<br />Requires `st.tps`
- **/st stats**: Displays your gameplay statistics.<br />Requires `st.stats` - **/st stats**: Displays your gameplay statistics.<br />Requires `st.stats`
- **/slogin**: Used to login if server password enabled.<br />Requires `st.slogin`
- **/st save-all**: Saves the players, worlds data on the server.<br />Requires `st.save` - **/st save-all**: Saves the players, worlds data on the server.<br />Requires `st.save`
- **/st pmanager** <LOAD/UNLOAD>: A built-in plugin manager.<br />Requires `st.pmanager.*` - **/st pmanager** <LOAD/UNLOAD>: A built-in plugin manager.<br />Requires `st.pmanager.*`
- **/st pmanager load** <PLUGIN-NAME>: Plugin loading.<br />Requires `st.pmanager.load (can be inherited from st.pmanager.*)` - **/st pmanager load** <PLUGIN-NAME>: Plugin loading.<br />Requires `st.pmanager.load (can be inherited from st.pmanager.*)`
- **/st pmanager unload** <PLUGIN-NAME>: Plugin unload.<br />Requires `st.pmanager.unload (can be inherited from st.pmanager.*)` - **/st pmanager unload** <PLUGIN-NAME>: Plugin unload.<br />Requires `st.pmanager.unload (can be inherited from st.pmanager.*)`
- **/st reload**: Plugin settings reload.<br />Requires `st.reload` - **/st reload**: Plugin settings reload.<br />Requires `st.reload`
## SimplifyTools - Permissions
- `st.slogin.skip` - If has this permission the user dont have to provide server password upon connect if enabled.
## SimplifyTools - Wildcard Permissions ## SimplifyTools - Wildcard Permissions
- **st.*** - Wildcard permission for all commands, permissions for this plugin. - **st.*** - Wildcard permission for all commands, permissions for this plugin.

View File

@ -6,7 +6,7 @@
<groupId>hu.ditservices</groupId> <groupId>hu.ditservices</groupId>
<artifactId>SimplifyTools</artifactId> <artifactId>SimplifyTools</artifactId>
<version>1.2.4-SNAPSHOT</version> <version>1.2.5-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>SimplifyTools</name> <name>SimplifyTools</name>
@ -110,6 +110,11 @@
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>com.tchristofferson</groupId>
<artifactId>ConfigUpdater</artifactId>
<version>2.2-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.jeff_media</groupId> <groupId>com.jeff_media</groupId>
<artifactId>SpigotUpdateChecker</artifactId> <artifactId>SpigotUpdateChecker</artifactId>

View File

@ -1,241 +0,0 @@
package com.tchristofferson.configupdater;
import com.google.common.base.Preconditions;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ConfigUpdater {
//Used for separating keys in the keyBuilder inside parseComments method
private static final char SEPARATOR = '.';
public static void update(Plugin plugin, String resourceName, File toUpdate, String... ignoredSections) throws IOException {
update(plugin, resourceName, toUpdate, Arrays.asList(ignoredSections));
}
public static void update(Plugin plugin, String resourceName, File toUpdate, List<String> ignoredSections) throws IOException {
Preconditions.checkArgument(toUpdate.exists(), "The toUpdate file doesn't exist!");
FileConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(plugin.getResource(resourceName), StandardCharsets.UTF_8));
FileConfiguration currentConfig = YamlConfiguration.loadConfiguration(toUpdate);
Map<String, String> comments = parseComments(plugin, resourceName, defaultConfig);
Map<String, String> ignoredSectionsValues = parseIgnoredSections(toUpdate, currentConfig, comments, ignoredSections == null ? Collections.emptyList() : ignoredSections);
// will write updated config file "contents" to a string
StringWriter writer = new StringWriter();
write(defaultConfig, currentConfig, new BufferedWriter(writer), comments, ignoredSectionsValues);
String value = writer.toString(); // config contents
Path toUpdatePath = toUpdate.toPath();
if (!value.equals(new String(Files.readAllBytes(toUpdatePath), StandardCharsets.UTF_8))) { // if updated contents are not the same as current file contents, update
Files.write(toUpdatePath, value.getBytes(StandardCharsets.UTF_8));
}
}
private static void write(FileConfiguration defaultConfig, FileConfiguration currentConfig, BufferedWriter writer, Map<String, String> comments, Map<String, String> ignoredSectionsValues) throws IOException {
//Used for converting objects to yaml, then cleared
FileConfiguration parserConfig = new YamlConfiguration();
keyLoop: for (String fullKey : defaultConfig.getKeys(true)) {
String indents = KeyBuilder.getIndents(fullKey, SEPARATOR);
if (ignoredSectionsValues.isEmpty()) {
writeCommentIfExists(comments, writer, fullKey, indents);
} else {
for (Map.Entry<String, String> entry : ignoredSectionsValues.entrySet()) {
if (entry.getKey().equals(fullKey)) {
writer.write(ignoredSectionsValues.get(fullKey) + "\n");
continue keyLoop;
} else if (KeyBuilder.isSubKeyOf(entry.getKey(), fullKey, SEPARATOR)) {
continue keyLoop;
}
}
writeCommentIfExists(comments, writer, fullKey, indents);
}
Object currentValue = currentConfig.get(fullKey);
if (currentValue == null)
currentValue = defaultConfig.get(fullKey);
String[] splitFullKey = fullKey.split("[" + SEPARATOR + "]");
String trailingKey = splitFullKey[splitFullKey.length - 1];
if (currentValue instanceof ConfigurationSection) {
writer.write(indents + trailingKey + ":");
if (!((ConfigurationSection) currentValue).getKeys(false).isEmpty())
writer.write("\n");
else
writer.write(" {}\n");
continue;
}
parserConfig.set(trailingKey, currentValue);
String yaml = parserConfig.saveToString();
yaml = yaml.substring(0, yaml.length() - 1).replace("\n", "\n" + indents);
String toWrite = indents + yaml + "\n";
parserConfig.set(trailingKey, null);
writer.write(toWrite);
}
String danglingComments = comments.get(null);
if (danglingComments != null)
writer.write(danglingComments);
writer.close();
}
//Returns a map of key comment pairs. If a key doesn't have any comments it won't be included in the map.
private static Map<String, String> parseComments(Plugin plugin, String resourceName, FileConfiguration defaultConfig) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(plugin.getResource(resourceName)));
Map<String, String> comments = new LinkedHashMap<>();
StringBuilder commentBuilder = new StringBuilder();
KeyBuilder keyBuilder = new KeyBuilder(defaultConfig, SEPARATOR);
String line;
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
//Only getting comments for keys. A list/array element comment(s) not supported
if (trimmedLine.startsWith("-")) {
continue;
}
if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) {//Is blank line or is comment
commentBuilder.append(trimmedLine).append("\n");
} else {//is a valid yaml key
keyBuilder.parseLine(trimmedLine);
String key = keyBuilder.toString();
//If there is a comment associated with the key it is added to comments map and the commentBuilder is reset
if (commentBuilder.length() > 0) {
comments.put(key, commentBuilder.toString());
commentBuilder.setLength(0);
}
//Remove the last key from keyBuilder if current path isn't a config section or if it is empty to prepare for the next key
if (!keyBuilder.isConfigSectionWithKeys()) {
keyBuilder.removeLastKey();
}
}
}
reader.close();
if (commentBuilder.length() > 0)
comments.put(null, commentBuilder.toString());
return comments;
}
private static Map<String, String> parseIgnoredSections(File toUpdate, FileConfiguration currentConfig, Map<String, String> comments, List<String> ignoredSections) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(toUpdate));
Map<String, String> ignoredSectionsValues = new LinkedHashMap<>(ignoredSections.size());
KeyBuilder keyBuilder = new KeyBuilder(currentConfig, SEPARATOR);
StringBuilder valueBuilder = new StringBuilder();
String currentIgnoredSection = null;
String line;
lineLoop : while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.isEmpty() || trimmedLine.startsWith("#"))
continue;
if (trimmedLine.startsWith("-")) {
for (String ignoredSection : ignoredSections) {
boolean isIgnoredParent = ignoredSection.equals(keyBuilder.toString());
if (isIgnoredParent || keyBuilder.isSubKeyOf(ignoredSection)) {
valueBuilder.append("\n").append(line);
continue lineLoop;
}
}
}
keyBuilder.parseLine(trimmedLine);
String fullKey = keyBuilder.toString();
//If building the value for an ignored section and this line is no longer a part of the ignored section,
// write the valueBuilder, reset it, and set the current ignored section to null
if (currentIgnoredSection != null && !KeyBuilder.isSubKeyOf(currentIgnoredSection, fullKey, SEPARATOR)) {
ignoredSectionsValues.put(currentIgnoredSection, valueBuilder.toString());
valueBuilder.setLength(0);
currentIgnoredSection = null;
}
for (String ignoredSection : ignoredSections) {
boolean isIgnoredParent = ignoredSection.equals(fullKey);
if (isIgnoredParent || keyBuilder.isSubKeyOf(ignoredSection)) {
if (valueBuilder.length() > 0)
valueBuilder.append("\n");
String comment = comments.get(fullKey);
if (comment != null) {
String indents = KeyBuilder.getIndents(fullKey, SEPARATOR);
valueBuilder.append(indents).append(comment.replace("\n", "\n" + indents));//Should end with new line (\n)
valueBuilder.setLength(valueBuilder.length() - indents.length());//Get rid of trailing \n and spaces
}
valueBuilder.append(line);
//Set the current ignored section for future iterations of while loop
//Don't set currentIgnoredSection to any ignoredSection sub-keys
if (isIgnoredParent)
currentIgnoredSection = fullKey;
break;
}
}
}
reader.close();
if (valueBuilder.length() > 0)
ignoredSectionsValues.put(currentIgnoredSection, valueBuilder.toString());
return ignoredSectionsValues;
}
private static void writeCommentIfExists(Map<String, String> comments, BufferedWriter writer, String fullKey, String indents) throws IOException {
String comment = comments.get(fullKey);
//Comments always end with new line (\n)
if (comment != null)
//Replaces all '\n' with '\n' + indents except for the last one
writer.write(indents + comment.substring(0, comment.length() - 1).replace("\n", "\n" + indents) + "\n");
}
//Input: 'key1.key2' Result: 'key1'
private static void removeLastKey(StringBuilder keyBuilder) {
if (keyBuilder.length() == 0)
return;
String keyString = keyBuilder.toString();
//Must be enclosed in brackets in case a regex special character is the separator
String[] split = keyString.split("[" + SEPARATOR + "]");
//Makes sure begin index isn't < 0 (error). Occurs when there is only one key in the path
int minIndex = Math.max(0, keyBuilder.length() - split[split.length - 1].length() - 1);
keyBuilder.replace(minIndex, keyBuilder.length(), "");
}
private static void appendNewLine(StringBuilder builder) {
if (builder.length() > 0)
builder.append("\n");
}
}

View File

@ -1,116 +0,0 @@
package com.tchristofferson.configupdater;
import org.bukkit.configuration.file.FileConfiguration;
public class KeyBuilder implements Cloneable {
private final FileConfiguration config;
private final char separator;
private final StringBuilder builder;
public KeyBuilder(FileConfiguration config, char separator) {
this.config = config;
this.separator = separator;
this.builder = new StringBuilder();
}
private KeyBuilder(KeyBuilder keyBuilder) {
this.config = keyBuilder.config;
this.separator = keyBuilder.separator;
this.builder = new StringBuilder(keyBuilder.toString());
}
public void parseLine(String line) {
line = line.trim();
String[] currentSplitLine = line.split(":");
String key = currentSplitLine[0].replace("'", "").replace("\"", "");
//Checks keyBuilder path against config to see if the path is valid.
//If the path doesn't exist in the config it keeps removing last key in keyBuilder.
while (builder.length() > 0 && !config.contains(builder.toString() + separator + key)) {
removeLastKey();
}
//Add the separator if there is already a key inside keyBuilder
//If currentSplitLine[0] is 'key2' and keyBuilder contains 'key1' the result will be 'key1.' if '.' is the separator
if (builder.length() > 0)
builder.append(separator);
//Appends the current key to keyBuilder
//If keyBuilder is 'key1.' and currentSplitLine[0] is 'key2' the resulting keyBuilder will be 'key1.key2' if separator is '.'
builder.append(key);
}
public String getLastKey() {
if (builder.length() == 0)
return "";
return builder.toString().split("[" + separator + "]")[0];
}
public boolean isEmpty() {
return builder.length() == 0;
}
//Checks to see if the full key path represented by this instance is a sub-key of the key parameter
public boolean isSubKeyOf(String parentKey) {
return isSubKeyOf(parentKey, builder.toString(), separator);
}
//Checks to see if subKey is a sub-key of the key path this instance represents
public boolean isSubKey(String subKey) {
return isSubKeyOf(builder.toString(), subKey, separator);
}
public static boolean isSubKeyOf(String parentKey, String subKey, char separator) {
if (parentKey.isEmpty())
return false;
return subKey.startsWith(parentKey)
&& subKey.substring(parentKey.length()).startsWith(String.valueOf(separator));
}
public static String getIndents(String key, char separator) {
String[] splitKey = key.split("[" + separator + "]");
StringBuilder builder = new StringBuilder();
for (int i = 1; i < splitKey.length; i++) {
builder.append(" ");
}
return builder.toString();
}
public boolean isConfigSection() {
String key = builder.toString();
return config.isConfigurationSection(key);
}
public boolean isConfigSectionWithKeys() {
String key = builder.toString();
return config.isConfigurationSection(key) && !config.getConfigurationSection(key).getKeys(false).isEmpty();
}
//Input: 'key1.key2' Result: 'key1'
public void removeLastKey() {
if (builder.length() == 0)
return;
String keyString = builder.toString();
//Must be enclosed in brackets in case a regex special character is the separator
String[] split = keyString.split("[" + separator + "]");
//Makes sure begin index isn't < 0 (error). Occurs when there is only one key in the path
int minIndex = Math.max(0, builder.length() - split[split.length - 1].length() - 1);
builder.replace(minIndex, builder.length(), "");
}
@Override
public String toString() {
return builder.toString();
}
@Override
protected KeyBuilder clone() {
return new KeyBuilder(this);
}
}

View File

@ -78,6 +78,11 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
private void scheduleTasks() { private void scheduleTasks() {
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new TPS(), 0, 1); Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new TPS(), 0, 1);
if (this.config.getBoolean("ClearDropItems.enabled")) {
new ClearDropItemsTask(this).runTaskTimer(this, 20L, 20L);
this.log.info(ChatColor.stripColor(this.getPrefix()) + "Clear dropped items enabled!");
}
if (this.config.getBoolean("Saving.enabled") && this.config.getInt("Saving.interval") > 0) { if (this.config.getBoolean("Saving.enabled") && this.config.getInt("Saving.interval") > 0) {
long intervalTicks = Math.convert(Math.Convert.SECONDS, Math.Convert.TICKS, instance.config.getInt("Saving.interval")); long intervalTicks = Math.convert(Math.Convert.SECONDS, Math.Convert.TICKS, instance.config.getInt("Saving.interval"));
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new SaveHandler(), 0L, intervalTicks); Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new SaveHandler(), 0L, intervalTicks);
@ -103,10 +108,11 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
private boolean initPlugin() { private boolean initPlugin() {
try { try {
this.ServerStartTime = ManagementFactory.getRuntimeMXBean().getStartTime(); this.ServerStartTime = ManagementFactory.getRuntimeMXBean().getStartTime();
if (Version.ServerVersion.isCurrentLower(Version.ServerVersion.v1_12_R1) || if (Version.ServerVersion.isCurrentLower(Version.ServerVersion.v1_12_R1)) {
Version.ServerVersion.isCurrentHigher(Version.ServerVersion.v1_21_5_R1)) throw new Exception("The server version is not supported! Update to to atleast 1.12 version to use this plugin!");
{ }
throw new Exception("The server version is not supported! Update to a version between 1.12 - 1.21.5 to run this plugin."); if (Version.ServerVersion.isCurrentHigher(Version.ServerVersion.v1_21_5_R1)) {
this.log.warning(ChatColor.stripColor(this.getPrefix()) + "You are running a server version which is not tested with this plugin, errors can be occur!");
} }
if (this.reload()) { if (this.reload()) {
@ -126,8 +132,8 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
return false; return false;
} }
} catch (Exception e) { } catch (Exception e) {
this.log.warning("[SimplifyTools] - INITIALIZATION ERROR: " + e.getMessage()); this.log.warning(ChatColor.stripColor(this.getPrefix()) + "INITIALIZATION ERROR: " + e.getMessage());
this.log.warning("[SimplifyTools] - Plugin disabled!"); this.log.warning(ChatColor.stripColor(this.getPrefix()) + "Plugin disabled!");
this.setEnabled(false); this.setEnabled(false);
return false; return false;
} }
@ -189,6 +195,24 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
return this.serverPasswordData; return this.serverPasswordData;
} }
private void copyDefaultLang(String filename, File langFolder) {
File outFile = new File(langFolder, filename);
if (!outFile.exists()) {
try (InputStream in = getResource(filename);
OutputStream out = new FileOutputStream(outFile)) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
} catch (IOException e) {
this.log.warning(ChatColor.stripColor(this.getPrefix()) + e.getMessage());
}
}
}
private void initLocalization() throws IOException { private void initLocalization() throws IOException {
File langFolder = new File(getDataFolder(), "lang"); File langFolder = new File(getDataFolder(), "lang");
if (!langFolder.exists()) { if (!langFolder.exists()) {
@ -199,16 +223,16 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
for (String l : languages) { for (String l : languages) {
File langFile = new File(langFolder, l + ".yml"); File langFile = new File(langFolder, l + ".yml");
if (!langFile.exists()) { if (!langFile.exists()) {
saveResource("lang/"+l+".yml", false); copyDefaultLang(l + ".yml", langFolder);
} }
ConfigUpdater.update(this, "lang/"+l+".yml", langFile, Collections.emptyList()); ConfigUpdater.update(this, l + ".yml", langFile, Collections.emptyList());
} }
String lang = this.config.getString("language", "en"); String lang = this.config.getString("language", "en");
File langFile = new File(getDataFolder(), "lang/" + lang + ".yml"); File langFile = new File(getDataFolder(), "lang" + File.separator + lang + ".yml");
if (!langFile.exists()) { if (!langFile.exists()) {
getLogger().warning("Language file for '" + lang + "' not found. Falling back to English."); getLogger().warning("Language file for '" + lang + "' not found. Falling back to English.");
langFile = new File(getDataFolder(), "lang/en.yml"); langFile = new File(getDataFolder(), "lang" + File.separator + "en.yml");
} }
this.langConfig = YamlConfiguration.loadConfiguration(langFile); this.langConfig = YamlConfiguration.loadConfiguration(langFile);
} }
@ -226,14 +250,14 @@ public final class STPlugin extends JavaPlugin implements CommandExecutor, Liste
} }
ConfigUpdater.update(this, "config.yml", configFile, Collections.emptyList()); ConfigUpdater.update(this, "config.yml", configFile, Collections.emptyList());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); this.log.warning(ChatColor.stripColor(this.getPrefix()) + e.getMessage());
} }
reloadConfig(); reloadConfig();
this.config = getConfig(); this.config = getConfig();
try { try {
this.initLocalization(); this.initLocalization();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); this.log.warning(ChatColor.stripColor(this.getPrefix()) + e.getMessage());
} }

View File

@ -0,0 +1,76 @@
package hu.ditservices.handlers;
import hu.ditservices.STPlugin;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.List;
public class ClearDropItemsTask extends BukkitRunnable {
private final STPlugin plugin;
private int countdown;
private final int interval; // In seconds
private final List<String> exceptItemList;
private final List<String> exceptWorldList;
private final boolean shouldBroadcast;
public ClearDropItemsTask(STPlugin plugin) {
this.plugin = plugin;
this.interval = plugin.getConfig().getInt("ClearDropItems.interval", 300);
this.countdown = this.interval;
// Get the exception list from the config (should be a list of item type names, e.g., ["DIAMOND", "GOLD_INGOT"])
this.exceptItemList = plugin.getConfig().getStringList("ClearDropItems.except");
this.exceptWorldList = plugin.getConfig().getStringList("ClearDropItems.skipWorlds");
this.shouldBroadcast = plugin.getConfig().getBoolean("ClearDropItems.broadcastMsg", true);
}
@Override
public void run() {
if (this.shouldBroadcast) {
if (countdown == 60) {
Bukkit.broadcastMessage(plugin.getPrefix() + ChatColor.RED + plugin.getTranslatedText("cleardropitems.oneMin"));
}
if (countdown == 10) {
Bukkit.broadcastMessage(plugin.getPrefix() + ChatColor.RED + plugin.getTranslatedText("cleardropitems.tenSec"));
}
}
if (countdown <= 0) {
int removed = 0;
for (World world : Bukkit.getWorlds()) {
if (this.exceptWorldList.contains(world.getName())) {
continue;
}
for (Entity entity : world.getEntities()) {
if (entity instanceof Item) {
Item item = (Item) entity;
String itemType = item.getItemStack().getType().toString();
// Check if the item is in the exception list
boolean isException = this.exceptItemList.stream()
.anyMatch(ex -> ex.equalsIgnoreCase(itemType));
if (!isException) {
item.remove();
removed++;
}
}
}
}
if (this.shouldBroadcast) {
Bukkit.broadcastMessage(plugin.getPrefix() + ChatColor.GREEN + plugin.getTranslatedText("cleardropitems.cleared").replace("%REMOVECOUNT%",String.valueOf(removed)));
}
// Reset the countdown to the interval for the next cycle
countdown = interval;
} else {
countdown--;
}
}
}

View File

@ -1,185 +0,0 @@
package hu.ditservices.handlers;
/*
import hu.ditservices.DITSystem;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.Event;
import org.bukkit.plugin.*;
import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
public class DitPluginManager {
private static DITSystem plugin;
public DitPluginManager(DITSystem instance){
plugin = instance;
}
public static int load(final String pluginName) {
PluginManager pm = Bukkit.getServer().getPluginManager();
boolean there = false;
for (Plugin pl : pm.getPlugins()){
if (pl.getName().toLowerCase().startsWith(pluginName)){
there = true;
}
}
if (there)
{ return 1;} //plugin already exists
else {
String name = "";
String path = plugin.getDataFolder().getParent();
File folder = new File(path);
ArrayList<File> files = new ArrayList<File>();
File[] listOfFiles = folder.listFiles();
for (File compare : listOfFiles) {
if (compare.isFile()) {
try {
name = plugin.getPluginLoader().getPluginDescription(compare).getName();
} catch (InvalidDescriptionException e) {
plugin.getLogger().warning("[Loading Plugin] " + compare.getName() + " didn't match");
}
if (name.toLowerCase().startsWith(pluginName.toLowerCase())) {
files.add(compare);
try {
Bukkit.getServer().getPluginManager().loadPlugin(compare);
} catch (UnknownDependencyException e) {
return 2; //missing dependent plugin
} catch (InvalidPluginException e) {
return -1; //not a plugin
} catch (InvalidDescriptionException e) {
return 3; //invalid description
}
}
}
}
Plugin[] plugins = pm.getPlugins();
for (Plugin pl : plugins) {
for (File compare : files) {
try {
if (pl.getName().equalsIgnoreCase(plugin.getPluginLoader().getPluginDescription(compare).getName()))
if (!pl.isEnabled()){
pm.enablePlugin(pl);
}else { return 5; }
} catch (InvalidDescriptionException e) {
e.printStackTrace();
}
}
}
}
return 0; //success
}
@SuppressWarnings("unchecked")
public static int unload(String pluginName) {
pluginName = pluginName.toLowerCase().trim();
PluginManager manager = Bukkit.getServer().getPluginManager();
SimplePluginManager spm = (SimplePluginManager) manager;
SimpleCommandMap commandMap = null;
List<Plugin> plugins = null;
Map<String, Plugin> lookupNames = null;
Map<String, Command> knownCommands = null;
Map<Event, SortedSet<RegisteredListener>> listeners = null;
boolean reloadlisteners = true;
try {
if (spm != null) {
Field pluginsField = spm.getClass().getDeclaredField("plugins");
pluginsField.setAccessible(true);
plugins = (List<Plugin>) pluginsField.get(spm);
Field lookupNamesField = spm.getClass().getDeclaredField("lookupNames");
lookupNamesField.setAccessible(true);
lookupNames = (Map<String, Plugin>) lookupNamesField.get(spm);
try {
Field listenersField = spm.getClass().getDeclaredField("listeners");
listenersField.setAccessible(true);
listeners = (Map<Event, SortedSet<RegisteredListener>>) listenersField.get(spm);
} catch (Exception e) {
reloadlisteners = false;
}
Field commandMapField = spm.getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
commandMap = (SimpleCommandMap) commandMapField.get(spm);
Field knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
knownCommands = (Map<String, Command>) knownCommandsField.get(commandMap);
}
} catch (Exception e) {
e.printStackTrace();
}
boolean in = false;
for (Plugin pl : Bukkit.getServer().getPluginManager().getPlugins()) {
if (in)
break;
if (pl.getName().toLowerCase().startsWith(pluginName.toLowerCase())) {
if (pl.isEnabled()) {
manager.disablePlugin(pl);
if (plugins != null && plugins.contains(pl))
plugins.remove(pl);
if (lookupNames != null && lookupNames.containsKey(pl.getName())) {
lookupNames.remove(pl.getName());
}
if (listeners != null && reloadlisteners) {
for (SortedSet<RegisteredListener> set : listeners.values()) {
for (Iterator<RegisteredListener> it = set.iterator(); it.hasNext(); ) {
RegisteredListener value = it.next();
if (value.getPlugin() == pl) {
it.remove();
}
}
}
}
if (commandMap != null) {
for (Iterator<Map.Entry<String, Command>> it = knownCommands.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Command> entry = it.next();
if (entry.getValue() instanceof PluginCommand) {
PluginCommand c = (PluginCommand) entry.getValue();
if (c.getPlugin() == pl) {
c.unregister(commandMap);
it.remove();
}
}
}
}
for (Plugin plu : Bukkit.getServer().getPluginManager().getPlugins()) {
if (plu.getDescription().getDepend() != null) {
for (String depend : plu.getDescription().getDepend()) {
if (depend.equalsIgnoreCase(pl.getName())) {
plugin.getLogger().info("[Unloading Plugin] " + plu.getName() + " must be disabled!");
unload(plu.getName());
return 1; //dependencies also disabled
}
}
}
}
in = true;
} else { return 5; }
}
}
if (!in) {
plugin.getLogger().info("Not an existing plugin");
return -1; //non-existent
}
System.gc();
return 0; //success
}
}
*/

View File

@ -39,9 +39,10 @@ public class TabHandler {
if(this.init()){ if(this.init()){
if (headerComponents.isEmpty() && footerComponents.isEmpty()){ if (headerComponents.isEmpty() && footerComponents.isEmpty()){
plugin.getLogger().warning(plugin.getPrefix()+"TAB customization disabled because empty customization config or feature related errors!"); plugin.getLogger().warning(ChatColor.stripColor(plugin.getPrefix()) + "TAB customization disabled because empty customization config or feature related errors!");
return; return;
} }
plugin.getLogger().info("- TAB customization enabled!");
this.updateTab(); this.updateTab();
} }
@ -149,7 +150,7 @@ public class TabHandler {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); plugin.getLogger().warning(ChatColor.stripColor(plugin.getPrefix()) + e.getMessage());
} }
},0,Math.convert(Math.Convert.SECONDS,Math.Convert.TICKS,this.refreshRate)); },0,Math.convert(Math.Convert.SECONDS,Math.Convert.TICKS,this.refreshRate));
@ -184,7 +185,7 @@ public class TabHandler {
} }
} catch (Exception e){ } catch (Exception e){
e.printStackTrace(); plugin.getLogger().warning(ChatColor.stripColor(plugin.getPrefix()) + e.getMessage());
} }
} }

View File

@ -25,9 +25,19 @@ public class ServerPasswordEvents implements Listener {
this.config = plugin.getConfig(); this.config = plugin.getConfig();
} }
private boolean skipIfOpPlayer(Player player)
{
return (player.isOp() && config.getBoolean("ServerPassword.exceptOps")) || player.hasPermission("st.slogin.skip");
}
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (skipIfOpPlayer(player)) {
return;
}
if ((!config.getBoolean("ServerPassword.rememberUntilRestart")) if ((!config.getBoolean("ServerPassword.rememberUntilRestart"))
|| (!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(player.getUniqueId(),false))) { || (!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(player.getUniqueId(),false))) {
plugin.getServerPasswordData().getAuthenticatedPlayers().put(player.getUniqueId(),false); plugin.getServerPasswordData().getAuthenticatedPlayers().put(player.getUniqueId(),false);
@ -57,6 +67,9 @@ public class ServerPasswordEvents implements Listener {
@EventHandler @EventHandler
public void onPlayerMove(PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
if (skipIfOpPlayer(event.getPlayer())) {
return;
}
if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false)) if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false))
&& config.getBoolean("ServerPassword.preventMove")) { && config.getBoolean("ServerPassword.preventMove")) {
event.setCancelled(true); event.setCancelled(true);
@ -65,6 +78,9 @@ public class ServerPasswordEvents implements Listener {
@EventHandler @EventHandler
public void onBlockBreak(BlockBreakEvent event) { public void onBlockBreak(BlockBreakEvent event) {
if (skipIfOpPlayer(event.getPlayer())) {
return;
}
if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false)) if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false))
&& config.getBoolean("ServerPassword.preventBuild")) { && config.getBoolean("ServerPassword.preventBuild")) {
event.setCancelled(true); event.setCancelled(true);
@ -73,6 +89,9 @@ public class ServerPasswordEvents implements Listener {
@EventHandler @EventHandler
public void onBlockPlace(BlockPlaceEvent event) { public void onBlockPlace(BlockPlaceEvent event) {
if (skipIfOpPlayer(event.getPlayer())) {
return;
}
if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false)) if ((!plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(event.getPlayer().getUniqueId(),false))
&& config.getBoolean("ServerPassword.preventBuild")) { && config.getBoolean("ServerPassword.preventBuild")) {
event.setCancelled(true); event.setCancelled(true);
@ -84,6 +103,10 @@ public class ServerPasswordEvents implements Listener {
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
if (skipIfOpPlayer(player)) {
return;
}
if (plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(uuid, false)) { if (plugin.getServerPasswordData().getAuthenticatedPlayers().getOrDefault(uuid, false)) {
if (!config.getBoolean("ServerPassword.rememberUntilRestart")) { if (!config.getBoolean("ServerPassword.rememberUntilRestart")) {
plugin.getServerPasswordData().getAuthenticatedPlayers().remove(uuid); plugin.getServerPasswordData().getAuthenticatedPlayers().remove(uuid);

View File

@ -1,7 +1,5 @@
package hu.ditservices.utils; package hu.ditservices.utils;
import java.io.IOException;
public class Math { public class Math {
public enum Convert { public enum Convert {
TICKS,SECONDS,MINUTES,HOURS TICKS,SECONDS,MINUTES,HOURS

View File

@ -1,6 +1,8 @@
package hu.ditservices.utils; package hu.ditservices.utils;
import hu.ditservices.STPlugin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.lang.Math; import java.lang.Math;
@ -50,7 +52,8 @@ public class Server {
} }
return player.getPing(); return player.getPing();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); STPlugin plugin = STPlugin.getInstance();
plugin.getLogger().warning(ChatColor.stripColor(plugin.getPrefix()) + e.getMessage());
return -1; return -1;
} }
} }

View File

@ -1,6 +1,8 @@
package hu.ditservices.utils; package hu.ditservices.utils;
import hu.ditservices.STPlugin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -91,7 +93,8 @@ public class Version {
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// The method doesn't exist in this server version; fall back below. // The method doesn't exist in this server version; fall back below.
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); STPlugin plugin = STPlugin.getInstance();
plugin.getLogger().warning(ChatColor.stripColor(plugin.getPrefix()) + e.getMessage());
} }
// Fallback: use Bukkit version if no version was obtained or it's empty. // Fallback: use Bukkit version if no version was obtained or it's empty.

View File

@ -91,12 +91,28 @@ ServerPassword:
preventMove: true preventMove: true
preventBuild: true preventBuild: true
# Dropped items clearing options
ClearDropItems:
enabled: false
interval: 300 # Interval in seconds between each clear cycle
broadcastMsg: true # Broadcasts alerts before clearing 1 minutes and 10 seconds before and when cleared. Set this to 'false' to make cleaning silently.
# Dropped items that should always be kept (never cleared)
except:
- DIAMOND
- GOLD_INGOT
# - COAL
# - IRON_INGOT
# - BREAD
# Worlds to skip (dropped items in these worlds won't be cleared)
skipWorlds:
- world_nether
- world_the_end
# Cooldown options (only applicable for this plugin!) # Cooldown options (only applicable for this plugin!)
Cooldown: Cooldown:
enabled: true enabled: true
# The time must be passed between using this plugin commands. # The time must be passed between using this plugin commands.
seconds: 2 #The command cooldown in seconds seconds: 1 #The command cooldown in seconds
Msg: '{PREFIX} Please wait {SECONDS} seconds!' # The message when the user spamming a command Msg: '{PREFIX} Please wait {SECONDS} seconds!' # The message when the user spamming a command
# #
# Plugin Manager - Enable/Disable plugins without server restart # Plugin Manager - Enable/Disable plugins without server restart

View File

@ -37,3 +37,6 @@ pmanager.success.enabled: " successfully enabled!"
pmanager.already.disabled: " already disabled!" pmanager.already.disabled: " already disabled!"
pmanager.success.disabled: " successfully disabled!" pmanager.success.disabled: " successfully disabled!"
list.help: "To list all SimplifyTools commands use the '/help SIMPLIFYTOOLS' command!" list.help: "To list all SimplifyTools commands use the '/help SIMPLIFYTOOLS' command!"
cleardropitems.oneMin: "Attention: Dropped items will be cleared in 1 minute!"
cleardropitems.tenSec: "Attention: Dropped items will be cleared in 10 seconds!"
cleardropitems.cleared: "Dropped items have been cleared (%REMOVECOUNT% items removed)."

View File

@ -37,3 +37,6 @@ pmanager.success.enabled: " sikeresen engedélyezve!"
pmanager.already.disabled: " már kivolt kapcsolva eddig is!" pmanager.already.disabled: " már kivolt kapcsolva eddig is!"
pmanager.success.disabled: " sikeresen kikapcsolva!" pmanager.success.disabled: " sikeresen kikapcsolva!"
list.help: "Használd a '/help SIMPLIFYTOOLS' parancsot a plugin parancsainak listázásához!" list.help: "Használd a '/help SIMPLIFYTOOLS' parancsot a plugin parancsainak listázásához!"
cleardropitems.oneMin: "Figyelem! Az eldobott tárgyak 1 perc múlva törlésre kerülnek!"
cleardropitems.tenSec: "Figyelem! Az eldobott tárgyak 10 másodpercen belül törlésre kerülnek!"
cleardropitems.cleared: "Az eldobott tárgyak törlésre kerültek. (%REMOVECOUNT% tárgy lett eltávolítva)."