├── .gitignore ├── LICENSE ├── README.md ├── api ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── github │ └── julyss2019 │ └── bukkit │ └── julysafe │ └── api │ ├── EntityFilter.java │ ├── ItemFilter.java │ └── event │ └── DropsCleanedEvent.kt ├── build.gradle ├── core ├── build.gradle ├── libs │ ├── MythicMobs-4.13.0.jar │ └── MythicMobs-5.7.1.jar └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── julyss2019 │ │ └── bukkit │ │ └── julysafe │ │ └── core │ │ ├── InclusiveExclusiveSet.kt │ │ ├── JulySafePlugin.kt │ │ ├── Metrics.java │ │ ├── Permission.kt │ │ ├── Scheduler.kt │ │ ├── bossbar │ │ └── BossBarManager.kt │ │ ├── command │ │ └── CommandSet.kt │ │ ├── config │ │ └── JulySafeConfig.kt │ │ ├── entity │ │ ├── EntitySet.kt │ │ └── filter │ │ │ ├── BaseEntityFilter.kt │ │ │ ├── ClassEntityFilter.kt │ │ │ ├── CoreEntityFilter.kt │ │ │ ├── CustomNameEntityFilter.kt │ │ │ ├── EnumEntityFilter.kt │ │ │ ├── GroovyEntityFilter.kt │ │ │ ├── HasPassengersEntityFilter.kt │ │ │ ├── MetadataEntityFilter.kt │ │ │ ├── MythicEntityFilter.kt │ │ │ ├── NameEntityFilter.kt │ │ │ └── RegexesEntityFilter.kt │ │ ├── executor │ │ ├── BaseExecutor.kt │ │ ├── Executor.kt │ │ ├── FixedTimeExecutor.kt │ │ ├── SchedulerExecutor.kt │ │ ├── TimerExecutor.kt │ │ ├── completer │ │ │ └── Completer.kt │ │ ├── countdown │ │ │ └── CountdownTimer.kt │ │ └── notification │ │ │ ├── ActionBarNotification.kt │ │ │ ├── BossBarNotification.kt │ │ │ ├── MessageNotification.kt │ │ │ ├── Notification.kt │ │ │ ├── TitleNotification.kt │ │ │ └── message │ │ │ └── processor │ │ │ ├── ColoredPlaceholderMessageProcessor.kt │ │ │ └── MessageProcessor.kt │ │ ├── internal │ │ └── command │ │ │ ├── DebugCommandGroup.kt │ │ │ └── PluginCommandGroup.kt │ │ ├── item │ │ ├── ItemSet.kt │ │ └── filter │ │ │ ├── BaseItemFilter.kt │ │ │ ├── CoreItemFilter.kt │ │ │ ├── DisplayNameItemFilter.kt │ │ │ ├── EnchantmentItemFilter.kt │ │ │ ├── EnumItemFilter.kt │ │ │ ├── GroovyItemFilter.kt │ │ │ ├── LoreItemFilter.kt │ │ │ └── RegexesItemFilter.kt │ │ ├── kotlin │ │ └── extension │ │ │ ├── BlockExtension.kt │ │ │ ├── ChunkExtension.kt │ │ │ ├── EntityExtension.kt │ │ │ ├── ItemExtension.kt │ │ │ ├── LocationExtension.kt │ │ │ └── PlayerExtension.kt │ │ ├── listener │ │ ├── BlockExplodeLimitListener.kt │ │ ├── BossBarListener.kt │ │ ├── ChatBlacklistListener.kt │ │ ├── ChatSpamLimitListener.kt │ │ ├── CommandLimitListener.kt │ │ ├── CommandSpamLimitListener.kt │ │ ├── CropTrampleLimitListener.kt │ │ ├── EntityAndItemDebugListener.kt │ │ ├── EntityExplodeLimitListener.kt │ │ ├── EntitySpawnLimitListener.kt │ │ ├── FireSpreadLimitListener.kt │ │ ├── IllegalPlayerLimitListener.kt │ │ ├── PlayerDropRecordListener.kt │ │ ├── PlayerListener.kt │ │ ├── PlayerPickupRecordListener.kt │ │ └── RedstoneLimitListener.kt │ │ ├── locale │ │ └── LocaleResource.kt │ │ ├── module │ │ ├── AutoRestartModule.kt │ │ ├── BaseModule.kt │ │ ├── BlockExplodeLimitModule.kt │ │ ├── ChatBlacklistModule.kt │ │ ├── ChatSpamLimitModule.kt │ │ ├── ChunkEntityLimitModule.kt │ │ ├── CommandLimitModule.kt │ │ ├── CommandSpamLimitModule.kt │ │ ├── CropTrampleLimitModule.kt │ │ ├── DropCleanModule.kt │ │ ├── EntityCleanModule.kt │ │ ├── EntityExplodeLimitModule.kt │ │ ├── EntitySpawnLimitModule.kt │ │ ├── FireSpreadLimitModule.kt │ │ ├── IllegalPlayerLimitModule.kt │ │ ├── Module.kt │ │ ├── ModuleManager.kt │ │ ├── PlayerDropRecordModule.kt │ │ ├── PlayerPickupRecordModule.kt │ │ ├── RedstoneLimitModule.kt │ │ └── support │ │ │ ├── EntitySetSupport.kt │ │ │ ├── ExecutorSupport.kt │ │ │ ├── ItemSetSupport.kt │ │ │ └── WorldSetSupport.kt │ │ ├── player │ │ ├── JulySafePlayer.kt │ │ └── JulySafePlayerManager.kt │ │ ├── redstone │ │ ├── detector │ │ │ ├── BaseDetector.kt │ │ │ ├── BlockCounterDetector.kt │ │ │ ├── BlockTimerDetector.kt │ │ │ ├── ChunkCounterDetector.kt │ │ │ ├── ChunkTimerDetector.kt │ │ │ └── Detector.kt │ │ └── record │ │ │ ├── BaseRecord.kt │ │ │ ├── CounterRecord.kt │ │ │ ├── Record.kt │ │ │ └── TimerRecord.kt │ │ ├── task │ │ └── IllegalPlayerLimitTask.kt │ │ ├── tps │ │ └── TpsManager.kt │ │ ├── util │ │ ├── CooldownTimer.kt │ │ ├── FileUtils.kt │ │ ├── MessageUtils.kt │ │ └── MinecraftVersion.kt │ │ └── world │ │ └── WorldSet.kt │ └── resources │ ├── defaults │ ├── config.yml │ ├── locales │ │ └── zh_CN.yml │ └── modules.yml │ └── plugin.yml ├── example-groovy-scripts ├── build.gradle └── src │ └── main │ └── java │ └── SimpleEntityFilter.groovy ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | /target 3 | *.iml 4 | .idea 5 | build 6 | .gradle 7 | out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JulySafe 2 | 3 | JulySafe 是一款为 Minecraft 服务器提供优化与安全功能的插件。 4 | 5 | ## 文档 6 | http://wiki.void01.com/JulySafe/ 7 | 8 | ## 构建 9 | 1. `gradle chean build` 10 | 2. 完成 -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'maven-publish' 3 | } 4 | 5 | publishing { 6 | publications { 7 | maven(MavenPublication) { 8 | artifactId = 'july-safe-api' 9 | from components.java 10 | } 11 | } 12 | 13 | repositories { 14 | mavenLocal() 15 | maven { 16 | allowInsecureProtocol = true 17 | url = 'https://maven.void01.com/repository/bukkit-plugin/' 18 | 19 | credentials { 20 | username = project.findProperty('nexus.username') 21 | password = project.findProperty('nexus.password') 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/com/github/julyss2019/bukkit/julysafe/api/EntityFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.api; 2 | 3 | import org.bukkit.entity.Entity; 4 | 5 | public interface EntityFilter { 6 | boolean filter(Entity entity); 7 | } 8 | -------------------------------------------------------------------------------- /api/src/main/java/com/github/julyss2019/bukkit/julysafe/api/ItemFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.api; 2 | 3 | import org.bukkit.entity.Item; 4 | 5 | public interface ItemFilter { 6 | boolean filter(Item item); 7 | } 8 | -------------------------------------------------------------------------------- /api/src/main/java/com/github/julyss2019/bukkit/julysafe/api/event/DropsCleanedEvent.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.api.event 2 | 3 | import org.bukkit.entity.Item 4 | import org.bukkit.event.Event 5 | import org.bukkit.event.HandlerList 6 | 7 | class DropsCleanedEvent(val drops: List) : Event() { 8 | companion object { 9 | @JvmStatic 10 | val handlerList = HandlerList() 11 | } 12 | 13 | override fun getHandlers(): HandlerList { 14 | return handlerList 15 | } 16 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.jetbrains.kotlin.jvm' version '1.9.0' 4 | } 5 | 6 | allprojects { 7 | group 'com.github.julyss2019.bukkit' 8 | 9 | apply plugin: 'java' 10 | apply plugin: 'org.jetbrains.kotlin.jvm' 11 | 12 | version '4.0.14' 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { 17 | url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' 18 | } 19 | maven { url 'https://repo.dmulloy2.net/repository/public/' } 20 | maven { 21 | url 'https://maven.void01.com/repository/bukkit-plugin/' 22 | } 23 | } 24 | 25 | dependencies { 26 | compileOnly 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' 27 | 28 | compileOnly 'com.void01.bukkit:void-framework-bukkit-api:latest' 29 | compileOnly 'com.void01.bukkit:void-framework-bukkit-api-legacy:latest' 30 | compileOnly 'com.void01.bukkit:void-framework-bukkit-common:latest' 31 | 32 | compileOnly 'net.md-5:bungeecord-chat:1.16-R0.4' 33 | compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT' 34 | } 35 | 36 | kotlin { 37 | jvmToolchain(8) 38 | } 39 | 40 | tasks.withType(JavaCompile) { 41 | options.encoding = "UTF-8" 42 | } 43 | } -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.filters.ReplaceTokens 2 | 3 | plugins { 4 | id 'com.github.johnrengelman.shadow' version '7.0.0' 5 | } 6 | 7 | shadowJar { 8 | archiveBaseName.set('JulySafe') 9 | archiveClassifier.set('') 10 | 11 | relocate 'kotlin', 'kotlin.v1_9_20' 12 | } 13 | 14 | dependencies { 15 | implementation project(':api') 16 | compileOnly 'com.comphenix.protocol:ProtocolLib:4.8.0' 17 | compileOnly fileTree(dir: 'libs', include: ['*.jar']) 18 | } 19 | 20 | processResources { 21 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 22 | 23 | filesMatching('plugin.yml') { 24 | filter(ReplaceTokens, beginToken: '${', endToken: '}', tokens: ['version': project.version]) 25 | } 26 | } 27 | 28 | tasks.named('build') { 29 | dependsOn { 30 | shadowJar 31 | } 32 | } -------------------------------------------------------------------------------- /core/libs/MythicMobs-4.13.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julyss2019/JulySafe/13ad11112ef114270d636c1ca3321145d91bc574/core/libs/MythicMobs-4.13.0.jar -------------------------------------------------------------------------------- /core/libs/MythicMobs-5.7.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julyss2019/JulySafe/13ad11112ef114270d636c1ca3321145d91bc574/core/libs/MythicMobs-5.7.1.jar -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/InclusiveExclusiveSet.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core 2 | 3 | interface InclusiveExclusiveSet { 4 | val includes: List 5 | val excludes: List 6 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/JulySafePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.bossbar.BossBarManager 4 | import com.github.julyss2019.bukkit.julysafe.core.config.JulySafeConfig 5 | import com.github.julyss2019.bukkit.julysafe.core.internal.command.DebugCommandGroup 6 | import com.github.julyss2019.bukkit.julysafe.core.internal.command.PluginCommandGroup 7 | import com.github.julyss2019.bukkit.julysafe.core.listener.BossBarListener 8 | import com.github.julyss2019.bukkit.julysafe.core.listener.EntityAndItemDebugListener 9 | import com.github.julyss2019.bukkit.julysafe.core.listener.PlayerListener 10 | import com.github.julyss2019.bukkit.julysafe.core.locale.LocaleResource 11 | import com.github.julyss2019.bukkit.julysafe.core.module.ModuleManager 12 | import com.github.julyss2019.bukkit.julysafe.core.player.JulySafePlayerManager 13 | import com.github.julyss2019.bukkit.julysafe.core.tps.TpsManager 14 | import com.github.julyss2019.bukkit.julysafe.core.util.FileUtils 15 | import com.github.julyss2019.bukkit.voidframework.VoidFramework 16 | import com.github.julyss2019.bukkit.voidframework.command.annotation.CommandMapping 17 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 18 | import com.github.julyss2019.bukkit.voidframework.text.Texts 19 | import com.void01.bukkit.voidframework.api.common.extension.VoidPlugin 20 | import org.bukkit.Bukkit 21 | import org.bukkit.command.CommandSender 22 | import java.io.File 23 | 24 | @CommandMapping(value = "july-safe", permission = "JulySafe.Command.All") 25 | class JulySafePlugin : VoidPlugin() { 26 | companion object { 27 | lateinit var instance: JulySafePlugin 28 | private set 29 | } 30 | 31 | lateinit var julySafeConfig: JulySafeConfig 32 | private set 33 | lateinit var bossBarManager: BossBarManager 34 | private set 35 | lateinit var tpsManager: TpsManager 36 | private set 37 | lateinit var moduleManager: ModuleManager 38 | private set 39 | lateinit var localeResource: LocaleResource 40 | private set 41 | lateinit var scheduler: Scheduler 42 | private set 43 | lateinit var julySafePlayerManager: JulySafePlayerManager 44 | private set 45 | 46 | override fun onPluginEnable() { 47 | saveDefaults() 48 | instance = this 49 | pluginLogger.info("插件版本: v${description.version}") 50 | 51 | saveDefaults() 52 | julySafeConfig = JulySafeConfig(this) 53 | julySafeConfig.load() 54 | 55 | localeResource = LocaleResource.fromPluginLocaleFolder(julySafeConfig.locale, this) 56 | localeResource.textProcessor = object : LocaleResource.TextProcessor { 57 | override fun process(text: String): String { 58 | return Texts.setPlaceholders(text, PlaceholderContainer().put("prefix", localeResource.getOriginalString("prefix"))) 59 | } 60 | } 61 | 62 | scheduler = createScheduler() 63 | 64 | julySafePlayerManager = JulySafePlayerManager() 65 | bossBarManager = BossBarManager() 66 | moduleManager = ModuleManager(this) 67 | 68 | pluginLogger.info("正在加载模块: ") 69 | moduleManager.load() 70 | scheduler.start() 71 | registerCommands() 72 | 73 | Bukkit.getPluginManager().run { 74 | registerEvents(EntityAndItemDebugListener(this@JulySafePlugin), this@JulySafePlugin) 75 | registerEvents(BossBarListener(this@JulySafePlugin), this@JulySafePlugin) 76 | registerEvents(PlayerListener(this@JulySafePlugin), this@JulySafePlugin) 77 | } 78 | 79 | if (julySafeConfig.bStatsEnabled) { 80 | Metrics(this, 8485) 81 | } 82 | 83 | pluginLogger.info("作者: 柒 月, QQ: 884633197.") 84 | pluginLogger.info("JulySafe 付费技术支持 QQ 群: 1148417878.") 85 | pluginLogger.info("插件已加载.") 86 | } 87 | 88 | override fun onPluginDisable() { 89 | pluginLogger.info("插件已卸载.") 90 | bossBarManager.unregisterBossBars() 91 | VoidFramework.getCommandManager().unregisterCommandFrameworks(this) 92 | VoidFramework.getLogManager().unregisterLoggers(this) 93 | } 94 | 95 | private fun createScheduler(): Scheduler { 96 | return Scheduler(this, julySafeConfig.scheduler.delay, julySafeConfig.scheduler.period) 97 | } 98 | 99 | private fun registerCommands() { 100 | val commandFramework = VoidFramework.getCommandManager().createCommandFramework(this) 101 | 102 | commandFramework.registerCommandGroup(DebugCommandGroup(this)) 103 | commandFramework.registerCommandGroup(PluginCommandGroup(this)) 104 | } 105 | 106 | fun reload() { 107 | saveDefaults() 108 | scheduler.stop() 109 | julySafeConfig.reload() 110 | localeResource.reload() 111 | this.scheduler = createScheduler() 112 | moduleManager.reload() 113 | scheduler.start() 114 | } 115 | 116 | fun checkPermission(sender: CommandSender, permission: Permission): Boolean { 117 | return sender.hasPermission(permission.toBukkitPermission()) || sender.hasPermission(Permission.ALL.toBukkitPermission()) 118 | } 119 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/Metrics.java: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import com.google.gson.JsonPrimitive; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.configuration.file.YamlConfiguration; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.plugin.Plugin; 11 | import org.bukkit.plugin.RegisteredServiceProvider; 12 | import org.bukkit.plugin.ServicePriority; 13 | 14 | import javax.net.ssl.HttpsURLConnection; 15 | import java.io.*; 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.lang.reflect.Method; 18 | import java.net.URL; 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.*; 21 | import java.util.concurrent.Callable; 22 | import java.util.logging.Level; 23 | import java.util.zip.GZIPOutputStream; 24 | 25 | /** 26 | * bStats collects some data for plugin authors. 27 | *

28 | * Check out https://bStats.org/ to learn more about bStats! 29 | */ 30 | @SuppressWarnings({"WeakerAccess", "unused"}) 31 | public class Metrics { 32 | 33 | static { 34 | // You can use the property to disable the check in your test environment 35 | if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { 36 | // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D 37 | final String defaultPackage = new String( 38 | new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); 39 | final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); 40 | // We want to make sure nobody just copy & pastes the example and use the wrong package names 41 | if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { 42 | throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); 43 | } 44 | } 45 | } 46 | 47 | // The version of this bStats class 48 | public static final int B_STATS_VERSION = 1; 49 | 50 | // The url to which the data is sent 51 | private static final String URL = "https://bStats.org/submitData/bukkit"; 52 | 53 | // Is bStats enabled on this server? 54 | private boolean enabled; 55 | 56 | // Should failed requests be logged? 57 | private static boolean logFailedRequests; 58 | 59 | // Should the sent data be logged? 60 | private static boolean logSentData; 61 | 62 | // Should the response text be logged? 63 | private static boolean logResponseStatusText; 64 | 65 | // The uuid of the server 66 | private static String serverUUID; 67 | 68 | // The plugin 69 | private final Plugin plugin; 70 | 71 | // The plugin id 72 | private final int pluginId; 73 | 74 | // A list with all custom charts 75 | private final List charts = new ArrayList<>(); 76 | 77 | /** 78 | * Class constructor. 79 | * 80 | * @param plugin The plugin which stats should be submitted. 81 | * @param pluginId The id of the plugin. 82 | * It can be found at What is my plugin id? 83 | */ 84 | public Metrics(Plugin plugin, int pluginId) { 85 | if (plugin == null) { 86 | throw new IllegalArgumentException("Plugin cannot be null!"); 87 | } 88 | this.plugin = plugin; 89 | this.pluginId = pluginId; 90 | 91 | // Get the config file 92 | File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); 93 | File configFile = new File(bStatsFolder, "config.yml"); 94 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 95 | 96 | // Check if the config file exists 97 | if (!config.isSet("serverUuid")) { 98 | 99 | // Add default values 100 | config.addDefault("enabled", true); 101 | // Every server gets it's unique random id. 102 | config.addDefault("serverUuid", UUID.randomUUID().toString()); 103 | // Should failed request be logged? 104 | config.addDefault("logFailedRequests", false); 105 | // Should the sent data be logged? 106 | config.addDefault("logSentData", false); 107 | // Should the response text be logged? 108 | config.addDefault("logResponseStatusText", false); 109 | 110 | // Inform the server owners about bStats 111 | config.options().header( 112 | "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + 113 | "To honor their work, you should not disable it.\n" + 114 | "This has nearly no effect on the server performance!\n" + 115 | "Check out https://bStats.org/ to learn more :)" 116 | ).copyDefaults(true); 117 | try { 118 | config.save(configFile); 119 | } catch (IOException ignored) { } 120 | } 121 | 122 | // Load the data 123 | enabled = config.getBoolean("enabled", true); 124 | serverUUID = config.getString("serverUuid"); 125 | logFailedRequests = config.getBoolean("logFailedRequests", false); 126 | logSentData = config.getBoolean("logSentData", false); 127 | logResponseStatusText = config.getBoolean("logResponseStatusText", false); 128 | 129 | if (enabled) { 130 | boolean found = false; 131 | // Search for all other bStats Metrics classes to see if we are the first one 132 | for (Class service : Bukkit.getServicesManager().getKnownServices()) { 133 | try { 134 | service.getField("B_STATS_VERSION"); // Our identifier :) 135 | found = true; // We aren't the first 136 | break; 137 | } catch (NoSuchFieldException ignored) { } 138 | } 139 | // Register our service 140 | Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); 141 | if (!found) { 142 | // We are the first! 143 | startSubmitting(); 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Checks if bStats is enabled. 150 | * 151 | * @return Whether bStats is enabled or not. 152 | */ 153 | public boolean isEnabled() { 154 | return enabled; 155 | } 156 | 157 | /** 158 | * Adds a custom chart. 159 | * 160 | * @param chart The chart to add. 161 | */ 162 | public void addCustomChart(CustomChart chart) { 163 | if (chart == null) { 164 | throw new IllegalArgumentException("Chart cannot be null!"); 165 | } 166 | charts.add(chart); 167 | } 168 | 169 | /** 170 | * Starts the Scheduler which submits our data every 30 minutes. 171 | */ 172 | private void startSubmitting() { 173 | final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags 174 | timer.scheduleAtFixedRate(new TimerTask() { 175 | @Override 176 | public void run() { 177 | if (!plugin.isEnabled()) { // Plugin was disabled 178 | timer.cancel(); 179 | return; 180 | } 181 | // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler 182 | // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) 183 | Bukkit.getScheduler().runTask(plugin, () -> submitData()); 184 | } 185 | }, 1000 * 60 * 5, 1000 * 60 * 30); 186 | // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start 187 | // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! 188 | // WARNING: Just don't do it! 189 | } 190 | 191 | /** 192 | * Gets the plugin specific data. 193 | * This method is called using Reflection. 194 | * 195 | * @return The plugin specific data. 196 | */ 197 | public JsonObject getPluginData() { 198 | JsonObject data = new JsonObject(); 199 | 200 | String pluginName = plugin.getDescription().getName(); 201 | String pluginVersion = plugin.getDescription().getVersion(); 202 | 203 | data.addProperty("pluginName", pluginName); // Append the name of the plugin 204 | data.addProperty("id", pluginId); // Append the id of the plugin 205 | data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin 206 | JsonArray customCharts = new JsonArray(); 207 | for (CustomChart customChart : charts) { 208 | // Add the data of the custom charts 209 | JsonObject chart = customChart.getRequestJsonObject(); 210 | if (chart == null) { // If the chart is null, we skip it 211 | continue; 212 | } 213 | customCharts.add(chart); 214 | } 215 | data.add("customCharts", customCharts); 216 | 217 | return data; 218 | } 219 | 220 | /** 221 | * Gets the server specific data. 222 | * 223 | * @return The server specific data. 224 | */ 225 | private JsonObject getServerData() { 226 | // Minecraft specific data 227 | int playerAmount; 228 | try { 229 | // Around MC 1.8 the return type was changed to a collection from an array, 230 | // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; 231 | Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); 232 | playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) 233 | ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() 234 | : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; 235 | } catch (Exception e) { 236 | playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed 237 | } 238 | int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; 239 | String bukkitVersion = Bukkit.getVersion(); 240 | String bukkitName = Bukkit.getName(); 241 | 242 | // OS/Java specific data 243 | String javaVersion = System.getProperty("java.version"); 244 | String osName = System.getProperty("os.name"); 245 | String osArch = System.getProperty("os.arch"); 246 | String osVersion = System.getProperty("os.version"); 247 | int coreCount = Runtime.getRuntime().availableProcessors(); 248 | 249 | JsonObject data = new JsonObject(); 250 | 251 | data.addProperty("serverUUID", serverUUID); 252 | 253 | data.addProperty("playerAmount", playerAmount); 254 | data.addProperty("onlineMode", onlineMode); 255 | data.addProperty("bukkitVersion", bukkitVersion); 256 | data.addProperty("bukkitName", bukkitName); 257 | 258 | data.addProperty("javaVersion", javaVersion); 259 | data.addProperty("osName", osName); 260 | data.addProperty("osArch", osArch); 261 | data.addProperty("osVersion", osVersion); 262 | data.addProperty("coreCount", coreCount); 263 | 264 | return data; 265 | } 266 | 267 | /** 268 | * Collects the data and sends it afterwards. 269 | */ 270 | private void submitData() { 271 | final JsonObject data = getServerData(); 272 | 273 | JsonArray pluginData = new JsonArray(); 274 | // Search for all other bStats Metrics classes to get their plugin data 275 | for (Class service : Bukkit.getServicesManager().getKnownServices()) { 276 | try { 277 | service.getField("B_STATS_VERSION"); // Our identifier :) 278 | 279 | for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { 280 | try { 281 | Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider()); 282 | if (plugin instanceof JsonObject) { 283 | pluginData.add((JsonObject) plugin); 284 | } else { // old bstats version compatibility 285 | try { 286 | Class jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject"); 287 | if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) { 288 | Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString"); 289 | jsonStringGetter.setAccessible(true); 290 | String jsonString = (String) jsonStringGetter.invoke(plugin); 291 | JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject(); 292 | pluginData.add(object); 293 | } 294 | } catch (ClassNotFoundException e) { 295 | // minecraft version 1.14+ 296 | if (logFailedRequests) { 297 | this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e); 298 | } 299 | } 300 | } 301 | } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } 302 | } 303 | } catch (NoSuchFieldException ignored) { } 304 | } 305 | 306 | data.add("plugins", pluginData); 307 | 308 | // Create a new thread for the connection to the bStats server 309 | new Thread(() -> { 310 | try { 311 | // Send the data 312 | sendData(plugin, data); 313 | } catch (Exception e) { 314 | // Something went wrong! :( 315 | if (logFailedRequests) { 316 | plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); 317 | } 318 | } 319 | }).start(); 320 | } 321 | 322 | /** 323 | * Sends the data to the bStats server. 324 | * 325 | * @param plugin Any plugin. It's just used to get a logger instance. 326 | * @param data The data to send. 327 | * @throws Exception If the request failed. 328 | */ 329 | private static void sendData(Plugin plugin, JsonObject data) throws Exception { 330 | if (data == null) { 331 | throw new IllegalArgumentException("Data cannot be null!"); 332 | } 333 | if (Bukkit.isPrimaryThread()) { 334 | throw new IllegalAccessException("This method must not be called from the main thread!"); 335 | } 336 | if (logSentData) { 337 | plugin.getLogger().info("Sending data to bStats: " + data); 338 | } 339 | HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); 340 | 341 | // Compress the data to save bandwidth 342 | byte[] compressedData = compress(data.toString()); 343 | 344 | // Add headers 345 | connection.setRequestMethod("POST"); 346 | connection.addRequestProperty("Accept", "application/json"); 347 | connection.addRequestProperty("Connection", "close"); 348 | connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request 349 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); 350 | connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format 351 | connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); 352 | 353 | // Send data 354 | connection.setDoOutput(true); 355 | try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { 356 | outputStream.write(compressedData); 357 | } 358 | 359 | StringBuilder builder = new StringBuilder(); 360 | try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { 361 | String line; 362 | while ((line = bufferedReader.readLine()) != null) { 363 | builder.append(line); 364 | } 365 | } 366 | 367 | if (logResponseStatusText) { 368 | plugin.getLogger().info("Sent data to bStats and received response: " + builder); 369 | } 370 | } 371 | 372 | /** 373 | * Gzips the given String. 374 | * 375 | * @param str The string to gzip. 376 | * @return The gzipped String. 377 | * @throws IOException If the compression failed. 378 | */ 379 | private static byte[] compress(final String str) throws IOException { 380 | if (str == null) { 381 | return null; 382 | } 383 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 384 | try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { 385 | gzip.write(str.getBytes(StandardCharsets.UTF_8)); 386 | } 387 | return outputStream.toByteArray(); 388 | } 389 | 390 | /** 391 | * Represents a custom chart. 392 | */ 393 | public static abstract class CustomChart { 394 | 395 | // The id of the chart 396 | final String chartId; 397 | 398 | /** 399 | * Class constructor. 400 | * 401 | * @param chartId The id of the chart. 402 | */ 403 | CustomChart(String chartId) { 404 | if (chartId == null || chartId.isEmpty()) { 405 | throw new IllegalArgumentException("ChartId cannot be null or empty!"); 406 | } 407 | this.chartId = chartId; 408 | } 409 | 410 | private JsonObject getRequestJsonObject() { 411 | JsonObject chart = new JsonObject(); 412 | chart.addProperty("chartId", chartId); 413 | try { 414 | JsonObject data = getChartData(); 415 | if (data == null) { 416 | // If the data is null we don't send the chart. 417 | return null; 418 | } 419 | chart.add("data", data); 420 | } catch (Throwable t) { 421 | if (logFailedRequests) { 422 | Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); 423 | } 424 | return null; 425 | } 426 | return chart; 427 | } 428 | 429 | protected abstract JsonObject getChartData() throws Exception; 430 | 431 | } 432 | 433 | /** 434 | * Represents a custom simple pie. 435 | */ 436 | public static class SimplePie extends CustomChart { 437 | 438 | private final Callable callable; 439 | 440 | /** 441 | * Class constructor. 442 | * 443 | * @param chartId The id of the chart. 444 | * @param callable The callable which is used to request the chart data. 445 | */ 446 | public SimplePie(String chartId, Callable callable) { 447 | super(chartId); 448 | this.callable = callable; 449 | } 450 | 451 | @Override 452 | protected JsonObject getChartData() throws Exception { 453 | JsonObject data = new JsonObject(); 454 | String value = callable.call(); 455 | if (value == null || value.isEmpty()) { 456 | // Null = skip the chart 457 | return null; 458 | } 459 | data.addProperty("value", value); 460 | return data; 461 | } 462 | } 463 | 464 | /** 465 | * Represents a custom advanced pie. 466 | */ 467 | public static class AdvancedPie extends CustomChart { 468 | 469 | private final Callable> callable; 470 | 471 | /** 472 | * Class constructor. 473 | * 474 | * @param chartId The id of the chart. 475 | * @param callable The callable which is used to request the chart data. 476 | */ 477 | public AdvancedPie(String chartId, Callable> callable) { 478 | super(chartId); 479 | this.callable = callable; 480 | } 481 | 482 | @Override 483 | protected JsonObject getChartData() throws Exception { 484 | JsonObject data = new JsonObject(); 485 | JsonObject values = new JsonObject(); 486 | Map map = callable.call(); 487 | if (map == null || map.isEmpty()) { 488 | // Null = skip the chart 489 | return null; 490 | } 491 | boolean allSkipped = true; 492 | for (Map.Entry entry : map.entrySet()) { 493 | if (entry.getValue() == 0) { 494 | continue; // Skip this invalid 495 | } 496 | allSkipped = false; 497 | values.addProperty(entry.getKey(), entry.getValue()); 498 | } 499 | if (allSkipped) { 500 | // Null = skip the chart 501 | return null; 502 | } 503 | data.add("values", values); 504 | return data; 505 | } 506 | } 507 | 508 | /** 509 | * Represents a custom drilldown pie. 510 | */ 511 | public static class DrilldownPie extends CustomChart { 512 | 513 | private final Callable>> callable; 514 | 515 | /** 516 | * Class constructor. 517 | * 518 | * @param chartId The id of the chart. 519 | * @param callable The callable which is used to request the chart data. 520 | */ 521 | public DrilldownPie(String chartId, Callable>> callable) { 522 | super(chartId); 523 | this.callable = callable; 524 | } 525 | 526 | @Override 527 | public JsonObject getChartData() throws Exception { 528 | JsonObject data = new JsonObject(); 529 | JsonObject values = new JsonObject(); 530 | Map> map = callable.call(); 531 | if (map == null || map.isEmpty()) { 532 | // Null = skip the chart 533 | return null; 534 | } 535 | boolean reallyAllSkipped = true; 536 | for (Map.Entry> entryValues : map.entrySet()) { 537 | JsonObject value = new JsonObject(); 538 | boolean allSkipped = true; 539 | for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { 540 | value.addProperty(valueEntry.getKey(), valueEntry.getValue()); 541 | allSkipped = false; 542 | } 543 | if (!allSkipped) { 544 | reallyAllSkipped = false; 545 | values.add(entryValues.getKey(), value); 546 | } 547 | } 548 | if (reallyAllSkipped) { 549 | // Null = skip the chart 550 | return null; 551 | } 552 | data.add("values", values); 553 | return data; 554 | } 555 | } 556 | 557 | /** 558 | * Represents a custom single line chart. 559 | */ 560 | public static class SingleLineChart extends CustomChart { 561 | 562 | private final Callable callable; 563 | 564 | /** 565 | * Class constructor. 566 | * 567 | * @param chartId The id of the chart. 568 | * @param callable The callable which is used to request the chart data. 569 | */ 570 | public SingleLineChart(String chartId, Callable callable) { 571 | super(chartId); 572 | this.callable = callable; 573 | } 574 | 575 | @Override 576 | protected JsonObject getChartData() throws Exception { 577 | JsonObject data = new JsonObject(); 578 | int value = callable.call(); 579 | if (value == 0) { 580 | // Null = skip the chart 581 | return null; 582 | } 583 | data.addProperty("value", value); 584 | return data; 585 | } 586 | 587 | } 588 | 589 | /** 590 | * Represents a custom multi line chart. 591 | */ 592 | public static class MultiLineChart extends CustomChart { 593 | 594 | private final Callable> callable; 595 | 596 | /** 597 | * Class constructor. 598 | * 599 | * @param chartId The id of the chart. 600 | * @param callable The callable which is used to request the chart data. 601 | */ 602 | public MultiLineChart(String chartId, Callable> callable) { 603 | super(chartId); 604 | this.callable = callable; 605 | } 606 | 607 | @Override 608 | protected JsonObject getChartData() throws Exception { 609 | JsonObject data = new JsonObject(); 610 | JsonObject values = new JsonObject(); 611 | Map map = callable.call(); 612 | if (map == null || map.isEmpty()) { 613 | // Null = skip the chart 614 | return null; 615 | } 616 | boolean allSkipped = true; 617 | for (Map.Entry entry : map.entrySet()) { 618 | if (entry.getValue() == 0) { 619 | continue; // Skip this invalid 620 | } 621 | allSkipped = false; 622 | values.addProperty(entry.getKey(), entry.getValue()); 623 | } 624 | if (allSkipped) { 625 | // Null = skip the chart 626 | return null; 627 | } 628 | data.add("values", values); 629 | return data; 630 | } 631 | 632 | } 633 | 634 | /** 635 | * Represents a custom simple bar chart. 636 | */ 637 | public static class SimpleBarChart extends CustomChart { 638 | 639 | private final Callable> callable; 640 | 641 | /** 642 | * Class constructor. 643 | * 644 | * @param chartId The id of the chart. 645 | * @param callable The callable which is used to request the chart data. 646 | */ 647 | public SimpleBarChart(String chartId, Callable> callable) { 648 | super(chartId); 649 | this.callable = callable; 650 | } 651 | 652 | @Override 653 | protected JsonObject getChartData() throws Exception { 654 | JsonObject data = new JsonObject(); 655 | JsonObject values = new JsonObject(); 656 | Map map = callable.call(); 657 | if (map == null || map.isEmpty()) { 658 | // Null = skip the chart 659 | return null; 660 | } 661 | for (Map.Entry entry : map.entrySet()) { 662 | JsonArray categoryValues = new JsonArray(); 663 | categoryValues.add(new JsonPrimitive(entry.getValue())); 664 | values.add(entry.getKey(), categoryValues); 665 | } 666 | data.add("values", values); 667 | return data; 668 | } 669 | 670 | } 671 | 672 | /** 673 | * Represents a custom advanced bar chart. 674 | */ 675 | public static class AdvancedBarChart extends CustomChart { 676 | 677 | private final Callable> callable; 678 | 679 | /** 680 | * Class constructor. 681 | * 682 | * @param chartId The id of the chart. 683 | * @param callable The callable which is used to request the chart data. 684 | */ 685 | public AdvancedBarChart(String chartId, Callable> callable) { 686 | super(chartId); 687 | this.callable = callable; 688 | } 689 | 690 | @Override 691 | protected JsonObject getChartData() throws Exception { 692 | JsonObject data = new JsonObject(); 693 | JsonObject values = new JsonObject(); 694 | Map map = callable.call(); 695 | if (map == null || map.isEmpty()) { 696 | // Null = skip the chart 697 | return null; 698 | } 699 | boolean allSkipped = true; 700 | for (Map.Entry entry : map.entrySet()) { 701 | if (entry.getValue().length == 0) { 702 | continue; // Skip this invalid 703 | } 704 | allSkipped = false; 705 | JsonArray categoryValues = new JsonArray(); 706 | for (int categoryValue : entry.getValue()) { 707 | categoryValues.add(new JsonPrimitive(categoryValue)); 708 | } 709 | values.add(entry.getKey(), categoryValues); 710 | } 711 | if (allSkipped) { 712 | // Null = skip the chart 713 | return null; 714 | } 715 | data.add("values", values); 716 | return data; 717 | } 718 | } 719 | 720 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/Permission.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core 2 | 3 | import java.lang.StringBuilder 4 | 5 | enum class Permission { 6 | ALL, 7 | COMMAND_ALL, 8 | BYPASS_CHAT_SPAM_LIMIT, 9 | BYPASS_CHAT_BLACKLIST, 10 | BYPASS_COMMAND_BLACKLIST, 11 | BYPASS_COMMAND_SPAM_LIMIT; 12 | 13 | fun toBukkitPermission(): String { 14 | return "JulySafe.${ 15 | this.name.let { 16 | val sb = StringBuilder() 17 | 18 | for (s in it.split("_")) { 19 | sb.append(s[0].uppercase() + s.substring(1)) 20 | } 21 | 22 | sb 23 | } 24 | }" 25 | } 26 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/Scheduler.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.SchedulerExecutor 4 | import org.bukkit.scheduler.BukkitRunnable 5 | import org.bukkit.scheduler.BukkitTask 6 | 7 | class Scheduler(private val plugin: JulySafePlugin, private var later: Int, private var interval: Int) { 8 | private val executors = mutableListOf() 9 | private var bukkitTask: BukkitTask? = null 10 | 11 | private var currentExecutorIndex = -1 12 | private var currentTick = 0 13 | 14 | fun isRunning(): Boolean { 15 | return bukkitTask != null 16 | } 17 | 18 | private fun checkRunning() { 19 | require(isRunning()) { 20 | "executor is not running" 21 | } 22 | } 23 | 24 | private fun checkNotRunning() { 25 | require(!isRunning()) { 26 | "executor is running" 27 | } 28 | } 29 | 30 | fun getCurrentExecutor(): SchedulerExecutor? { 31 | checkRunning() 32 | 33 | if (currentExecutorIndex == -1) { 34 | return null 35 | } 36 | 37 | return executors[currentExecutorIndex] 38 | } 39 | 40 | fun clearExecutors() { 41 | checkNotRunning() 42 | 43 | executors.clear() 44 | } 45 | 46 | fun removeExecutor(schedulerExecutor: SchedulerExecutor) { 47 | checkNotRunning() 48 | 49 | executors.remove(schedulerExecutor) 50 | } 51 | 52 | fun addExecutor(schedulerExecutor: SchedulerExecutor) { 53 | checkNotRunning() 54 | 55 | executors.add(schedulerExecutor) 56 | executors.sortByDescending { it.priority } // 优先级降序 57 | } 58 | 59 | fun nextExecutor() { 60 | if (executors.isEmpty()) { 61 | return 62 | } 63 | 64 | currentExecutorIndex++ 65 | 66 | if (currentExecutorIndex == executors.size) { 67 | currentExecutorIndex = 0 68 | } 69 | } 70 | 71 | fun stop() { 72 | bukkitTask?.cancel() 73 | currentTick = 0 74 | currentExecutorIndex = -1 75 | bukkitTask = null 76 | } 77 | 78 | fun start() { 79 | require(!isRunning()) { 80 | "already running" 81 | } 82 | 83 | nextExecutor() 84 | 85 | // 无任务 86 | if (executors.isEmpty()) { 87 | return 88 | } 89 | 90 | bukkitTask = object : BukkitRunnable() { 91 | override fun run() { 92 | val executor = getCurrentExecutor()!! 93 | val countdown = interval - currentTick 94 | val countdownTimer = executor.countdownTimer 95 | 96 | executor.notifyCountdown(countdown) 97 | 98 | if (countdown == 0) { 99 | val task = executor.task 100 | 101 | task.run() 102 | countdownTimer?.notification?.clear() 103 | nextExecutor() 104 | currentTick = 0 105 | return 106 | } else { 107 | currentTick++ 108 | } 109 | } 110 | }.runTaskTimer(plugin, later * 20L, 20L) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/bossbar/BossBarManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.bossbar 2 | 3 | import org.bukkit.Bukkit 4 | import org.bukkit.boss.BossBar 5 | import org.bukkit.entity.Player 6 | 7 | /** 8 | * 主要用于管理 BoosBar 9 | */ 10 | class BossBarManager { 11 | private val bars = mutableListOf() 12 | 13 | fun getBars(): List { 14 | return bars.toList() 15 | } 16 | 17 | fun registerBossBar(bossBar: BossBar) { 18 | require(!bars.contains(bossBar)) { 19 | "bossbar already registered" 20 | } 21 | 22 | Bukkit.getOnlinePlayers().forEach { player: Player -> bossBar.addPlayer(player) } 23 | bars.add(bossBar) 24 | } 25 | 26 | fun containsBossBar(bossBar: BossBar): Boolean { 27 | return bars.contains(bossBar) 28 | } 29 | 30 | fun unregisterBossBars() { 31 | bars.forEach { 32 | it.removeAll() // 移除所有玩家 33 | } 34 | bars.clear() 35 | } 36 | 37 | fun unregisterBossBar(bossBar: BossBar) { 38 | require(bars.contains(bossBar)) { 39 | "bossbar not registered" 40 | } 41 | 42 | bossBar.removeAll() 43 | bars.remove(bossBar) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/command/CommandSet.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.command 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.InclusiveExclusiveSet 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | class CommandSet(override val includes: List, override val excludes: List) : 7 | InclusiveExclusiveSet { 8 | fun contains(command: String): Boolean { 9 | return !excludes.any { it.matches(command) } && includes.any { it.matches(command) } 10 | } 11 | 12 | object Parser { 13 | fun parse(section: Section): CommandSet { 14 | return CommandSet( 15 | includes = section.getStringList("includes").map { it.toRegex() }, 16 | excludes = section.getStringList("excludes").map { it.toRegex() }) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/config/JulySafeConfig.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.config 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.voidframework.locale.LocaleParser 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Yaml 6 | import java.util.* 7 | 8 | class JulySafeConfig(private val plugin: JulySafePlugin) { 9 | class Scheduler(val delay: Int, val period: Int) 10 | 11 | lateinit var locale: Locale 12 | private set 13 | lateinit var scheduler: Scheduler 14 | private set 15 | var bStatsEnabled: Boolean = false 16 | 17 | 18 | fun reload() { 19 | load() 20 | } 21 | 22 | fun load() { 23 | val yaml = Yaml.fromPluginConfigFile(plugin) 24 | 25 | bStatsEnabled = yaml.getBoolean("bStats_enabled") 26 | locale = LocaleParser.parse(yaml.getString("locale")) 27 | scheduler = Scheduler(yaml.getInt("scheduler.delay"), 28 | yaml.getInt("scheduler.period")) 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/EntitySet.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity 2 | 3 | 4 | import com.github.julyss2019.bukkit.julysafe.core.entity.filter.CoreEntityFilter 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | import org.bukkit.World 7 | import org.bukkit.entity.Entity 8 | import org.bukkit.entity.Player 9 | 10 | class EntitySet(override val includes: List, override val excludes: List) : 11 | com.github.julyss2019.bukkit.julysafe.core.InclusiveExclusiveSet { 12 | fun contains(entity: Entity): Boolean { 13 | if (entity is Player) { 14 | return false 15 | } 16 | 17 | for (filter in excludes) { 18 | if (filter.filter(entity)) { 19 | return false 20 | } 21 | } 22 | 23 | for (filter in includes) { 24 | if (filter.filter(entity)) { 25 | return true 26 | } 27 | } 28 | 29 | return false 30 | } 31 | 32 | fun getAllByWorld(world: World): List { 33 | return world.entities.filter { contains(it) } 34 | } 35 | 36 | override fun toString(): String { 37 | return "EntitySet(includes = $includes, excludes = $excludes)" 38 | } 39 | 40 | object Parser { 41 | fun parse(section: Section): EntitySet { 42 | return EntitySet(includes = section.getSection("includes").subSections.map { 43 | CoreEntityFilter.Parser.parse(it) 44 | }, excludes = section.getSection("excludes").subSections.map { 45 | CoreEntityFilter.Parser.parse(it) 46 | }) 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/BaseEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | abstract class BaseEntityFilter : CoreEntityFilter { 4 | override lateinit var id: String 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/ClassEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | import org.bukkit.entity.Entity 5 | 6 | class ClassEntityFilter : BaseEntityFilter() { 7 | private lateinit var classes: List> 8 | 9 | override fun filter(entity: Entity): Boolean { 10 | return classes.any { 11 | it.isAssignableFrom(entity.javaClass) 12 | } 13 | } 14 | 15 | override fun setProperties(section: Section) { 16 | classes = section.getStringList("classes").map { 17 | val clazz: Class<*> 18 | 19 | try { 20 | clazz = Class.forName(it) 21 | } catch (ex: Exception) { 22 | throw RuntimeException("invalid Entity class: $it") 23 | } 24 | 25 | if (!Entity::class.java.isAssignableFrom(clazz)) { 26 | throw RuntimeException("not an Entity class: $it") 27 | } 28 | 29 | clazz as Class 30 | } 31 | } 32 | 33 | override fun toString(): String { 34 | return "ClassEntityFilter(id=$id, classes=$classes)" 35 | } 36 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/CoreEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.julysafe.api.EntityFilter 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | interface CoreEntityFilter : EntityFilter { 7 | var id: String 8 | 9 | enum class Type(val mappingClass: Class) { 10 | CLASS(ClassEntityFilter::class.java), 11 | CUSTOM_NAME(CustomNameEntityFilter::class.java), 12 | NAME(NameEntityFilter::class.java), 13 | ENUM(EnumEntityFilter::class.java), 14 | GROOVY(GroovyEntityFilter::class.java), 15 | METADATA(MetadataEntityFilter::class.java), 16 | HAS_PASSENGERS(HasPassengersEntityFilter::class.java), 17 | MYTHIC_MOBS(MythicEntityFilter::class.java) 18 | } 19 | 20 | fun setProperties(section: Section) 21 | 22 | object Parser { 23 | fun parse(section: Section): CoreEntityFilter { 24 | return section.getEnum("type", Type::class.java).mappingClass.newInstance() 25 | .apply { 26 | id = section.name 27 | setProperties(section.getSection("properties")) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/CustomNameEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import org.bukkit.entity.Entity 4 | 5 | class CustomNameEntityFilter : RegexesEntityFilter() { 6 | override fun filter(entity: Entity): Boolean { 7 | return try { 8 | matchRegex(entity.customName) 9 | } catch (ex: Exception) { // CatServer 获取 customName 可能会抛出异常 10 | false 11 | } 12 | } 13 | 14 | override fun toString(): String { 15 | return "CustomNameEntityFilter(id=$id, regexes=$regexes)" 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/EnumEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import org.bukkit.entity.Entity 4 | 5 | class EnumEntityFilter : RegexesEntityFilter() { 6 | override fun filter(entity: Entity): Boolean { 7 | return matchRegex(entity.type.name) 8 | } 9 | 10 | override fun toString(): String { 11 | return "EnumEntityFilter(id=$id, regexes=$regexes)" 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/GroovyEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.julysafe.api.EntityFilter 4 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | import com.void01.bukkit.voidframework.api.common.VoidFramework2 7 | import com.void01.bukkit.voidframework.api.common.groovy.GroovyConfig 8 | import org.bukkit.entity.Entity 9 | import java.io.File 10 | 11 | class GroovyEntityFilter : BaseEntityFilter() { 12 | private lateinit var entityFilter: EntityFilter 13 | override fun filter(entity: Entity): Boolean { 14 | return entityFilter.filter(entity) 15 | } 16 | 17 | override fun setProperties(section: Section) { 18 | val scriptFile = File(JulySafePlugin.instance.dataFolder, section.getString("file")) 19 | 20 | entityFilter = VoidFramework2.getGroovyManager().parseClass(scriptFile, GroovyConfig().apply { 21 | sourceEncoding = "UTF-8" 22 | }).newInstance() as EntityFilter 23 | } 24 | 25 | override fun toString(): String { 26 | return "GroovyEntityFilter(entityFilter=$entityFilter)" 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/HasPassengersEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | import org.bukkit.entity.Entity 5 | 6 | class HasPassengersEntityFilter : BaseEntityFilter() { 7 | override fun filter(entity: Entity): Boolean { 8 | return entity.passengers.isNotEmpty() 9 | } 10 | 11 | override fun setProperties(section: Section) { 12 | } 13 | 14 | override fun toString(): String { 15 | return "HasPassengersEntityFilter(id=$id)" 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/MetadataEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | import org.bukkit.entity.Entity 5 | 6 | class MetadataEntityFilter : BaseEntityFilter() { 7 | private lateinit var keys : List 8 | 9 | override fun filter(entity: Entity): Boolean { 10 | return keys.any { 11 | return entity.hasMetadata(it) 12 | } 13 | } 14 | 15 | override fun setProperties(section: Section) { 16 | keys = section.getStringList("keys") 17 | } 18 | 19 | override fun toString(): String { 20 | return "MetadataEntityFilter(id=$id, keys=$keys)" 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/MythicEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import io.lumine.mythic.bukkit.MythicBukkit 4 | import io.lumine.xikage.mythicmobs.MythicMobs 5 | import org.bukkit.entity.Entity 6 | 7 | class MythicEntityFilter : RegexesEntityFilter() { 8 | companion object { 9 | val isLegacy = try { 10 | MythicMobs.inst() 11 | true 12 | } catch (ex: ClassNotFoundException) { 13 | try { 14 | MythicBukkit.inst() 15 | false 16 | } catch (ex: ClassNotFoundException) { 17 | throw UnsupportedOperationException("MythicMobs class not found") 18 | } 19 | } 20 | } 21 | 22 | override fun filter(entity: Entity): Boolean { 23 | if (isLegacy) { 24 | val mythicMobInstance: io.lumine.xikage.mythicmobs.mobs.ActiveMob? = 25 | MythicMobs.inst().apiHelper.getMythicMobInstance(entity) 26 | 27 | return if (mythicMobInstance == null) { 28 | false 29 | } else { 30 | matchRegex(mythicMobInstance.mobType) 31 | } 32 | } else { 33 | val mythicMobInstance: io.lumine.mythic.core.mobs.ActiveMob? = 34 | MythicBukkit.inst().apiHelper.getMythicMobInstance(entity) 35 | 36 | return if (mythicMobInstance == null) { 37 | false 38 | } else { 39 | matchRegex(mythicMobInstance.mobType) 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/NameEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import org.bukkit.entity.Entity 4 | 5 | class NameEntityFilter : RegexesEntityFilter() { 6 | override fun filter(entity: Entity): Boolean { 7 | return matchRegex(entity.name) 8 | } 9 | 10 | override fun toString(): String { 11 | return "NameEntityFilter(id=$id, regexes=$regexes)" 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/entity/filter/RegexesEntityFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.entity.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | 5 | abstract class RegexesEntityFilter : BaseEntityFilter() { 6 | lateinit var regexes: List 7 | private set 8 | 9 | fun matchRegex(string: String?): Boolean { 10 | if (string == null) { 11 | return false 12 | } 13 | 14 | return regexes.any { it.matches(string) } 15 | } 16 | 17 | override fun setProperties(section: Section) { 18 | regexes = section.getStringList("regexes").map { it.toRegex() } 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/BaseExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.completer.Completer 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.countdown.CountdownTimer 5 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.ColoredPlaceholderMessageProcessor 6 | import com.github.julyss2019.bukkit.julysafe.core.module.Module 7 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 8 | 9 | abstract class BaseExecutor : Executor { 10 | override var countdownTimer: CountdownTimer? = null 11 | override var completer: Completer? = null 12 | override lateinit var task: Executor.Task 13 | override lateinit var module: Module 14 | 15 | fun notifyCountdown(countdown: Int) { 16 | countdownTimer?.let { 17 | if (it.seconds.contains(countdown)) { 18 | it.notification.notifyCountdown( 19 | ColoredPlaceholderMessageProcessor( 20 | PlaceholderContainer().put( 21 | "countdown", countdown 22 | ) 23 | ), countdown, 24 | countdownTimer!!.maxSecond 25 | ) 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/Executor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.completer.Completer 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.countdown.CountdownTimer 5 | import com.github.julyss2019.bukkit.julysafe.core.module.Module 6 | import com.github.julyss2019.bukkit.voidframework.yaml.DefaultValue 7 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 8 | 9 | 10 | interface Executor { 11 | var countdownTimer: CountdownTimer? 12 | var completer: Completer? 13 | var task: Task 14 | var module: Module 15 | 16 | fun setProperties(section: Section) 17 | 18 | fun start() 19 | 20 | fun stop() 21 | 22 | enum class Type(val classMapping: Class) { 23 | TIMER(TimerExecutor::class.java), 24 | SCHEDULER(SchedulerExecutor::class.java), 25 | FIXED_TIME(FixedTimeExecutor::class.java) 26 | } 27 | 28 | interface Task { 29 | fun run() 30 | } 31 | 32 | object Parser { 33 | fun parse(section: Section): Executor { 34 | val instance = section.getEnum("type", Type::class.java).classMapping.newInstance() 35 | 36 | instance.completer = section.getSection("completer").run { 37 | if (getBoolean("enabled", DefaultValue.of(true))) { 38 | Completer.Parser.parse(this) 39 | } else { 40 | null 41 | } 42 | } 43 | instance.countdownTimer = section.getSection("countdown_timer").run { 44 | if (getBoolean("enabled", DefaultValue.of(true))) { 45 | CountdownTimer.Parser.parse(this) 46 | } else { 47 | null 48 | } 49 | } 50 | instance.setProperties(section.getSection("properties")) 51 | return instance 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/FixedTimeExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | import org.bukkit.scheduler.BukkitRunnable 5 | import org.bukkit.scheduler.BukkitTask 6 | import java.time.Duration 7 | import java.time.LocalTime 8 | import java.time.format.DateTimeFormatter 9 | import java.time.temporal.ChronoUnit 10 | 11 | class FixedTimeExecutor : BaseExecutor() { 12 | companion object { 13 | private val timePattern = DateTimeFormatter.ofPattern("HH:mm:ss") 14 | } 15 | 16 | private lateinit var times: List 17 | private lateinit var bukkitTask: BukkitTask 18 | private var lastExecute: LocalTime? = null 19 | 20 | override fun setProperties(section: Section) { 21 | times = section.getStringList("times") 22 | .map { 23 | try { 24 | LocalTime.parse(it, timePattern) 25 | } catch (ex: Exception) { 26 | throw RuntimeException("invalid time expression: $it") 27 | } 28 | } 29 | } 30 | 31 | override fun start() { 32 | val runnable = object : BukkitRunnable() { 33 | override fun run() { 34 | // 忽略毫秒 35 | val now = LocalTime.now().truncatedTo(ChronoUnit.SECONDS) 36 | 37 | times.forEach { localTime -> 38 | // 因为扫描频率较高,所以要避免重复秒问题 39 | if (lastExecute == now) { 40 | return 41 | } 42 | 43 | lastExecute = now 44 | 45 | var duration = Duration.between(now, localTime) 46 | 47 | // 如果时间为 0 点,则 duration 是负的,需要修正 48 | if (duration.isNegative) { 49 | duration = duration.plusHours(24) 50 | } 51 | 52 | val countdown: Int = duration.seconds.toInt() 53 | 54 | notifyCountdown(countdown) 55 | 56 | // 忽略毫秒 57 | if (now.truncatedTo(ChronoUnit.SECONDS).equals(localTime)) { 58 | // 转为同步线程 59 | object : BukkitRunnable() { 60 | override fun run() { 61 | task.run() 62 | } 63 | }.runTask(com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin.instance) 64 | } 65 | } 66 | } 67 | } 68 | 69 | // 50ms 一次,降低扫描间隔以避免跳秒问题 70 | bukkitTask = runnable.runTaskTimerAsynchronously(module.context.plugin, 0L, 1L) 71 | module.context.addRunningTask(bukkitTask) 72 | } 73 | 74 | override fun stop() { 75 | bukkitTask.cancel() 76 | } 77 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/SchedulerExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | 5 | class SchedulerExecutor : BaseExecutor() { 6 | var priority: Int = -1 7 | 8 | override fun setProperties(section: Section) { 9 | priority = section.getInt("priority") 10 | } 11 | 12 | override fun start() { 13 | module.context.plugin.scheduler.addExecutor(this) 14 | } 15 | 16 | override fun stop() { 17 | module.context.plugin.scheduler.removeExecutor(this) 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/TimerExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.DefaultValue 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | import org.bukkit.scheduler.BukkitRunnable 6 | import org.bukkit.scheduler.BukkitTask 7 | 8 | class TimerExecutor : BaseExecutor() { 9 | private var delay: Int = -1 10 | private var period: Int = -1 11 | private lateinit var bukkitTask: BukkitTask 12 | private var tick = 0 13 | 14 | override fun setProperties(section: Section) { 15 | delay = section.getInt("delay", DefaultValue.of(0)) 16 | period = section.getInt("period") 17 | } 18 | 19 | override fun start() { 20 | bukkitTask = object : BukkitRunnable() { 21 | override fun run() { 22 | val countdown = period - tick 23 | 24 | notifyCountdown(countdown) 25 | 26 | if (countdown == 0) { 27 | task.run() 28 | countdownTimer?.notification?.clear() 29 | tick = 0 30 | } else { 31 | tick++ 32 | } 33 | } 34 | }.runTaskTimer(module.context.plugin, delay * 20L, 20L) 35 | } 36 | 37 | override fun stop() { 38 | bukkitTask.cancel() 39 | } 40 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/completer/Completer.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.completer 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.Notification 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | 7 | class Completer(val notification: Notification) { 8 | object Parser{ 9 | fun parse(section: Section): Completer { 10 | return Completer(Notification.Parser.parse(section.getSection("notification"))) 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/countdown/CountdownTimer.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.countdown 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.Notification 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | class CountdownTimer(val seconds: List, val notification: Notification) { 7 | val maxSecond = seconds.max() 8 | 9 | object SecondsParser { 10 | fun parse(expression: String): List { 11 | val seconds = mutableListOf() 12 | 13 | expression.split(",").forEach { 14 | val rangeArray = it.split("-") 15 | 16 | if (rangeArray.size == 1) { 17 | seconds.add(rangeArray[0].toInt()) 18 | } else { 19 | for (i in rangeArray[0].toInt()..rangeArray[1].toInt()) { 20 | seconds.add(i) 21 | } 22 | } 23 | } 24 | 25 | return seconds 26 | } 27 | } 28 | 29 | object Parser { 30 | fun parse(section: Section): CountdownTimer { 31 | return CountdownTimer( 32 | seconds = SecondsParser.parse(section.getString("seconds")), 33 | notification = Notification.Parser.parse(section.getSection("notification")) 34 | ) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/ActionBarNotification.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification 2 | 3 | import com.comphenix.protocol.PacketType 4 | import com.comphenix.protocol.ProtocolLibrary 5 | import com.comphenix.protocol.events.PacketContainer 6 | import com.comphenix.protocol.wrappers.EnumWrappers 7 | import com.comphenix.protocol.wrappers.WrappedChatComponent 8 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.MessageProcessor 9 | import com.github.julyss2019.bukkit.julysafe.core.util.MinecraftVersion 10 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 11 | import org.bukkit.Bukkit 12 | import org.bukkit.entity.Player 13 | 14 | 15 | class ActionBarNotification : Notification { 16 | private lateinit var message: String 17 | override fun notifyCountdown(messageProcessor: MessageProcessor, currentCountdown: Int, maxCountdown: Int) { 18 | notifyCompleted(messageProcessor) 19 | } 20 | 21 | override fun notifyCompleted(messageProcessor: MessageProcessor) { 22 | Bukkit.getOnlinePlayers().forEach { player: Player? -> 23 | if (MinecraftVersion.getCurrentVersion().compareVersion(12, 2) >= 0) { 24 | val packetContainer = PacketContainer(PacketType.Play.Server.SET_ACTION_BAR_TEXT) 25 | 26 | packetContainer.chatComponents.write(0, WrappedChatComponent.fromText(messageProcessor.process(message))) 27 | ProtocolLibrary.getProtocolManager().sendServerPacket(player, packetContainer) 28 | } else { 29 | // https://wiki.vg/index.php?title=Protocol&oldid=14204#Chat_Message_.28clientbound.29 30 | val packetContainer = PacketContainer(PacketType.Play.Server.CHAT) 31 | 32 | packetContainer.chatComponents.write(0, WrappedChatComponent.fromText(messageProcessor.process(message))) 33 | packetContainer.chatTypes.write(0, EnumWrappers.ChatType.GAME_INFO) 34 | ProtocolLibrary.getProtocolManager().sendServerPacket(player, packetContainer) 35 | } 36 | } 37 | } 38 | 39 | override fun setProperties(section: Section) { 40 | message = section.getString("message") 41 | } 42 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/BossBarNotification.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.MessageProcessor 5 | import com.github.julyss2019.bukkit.voidframework.yaml.DefaultValue 6 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 7 | import org.bukkit.Bukkit 8 | import org.bukkit.boss.BarColor 9 | import org.bukkit.boss.BarStyle 10 | import org.bukkit.boss.BossBar 11 | 12 | class BossBarNotification : Notification { 13 | enum class ProgressType { 14 | INCREASING, DECREASING 15 | } 16 | 17 | private lateinit var title: String 18 | private lateinit var color: BarColor 19 | private lateinit var style: BarStyle 20 | private lateinit var progressType: ProgressType 21 | private var bossBar: BossBar? = null 22 | override fun notifyCountdown(messageProcessor: MessageProcessor, currentCountdown: Int, maxCountdown: Int) { 23 | if (bossBar == null) { 24 | bossBar = Bukkit.createBossBar("-", color, style) 25 | JulySafePlugin.instance.bossBarManager.registerBossBar(bossBar!!) 26 | } 27 | 28 | bossBar!!.run { 29 | title = messageProcessor.process(this@BossBarNotification.title) 30 | val tmp = currentCountdown / maxCountdown.toDouble() 31 | progress = if (progressType == ProgressType.DECREASING) tmp else 1 - tmp 32 | } 33 | } 34 | 35 | override fun notifyCompleted(messageProcessor: MessageProcessor) { 36 | throw UnsupportedOperationException() 37 | } 38 | 39 | override fun setProperties(section: Section) { 40 | title = section.getString("title") 41 | color = section.getEnum("color", BarColor::class.java, DefaultValue.of(BarColor.RED)) 42 | style = section.getEnum("style", BarStyle::class.java, DefaultValue.of(BarStyle.SOLID)) 43 | progressType = 44 | section.getEnum("progress_type", ProgressType::class.java, DefaultValue.of(ProgressType.DECREASING)) 45 | } 46 | 47 | override fun clear() { 48 | super.clear() 49 | 50 | JulySafePlugin.instance.bossBarManager.unregisterBossBar(bossBar!!) 51 | bossBar = null 52 | } 53 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/MessageNotification.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.MessageProcessor 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | import org.bukkit.Bukkit 6 | 7 | class MessageNotification : Notification { 8 | private lateinit var message: String 9 | override fun notifyCountdown(messageProcessor: MessageProcessor, currentCountdown: Int, maxCountdown: Int) { 10 | notifyCompleted(messageProcessor) 11 | } 12 | 13 | override fun notifyCompleted(messageProcessor: MessageProcessor) { 14 | Bukkit.broadcastMessage(messageProcessor.process(message)) 15 | } 16 | 17 | override fun setProperties(section: Section) { 18 | message = section.getString("message") 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/Notification.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.MessageProcessor 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | interface Notification { 7 | fun notifyCountdown(messageProcessor: MessageProcessor, currentCountdown: Int, maxCountdown: Int) 8 | 9 | fun notifyCompleted(messageProcessor: MessageProcessor) 10 | 11 | fun clear() {} 12 | 13 | fun setProperties(section: Section) 14 | 15 | enum class Type(val classMapping: Class) { 16 | TITLE(TitleNotification::class.java), 17 | ACTION_BAR(ActionBarNotification::class.java), 18 | MESSAGE(MessageNotification::class.java), 19 | BOSS_BAR(BossBarNotification::class.java) 20 | } 21 | 22 | object Parser { 23 | fun parse(section: Section): Notification { 24 | section.getEnum("type", Type::class.java).classMapping.newInstance().let { 25 | it.setProperties(section.getSection("properties")) 26 | return it 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/TitleNotification.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification 2 | 3 | import com.comphenix.protocol.PacketType 4 | import com.comphenix.protocol.ProtocolLibrary 5 | import com.comphenix.protocol.events.PacketContainer 6 | import com.comphenix.protocol.wrappers.EnumWrappers 7 | import com.comphenix.protocol.wrappers.WrappedChatComponent 8 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.MessageProcessor 9 | import com.github.julyss2019.bukkit.julysafe.core.util.MinecraftVersion 10 | import com.github.julyss2019.bukkit.voidframework.text.Texts 11 | import com.github.julyss2019.bukkit.voidframework.yaml.DefaultValue 12 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 13 | import org.bukkit.Bukkit 14 | import org.bukkit.entity.Player 15 | 16 | class TitleNotification : Notification { 17 | companion object { 18 | fun sendProtocolTitle( 19 | player: Player, 20 | isBigTitle: Boolean, 21 | content: String, 22 | fadeIn: Int, 23 | stay: Int, 24 | fadeOut: Int 25 | ) { 26 | val packetContainer = PacketContainer(PacketType.Play.Server.TITLE) 27 | 28 | packetContainer.titleActions.write( 29 | 0, 30 | if (isBigTitle) EnumWrappers.TitleAction.TITLE else EnumWrappers.TitleAction.SUBTITLE 31 | ) 32 | packetContainer.chatComponents.write(0, WrappedChatComponent.fromText(Texts.getColoredText(content))) 33 | packetContainer.integers.write(0, fadeIn) 34 | packetContainer.integers.write(1, stay) 35 | packetContainer.integers.write(2, fadeOut) 36 | ProtocolLibrary.getProtocolManager().sendServerPacket(player, packetContainer) 37 | } 38 | } 39 | 40 | 41 | private lateinit var title: String 42 | private lateinit var subtitle: String 43 | private var fadeIn: Int = -1 44 | private var stay: Int = -1 45 | private var fadeOut: Int = -1 46 | override fun notifyCountdown(messageProcessor: MessageProcessor, currentCountdown: Int, maxCountdown: Int) { 47 | notifyCompleted(messageProcessor) 48 | } 49 | 50 | override fun notifyCompleted(messageProcessor: MessageProcessor) { 51 | Bukkit.getOnlinePlayers().forEach { 52 | if (MinecraftVersion.getCurrentVersion().compareVersion(12, 0) >= 0) { 53 | it.sendTitle( 54 | messageProcessor.process(title), 55 | messageProcessor.process(subtitle), 56 | fadeIn, stay, fadeOut 57 | ) 58 | } else { 59 | sendProtocolTitle( 60 | it, 61 | true, 62 | Texts.getColoredText(messageProcessor.process(title)), 63 | fadeIn, 64 | stay, 65 | fadeOut 66 | ) 67 | sendProtocolTitle( 68 | it, 69 | false, 70 | Texts.getColoredText(messageProcessor.process(subtitle)), 71 | fadeIn, 72 | stay, 73 | fadeOut 74 | ) 75 | } 76 | } 77 | } 78 | 79 | override fun setProperties(section: Section) { 80 | title = section.getString("title") 81 | subtitle = section.getString("subtitle") 82 | fadeIn = section.getInt("fade_in", DefaultValue.of(20)) 83 | stay = section.getInt("stay", DefaultValue.of(20)) 84 | fadeOut = section.getInt("fade_out", DefaultValue.of(20)) 85 | } 86 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/message/processor/ColoredPlaceholderMessageProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor 2 | 3 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 4 | import com.github.julyss2019.bukkit.voidframework.text.Texts 5 | 6 | class ColoredPlaceholderMessageProcessor(private val placeholderContainer: PlaceholderContainer) : MessageProcessor { 7 | override fun process(message: String): String { 8 | return message 9 | .let { Texts.getColoredText(it) } 10 | .let { Texts.setPlaceholders(it, placeholderContainer) } 11 | } 12 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/executor/notification/message/processor/MessageProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor 2 | 3 | interface MessageProcessor { 4 | fun process(message: String): String 5 | } 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/internal/command/DebugCommandGroup.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.internal.command 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.julysafe.core.util.MessageUtils 5 | import com.github.julyss2019.bukkit.voidframework.command.CommandGroup 6 | import com.github.julyss2019.bukkit.voidframework.command.annotation.CommandBody 7 | import com.github.julyss2019.bukkit.voidframework.command.annotation.CommandMapping 8 | import org.bukkit.entity.Player 9 | 10 | @CommandMapping(value = "debugger") class DebugCommandGroup(private val plugin: JulySafePlugin) : CommandGroup { 11 | private val julySafePlayerManager = plugin.julySafePlayerManager 12 | 13 | @CommandBody(value = "debug", description = "开关调试模式") fun debug(player: Player) { 14 | val julySafePlayer = julySafePlayerManager.getJulySafePlayer(player) 15 | 16 | julySafePlayer.debugEnabled = !julySafePlayer.debugEnabled 17 | 18 | if (julySafePlayer.debugEnabled) { 19 | MessageUtils.sendMessage(player, "已开启调试模式, 你将收到以下的调试消息: ") 20 | MessageUtils.sendMessage(player, "- 红石阈值(接近红石通知范围可收到阈值递增提示)") 21 | MessageUtils.sendMessage(player, "- 实体过滤器(右键实体可显示被哪个过滤器匹配)") 22 | MessageUtils.sendMessage(player, "- 物品过滤器(丢弃物品可显示被哪个过滤器匹配)") 23 | } else { 24 | MessageUtils.sendMessage(player, "已关闭调试模式.") 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/internal/command/PluginCommandGroup.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.internal.command 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.julysafe.core.Permission 5 | import com.github.julyss2019.bukkit.julysafe.core.util.MessageUtils 6 | import com.github.julyss2019.bukkit.voidframework.command.CommandGroup 7 | import com.github.julyss2019.bukkit.voidframework.command.annotation.CommandBody 8 | import com.github.julyss2019.bukkit.voidframework.command.annotation.CommandMapping 9 | import org.bukkit.command.CommandSender 10 | 11 | @CommandMapping(value = "plugin") 12 | class PluginCommandGroup(private val plugin: JulySafePlugin) : CommandGroup { 13 | @CommandBody(value = "version", description = "显示插件版本") 14 | fun version(sender: CommandSender) { 15 | MessageUtils.sendMessage(sender, "当前版本: ${plugin.description.version}.") 16 | } 17 | 18 | @CommandBody(value = "reload", description = "重载插件(不建议在生产环境使用)") 19 | fun reload(sender: CommandSender) { 20 | plugin.reload() 21 | MessageUtils.sendMessage(sender, "重载完毕.") 22 | } 23 | 24 | @CommandBody(value = "listPermissions", description = "列出所有权限") 25 | fun listPermissions(sender: CommandSender) { 26 | MessageUtils.sendMessage(sender, "以下: ") 27 | com.github.julyss2019.bukkit.julysafe.core.Permission.entries.forEach { 28 | MessageUtils.sendMessage(sender, "- ${it.toBukkitPermission()}") 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/ItemSet.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.InclusiveExclusiveSet 4 | import com.github.julyss2019.bukkit.julysafe.core.item.filter.CoreItemFilter 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | import org.bukkit.World 7 | import org.bukkit.entity.Item 8 | 9 | class ItemSet(override val includes: List, override val excludes: List) : InclusiveExclusiveSet { 10 | fun contains(item: Item): Boolean { 11 | for (filter in excludes) { 12 | if (filter.filter(item)) { 13 | return false 14 | } 15 | } 16 | 17 | for (filter in includes) { 18 | if (filter.filter(item)) { 19 | return true 20 | } 21 | } 22 | 23 | return false 24 | } 25 | 26 | fun getAll(world: World): List { 27 | return world.entities.filterIsInstance().filter { contains(it) } 28 | } 29 | 30 | override fun toString(): String { 31 | return "ItemSet(includes = $includes, excludes = $excludes)" 32 | } 33 | 34 | object Parser { 35 | fun parse(section: Section): ItemSet { 36 | return ItemSet(includes = section.getSection("includes").subSections.map { CoreItemFilter.Parser.parse(it) }, 37 | excludes = section.getSection("excludes").subSections.map { CoreItemFilter.Parser.parse(it) }) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/BaseItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | abstract class BaseItemFilter : CoreItemFilter { 4 | // 反射自动注入 5 | override lateinit var id: String 6 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/CoreItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import com.github.julyss2019.bukkit.julysafe.api.ItemFilter 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | import org.bukkit.entity.Item 6 | 7 | interface CoreItemFilter : ItemFilter { 8 | var id: String 9 | 10 | enum class Type(val mappingClass: Class) { 11 | DISPLAY_NAME(DisplayNameItemFilter::class.java), 12 | ENUM(EnumItemFilter::class.java), 13 | ENCHANTMENT(EnchantmentItemFilter::class.java), 14 | LORE(LoreItemFilter::class.java), 15 | GROOVY(GroovyItemFilter::class.java) 16 | } 17 | 18 | fun setProperties(section: Section) 19 | 20 | object Parser { 21 | fun parse(section: Section): CoreItemFilter { 22 | return section.getEnum("type", Type::class.java).mappingClass.newInstance() 23 | .apply { 24 | id = section.name 25 | setProperties(section.getSection("properties")) 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/DisplayNameItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.common.Items 4 | import org.bukkit.entity.Item 5 | 6 | class DisplayNameItemFilter : RegexesItemFilter() { 7 | override fun filter(item: Item): Boolean { 8 | val displayName: String = Items.getDisplayName(item.itemStack) ?: return false 9 | 10 | if (displayName == "") { 11 | return false 12 | } 13 | 14 | return matchRegex(displayName) 15 | } 16 | 17 | override fun toString(): String { 18 | return "DisplayNameItemFilter(id = $id, regexes = $regexes)" 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/EnchantmentItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import org.bukkit.entity.Item 4 | 5 | class EnchantmentItemFilter : RegexesItemFilter() { 6 | override fun filter(item: Item): Boolean { 7 | return item.itemStack.enchantments.keys.any { matchRegex(it.name) } 8 | } 9 | 10 | override fun toString(): String { 11 | return "EnchantmentItemFilter(id = $id, regexes = $regexes)" 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/EnumItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import org.bukkit.entity.Item 4 | 5 | class EnumItemFilter : RegexesItemFilter() { 6 | override fun filter(item: Item): Boolean { 7 | return matchRegex(item.itemStack.type.name) 8 | } 9 | 10 | override fun toString(): String { 11 | return "EnumItemFilter(id = $id, regexes = $regexes)" 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/GroovyItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import com.github.julyss2019.bukkit.julysafe.api.ItemFilter 4 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | import com.void01.bukkit.voidframework.api.common.VoidFramework2 7 | import com.void01.bukkit.voidframework.api.common.groovy.GroovyConfig 8 | import org.bukkit.entity.Item 9 | import java.io.File 10 | 11 | 12 | class GroovyItemFilter : BaseItemFilter() { 13 | private lateinit var itemFilter: ItemFilter 14 | override fun filter(item: Item): Boolean { 15 | return itemFilter.filter(item) 16 | } 17 | 18 | override fun setProperties(section: Section) { 19 | val scriptFile = File(JulySafePlugin.instance.dataFolder, section.getString("file")) 20 | 21 | itemFilter = VoidFramework2.getGroovyManager().parseClass(scriptFile, GroovyConfig().apply { 22 | sourceEncoding = "UTF-8" 23 | }).newInstance() as ItemFilter 24 | } 25 | 26 | override fun toString(): String { 27 | return "GroovyEntityFilter(itemFilter=$itemFilter)" 28 | } 29 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/LoreItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.common.Items 4 | import org.bukkit.entity.Item 5 | import org.bukkit.inventory.ItemStack 6 | 7 | class LoreItemFilter : RegexesItemFilter() { 8 | override fun filter(item: Item): Boolean { 9 | return Items.getLores(item.itemStack).any { matchRegex(it) } 10 | } 11 | 12 | override fun toString(): String { 13 | return "LoreItemFilter(id = $id, regexes = $regexes)" 14 | } 15 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/item/filter/RegexesItemFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.item.filter 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | 5 | abstract class RegexesItemFilter : BaseItemFilter() { 6 | lateinit var regexes: List 7 | private set 8 | 9 | fun matchRegex(string: String?): Boolean { 10 | if (string == null) { 11 | return false 12 | } 13 | 14 | return regexes.any { it.matches(string) } 15 | } 16 | 17 | override fun setProperties(section: Section) { 18 | regexes = section.getStringList("regexes").map { it.toRegex() } 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/BlockExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.block.Block 4 | 5 | internal fun Block.getAsSimpleString(): String { 6 | return type.name 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/ChunkExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.Chunk 4 | 5 | internal fun Chunk.getAsSimpleString(): String { 6 | return "Chunk(${this.x}, ${this.z})" 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/EntityExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.entity.Entity 4 | 5 | internal fun Entity.getAsSimpleString(): String { 6 | return "${type.name}(${uniqueId})" 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/ItemExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.entity.Item 4 | 5 | internal fun Item.getAsSimpleString(): String { 6 | return "Item(location = ${location.getAsSimpleString()}, item_stack = $itemStack)" 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/LocationExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.Location 4 | 5 | internal fun Location.getAsSimpleString(): String { 6 | return "${this.world!!.name}(${this.x}, ${this.y}, ${this.z}, ${this.yaw}, ${this.pitch})" 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/kotlin/extension/PlayerExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.kotlin.extension 2 | 3 | import org.bukkit.entity.Player 4 | 5 | internal fun Player.getNameAndUuid(): String { 6 | return "${this.name}(${this.uniqueId})" 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/BlockExplodeLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.module.BlockExplodeLimitModule 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.Listener 7 | import org.bukkit.event.block.BlockExplodeEvent 8 | 9 | class BlockExplodeLimitListener(private val module: BlockExplodeLimitModule) : Listener { 10 | @EventHandler 11 | fun onBlockExplodeEvent(event: BlockExplodeEvent) { 12 | val block = event.block 13 | 14 | if (module.worldSet.contains(block.world)) { 15 | event.isCancelled = true 16 | event.yield = 0F 17 | module.debug("cancelled, location = ${block.location.getAsSimpleString()}.") 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/BossBarListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.bukkit.event.player.PlayerJoinEvent 7 | import org.bukkit.event.player.PlayerQuitEvent 8 | 9 | class BossBarListener(plugin: JulySafePlugin) : Listener { 10 | private val bossBarManager = plugin.bossBarManager 11 | 12 | @EventHandler 13 | fun onPlayerJoinEvent(event: PlayerJoinEvent) { 14 | bossBarManager.getBars().forEach { 15 | it.addPlayer(event.player) 16 | } 17 | } 18 | 19 | @EventHandler 20 | fun onPlayerQuitEvent(event: PlayerQuitEvent) { 21 | bossBarManager.getBars().forEach { 22 | it.removePlayer(event.player) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/ChatBlacklistListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.Permission 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.ChatBlacklistModule 6 | import com.github.julyss2019.bukkit.voidframework.common.Messages 7 | import org.bukkit.event.EventHandler 8 | import org.bukkit.event.EventPriority 9 | import org.bukkit.event.Listener 10 | import org.bukkit.event.player.AsyncPlayerChatEvent 11 | 12 | class ChatBlacklistListener(private val module: ChatBlacklistModule) : Listener { 13 | private val localeResource = module.getLocalResource() 14 | 15 | @EventHandler(priority = EventPriority.LOWEST) 16 | fun onAsyncPlayerChatEvent(event: AsyncPlayerChatEvent) { 17 | val player = event.player 18 | 19 | if (module.context.plugin.checkPermission(player, Permission.BYPASS_CHAT_BLACKLIST)) { 20 | return 21 | } 22 | 23 | val message = event.message 24 | 25 | module.blacklistRegexes.forEach { regex -> 26 | val matchResult = regex.find(message) 27 | 28 | if (matchResult != null) { 29 | if (module.cancelEvent) { 30 | event.isCancelled = true 31 | 32 | module.debug("cancelled, player = ${player.getNameAndUuid()}, message = ${event.message}.") 33 | Messages.sendColoredMessage(player, localeResource.getString("cancelled")) 34 | } else { 35 | var processedMessage = message 36 | 37 | module.blacklistRegexes.forEach { regex1 -> 38 | processedMessage = processedMessage.replace(regex1, module.replaceString) 39 | } 40 | module.debug("replaced, player = ${player.getNameAndUuid()}, old_message = ${event.message}, new_message = $processedMessage.") 41 | Messages.sendColoredMessage(player, localeResource.getString("replaced")) 42 | 43 | event.message = processedMessage 44 | } 45 | 46 | return 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/ChatSpamLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.Permission 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.ChatSpamLimitModule 6 | import com.github.julyss2019.bukkit.julysafe.core.util.CooldownTimer 7 | import com.github.julyss2019.bukkit.voidframework.common.Messages 8 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 9 | import com.github.julyss2019.bukkit.voidframework.text.Texts 10 | import org.bukkit.event.EventHandler 11 | import org.bukkit.event.Listener 12 | import org.bukkit.event.player.AsyncPlayerChatEvent 13 | import java.util.* 14 | import java.util.concurrent.ConcurrentHashMap 15 | 16 | class ChatSpamLimitListener(private val module: ChatSpamLimitModule) : Listener { 17 | private val localeResource = module.getLocalResource() 18 | private val lastChatMap = ConcurrentHashMap() 19 | 20 | @EventHandler(ignoreCancelled = true) 21 | fun onAsyncPlayerChatEvent(event: AsyncPlayerChatEvent) { 22 | val player = event.player 23 | val playerUuid = player.uniqueId 24 | 25 | if (module.context.plugin.checkPermission(player, Permission.BYPASS_CHAT_SPAM_LIMIT)) { 26 | return 27 | } 28 | 29 | if (lastChatMap.containsKey(playerUuid)) { 30 | val cooldownTimer = lastChatMap[playerUuid]!! 31 | 32 | if (cooldownTimer.isInCooldown()) { 33 | Messages.sendColoredMessage( 34 | player, 35 | Texts.setPlaceholders( 36 | localeResource.getString("denied"), 37 | PlaceholderContainer().put( 38 | "cooldown", 39 | cooldownTimer.getFormattedRemainingCooldown() 40 | ) 41 | ) 42 | ) 43 | 44 | module.debug("cancelled, player = ${player.getNameAndUuid()}.") 45 | event.isCancelled = true 46 | return 47 | } 48 | } 49 | 50 | lastChatMap[playerUuid] = CooldownTimer.createNewAndStart(module.threshold) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/CommandLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.Permission 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.CommandLimitModule 6 | import com.github.julyss2019.bukkit.voidframework.common.Messages 7 | import org.bukkit.event.EventHandler 8 | import org.bukkit.event.Listener 9 | import org.bukkit.event.player.PlayerCommandPreprocessEvent 10 | 11 | class CommandLimitListener(private val module: CommandLimitModule) : Listener { 12 | private val localeResource = module.getLocalResource() 13 | 14 | @EventHandler(ignoreCancelled = true) 15 | fun onPlayerCommandPreprocessEvent(event: PlayerCommandPreprocessEvent) { 16 | val commandLine = event.message 17 | val player = event.player 18 | 19 | if (module.context.plugin.checkPermission(player, Permission.BYPASS_COMMAND_BLACKLIST)) { 20 | return 21 | } 22 | 23 | if (module.commandSet.contains(commandLine)) { 24 | event.isCancelled = true 25 | Messages.sendColoredMessage(player, localeResource.getString("denied")) 26 | module.debug("cancelled, player = ${player.getNameAndUuid()}, command_line = $commandLine") 27 | return 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/CommandSpamLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.Permission 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.CommandSpamLimitModule 6 | import com.github.julyss2019.bukkit.julysafe.core.util.CooldownTimer 7 | import com.github.julyss2019.bukkit.voidframework.common.Messages 8 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 9 | import com.github.julyss2019.bukkit.voidframework.text.Texts 10 | import org.bukkit.event.EventHandler 11 | import org.bukkit.event.Listener 12 | import org.bukkit.event.player.PlayerCommandPreprocessEvent 13 | import java.util.* 14 | import java.util.concurrent.ConcurrentHashMap 15 | 16 | class CommandSpamLimitListener(private val module: CommandSpamLimitModule) : Listener { 17 | private val lastExecuteCommandMap = ConcurrentHashMap() 18 | private val localeResource = module.getLocalResource() 19 | 20 | @EventHandler(ignoreCancelled = true) 21 | fun onPlayerCommandPreprocessEvent(event: PlayerCommandPreprocessEvent) { 22 | val player = event.player 23 | val playerUuid = player.uniqueId 24 | 25 | if (module.context.plugin.checkPermission(player, Permission.BYPASS_COMMAND_SPAM_LIMIT)) { 26 | return 27 | } 28 | 29 | if (lastExecuteCommandMap.containsKey(playerUuid)) { 30 | val cooldownTimer = lastExecuteCommandMap[playerUuid]!! 31 | 32 | if (cooldownTimer.isInCooldown()) { 33 | Messages.sendColoredMessage( 34 | player, Texts.setPlaceholders(localeResource.getString("denied"), PlaceholderContainer().put("cooldown", cooldownTimer.getFormattedRemainingCooldown())) 35 | ) 36 | 37 | module.debug("cancelled, player = ${player.getNameAndUuid()}.") 38 | event.isCancelled = true 39 | return 40 | } 41 | } 42 | 43 | lastExecuteCommandMap[playerUuid] = CooldownTimer.createNewAndStart(module.threshold) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/CropTrampleLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 4 | import com.github.julyss2019.bukkit.julysafe.core.module.CropTrampleLimitModule 5 | import org.bukkit.Material 6 | import org.bukkit.event.EventHandler 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.block.Action 9 | import org.bukkit.event.player.PlayerInteractEvent 10 | 11 | class CropTrampleLimitListener(private val module: CropTrampleLimitModule) : Listener { 12 | companion object { 13 | private var material: Material = try { 14 | Material.valueOf("FARMLAND") // 高于 1.12.2 15 | } catch (ex: Exception) { 16 | try { 17 | Material.valueOf("SOIL") // 低于或等于 1.12 18 | } catch (ex: Exception) { 19 | throw UnsupportedOperationException("Unable to find farmland material") 20 | } 21 | } 22 | } 23 | 24 | @EventHandler 25 | fun onPlayerInteractEvent(event: PlayerInteractEvent) { 26 | val block = event.clickedBlock ?: return 27 | 28 | if (!module.worldSet.contains(block.world)) { 29 | return 30 | } 31 | 32 | val player = event.player 33 | 34 | if (event.action == Action.PHYSICAL && event.hand == null && block.type == material) { 35 | event.isCancelled = true 36 | module.debug("cancelled, player = ${player.getNameAndUuid()}, location = ${block.location}.") 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/EntityAndItemDebugListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 5 | import com.github.julyss2019.bukkit.julysafe.core.item.ItemSet 6 | import com.github.julyss2019.bukkit.julysafe.core.module.ChunkEntityLimitModule 7 | import com.github.julyss2019.bukkit.julysafe.core.module.DropCleanModule 8 | import com.github.julyss2019.bukkit.julysafe.core.module.EntitySpawnLimitModule 9 | import com.github.julyss2019.bukkit.julysafe.core.module.support.EntitySetSupport 10 | import com.github.julyss2019.bukkit.julysafe.core.util.MinecraftVersion 11 | import com.github.julyss2019.bukkit.voidframework.common.Items 12 | import org.bukkit.entity.Entity 13 | import org.bukkit.entity.Item 14 | import org.bukkit.event.EventHandler 15 | import org.bukkit.event.Listener 16 | import org.bukkit.event.player.PlayerDropItemEvent 17 | import org.bukkit.event.player.PlayerInteractEntityEvent 18 | import org.bukkit.inventory.EquipmentSlot 19 | 20 | class EntityAndItemDebugListener(private val plugin: JulySafePlugin) : Listener { 21 | private val julySafePlayerManager = plugin.julySafePlayerManager 22 | 23 | @Suppress("DuplicatedCode") 24 | @EventHandler fun onPlayerPickupItemEvent(event: PlayerDropItemEvent) { 25 | val item = event.itemDrop 26 | val itemStack = item.itemStack 27 | val julySafePlayer = julySafePlayerManager.getJulySafePlayer(event.player) 28 | 29 | if (julySafePlayer.debugEnabled) { 30 | julySafePlayer.debug("item: $item(type = ${itemStack.type}, display_name = ${Items.getDisplayName(itemStack)}, lores = ${ 31 | Items.getLores(itemStack) 32 | }).") 33 | 34 | plugin.moduleManager.getModuleByClass(DropCleanModule::class.java)!!.let { 35 | if (it.enabled) { 36 | julySafePlayer.debug(getItemSetMatchMessage(item, it.itemSet)) 37 | } 38 | } 39 | 40 | event.isCancelled = true 41 | } 42 | } 43 | 44 | @Suppress("DuplicatedCode") 45 | @EventHandler fun onPlayerInteractEntityEvent(event: PlayerInteractEntityEvent) { 46 | if (MinecraftVersion.getCurrentVersion().compareVersion(12, 2) >= 0) { 47 | if (event.hand != EquipmentSlot.HAND) { 48 | return 49 | } 50 | } 51 | 52 | val rightClicked = event.rightClicked 53 | val julySafePlayer = julySafePlayerManager.getJulySafePlayer(event.player) 54 | 55 | if (julySafePlayer.debugEnabled) { 56 | julySafePlayer.debug("entity: $rightClicked(type = ${rightClicked.type}, custom_name = ${rightClicked.customName}, name = ${rightClicked.name}).") 57 | 58 | plugin.moduleManager.getEnabledModules() 59 | .forEach { module -> 60 | if (module is EntitySetSupport) { 61 | julySafePlayer.debug("${module.javaClass.simpleName}: ${getEntitySetMatchMessage(rightClicked, module.entitySet)}.") 62 | } else if (module is ChunkEntityLimitModule) { 63 | module.limits.forEach { 64 | julySafePlayer.debug("${module.javaClass.simpleName}(${it.id}): ${getEntitySetMatchMessage(rightClicked, it.entitySet)}.") 65 | } 66 | } else if (module is EntitySpawnLimitModule) { 67 | module.limits.forEach { 68 | julySafePlayer.debug("${module.javaClass.simpleName}(${it.id}): ${getEntitySetMatchMessage(rightClicked, it.entitySet)}.") 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | private fun getItemSetMatchMessage(item: Item, itemSet: ItemSet): String { 76 | itemSet.run { 77 | for (exclude in excludes) { 78 | if (exclude.filter(item)) { 79 | return "excluded by ${exclude.javaClass.simpleName}(${exclude.id})" 80 | } 81 | } 82 | 83 | for (include in includes) { 84 | if (include.filter(item)) { 85 | return "included by ${include.javaClass.simpleName}(${include.id})" 86 | } 87 | } 88 | 89 | return "non-match" 90 | } 91 | } 92 | 93 | private fun getEntitySetMatchMessage(entity: Entity, entitySet: EntitySet): String { 94 | entitySet.run { 95 | for (exclude in excludes) { 96 | if (exclude.filter(entity)) { 97 | return "excluded by ${exclude.javaClass.simpleName}(${exclude.id})" 98 | } 99 | } 100 | 101 | for (include in includes) { 102 | if (include.filter(entity)) { 103 | return "included by ${include.javaClass.simpleName}(${include.id})" 104 | } 105 | } 106 | 107 | return "non-match" 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/EntityExplodeLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.module.EntityExplodeLimitModule 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.Listener 7 | import org.bukkit.event.entity.EntityExplodeEvent 8 | 9 | class EntityExplodeLimitListener(private val module: EntityExplodeLimitModule) : Listener { 10 | @EventHandler 11 | fun onEntityExplodeEvent(event: EntityExplodeEvent) { 12 | val entity = event.entity 13 | 14 | if (module.worldSet.contains(entity.world)) { 15 | event.isCancelled = true 16 | event.yield = 0F 17 | module.debug("cancelled, entity = ${entity.getAsSimpleString()}.") 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/EntitySpawnLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.module.EntitySpawnLimitModule 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.Listener 7 | import org.bukkit.event.entity.CreatureSpawnEvent 8 | 9 | class EntitySpawnLimitListener(private val module: EntitySpawnLimitModule) : Listener { 10 | private val lastSpawnMap = mutableMapOf() 11 | 12 | @EventHandler 13 | fun onEntitySpawnEvent(event: CreatureSpawnEvent) { 14 | val entity = event.entity 15 | 16 | if (!module.worldSet.contains(entity.world)) { 17 | return 18 | } 19 | 20 | val spawnReason = event.spawnReason 21 | 22 | for (limit in module.limits) { 23 | if (!limit.spawnReasonRegexes.any { it.matches(spawnReason.name) }) { 24 | continue 25 | } 26 | 27 | if (!limit.entitySet.contains(entity)) { 28 | continue 29 | } 30 | 31 | if (limit.cancelled) { 32 | event.isCancelled = true 33 | module.debug("direct cancelled, spawn_reason = $spawnReason, entity = ${entity.getAsSimpleString()}, location = ${entity.location}.") 34 | return 35 | } 36 | 37 | if (limit.interval != -1) { 38 | if (!lastSpawnMap.contains(limit)) { 39 | lastSpawnMap[limit] = System.currentTimeMillis() 40 | } 41 | 42 | val interval = System.currentTimeMillis() - lastSpawnMap[limit]!! 43 | 44 | if (interval < limit.interval) { 45 | event.isCancelled = true 46 | module.debug("interval cancelled, spawn_reason = $spawnReason, entity = ${entity.getAsSimpleString()}, location = ${entity.location}.") 47 | } else { 48 | lastSpawnMap[limit] = System.currentTimeMillis() 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/FireSpreadLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.module.FireSpreadLimitModule 5 | import org.bukkit.Material 6 | import org.bukkit.event.EventHandler 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.block.BlockBurnEvent 9 | import org.bukkit.event.block.BlockSpreadEvent 10 | 11 | class FireSpreadLimitListener(private val module: FireSpreadLimitModule) : Listener { 12 | @EventHandler 13 | fun onBlockSpreadEvent(event: BlockSpreadEvent) { 14 | val source = event.source 15 | 16 | if (!module.worldSet.contains(source.world)) { 17 | return 18 | } 19 | 20 | if (source.type == Material.FIRE) { 21 | event.isCancelled = true 22 | module.debug("BlockSpreadEvent cancelled, block = ${event.block.getAsSimpleString()}, location = ${event.block.location.getAsSimpleString()}") 23 | } 24 | } 25 | 26 | @EventHandler 27 | fun onBlockBurnEvent(event: BlockBurnEvent) { 28 | val block = event.block 29 | 30 | if (!module.worldSet.contains(block.world)) { 31 | return 32 | } 33 | 34 | event.isCancelled = true 35 | module.debug("BlockBurnEvent cancelled, block = ${block.getAsSimpleString()}, location = ${block.location.getAsSimpleString()}") 36 | } 37 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/IllegalPlayerLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 4 | import com.github.julyss2019.bukkit.julysafe.core.module.IllegalPlayerLimitModule 5 | import org.bukkit.GameMode 6 | import org.bukkit.event.EventHandler 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.player.PlayerQuitEvent 9 | 10 | class IllegalPlayerLimitListener(private val module: IllegalPlayerLimitModule) : Listener { 11 | 12 | @EventHandler 13 | fun onPlayerQuitEvent(event: PlayerQuitEvent) { 14 | val player = event.player 15 | 16 | if (module.deopOnQuit && player.isOp) { 17 | player.isOp = false 18 | module.info("deop op on quit, player = ${player.getNameAndUuid()}.") 19 | } 20 | 21 | if (module.setSurvivalModeOnQuit && player.gameMode == GameMode.CREATIVE) { 22 | player.gameMode = GameMode.SURVIVAL 23 | module.info("set survival mode on quit, player = ${player.getNameAndUuid()}.") 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/PlayerDropRecordListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.PlayerDropRecordModule 6 | import org.bukkit.event.EventHandler 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.player.PlayerDropItemEvent 9 | 10 | class PlayerDropRecordListener(private val module: PlayerDropRecordModule) : Listener { 11 | @EventHandler 12 | fun onPlayerDropItemEvent(event: PlayerDropItemEvent) { 13 | val itemDrop = event.itemDrop 14 | 15 | if (module.worldSet.contains(itemDrop.world)) { 16 | module.debug("record, player = ${event.player.getNameAndUuid()}, item = ${itemDrop.getAsSimpleString()}.") 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/PlayerListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.bukkit.event.player.PlayerQuitEvent 7 | 8 | class PlayerListener(plugin: JulySafePlugin) : Listener { 9 | private val julySafePlayerManager = plugin.julySafePlayerManager 10 | 11 | @EventHandler 12 | fun onPlayerQuit(event: PlayerQuitEvent) { 13 | julySafePlayerManager.unloadJulySafePlayer(event.player) 14 | } 15 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/PlayerPickupRecordListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 5 | import com.github.julyss2019.bukkit.julysafe.core.module.PlayerPickupRecordModule 6 | import org.bukkit.event.EventHandler 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.player.PlayerPickupItemEvent 9 | 10 | class PlayerPickupRecordListener(private val module: PlayerPickupRecordModule) : Listener { 11 | @EventHandler 12 | fun onPlayerDropItemEvent(event: PlayerPickupItemEvent) { 13 | val itemDrop = event.item 14 | 15 | if (module.worldSet.contains(itemDrop.world)) { 16 | module.debug("record, player = ${event.player.getNameAndUuid()}, item = ${itemDrop.getAsSimpleString()}.") 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/listener/RedstoneLimitListener.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.listener 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.module.RedstoneLimitModule 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.bukkit.event.block.BlockRedstoneEvent 7 | 8 | 9 | // 预处理,转发 Event 10 | class RedstoneLimitListener(private val module: RedstoneLimitModule) : Listener { 11 | private val redstoneDetector = module.detector 12 | 13 | @EventHandler fun onBlockRedstoneEvent(event: BlockRedstoneEvent) { 14 | val block = event.block 15 | 16 | if (!redstoneDetector.worldSet.contains(block.world)) { 17 | return 18 | } 19 | 20 | if (redstoneDetector.blockWhitelist.contains(block.type)) { 21 | return 22 | } 23 | 24 | // 过滤衰减 25 | if (event.newCurrent < event.oldCurrent) { 26 | return 27 | } 28 | 29 | module.detector.detect(event) 30 | } 31 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/locale/LocaleResource.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.locale 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Yaml 4 | import org.bukkit.plugin.Plugin 5 | import java.io.File 6 | import java.util.* 7 | 8 | open class LocaleResource private constructor(val locale: Locale, val resourcesFolder: File) { 9 | interface TextProcessor { 10 | fun process(text: String): String 11 | } 12 | 13 | companion object { 14 | private val DEFAULT_TEXT_PROCESSOR = object : TextProcessor { 15 | override fun process(text: String): String { 16 | return text 17 | } 18 | } 19 | 20 | /** 21 | * 获取默认的本土化资源文件夹 22 | * @param plugin 插件 23 | * @return 24 | */ 25 | fun getDefaultLocaleFolder(plugin: Plugin): File { 26 | return File(plugin.dataFolder, "locales") 27 | } 28 | 29 | /** 30 | * 载入一个本土化资源 31 | * 以 lang_country.yml 载入文件,例:zh_CN.yml 32 | * @param locale 本土化 33 | * @param resourceFolder 资源文件夹 34 | */ 35 | fun fromFolder( 36 | locale: Locale, 37 | resourceFolder: File 38 | ): LocaleResource { 39 | return LocaleResource(locale, resourceFolder) 40 | } 41 | 42 | /** 43 | * 载入一个本土化资源 44 | * 以插件文件夹下的 locale 文件夹作为资源文件夹 45 | * @param locale 本土化 46 | * @param plugin 插件 47 | */ 48 | fun fromPluginLocaleFolder( 49 | locale: Locale, 50 | plugin: Plugin 51 | ): LocaleResource { 52 | return LocaleResource(locale, File(plugin.dataFolder, "locales")) 53 | } 54 | } 55 | 56 | private val file: File = 57 | File(resourcesFolder, locale.language + "_" + locale.country.uppercase(Locale.getDefault()) + ".yml") 58 | private lateinit var yaml: Yaml 59 | var textProcessor = DEFAULT_TEXT_PROCESSOR 60 | 61 | init { 62 | load() 63 | } 64 | 65 | private fun load() { 66 | if (!file.exists()) { 67 | throw RuntimeException("locale resource file not found: " + file.absolutePath) 68 | } 69 | 70 | yaml = Yaml.fromFile(file) 71 | } 72 | 73 | open fun reload() { 74 | load() 75 | } 76 | 77 | open fun getStringList(key: String): List { 78 | return yaml.getStringList(key) 79 | .map { textProcessor.process(it) } 80 | } 81 | 82 | open fun getOriginalString(key: String) : String { 83 | return yaml.getString(key) 84 | } 85 | 86 | open fun getString(key: String): String { 87 | return yaml.getString(key) 88 | .let { textProcessor.process(it) } 89 | } 90 | 91 | open fun getLocalResource(key: String): LocaleResource { 92 | if (!yaml.contains(key)) { 93 | throw RuntimeException("locale resource not found: $key") 94 | } 95 | 96 | return object : LocaleResource(locale, resourcesFolder) { 97 | val parent = this@LocaleResource 98 | 99 | private fun convertKey(key1: String): String { 100 | return "$key.$key1" 101 | } 102 | 103 | override fun getLocalResource(key: String): LocaleResource { 104 | return parent.getLocalResource(convertKey(key)) 105 | } 106 | 107 | override fun getStringList(key: String): List { 108 | return parent.getStringList(convertKey(key)) 109 | } 110 | 111 | override fun getString(key: String): String { 112 | return parent.getString(convertKey(key)) 113 | } 114 | 115 | override fun reload() { 116 | parent.reload() 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/AutoRestartModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 5 | import com.github.julyss2019.bukkit.voidframework.text.Texts 6 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 7 | import org.bukkit.Bukkit 8 | 9 | @Module.YamlSectionId("auto_restart") 10 | class AutoRestartModule : BaseModule(), ExecutorSupport { 11 | override lateinit var executor: Executor 12 | 13 | lateinit var beforeCommands: List 14 | private set 15 | var kickAllBeforeRestart: Boolean = false 16 | private set 17 | 18 | override fun setProperties(section: Section) { 19 | beforeCommands = section.getStringList("before_restart_commands") 20 | kickAllBeforeRestart = section.getBoolean("kick_all_before_restart") 21 | } 22 | 23 | override fun onEnable() { 24 | executor.task = object : Executor.Task { 25 | override fun run() { 26 | if (kickAllBeforeRestart) { 27 | Bukkit.getOnlinePlayers().forEach { 28 | it.kickPlayer(Texts.getColoredText(getLocalResource().getString("kick_all"))) 29 | } 30 | } 31 | 32 | beforeCommands.forEach { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), it) } 33 | Bukkit.shutdown() 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/BaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.locale.LocaleResource 4 | 5 | abstract class BaseModule : Module { 6 | override lateinit var yamlId: String 7 | override lateinit var context: Module.Context 8 | override var enabled: Boolean = false 9 | override val name: String = javaClass.simpleName 10 | 11 | fun getLocalResource(): LocaleResource { 12 | return context.plugin.localeResource.getLocalResource(yamlId) 13 | } 14 | 15 | fun debug(message: String) { 16 | context.plugin.pluginLogger.debug("[$name] $message") 17 | } 18 | 19 | fun info(message: String) { 20 | context.plugin.pluginLogger.info("[$name] $message") 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/BlockExplodeLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.BlockExplodeLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("block_explode_limit") 8 | class BlockExplodeLimitModule : BaseModule(), WorldSetSupport { 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(BlockExplodeLimitListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/ChatBlacklistModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.ChatBlacklistListener 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | @Module.YamlSectionId("chat_blacklist") 7 | class ChatBlacklistModule : BaseModule() { 8 | lateinit var blacklistRegexes: List 9 | private set 10 | lateinit var replaceString: String 11 | private set 12 | var cancelEvent: Boolean = false 13 | private set 14 | 15 | override fun setProperties(section: Section) { 16 | blacklistRegexes = section.getStringList("blacklist").map { it.toRegex() } 17 | replaceString = section.getString("replace_string") 18 | cancelEvent = section.getBoolean("cancel_event") 19 | } 20 | 21 | override fun onEnable() { 22 | context.registerListener(ChatBlacklistListener(this)) 23 | } 24 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/ChatSpamLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.ChatSpamLimitListener 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | @Module.YamlSectionId("chat_spam_limit") 7 | class ChatSpamLimitModule : BaseModule() { 8 | var threshold: Int = -1 9 | private set 10 | 11 | override fun setProperties(section: Section) { 12 | threshold = section.getInt("threshold") 13 | } 14 | 15 | override fun onEnable() { 16 | context.registerListener(ChatSpamLimitListener(this)) 17 | } 18 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/ChunkEntityLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 5 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.ColoredPlaceholderMessageProcessor 6 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 7 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 8 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 9 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 10 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 11 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 12 | 13 | @Module.YamlSectionId("chunk_entity_limit") 14 | class ChunkEntityLimitModule : BaseModule(), WorldSetSupport, ExecutorSupport { 15 | class Limit(val id: String, val threshold: Int, val entitySet: EntitySet) 16 | 17 | override lateinit var executor: Executor 18 | override lateinit var worldSet: WorldSet 19 | lateinit var limits: List 20 | 21 | override fun setProperties(section: Section) { 22 | limits = section.getSection("limits") 23 | .subSections 24 | .map { 25 | Limit(it.name, it.getInt("threshold"), EntitySet.Parser.parse(it.getSection("entity_set"))) 26 | } 27 | } 28 | 29 | override fun onEnable() { 30 | executor.task = object : Executor.Task { 31 | override fun run() { 32 | var removed = 0 33 | 34 | worldSet.getAll() 35 | .map { it.loadedChunks } 36 | .flatMap { it.asIterable() } 37 | .forEach { chunk -> 38 | for (limit in limits) { 39 | var counter = 0 40 | 41 | for (entity in chunk.entities) { 42 | if (limit.entitySet.contains(entity)) { 43 | counter++ 44 | 45 | if (counter > limit.threshold) { 46 | entity.remove() 47 | debug("removed, entity = ${entity.getAsSimpleString()}, chunk = ${chunk.getAsSimpleString()}, location = ${entity.location.getAsSimpleString()}") 48 | removed++ 49 | } 50 | } 51 | } 52 | } 53 | } 54 | executor.completer?.notification?.notifyCompleted( 55 | ColoredPlaceholderMessageProcessor( 56 | PlaceholderContainer().put("total", removed) 57 | ) 58 | ) 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/CommandLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.command.CommandSet 4 | import com.github.julyss2019.bukkit.julysafe.core.listener.CommandLimitListener 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | 7 | @Module.YamlSectionId("command_limit") 8 | class CommandLimitModule : BaseModule() { 9 | lateinit var commandSet: CommandSet 10 | private set 11 | 12 | override fun setProperties(section: Section) { 13 | this.commandSet = CommandSet.Parser.parse(section.getSection("command_set")) 14 | } 15 | 16 | override fun onEnable() { 17 | context.registerListener(CommandLimitListener(this)) 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/CommandSpamLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.CommandSpamLimitListener 4 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 5 | 6 | @Module.YamlSectionId("command_spam_limit") 7 | class CommandSpamLimitModule : BaseModule() { 8 | var threshold: Int = -1 9 | private set 10 | 11 | override fun setProperties(section: Section) { 12 | threshold = section.getInt("threshold") 13 | } 14 | 15 | override fun onEnable() { 16 | context.registerListener(CommandSpamLimitListener(this)) 17 | } 18 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/CropTrampleLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.CropTrampleLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("crop_trample_limit") 8 | class CropTrampleLimitModule : BaseModule() , WorldSetSupport{ 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(CropTrampleLimitListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/DropCleanModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | 4 | import com.github.julyss2019.bukkit.julysafe.api.event.DropsCleanedEvent 5 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 6 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.ColoredPlaceholderMessageProcessor 7 | import com.github.julyss2019.bukkit.julysafe.core.item.ItemSet 8 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 9 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 10 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ItemSetSupport 11 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 12 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 13 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 14 | import org.bukkit.Bukkit 15 | import org.bukkit.entity.Item 16 | 17 | @Module.YamlSectionId("drop_clean") 18 | class DropCleanModule : BaseModule(), WorldSetSupport, ExecutorSupport, ItemSetSupport { 19 | override lateinit var executor: Executor 20 | override lateinit var itemSet: ItemSet 21 | override lateinit var worldSet: WorldSet 22 | 23 | override fun onEnable() { 24 | executor.task = object : Executor.Task { 25 | override fun run() { 26 | var total = 0 27 | val cleanedDrops = mutableListOf() 28 | 29 | for (world in worldSet.getAll()) { 30 | for (item in itemSet.getAll(world)) { 31 | total += 1 32 | item.remove() 33 | cleanedDrops.add(item) 34 | debug("removed, item_stack = ${item.itemStack}, location = ${item.location.getAsSimpleString()}.") 35 | } 36 | } 37 | 38 | Bukkit.getPluginManager().callEvent(DropsCleanedEvent(cleanedDrops)) 39 | executor.completer?.notification?.notifyCompleted(ColoredPlaceholderMessageProcessor(PlaceholderContainer().put("total", total))) 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/EntityCleanModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 5 | import com.github.julyss2019.bukkit.julysafe.core.executor.notification.message.processor.ColoredPlaceholderMessageProcessor 6 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 7 | import com.github.julyss2019.bukkit.julysafe.core.module.support.EntitySetSupport 8 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 9 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 10 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 11 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 12 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 13 | 14 | @Module.YamlSectionId("entity_clean") class EntityCleanModule : BaseModule(), WorldSetSupport, ExecutorSupport, EntitySetSupport { 15 | override lateinit var entitySet: EntitySet 16 | override lateinit var executor: Executor 17 | override lateinit var worldSet: WorldSet 18 | 19 | override fun setProperties(section: Section) {} 20 | 21 | override fun onEnable() { 22 | executor.task = object : Executor.Task { 23 | override fun run() { 24 | var total = 0 25 | 26 | for (world in worldSet.getAll()) { 27 | for (entity in entitySet.getAllByWorld(world)) { 28 | entity.remove() 29 | total++ 30 | debug("removed, entity = ${entity.getAsSimpleString()}, location = ${entity.location.getAsSimpleString()}.") 31 | } 32 | } 33 | 34 | executor.completer?.notification?.notifyCompleted(ColoredPlaceholderMessageProcessor(PlaceholderContainer().put("total", total))) 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/EntityExplodeLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.EntityExplodeLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("entity_explode_limit") 8 | class EntityExplodeLimitModule : BaseModule(), WorldSetSupport { 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(EntityExplodeLimitListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/EntitySpawnLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 4 | import com.github.julyss2019.bukkit.julysafe.core.listener.EntitySpawnLimitListener 5 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 6 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 7 | import com.github.julyss2019.bukkit.voidframework.yaml.DefaultValue 8 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 9 | 10 | @Module.YamlSectionId("entity_spawn_limit") 11 | class EntitySpawnLimitModule : BaseModule(), WorldSetSupport { 12 | class Limit(val id: String, val cancelled: Boolean, val interval: Int, val spawnReasonRegexes: List, val entitySet: EntitySet) 13 | 14 | override lateinit var worldSet: WorldSet 15 | 16 | lateinit var limits: List 17 | 18 | override fun setProperties(section: Section) { 19 | limits = section.getSection("limits") 20 | .subSections 21 | .map { subSection -> 22 | Limit(subSection.name, 23 | subSection.getBoolean("cancelled"), 24 | subSection.getInt("interval", DefaultValue.of(-1)), 25 | subSection.getStringList("spawn_reasons").map { it.toRegex() }, 26 | EntitySet.Parser.parse(subSection.getSection("entity_set")) 27 | ) 28 | } 29 | } 30 | 31 | override fun onEnable() { 32 | context.registerListener(EntitySpawnLimitListener(this)) 33 | } 34 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/FireSpreadLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.FireSpreadLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("fire_spread_limit") 8 | class FireSpreadLimitModule : BaseModule(), WorldSetSupport { 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(FireSpreadLimitListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/IllegalPlayerLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.IllegalPlayerLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.task.IllegalPlayerLimitTask 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | 7 | @Module.YamlSectionId("illegal_player_limit") 8 | class IllegalPlayerLimitModule : BaseModule() { 9 | var deopOnQuit: Boolean = false 10 | private set 11 | var setSurvivalModeOnQuit: Boolean = false 12 | private set 13 | lateinit var creativeModeWhitelist: List 14 | private set 15 | lateinit var opWhitelist: List 16 | private set 17 | 18 | override fun setProperties(section: Section) { 19 | deopOnQuit = section.getBoolean("deop_on_quit") 20 | setSurvivalModeOnQuit = section.getBoolean("set_survival_mode_on_quit") 21 | creativeModeWhitelist = section.getStringList("creative_mode_whitelist") 22 | opWhitelist = section.getStringList("op_whitelist") 23 | } 24 | 25 | override fun onEnable() { 26 | context.addRunningTask(IllegalPlayerLimitTask(this).runTaskTimer(context.plugin, 0L, 20L)) 27 | context.registerListener(IllegalPlayerLimitListener(this)) 28 | } 29 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/Module.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 4 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 5 | import com.github.julyss2019.bukkit.julysafe.core.item.ItemSet 6 | import com.github.julyss2019.bukkit.julysafe.core.module.support.EntitySetSupport 7 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 8 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ItemSetSupport 9 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 10 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 11 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 12 | import org.bukkit.Bukkit 13 | import org.bukkit.event.HandlerList 14 | import org.bukkit.event.Listener 15 | import org.bukkit.scheduler.BukkitTask 16 | 17 | interface Module { 18 | annotation class YamlSectionId(val value: String) 19 | 20 | annotation class AutoInject 21 | 22 | var yamlId: String 23 | var enabled: Boolean 24 | var context: Context 25 | val name: String 26 | 27 | fun setProperties(section: Section) {} 28 | 29 | fun onEnable() {} 30 | 31 | class Context(val plugin: com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin) { 32 | private val mutableRunningTasks = mutableListOf() 33 | private val registeredListeners = mutableListOf() 34 | 35 | val runningTasks: List 36 | get() { 37 | return mutableRunningTasks 38 | } 39 | 40 | fun cancelRunningTasks() { 41 | mutableRunningTasks.forEach { 42 | it.cancel() 43 | } 44 | } 45 | 46 | fun addRunningTask(bukkitTask: BukkitTask) { 47 | mutableRunningTasks.add(bukkitTask) 48 | } 49 | 50 | fun unregisterListeners() { 51 | registeredListeners.forEach { 52 | HandlerList.unregisterAll(it) 53 | } 54 | } 55 | 56 | fun registerListener(listener: Listener) { 57 | Bukkit.getPluginManager().registerEvents(listener, plugin) 58 | registeredListeners.add(listener) 59 | } 60 | } 61 | 62 | companion object { 63 | private val moduleClasses = mutableListOf>() 64 | 65 | init { 66 | registerModuleClass(AutoRestartModule::class.java) 67 | 68 | registerModuleClass(DropCleanModule::class.java) 69 | 70 | registerModuleClass(EntityExplodeLimitModule::class.java) 71 | registerModuleClass(ChunkEntityLimitModule::class.java) 72 | registerModuleClass(EntityCleanModule::class.java) 73 | registerModuleClass(EntitySpawnLimitModule::class.java) 74 | 75 | registerModuleClass(FireSpreadLimitModule::class.java) 76 | registerModuleClass(CropTrampleLimitModule::class.java) 77 | 78 | registerModuleClass(CommandLimitModule::class.java) 79 | registerModuleClass(CommandSpamLimitModule::class.java) 80 | 81 | registerModuleClass(ChatSpamLimitModule::class.java) 82 | registerModuleClass(ChatBlacklistModule::class.java) 83 | 84 | registerModuleClass(BlockExplodeLimitModule::class.java) 85 | registerModuleClass(RedstoneLimitModule::class.java) 86 | 87 | registerModuleClass(IllegalPlayerLimitModule::class.java) 88 | registerModuleClass(PlayerDropRecordModule::class.java) 89 | registerModuleClass(PlayerPickupRecordModule::class.java) 90 | } 91 | 92 | fun getModuleClasses(): List> { 93 | return moduleClasses.toList() 94 | } 95 | 96 | private fun registerModuleClass(moduleClass: Class) { 97 | require(!moduleClasses.contains(moduleClass)) { 98 | "already registered" 99 | } 100 | 101 | moduleClasses.add(moduleClass) 102 | } 103 | } 104 | 105 | object Loader { 106 | fun load(section: Section, moduleClass: Class): Module { 107 | val inst = moduleClass.newInstance() as Module 108 | 109 | inst.enabled = section.getBoolean("enabled") 110 | inst.yamlId = section.name 111 | 112 | if (!inst.enabled) { 113 | return inst 114 | } 115 | 116 | inst.setProperties(section) 117 | 118 | if (WorldSetSupport::class.java.isAssignableFrom(moduleClass)) { 119 | (inst as WorldSetSupport).worldSet = section.getSection("world_set").let { WorldSet.Parser.parse(it) } 120 | } 121 | 122 | if (ItemSetSupport::class.java.isAssignableFrom(moduleClass)) { 123 | (inst as ItemSetSupport).itemSet = section.getSection("item_set").let { ItemSet.Parser.parse(it) } 124 | } 125 | 126 | if (EntitySetSupport::class.java.isAssignableFrom(moduleClass)) { 127 | (inst as EntitySetSupport).entitySet = section.getSection("entity_set").let { EntitySet.Parser.parse(it) } 128 | } 129 | 130 | if (ExecutorSupport::class.java.isAssignableFrom(moduleClass)) { 131 | (inst as ExecutorSupport).executor = section.getSection("executor").let { Executor.Parser.parse(it) } 132 | } 133 | 134 | return inst 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/ModuleManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.ExecutorSupport 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Yaml 6 | import java.io.File 7 | 8 | class ModuleManager(val plugin: JulySafePlugin) { 9 | private val moduleMap = mutableMapOf, Module>() 10 | 11 | fun reload() { 12 | getEnabledModules().forEach { 13 | it.context.run { 14 | cancelRunningTasks() 15 | unregisterListeners() 16 | } 17 | 18 | if (it is ExecutorSupport) { 19 | it.executor.stop() 20 | } 21 | } 22 | moduleMap.clear() 23 | load() 24 | } 25 | 26 | fun load() { 27 | val yaml = Yaml.fromFile(File(plugin.dataFolder, "modules.yml")) 28 | 29 | Module.getModuleClasses().forEach { 30 | val sectionId = it.getAnnotation(Module.YamlSectionId::class.java).value 31 | val module: Module 32 | 33 | try { 34 | module = Module.Loader.load(yaml.getSection(sectionId), it) 35 | 36 | if (module.enabled) { 37 | module.context = Module.Context(plugin) 38 | module.onEnable() 39 | 40 | if (module is ExecutorSupport) { 41 | val executor = module.executor 42 | 43 | executor.module = module 44 | executor.start() 45 | } 46 | 47 | plugin.pluginLogger.info("${module.javaClass.simpleName}: 已启用.") 48 | } else { 49 | plugin.pluginLogger.info("${module.javaClass.simpleName}: 未启用.") 50 | } 51 | } catch (ex: Exception) { 52 | throw RuntimeException("an exception occurred while loading module", ex) 53 | } 54 | 55 | moduleMap[it] = module 56 | } 57 | } 58 | 59 | fun getEnabledModules(): List { 60 | return getModules().filter { it.enabled } 61 | } 62 | 63 | fun getModules(): List { 64 | return moduleMap.values.toList() 65 | } 66 | 67 | fun getModuleByClass(moduleClass: Class): T? { 68 | return moduleMap[moduleClass] as T? 69 | } 70 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/PlayerDropRecordModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.PlayerDropRecordListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("player_drop_record") 8 | class PlayerDropRecordModule : BaseModule(), WorldSetSupport { 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(PlayerDropRecordListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/PlayerPickupRecordModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.PlayerPickupRecordListener 4 | import com.github.julyss2019.bukkit.julysafe.core.module.support.WorldSetSupport 5 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 6 | 7 | @Module.YamlSectionId("player_pickup_record") 8 | class PlayerPickupRecordModule : BaseModule(), WorldSetSupport { 9 | override lateinit var worldSet: WorldSet 10 | 11 | override fun onEnable() { 12 | context.registerListener(PlayerPickupRecordListener(this)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/RedstoneLimitModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.listener.RedstoneLimitListener 4 | import com.github.julyss2019.bukkit.julysafe.core.redstone.detector.Detector 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | 7 | @Module.YamlSectionId("redstone_limit") 8 | class RedstoneLimitModule : BaseModule() { 9 | lateinit var detector: Detector 10 | private set 11 | 12 | override fun setProperties(section: Section) { 13 | detector = Detector.Parser.parse(this, section) 14 | } 15 | 16 | override fun onEnable() { 17 | detector.onEnabled() 18 | context.registerListener(RedstoneLimitListener(this)) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/support/EntitySetSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module.support 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.entity.EntitySet 4 | import com.github.julyss2019.bukkit.julysafe.core.module.Module 5 | 6 | interface EntitySetSupport : Module { 7 | var entitySet: EntitySet 8 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/support/ExecutorSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module.support 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.executor.Executor 4 | import com.github.julyss2019.bukkit.julysafe.core.module.Module 5 | 6 | interface ExecutorSupport : Module { 7 | var executor: Executor 8 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/support/ItemSetSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module.support 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.item.ItemSet 4 | import com.github.julyss2019.bukkit.julysafe.core.module.Module 5 | 6 | interface ItemSetSupport : Module { 7 | var itemSet: ItemSet 8 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/module/support/WorldSetSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.module.support 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 4 | 5 | interface WorldSetSupport { 6 | var worldSet: WorldSet 7 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/player/JulySafePlayer.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.player 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.util.MessageUtils 4 | import org.bukkit.entity.Player 5 | 6 | class JulySafePlayer(val bukkitPlayer: Player) { 7 | var lastRedstoneLimitNotify: Long = -1L 8 | var debugEnabled: Boolean = false 9 | 10 | fun debug(message: String) { 11 | MessageUtils.sendMessage(bukkitPlayer, "[Debug] &f$message") 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/player/JulySafePlayerManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.player 2 | 3 | import org.bukkit.Bukkit 4 | import org.bukkit.entity.Player 5 | import java.util.* 6 | 7 | class JulySafePlayerManager { 8 | private val julySafePlayerMap = mutableMapOf() 9 | 10 | fun getJulySafePlayer(bukkitPlayer: Player): JulySafePlayer { 11 | require(bukkitPlayer.isOnline) { "Player is offline" } 12 | 13 | val uuid = bukkitPlayer.uniqueId 14 | 15 | if (!julySafePlayerMap.containsKey(uuid)) { 16 | julySafePlayerMap[uuid] = JulySafePlayer(Bukkit.getPlayer(bukkitPlayer.uniqueId)) 17 | } 18 | 19 | return julySafePlayerMap[uuid]!! 20 | } 21 | 22 | fun unloadJulySafePlayer(bukkitPlayer: Player) { 23 | julySafePlayerMap.remove(bukkitPlayer.uniqueId) 24 | } 25 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/BaseDetector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.locale.LocaleResource 5 | import com.github.julyss2019.bukkit.julysafe.core.module.RedstoneLimitModule 6 | import com.github.julyss2019.bukkit.julysafe.core.redstone.record.Record 7 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 8 | import com.github.julyss2019.bukkit.voidframework.common.Messages 9 | import com.github.julyss2019.bukkit.voidframework.text.PlaceholderContainer 10 | import com.github.julyss2019.bukkit.voidframework.text.Texts 11 | import org.bukkit.Chunk 12 | import org.bukkit.Location 13 | import org.bukkit.Material 14 | import org.bukkit.block.Block 15 | import org.bukkit.entity.Player 16 | import java.text.SimpleDateFormat 17 | 18 | abstract class BaseDetector : Detector { 19 | private lateinit var simpleDateFormat: SimpleDateFormat 20 | private lateinit var locale: LocaleResource 21 | 22 | override lateinit var module: RedstoneLimitModule 23 | override lateinit var worldSet: WorldSet 24 | override lateinit var blockWhitelist: List 25 | override lateinit var notifyPlayerRange: Detector.NotifyPlayerRange 26 | override var resetPeriod: Int = -1 27 | override var threshold: Int = -1 28 | override var banDuration: Int = -1 29 | override var notifyPlayerInterval: Int = -1 30 | override var destroyBlock: Boolean = false 31 | 32 | override fun onEnabled() { 33 | locale = module.getLocalResource() 34 | simpleDateFormat = SimpleDateFormat(locale.getString("date_time_format")) 35 | } 36 | 37 | protected fun destroyBlockIfNecessary(block: Block) { 38 | if (destroyBlock) { 39 | block.breakNaturally() 40 | } 41 | } 42 | 43 | protected fun playerDebug(nearbyPlayers: List, message: String) { 44 | nearbyPlayers 45 | .filter { it.isOnline } 46 | .map { module.context.plugin.julySafePlayerManager.getJulySafePlayer(it) } 47 | .filter { it.debugEnabled } 48 | .forEach { 49 | it.debug(message) 50 | } 51 | } 52 | 53 | protected fun debugChunk(chunk: Chunk, nearbyPlayers: List) { 54 | module.debug("banned, chunk = ${chunk.getAsSimpleString()}, nearby_players = ${nearbyPlayers.map { it.getAsSimpleString() }}.") 55 | } 56 | 57 | protected fun debugBlock(block: Block, nearbyPlayers: List) { 58 | module.debug("banned, block = ${block.getAsSimpleString()}, location = ${block.location.getAsSimpleString()}, nearby_players = ${nearbyPlayers.map { it.getAsSimpleString() }}.") 59 | } 60 | 61 | protected fun getNearbyPlayers(location: Location): List { 62 | val range = notifyPlayerRange 63 | 64 | return location.world!!.getNearbyEntities(location, range.x, range.y, range.z).filterIsInstance() 65 | } 66 | 67 | protected fun notifyPlayers(record: Record, location: Location, nearbyPlayers: List) { 68 | nearbyPlayers 69 | .filter { it.isOnline } 70 | .forEach { 71 | val julySafePlayer = module.context.plugin.julySafePlayerManager.getJulySafePlayer(it) 72 | val now = System.currentTimeMillis() 73 | 74 | if (now - julySafePlayer.lastRedstoneLimitNotify > notifyPlayerInterval * 1000L) { 75 | Messages.sendColoredMessage( 76 | it, Texts.setPlaceholders( 77 | locale.getString("banned"), PlaceholderContainer() 78 | .put("x", location.blockX) 79 | .put("y", location.blockY) 80 | .put("z", location.blockZ) 81 | .put("expired", simpleDateFormat.format(record.getBanExpired())) 82 | ) 83 | ) 84 | julySafePlayer.lastRedstoneLimitNotify = now 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/BlockCounterDetector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.redstone.record.CounterRecord 5 | import org.bukkit.Location 6 | import org.bukkit.event.block.BlockRedstoneEvent 7 | 8 | class BlockCounterDetector : BaseDetector() { 9 | private val blockMap = mutableMapOf() 10 | 11 | @Suppress("DuplicatedCode") 12 | override fun detect(event: BlockRedstoneEvent) { 13 | val block = event.block 14 | val location = block.location 15 | 16 | if (!blockMap.containsKey(location)) { 17 | blockMap[location] = CounterRecord(this) 18 | } else { 19 | blockMap[location]!!.update() 20 | } 21 | 22 | val counterRecord: CounterRecord = blockMap[location]!! 23 | val nearbyPlayers = getNearbyPlayers(location) 24 | 25 | if (counterRecord.isBanned()) { 26 | event.newCurrent = 0 27 | 28 | notifyPlayers(counterRecord, location, nearbyPlayers) 29 | return 30 | } 31 | 32 | counterRecord.addCount() 33 | playerDebug(nearbyPlayers, "detector = ${javaClass.simpleName}, block = ${block.getAsSimpleString()}, location = ${location.getAsSimpleString()}, count/threshold = ${counterRecord.count}/${threshold}.") 34 | 35 | if (counterRecord.count == threshold) { 36 | event.newCurrent = 0 37 | counterRecord.ban() 38 | notifyPlayers(counterRecord, location, nearbyPlayers) 39 | destroyBlockIfNecessary(block) 40 | debugBlock(block, nearbyPlayers) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/BlockTimerDetector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.redstone.record.TimerRecord 5 | import org.bukkit.Location 6 | import org.bukkit.event.block.BlockRedstoneEvent 7 | 8 | class BlockTimerDetector : BaseDetector() { 9 | private val blockMap = mutableMapOf() 10 | 11 | @Suppress("DuplicatedCode") 12 | override fun detect(event: BlockRedstoneEvent) { 13 | val block = event.block 14 | val location = block.location 15 | 16 | if (!blockMap.containsKey(location)) { 17 | blockMap[location] = TimerRecord(this) 18 | } else { 19 | blockMap[location]!!.update() 20 | } 21 | 22 | val timerRecord = blockMap[location]!! 23 | val nearbyPlayers = getNearbyPlayers(location) 24 | 25 | if (timerRecord.isBanned()) { 26 | event.newCurrent = 0 27 | 28 | notifyPlayers(timerRecord, location, nearbyPlayers) 29 | return 30 | } 31 | 32 | if (timerRecord.isGap()) { 33 | return 34 | } 35 | 36 | timerRecord.addSecond() 37 | playerDebug(nearbyPlayers, "detector = ${javaClass.simpleName}, block = ${block.getAsSimpleString()}, location = ${location.getAsSimpleString()}, seconds/threshold = ${timerRecord.seconds}/${threshold}.") 38 | 39 | if (timerRecord.seconds == threshold) { 40 | event.newCurrent = 0 41 | timerRecord.ban() 42 | notifyPlayers(timerRecord, location, nearbyPlayers) 43 | destroyBlockIfNecessary(block) 44 | debugBlock(block, nearbyPlayers) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/ChunkCounterDetector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.redstone.record.CounterRecord 5 | import org.bukkit.Chunk 6 | import org.bukkit.event.block.BlockRedstoneEvent 7 | 8 | class ChunkCounterDetector : BaseDetector() { 9 | private val chunkMap = mutableMapOf() 10 | 11 | @Suppress("DuplicatedCode") 12 | override fun detect(event: BlockRedstoneEvent) { 13 | val block = event.block 14 | val location = block.location 15 | val chunk = location.chunk 16 | 17 | if (!chunkMap.containsKey(chunk)) { 18 | chunkMap[chunk] = CounterRecord(this) 19 | } else { 20 | chunkMap[chunk]!!.update() 21 | } 22 | 23 | val counterRecord: CounterRecord = chunkMap[chunk]!! 24 | val nearbyPlayers = getNearbyPlayers(location) 25 | 26 | if (counterRecord.isBanned()) { 27 | event.newCurrent = 0 28 | 29 | notifyPlayers(counterRecord, location, nearbyPlayers) 30 | return 31 | } 32 | 33 | counterRecord.addCount() 34 | playerDebug(nearbyPlayers, "detector = ${javaClass.simpleName}, block = ${block.getAsSimpleString()}, location = ${location.getAsSimpleString()}, chunk = ${chunk.getAsSimpleString()}, " + 35 | "count/threshold = ${counterRecord.count}/${threshold}.") 36 | 37 | if (counterRecord.count == threshold) { 38 | event.newCurrent = 0 39 | counterRecord.ban() 40 | notifyPlayers(counterRecord, location, nearbyPlayers) 41 | destroyBlockIfNecessary(block) 42 | debugChunk(chunk, nearbyPlayers) 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/ChunkTimerDetector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getAsSimpleString 4 | import com.github.julyss2019.bukkit.julysafe.core.redstone.record.TimerRecord 5 | import org.bukkit.Chunk 6 | import org.bukkit.event.block.BlockRedstoneEvent 7 | 8 | class ChunkTimerDetector : BaseDetector() { 9 | private val chunkMap = mutableMapOf() 10 | 11 | @Suppress("DuplicatedCode") 12 | override fun detect(event: BlockRedstoneEvent) { 13 | val block = event.block 14 | val location = block.location 15 | val chunk = location.chunk 16 | 17 | if (!chunkMap.containsKey(chunk)) { 18 | chunkMap[chunk] = TimerRecord(this) 19 | } else { 20 | chunkMap[chunk]!!.update() 21 | } 22 | 23 | val timerRecord = chunkMap[chunk]!! 24 | val nearbyPlayers = getNearbyPlayers(location) 25 | 26 | if (timerRecord.isBanned()) { 27 | event.newCurrent = 0 28 | 29 | notifyPlayers(timerRecord, location, nearbyPlayers) 30 | return 31 | } 32 | 33 | if (!timerRecord.isGap()) { 34 | return 35 | } 36 | 37 | timerRecord.addSecond() 38 | playerDebug(nearbyPlayers, "detector = ${javaClass.simpleName}, block = ${block.getAsSimpleString()}, location = ${location.getAsSimpleString()}, chunk = ${chunk.getAsSimpleString()}, seconds/threshold = ${timerRecord.seconds}/${threshold}.") 39 | 40 | if (timerRecord.seconds == threshold) { 41 | event.newCurrent = 0 42 | timerRecord.ban() 43 | notifyPlayers(timerRecord, location, nearbyPlayers) 44 | destroyBlockIfNecessary(block) 45 | debugChunk(chunk, nearbyPlayers) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/detector/Detector.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.detector 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.module.RedstoneLimitModule 4 | import com.github.julyss2019.bukkit.julysafe.core.world.WorldSet 5 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 6 | import org.bukkit.Material 7 | import org.bukkit.event.block.BlockRedstoneEvent 8 | 9 | interface Detector { 10 | enum class Type(val mappingClass: Class) { 11 | BLOCK_COUNTER(BlockCounterDetector::class.java), 12 | CHUNK_COUNTER(ChunkCounterDetector::class.java), 13 | BLOCK_TIMER(BlockTimerDetector::class.java), 14 | CHUNK_TIMER(ChunkTimerDetector::class.java) 15 | } 16 | 17 | class NotifyPlayerRange(val x: Double, val y: Double, val z: Double) 18 | 19 | object Parser { 20 | fun parse(module: RedstoneLimitModule, section: Section): Detector { 21 | val instance = section.getEnum("detector_type", Type::class.java).mappingClass.newInstance() 22 | 23 | instance.run { 24 | blockWhitelist = section.getEnumSet("block_whitelist", Material::class.java).toList() 25 | resetPeriod = section.getInt("reset_period") 26 | threshold = section.getInt("threshold") 27 | banDuration = section.getInt("ban_duration") 28 | notifyPlayerInterval = section.getInt("notify_player_interval") 29 | worldSet = WorldSet.Parser.parse(section.getSection("world_set")) 30 | 31 | val rangeExpression = section.getString("notify_player_range") 32 | 33 | try { 34 | val array = rangeExpression.split(",") 35 | 36 | notifyPlayerRange = NotifyPlayerRange(array[0].toDouble(), array[1].toDouble(), array[2].toDouble()) 37 | } catch (ex: NumberFormatException) { 38 | throw RuntimeException("exists invalid number in $rangeExpression", ex) 39 | } catch (ex: ArrayIndexOutOfBoundsException) { 40 | throw RuntimeException("invalid array length in $rangeExpression") 41 | } 42 | 43 | destroyBlock = section.getBoolean("destroy_block") 44 | } 45 | instance.module = module 46 | 47 | return instance 48 | } 49 | } 50 | 51 | var module: RedstoneLimitModule 52 | var worldSet: WorldSet 53 | var blockWhitelist: List 54 | var resetPeriod: Int 55 | var threshold: Int 56 | var banDuration: Int 57 | var notifyPlayerInterval: Int 58 | var notifyPlayerRange: NotifyPlayerRange 59 | var destroyBlock: Boolean 60 | 61 | fun onEnabled() 62 | 63 | fun detect(event: BlockRedstoneEvent) 64 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/record/BaseRecord.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.record 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.redstone.detector.Detector 4 | 5 | abstract class BaseRecord(override val detector: Detector) : Record { 6 | private var banExpired: Long = -1 7 | 8 | override fun ban() { 9 | banExpired = System.currentTimeMillis() + detector.banDuration * 1000L 10 | } 11 | 12 | override fun isBanned(): Boolean { 13 | return System.currentTimeMillis() < banExpired 14 | } 15 | 16 | override fun getBanExpired(): Long { 17 | return banExpired 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/record/CounterRecord.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.record 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.redstone.detector.Detector 4 | 5 | class CounterRecord(override val detector: Detector) : BaseRecord(detector) { 6 | var count: Int = 0 7 | private set 8 | private var detectBeginning: Long = -1L // 检测开始时间 9 | 10 | init { 11 | detectBeginning = System.currentTimeMillis() 12 | } 13 | 14 | fun addCount() { 15 | count += 1 16 | } 17 | 18 | /** 19 | * 检测重置周期,若到了周期则重置 20 | */ 21 | override fun update() { 22 | // 重置周期 23 | if (System.currentTimeMillis() - detectBeginning > detector.resetPeriod * 1000L) { 24 | count = 0 25 | detectBeginning = System.currentTimeMillis() 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/record/Record.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.record 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.redstone.detector.Detector 4 | 5 | interface Record { 6 | val detector: Detector 7 | 8 | fun getBanExpired(): Long 9 | 10 | fun ban() 11 | 12 | fun isBanned(): Boolean 13 | 14 | fun update() 15 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/redstone/record/TimerRecord.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.redstone.record 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.redstone.detector.Detector 4 | 5 | class TimerRecord(override val detector: Detector) : BaseRecord(detector) { 6 | var seconds: Int = 0 7 | private set 8 | private var detectBeginning: Long = -1L // 检测开始时间 9 | private var lastTimer : Long = -1 10 | 11 | init { 12 | detectBeginning = System.currentTimeMillis() 13 | } 14 | 15 | // 是否在空档期,即本次秒与上一次秒为同秒 16 | fun isGap(): Boolean { 17 | return System.currentTimeMillis() / 1000L == lastTimer / 1000L 18 | } 19 | 20 | fun addSecond() { 21 | val now = System.currentTimeMillis() 22 | 23 | if (lastTimer / 1000 == now) { 24 | return 25 | } 26 | 27 | seconds += 1 28 | lastTimer = now 29 | } 30 | 31 | /** 32 | * 检测重置周期,若到了周期则重置 33 | */ 34 | override fun update() { 35 | // 重置周期 36 | if (System.currentTimeMillis() - detectBeginning > detector.resetPeriod * 1000L) { 37 | seconds = 0 38 | detectBeginning = System.currentTimeMillis() 39 | lastTimer = 0 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/task/IllegalPlayerLimitTask.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.task 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.kotlin.extension.getNameAndUuid 4 | import com.github.julyss2019.bukkit.julysafe.core.module.IllegalPlayerLimitModule 5 | import com.github.julyss2019.bukkit.voidframework.text.Texts 6 | import org.bukkit.BanList 7 | import org.bukkit.Bukkit 8 | import org.bukkit.GameMode 9 | import org.bukkit.entity.Player 10 | import org.bukkit.scheduler.BukkitRunnable 11 | 12 | class IllegalPlayerLimitTask(private val module: IllegalPlayerLimitModule) : BukkitRunnable() { 13 | private val localeResource = module.getLocalResource() 14 | 15 | override fun run() { 16 | for (player in Bukkit.getOnlinePlayers()) { 17 | if (!module.creativeModeWhitelist.contains(player.name) && player.gameMode == GameMode.CREATIVE) { 18 | player.gameMode = GameMode.SURVIVAL 19 | banForeverAndKick(player, localeResource.getString("illegal_creative_mode_banned")) 20 | module.info("ban illegal creative player, player = ${player.getNameAndUuid()}") 21 | } 22 | 23 | if (!module.opWhitelist.contains(player.name) && player.isOp) { 24 | player.isOp = false 25 | banForeverAndKick(player, localeResource.getString("illegal_op_banned")) 26 | module.info("ban illegal op, player = ${player.getNameAndUuid()}") 27 | } 28 | } 29 | } 30 | 31 | private fun banForeverAndKick(player: Player, reason: String) { 32 | Bukkit.getBanList(BanList.Type.NAME).addBan(player.name, Texts.getColoredText(reason), null, "JulySafe") 33 | player.kickPlayer(Texts.getColoredText(reason)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/tps/TpsManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.tps 2 | 3 | import com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 4 | import org.bukkit.scheduler.BukkitRunnable 5 | import java.util.* 6 | 7 | class TpsManager(private val plugin: JulySafePlugin) { 8 | var tps = 0.0 9 | private set 10 | val maxSampleCount = 10 11 | private var lastSample: Long = -1 12 | private val samples = LinkedList() 13 | 14 | init { 15 | object : BukkitRunnable() { 16 | override fun run() { 17 | val now = System.nanoTime() 18 | 19 | if (lastSample == -1L) { 20 | lastSample = now 21 | return 22 | } 23 | 24 | val timeSpent = now - lastSample 25 | 26 | if (samples.size > maxSampleCount) { 27 | samples.remove() 28 | } 29 | 30 | // 总 tick / seconds = tps(ticks per second,每秒能处理几个tick) 31 | // 20 tick, 1纳秒 = 1e9秒 32 | val tps = 20.0 / (timeSpent / 1E9) 33 | 34 | samples.add(tps) 35 | lastSample = now 36 | } 37 | } 38 | } 39 | 40 | fun getAverageTps(): Double { 41 | if (samples.size != maxSampleCount) { 42 | return -1.0 43 | } 44 | 45 | return samples.average() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/util/CooldownTimer.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.util 2 | 3 | import java.math.RoundingMode 4 | import java.text.DecimalFormat 5 | import kotlin.math.max 6 | 7 | class CooldownTimer { 8 | companion object { 9 | private val decimalFormat = DecimalFormat("#.##") 10 | .apply { roundingMode = RoundingMode.CEILING } 11 | 12 | fun createNewAndStart(cooldownMills: Int): CooldownTimer { 13 | val cooldownTimer = CooldownTimer() 14 | 15 | cooldownTimer.cooldown = cooldownMills 16 | cooldownTimer.start() 17 | return cooldownTimer 18 | } 19 | } 20 | 21 | var startTime: Long = -1 22 | private set 23 | var cooldown: Int = -1 24 | private set 25 | private val started: Boolean 26 | get() { 27 | return startTime != -1L 28 | } 29 | 30 | fun isInCooldown(): Boolean { 31 | require(started) { 32 | "not started" 33 | } 34 | 35 | return getRemainingCooldown() > 0 36 | } 37 | 38 | /** 39 | * 获取经过格式化的剩余的冷却时间,以秒为单位 40 | * 保留 2 位小数,四舍五入 41 | */ 42 | fun getFormattedRemainingCooldown(): String { 43 | return decimalFormat.format(getRemainingCooldown() / 1000.0) 44 | } 45 | 46 | /** 47 | * 获取剩余的冷却时间,以毫秒为单位 48 | */ 49 | fun getRemainingCooldown(): Long { 50 | require(started) { 51 | "not started" 52 | } 53 | 54 | return max(0L, cooldown - (System.currentTimeMillis() - startTime)) 55 | } 56 | 57 | fun start() { 58 | require(!started) { 59 | "already started" 60 | } 61 | 62 | startTime = System.currentTimeMillis() 63 | } 64 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/util/FileUtils.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.util 2 | 3 | import java.io.File 4 | import java.io.FileOutputStream 5 | import java.io.InputStream 6 | 7 | object FileUtils { 8 | fun writeInputStreamToFile(input: InputStream, file: File, overwrite: Boolean) { 9 | if (!overwrite && file.exists()) { 10 | return 11 | } 12 | 13 | file.parentFile.let { 14 | if (!it.exists() && !it.mkdirs()) { 15 | throw RuntimeException("mkdirs failed: ${it.absolutePath}") 16 | } 17 | } 18 | 19 | val out = FileOutputStream(file) 20 | val buffer = ByteArray(1024) 21 | var read: Int 22 | 23 | while (input.read(buffer).also { read = it } != -1) { 24 | out.write(buffer, 0, read) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/util/MessageUtils.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.util 2 | 3 | import com.github.julyss2019.bukkit.voidframework.common.Messages 4 | import org.bukkit.command.CommandSender 5 | 6 | object MessageUtils { 7 | fun sendMessage(sender: CommandSender, message: String) { 8 | Messages.sendColoredMessage(sender, "&a[JulySafe] &f$message") 9 | } 10 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/util/MinecraftVersion.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.util 2 | 3 | import org.bukkit.Bukkit 4 | 5 | data class MinecraftVersion(val major: Int, val minor: Int) { 6 | companion object { 7 | private val CURRENT_VERSION: MinecraftVersion 8 | 9 | init { 10 | val version = Bukkit.getBukkitVersion().substringBefore("-") 11 | val array = version.split(".") 12 | 13 | CURRENT_VERSION = MinecraftVersion(array[1].toInt(), array.getOrNull(2)?.toInt() ?: 0) 14 | } 15 | 16 | fun getCurrentVersion(): MinecraftVersion { 17 | return CURRENT_VERSION 18 | } 19 | } 20 | 21 | fun compareVersion(major: Int, minor: Int = 0): Int { 22 | val tmp = this.major.compareTo(major) 23 | 24 | if (tmp != 0) { 25 | return tmp 26 | } 27 | 28 | return this.minor.compareTo(minor) 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/julyss2019/bukkit/julysafe/core/world/WorldSet.kt: -------------------------------------------------------------------------------- 1 | package com.github.julyss2019.bukkit.julysafe.core.world 2 | 3 | import com.github.julyss2019.bukkit.voidframework.yaml.Section 4 | import org.bukkit.Bukkit 5 | import org.bukkit.World 6 | 7 | /** 8 | * 世界差集 9 | */ 10 | class WorldSet(private val includes: List, private val excludes: List) { 11 | fun contains(target: World): Boolean { 12 | val worldName = target.name 13 | 14 | for (excludeRegex in excludes) { 15 | if (worldName.matches(excludeRegex)) { 16 | return false 17 | } 18 | } 19 | 20 | for (includeRegex in includes) { 21 | if (worldName.matches(includeRegex)) { 22 | return true 23 | } 24 | } 25 | 26 | return false 27 | } 28 | 29 | fun getAll(): List { 30 | return Bukkit.getWorlds() 31 | .filter { 32 | contains(it) 33 | } 34 | } 35 | 36 | override fun toString(): String { 37 | return "WorldSet(includes=$includes, excludes=$excludes)" 38 | } 39 | 40 | object Parser { 41 | fun parse(section: Section): WorldSet { 42 | return WorldSet( 43 | section.getStringList("includes").map { it.toRegex() }, 44 | section.getStringList("excludes").map { it.toRegex() }) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /core/src/main/resources/defaults/config.yml: -------------------------------------------------------------------------------- 1 | # 日志级别 2 | log.threshold: INFO 3 | 4 | # 开启后将上传一些匿名统计信息 5 | bStats_enabled: true 6 | 7 | # 本土化 8 | locale: 'zh_CN' 9 | 10 | # 任务调度器 11 | scheduler: 12 | # 首次执行延迟 13 | delay: 0 14 | # 执行间隔,秒 15 | period: 300 16 | -------------------------------------------------------------------------------- /core/src/main/resources/defaults/locales/zh_CN.yml: -------------------------------------------------------------------------------- 1 | prefix: '&a[JulySafe] &c' 2 | chat_blacklist: 3 | cancelled: '${prefix}请文明发言, 你发送的内容已被屏蔽.' 4 | replaced: '${prefix}请文明发言, 你发送的聊天内容部分被屏蔽.' 5 | chat_spam_limit: 6 | denied: '${prefix}请降低语速, 冷却时间: ${cooldown}s.' 7 | command_spam_limit: 8 | denied: '${prefix}请降低使用命令的速度, 冷却时间: ${cooldown}s.' 9 | command_limit: 10 | denied: '${prefix}该指令已被禁用.' 11 | illegal_player_limit: 12 | illegal_creative_mode_banned: '${prefix}非法的创造模式.' 13 | illegal_op_banned: '${prefix}非法的 OP.' 14 | redstone_limit: 15 | date_time_format: 'yyyy/MM/dd HH:mm:ss' 16 | banned: '${prefix}在坐标 (${x},${y},${z}) 检测到高频红石, 红石装置已被断开, 恢复时间: ${expired}.' 17 | auto_restart: 18 | kick_all: '${prefix}服务器重启, 请 5 分钟后重新进入.' -------------------------------------------------------------------------------- /core/src/main/resources/defaults/modules.yml: -------------------------------------------------------------------------------- 1 | # 实体清理 2 | entity_clean: 3 | # 是否开启 4 | enabled: true 5 | # 世界集 6 | world_set: 7 | # 包含的世界正则表达式 8 | includes: 9 | - '.*' 10 | # 排除的世界正则表达式 11 | excludes: 12 | - 'excluded_world' 13 | # 执行器 14 | executor: 15 | # 使用调度器 16 | type: SCHEDULER 17 | # 参数 18 | properties: 19 | # 优先级 20 | priority: 1 21 | # 倒计时器 22 | countdown_timer: 23 | # 倒计时秒数 24 | seconds: '1-60' 25 | # 倒计时通知 26 | notification: 27 | # 倒计时类型 28 | type: TITLE 29 | # 参数 30 | properties: 31 | title: '&e即将清理生物' 32 | subtitle: '&c倒计时 ${countdown} 秒' 33 | stay: 20 34 | fade_in: 20 35 | fade_out: 20 36 | # 完成器 37 | completer: 38 | notification: 39 | type: MESSAGE 40 | properties: 41 | message: '&a[JulySafe] &b清理完毕, 共计清理 &e${total} &b个生物.' 42 | # 实体集 43 | entity_set: 44 | # 包含 45 | includes: 46 | 'entity_filter_1': 47 | # 类过滤器 48 | type: CLASS 49 | properties: 50 | classes: 51 | - 'org.bukkit.entity.Monster' 52 | # 除外 53 | excludes: 54 | 'entity_filter_1': 55 | # METADATA 过滤器 56 | type: METADATA 57 | properties: 58 | keys: 59 | - 'MyPet' # 宠物插件生物 60 | - 'NPC' # Citizens 插件生物 61 | 'entity_filter_2': 62 | # 自定义名过滤器(头顶的名字) 63 | type: CUSTOM_NAME 64 | properties: 65 | regexes: 66 | - '.*' 67 | 'entity_filter_3': 68 | # 枚举过滤器 69 | type: ENUM 70 | properties: 71 | regexes: 72 | - 'ENDER_DRAGON' 73 | - 'ZOMBIE_VILLAGER' 74 | 75 | # 区块实体限制 76 | chunk_entity_limit: 77 | # 是否开启 78 | enabled: true 79 | # 世界集 80 | world_set: 81 | # 包含的世界正则表达式 82 | includes: 83 | - '.*' 84 | # 排除的世界正则表达式 85 | excludes: 86 | - 'excluded_world' 87 | # 执行器 88 | executor: 89 | # 定时执行器 90 | type: TIMER 91 | properties: 92 | # 周期,秒 93 | period: 0 94 | # 显式关闭倒计时器和完成器 95 | countdown_timer.enabled: false 96 | completer.enabled: false 97 | # 限制 98 | limits: 99 | 'limit_1': 100 | # 阈值 101 | threshold: 10 102 | # 实体集 103 | entity_set: 104 | # 包含 105 | includes: 106 | 'entity_filter_1': 107 | type: CLASS 108 | properties: 109 | classes: 110 | - 'org.bukkit.entity.Animals' 111 | # 不包含 112 | excludes: {} 113 | 114 | # 实体生成限制 115 | entity_spawn_limit: 116 | # 是否开启 117 | enabled: true 118 | # 世界集 119 | world_set: 120 | # 包含的世界正则表达式 121 | includes: 122 | - '.*' 123 | # 排除的世界正则表达式 124 | excludes: 125 | - 'excluded_world' 126 | # 限制 127 | limits: 128 | 'limit_1': 129 | # 生成原因(正则表达式) 130 | spawn_reasons: 131 | - '.*' 132 | # 实体集 133 | entity_set: 134 | # 包含 135 | includes: 136 | 'entity_filter_1': 137 | type: ENUM 138 | properties: 139 | regexes: 140 | - 'PIG_ZOMBIE' 141 | # 不包含 142 | excludes: {} 143 | # 取消生成 144 | cancelled: false 145 | # 间隔阈值(毫秒,当且仅当 cancelled 不为 true 时有效,可删除该项,默认为 -1,即不限制) 146 | interval: 1000 147 | 148 | # 清理掉落物 149 | drop_clean: 150 | # 是否开启 151 | enabled: true 152 | # 世界集 153 | world_set: 154 | # 包含的世界正则表达式 155 | includes: 156 | - '.*' 157 | # 排除的世界正则表达式 158 | excludes: 159 | - 'excluded_world' 160 | # 执行器 161 | executor: 162 | # 使用调度器 163 | type: SCHEDULER 164 | # 参数 165 | properties: 166 | # 优先级 167 | priority: 1 168 | # 倒计时器 169 | countdown_timer: 170 | # 倒计时秒数 171 | seconds: '1-60' 172 | # 倒计时通知 173 | notification: 174 | # 倒计时类型 175 | type: TITLE 176 | # 参数 177 | properties: 178 | title: '&e即将清理掉落物' 179 | subtitle: '&c倒计时 ${countdown} 秒' 180 | stay: 20 181 | fade_in: 20 182 | fade_out: 20 183 | # 完成器 184 | completer: 185 | notification: 186 | type: MESSAGE 187 | properties: 188 | message: '&a[JulySafe] &b清理完毕, 共计清理 &e${total} &b个掉落物.' 189 | # 物品集 190 | item_set: 191 | # 包含 192 | includes: 193 | 'item_filter_1': 194 | # 枚举过滤器 195 | type: ENUM 196 | properties: 197 | regexes: 198 | - '.*' 199 | # 不包含 200 | excludes: 201 | 'item_filter_1': 202 | # 附魔过滤器 203 | type: ENCHANTMENT 204 | properties: 205 | regexes: 206 | - 'DAMAGE_ALL' 207 | 'item_filter_2': 208 | # 自定义名过滤器 209 | type: DISPLAY_NAME 210 | properties: 211 | regexes: 212 | - '.*' 213 | 'item_filter_3': 214 | # LORE 过滤器 215 | type: LORE 216 | properties: 217 | regexes: 218 | - '已绑定' 219 | 'item_filter_4': 220 | # 枚举过滤器 221 | type: ENUM 222 | properties: 223 | regexes: 224 | - '.*_BOX' 225 | - 'DIAMOND' 226 | 227 | # 自动重启 228 | auto_restart: 229 | # 是否开启 230 | enabled: false 231 | # 执行器 232 | executor: 233 | # 定时执行器 234 | type: FIXED_TIME 235 | properties: 236 | # 24 小时制时间 237 | times: 238 | - '10:55:00' 239 | # 倒计时器 240 | countdown_timer: 241 | # 倒计时秒数 242 | seconds: '1-60' 243 | notification: 244 | # 倒计时类型 245 | type: TITLE 246 | # 参数 247 | properties: 248 | stay: 20 249 | fade_in: 20 250 | fade_out: 20 251 | title: '&a即将重启服务器' 252 | subtitle: '&e倒计时: ${countdown} 秒' 253 | completer.enabled: false 254 | # 重启时执行的指令 255 | before_restart_commands: 256 | - 'save-all' 257 | # 在重启时踢出所有玩家 258 | kick_all_before_restart: true 259 | 260 | # 玩家丢下掉落物记录 261 | player_drop_record: 262 | # 是否开启 263 | enabled: true 264 | # 世界集 265 | world_set: 266 | # 包含的世界正则表达式 267 | includes: 268 | - '.*' 269 | # 排除的世界正则表达式 270 | excludes: 271 | - 'excluded_world' 272 | 273 | # 玩家捡起掉落物记录 274 | player_pickup_record: 275 | # 是否开启 276 | enabled: true 277 | # 世界集 278 | world_set: 279 | # 包含的世界正则表达式 280 | includes: 281 | - '.*' 282 | # 排除的世界正则表达式 283 | excludes: 284 | - 'excluded_world' 285 | 286 | # 红石限制 287 | redstone_limit: 288 | # 是否开启 289 | enabled: true 290 | # 世界集 291 | world_set: 292 | # 包含的世界正则表达式 293 | includes: 294 | - '.*' 295 | # 排除的世界正则表达式 296 | excludes: 297 | - 'excluded_world' 298 | # 检测器类型 299 | # BLOCK_COUNTER 单方块计次器 300 | # CHUNK_COUNTER 区块计次器 301 | # BLOCK_TIMER 单方块计时器 302 | # CHUNK_TIMER 区块计时器 303 | detector_type: CHUNK_COUNTER 304 | # 方块白名单 305 | block_whitelist: 306 | - 'DAYLIGHT_DETECTOR' 307 | # 检测重置周期(秒) 308 | reset_period: 10 309 | # 阈值 310 | threshold: 5 311 | # 封禁时间,秒 312 | ban_duration: 120 313 | # 通知附近玩家的范围(x,y,z) 314 | notify_player_range: '15,255,15' 315 | # 封禁通知间隔 316 | notify_player_interval: 10 317 | # 是否破坏红石装置 318 | destroy_block: true 319 | 320 | # 非法玩家限制 321 | illegal_player_limit: 322 | # 是否开启 323 | enabled: false 324 | # 下线就取消OP 325 | deop_on_quit: true 326 | # 下线就设置为生存模式 327 | set_survival_mode_on_quit: true 328 | # 创造模式白名单 329 | creative_mode_whitelist: 330 | - 'July_ss' 331 | op_whitelist: 332 | - 'July_ss' 333 | 334 | # 农作物防踩踏 335 | crop_trample_limit: 336 | # 是否开启 337 | enabled: true 338 | # 世界集 339 | world_set: 340 | # 包含的世界正则表达式 341 | includes: 342 | - '.*' 343 | # 排除的世界正则表达式 344 | excludes: 345 | - 'excluded_world' 346 | 347 | # 火蔓延限制 348 | fire_spread_limit: 349 | enabled: true 350 | # 世界集 351 | world_set: 352 | # 包含的世界正则表达式 353 | includes: 354 | - '.*' 355 | # 排除的世界正则表达式 356 | excludes: 357 | - 'excluded_world' 358 | 359 | # 实体爆炸限制 360 | entity_explode_limit: 361 | # 是否开启 362 | enabled: true 363 | # 世界集 364 | world_set: 365 | # 包含的世界正则表达式 366 | includes: 367 | - '.*' 368 | # 排除的世界正则表达式 369 | excludes: 370 | - 'excluded_world' 371 | 372 | # 方块爆炸限制 373 | block_explode_limit: 374 | # 是否开启 375 | enabled: true 376 | # 世界集 377 | world_set: 378 | # 包含的世界正则表达式 379 | includes: 380 | - '.*' 381 | # 排除的世界正则表达式 382 | excludes: 383 | - 'excluded_world' 384 | 385 | # 聊天刷屏限制 386 | chat_spam_limit: 387 | # 是否开启 388 | enabled: true 389 | # 最短发言间隔限制 390 | threshold: 3000 391 | 392 | # 聊天黑名单 393 | chat_blacklist: 394 | # 是否开启 395 | enabled: true 396 | # 黑名单正则表达式 397 | blacklist: 398 | - '傻逼' 399 | - 'nmsl' 400 | - '[草操艹]你[吗妈]' 401 | # 替换字符串 402 | replace_string: '你是靓仔' 403 | # 是否直接取消发送 404 | cancel_event: false 405 | 406 | # 命令限制 407 | command_limit: 408 | # 是否开启 409 | enabled: true 410 | # 命令集 411 | command_set: 412 | # 包含的命令正则表达式 413 | includes: 414 | - '/minecraft:.*' 415 | # 排除的命令正则表达式 416 | excludes: {} 417 | 418 | # 命令使用间隔限制 419 | command_spam_limit: 420 | # 是否开启 421 | enabled: true 422 | # 阈值 423 | threshold: 1000 -------------------------------------------------------------------------------- /core/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: JulySafe 2 | author: July_ss 3 | version: ${version} 4 | main: com.github.julyss2019.bukkit.julysafe.core.JulySafePlugin 5 | api-version: 1.13 6 | depend: 7 | - VoidFramework-Bukkit -------------------------------------------------------------------------------- /example-groovy-scripts/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compileOnly project(':api') 3 | compileOnly 'org.apache.groovy:groovy:4.0.19' 4 | } -------------------------------------------------------------------------------- /example-groovy-scripts/src/main/java/SimpleEntityFilter.groovy: -------------------------------------------------------------------------------- 1 | import com.github.julyss2019.bukkit.julysafe.api.EntityFilter 2 | import org.bukkit.entity.Entity 3 | 4 | class SimpleEntityFilter implements EntityFilter { 5 | @Override 6 | boolean filter(Entity entity) { 7 | def handle = entity.class.getMethod("getHandle").invoke(entity) 8 | def name = handle.getClass().name 9 | 10 | if (name.contains("Lobster") || name.contains("XXX")) { 11 | return true 12 | } 13 | 14 | return false 15 | } 16 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julyss2019/JulySafe/13ad11112ef114270d636c1ca3321145d91bc574/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'api' 2 | include 'core' 3 | include 'example-groovy-scripts' 4 | 5 | --------------------------------------------------------------------------------