Fix config/plugin lateinit error, convert API, color codes

- The MainPlugin.instance field wasn't initialized when the MainPlugin was being initialized (config), so delayed the config saving until it's actually running
- Also added library to support Kotlin coroutines in Bukkit but not using it yet
This commit is contained in:
Norbi Peti 2023-05-03 00:26:24 +02:00
parent d4aeb198a8
commit 6c70e8707d
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
6 changed files with 212 additions and 175 deletions

View file

@ -73,6 +73,9 @@
<artifactSet> <artifactSet>
<includes> <includes>
<include>me.lucko:commodore</include> <include>me.lucko:commodore</include>
<include>org.jetbrains.kotlin:kotlin-stdlib</include>
<include>com.github.shynixn.mccoroutine:mccoroutine-bukkit-api</include>
<include>com.github.shynixn.mccoroutine:mccoroutine-bukkit-core</include>
</includes> </includes>
</artifactSet> </artifactSet>
<relocations> <relocations>
@ -231,6 +234,21 @@
<artifactId>kotlin-stdlib</artifactId> <artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version> <version>${kotlin.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>com.github.shynixn.mccoroutine</groupId>
<artifactId>mccoroutine-bukkit-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.github.shynixn.mccoroutine</groupId>
<artifactId>mccoroutine-bukkit-core</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies> </dependencies>
<organization> <organization>
<name>TBMCPlugins</name> <name>TBMCPlugins</name>

View file

@ -11,6 +11,7 @@ import buttondevteam.core.component.towny.TownyComponent
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component.Companion.registerComponent import buttondevteam.lib.architecture.Component.Companion.registerComponent
import buttondevteam.lib.architecture.ConfigData
import buttondevteam.lib.chat.Color import buttondevteam.lib.chat.Color
import buttondevteam.lib.chat.TBMCChatAPI import buttondevteam.lib.chat.TBMCChatAPI
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
@ -55,13 +56,14 @@ class MainPlugin : ButtonPlugin() {
* If a Chroma command clashes with another plugin's command, this setting determines whether the Chroma command should be executed or the other plugin's. * If a Chroma command clashes with another plugin's command, this setting determines whether the Chroma command should be executed or the other plugin's.
*/ */
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false) val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
public override fun pluginEnable() { public override fun pluginEnable() {
instance = this instance = this
val pdf = description val pdf = description
setupPermissions() setupPermissions()
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials
logger.warning("No economy plugin found! Components using economy will not be registered.") logger.warning("No economy plugin found! Components using economy will not be registered.")
saveConfig() ConfigData.saveNow(config) // Run pending save tasks
registerComponent(this, RestartComponent()) registerComponent(this, RestartComponent())
registerComponent(this, ChannelComponent()) registerComponent(this, ChannelComponent())
registerComponent(this, RandomTPComponent()) registerComponent(this, RandomTPComponent())
@ -102,7 +104,7 @@ class MainPlugin : ButtonPlugin() {
) )
.also { Channel.adminChat = it }) .also { Channel.adminChat = it })
TBMCChatAPI.registerChatChannel(Channel( TBMCChatAPI.registerChatChannel(Channel(
"§9MOD${ChatColor.WHITE}", "${ChatColor.BLUE}MOD${ChatColor.WHITE}",
Color.Blue, Color.Blue,
"mod", "mod",
Channel.inGroupFilter("mod") Channel.inGroupFilter("mod")
@ -118,10 +120,10 @@ class MainPlugin : ButtonPlugin() {
) // TODO: Make groups configurable ) // TODO: Make groups configurable
TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.RED}RED${ChatColor.WHITE}", Color.DarkRed, "red")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.RED}RED${ChatColor.WHITE}", Color.DarkRed, "red"))
TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.GOLD}ORANGE${ChatColor.WHITE}", Color.Gold, "orange")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.GOLD}ORANGE${ChatColor.WHITE}", Color.Gold, "orange"))
TBMCChatAPI.registerChatChannel(ChatRoom("§eYELLOW${ChatColor.WHITE}", Color.Yellow, "yellow")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.YELLOW}YELLOW${ChatColor.WHITE}", Color.Yellow, "yellow"))
TBMCChatAPI.registerChatChannel(ChatRoom("§aGREEN${ChatColor.WHITE}", Color.Green, "green")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.GREEN}GREEN${ChatColor.WHITE}", Color.Green, "green"))
TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.AQUA}BLUE${ChatColor.WHITE}", Color.Blue, "blue")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.AQUA}BLUE${ChatColor.WHITE}", Color.Blue, "blue"))
TBMCChatAPI.registerChatChannel(ChatRoom("§5PURPLE${ChatColor.WHITE}", Color.DarkPurple, "purple")) TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.LIGHT_PURPLE}PURPLE${ChatColor.WHITE}", Color.DarkPurple, "purple"))
val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj -> obj.name }.asIterable() } val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj -> obj.name }.asIterable() }
command2MC.addParamConverter( command2MC.addParamConverter(
OfflinePlayer::class.java, OfflinePlayer::class.java,
@ -173,5 +175,7 @@ class MainPlugin : ButtonPlugin() {
@JvmField @JvmField
var ess: Essentials? = null var ess: Essentials? = null
val isInitialized get() = ::instance.isInitialized
} }
} }

View file

@ -92,8 +92,8 @@ public class RandomTP extends ICommand2MC
&& !newLocation()) && !newLocation())
{ {
//if unable to find new location, message player and return false //if unable to find new location, message player and return false
player.sendMessage("${ChatColor.RED} could not find a location in 10,000 attempts"); player.sendMessage("§ccould not find a location in 10,000 attempts");
player.sendMessage("${ChatColor.RED} (sorry bud... I did try!)"); player.sendMessage("§c (sorry bud... I did try!)");
return false; return false;
} }

View file

@ -1,123 +1,119 @@
package buttondevteam.lib; package buttondevteam.lib
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.potato.DebugPotato; import buttondevteam.lib.player.ChromaGamerBase.Companion.registerPluginUserClass
import org.bukkit.Bukkit; import buttondevteam.lib.potato.DebugPotato
import org.bukkit.entity.Player; import org.bukkit.Bukkit
import org.bukkit.event.Listener; import org.bukkit.ChatColor
import org.bukkit.plugin.Plugin; import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.event.Listener
import org.bukkit.plugin.Plugin
import org.bukkit.plugin.java.JavaPlugin
import java.io.IOException
import java.net.URL
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
import java.io.IOException; object TBMCCoreAPI {
import java.io.InputStream; val coders: List<String> = listOf("Alisolarflare", "NorbiPeti", "iie", "thewindmillman", "mayskam1995")
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class TBMCCoreAPI { @JvmStatic
static final List<String> coders = new ArrayList<String>() { @Throws(IOException::class)
private static final long serialVersionUID = -4462159250738367334L; fun DownloadString(urlstr: String?): String {
val url = URL(urlstr)
{ val con = url.openConnection()
add("Alisolarflare"); con.setRequestProperty("User-Agent", "TBMCPlugins")
add("NorbiPeti"); val `in` = con.getInputStream()
add("iie"); var encoding = con.contentEncoding
add("thewindmillman"); encoding = encoding ?: "UTF-8"
add("mayskam1995"); val s = Scanner(`in`).useDelimiter("\\A")
} val body = if (s.hasNext()) s.next() else ""
}; `in`.close()
return body
public static String DownloadString(String urlstr) throws IOException {
URL url = new URL(urlstr);
URLConnection con = url.openConnection();
con.setRequestProperty("User-Agent", "TBMCPlugins");
InputStream in = con.getInputStream();
String encoding = con.getContentEncoding();
encoding = encoding == null ? "UTF-8" : encoding;
Scanner s = new Scanner(in).useDelimiter("\\A");
String body = s.hasNext() ? s.next() : "";
in.close();
return body;
} }
private static final HashMap<String, Throwable> exceptionsToSend = new HashMap<>(); private val exceptionsToSend = HashMap<String, Throwable>()
private static final List<String> debugMessagesToSend = new ArrayList<>(); private val debugMessagesToSend: MutableList<String> = ArrayList()
/** /**
* Send exception to the {@link TBMCExceptionEvent}. * Send exception to the [TBMCExceptionEvent].
* *
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message) * @param sourcemsg A message that is shown at the top of the exception (before the exception's message)
* @param e The exception to send * @param e The exception to send
*/ */
public static void SendException(String sourcemsg, Throwable e, Component<?> component) { @JvmStatic
SendException(sourcemsg, e, false, component::logWarn); fun SendException(sourcemsg: String, e: Throwable, component: Component<*>) {
SendException(sourcemsg, e, false) { message: String? -> component.logWarn(message!!) }
} }
/** /**
* Send exception to the {@link TBMCExceptionEvent}. * Send exception to the [TBMCExceptionEvent].
* *
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message) * @param sourcemsg A message that is shown at the top of the exception (before the exception's message)
* @param e The exception to send * @param e The exception to send
*/ */
public static void SendException(String sourcemsg, Throwable e, JavaPlugin plugin) { @JvmStatic
SendException(sourcemsg, e, false, plugin.getLogger()::warning); fun SendException(sourcemsg: String, e: Throwable, plugin: JavaPlugin) {
SendException(sourcemsg, e, false) { msg: String? -> plugin.logger.warning(msg) }
} }
public static void SendException(String sourcemsg, Throwable e, boolean debugPotato, Consumer<String> logWarn) { @JvmStatic
fun SendException(sourcemsg: String, e: Throwable, debugPotato: Boolean, logWarn: Consumer<String?>) {
try { try {
SendUnsentExceptions(); SendUnsentExceptions()
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e); val event = TBMCExceptionEvent(sourcemsg, e)
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event)
synchronized (exceptionsToSend) { synchronized(exceptionsToSend) { if (!event.isHandled) exceptionsToSend[sourcemsg] = e }
if (!event.isHandled()) logWarn.accept(sourcemsg)
exceptionsToSend.put(sourcemsg, e); e.printStackTrace()
}
logWarn.accept(sourcemsg);
e.printStackTrace();
if (debugPotato) { if (debugPotato) {
List<Player> devsOnline = new ArrayList<>(); val devsOnline: MutableList<Player> = ArrayList()
for (Player player : Bukkit.getOnlinePlayers()) { for (player in Bukkit.getOnlinePlayers()) {
if (coders.contains(player.getName())) { if (coders.contains(player.name)) {
devsOnline.add(player); devsOnline.add(player)
} }
} }
if (!devsOnline.isEmpty()) { if (devsOnline.isNotEmpty()) {
DebugPotato potato = new DebugPotato() val potato = DebugPotato()
.setMessage(new String[]{ // .setMessage(
"${ChatColor.AQUA}§o" + e.getClass().getSimpleName(), // arrayOf( //
"${ChatColor.RED}§o" + sourcemsg, // "${ChatColor.AQUA}${ChatColor.ITALIC}" + e.javaClass.simpleName, //
"§a§oFind a dev to fix this issue"}) "${ChatColor.RED}${ChatColor.ITALIC}$sourcemsg", //
.setType(e instanceof IOException ? "Throwable Potato" "${ChatColor.GREEN}${ChatColor.ITALIC}Find a dev to fix this issue"
: e instanceof ClassCastException ? "Squished Potato" )
: e instanceof NullPointerException ? "Plain Potato" )
: e instanceof StackOverflowError ? "Chips" : "Error Potato"); .setType(
for (Player dev : devsOnline) { when (e) {
potato.Send(dev); is IOException -> "Throwable Potato"
is ClassCastException -> "Squished Potato"
is NullPointerException -> "Plain Potato"
is StackOverflowError -> "Chips"
else -> "Error Potato"
}
)
for (dev in devsOnline) {
potato.Send(dev)
} }
} }
} }
} catch (Exception ee) { } catch (ee: Exception) {
System.err.println("Failed to send exception!"); System.err.println("Failed to send exception!")
ee.printStackTrace(); ee.printStackTrace()
} }
} }
public static void sendDebugMessage(String debugMessage) { @JvmStatic
SendUnsentDebugMessages(); fun sendDebugMessage(debugMessage: String) {
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(debugMessage); SendUnsentDebugMessages()
Bukkit.getPluginManager().callEvent(event); val event = TBMCDebugMessageEvent(debugMessage)
synchronized (debugMessagesToSend) { Bukkit.getPluginManager().callEvent(event)
if (!event.isSent()) synchronized(debugMessagesToSend) { if (!event.isSent) debugMessagesToSend.add(debugMessage) }
debugMessagesToSend.add(debugMessage);
}
} }
private static EventExceptionCoreHandler eventExceptionCoreHandler; private var eventExceptionCoreHandler: EventExceptionCoreHandler? = null
/** /**
* Registers Bukkit events, handling the exceptions occurring in those events * Registers Bukkit events, handling the exceptions occurring in those events
@ -125,52 +121,56 @@ public class TBMCCoreAPI {
* @param listener The class that handles the events * @param listener The class that handles the events
* @param plugin The plugin which the listener belongs to * @param plugin The plugin which the listener belongs to
*/ */
public static void RegisterEventsForExceptions(Listener listener, Plugin plugin) { @JvmStatic
if (eventExceptionCoreHandler == null) eventExceptionCoreHandler = new EventExceptionCoreHandler(); fun RegisterEventsForExceptions(listener: Listener, plugin: Plugin) {
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler); if (eventExceptionCoreHandler == null) eventExceptionCoreHandler = EventExceptionCoreHandler()
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler)
} }
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass, Supplier<T> constructor) { @JvmStatic
ChromaGamerBase.registerPluginUserClass(userclass, constructor); fun <T : ChromaGamerBase> RegisterUserClass(userclass: Class<T>, constructor: Supplier<T>) {
registerPluginUserClass(userclass, constructor)
} }
/** /**
* Send exceptions that haven't been sent (their events didn't get handled). This method is used by the DiscordPlugin's ready event * Send exceptions that haven't been sent (their events didn't get handled). This method is used by the DiscordPlugin's ready event
*/ */
public static void SendUnsentExceptions() { @JvmStatic
synchronized (exceptionsToSend) { fun SendUnsentExceptions() {
if (exceptionsToSend.size() > 20) { synchronized(exceptionsToSend) {
exceptionsToSend.clear(); // Don't call more and more events if all the handler plugins are unloaded if (exceptionsToSend.size > 20) {
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!"); exceptionsToSend.clear() // Don't call more and more events if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!")
} }
for (Iterator<Entry<String, Throwable>> iterator = exceptionsToSend.entrySet().iterator(); iterator.hasNext(); ) { val iterator: MutableIterator<Map.Entry<String, Throwable>> = exceptionsToSend.entries.iterator()
Entry<String, Throwable> entry = iterator.next(); while (iterator.hasNext()) {
TBMCExceptionEvent event = new TBMCExceptionEvent(entry.getKey(), entry.getValue()); val (key, value) = iterator.next()
Bukkit.getPluginManager().callEvent(event); val event = TBMCExceptionEvent(key, value)
if (event.isHandled()) Bukkit.getPluginManager().callEvent(event)
iterator.remove(); if (event.isHandled) iterator.remove()
} }
} }
} }
public static void SendUnsentDebugMessages() { @JvmStatic
synchronized (debugMessagesToSend) { fun SendUnsentDebugMessages() {
if (debugMessagesToSend.size() > 20) { synchronized(debugMessagesToSend) {
debugMessagesToSend.clear(); // Don't call more and more DebugMessages if all the handler plugins are unloaded if (debugMessagesToSend.size > 20) {
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!"); debugMessagesToSend.clear() // Don't call more and more DebugMessages if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!")
} }
for (Iterator<String> iterator = debugMessagesToSend.iterator(); iterator.hasNext(); ) { val iterator = debugMessagesToSend.iterator()
String message = iterator.next(); while (iterator.hasNext()) {
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(message); val message = iterator.next()
Bukkit.getPluginManager().callEvent(event); val event = TBMCDebugMessageEvent(message)
if (event.isSent()) Bukkit.getPluginManager().callEvent(event)
iterator.remove(); if (event.isSent) iterator.remove()
} }
} }
} }
public static boolean IsTestServer() { @JvmStatic
if (MainPlugin.instance == null) return true; fun IsTestServer(): Boolean {
return MainPlugin.instance.test.get(); return MainPlugin.instance.test.get()
} }
} }

View file

@ -76,7 +76,10 @@ class ConfigData<T> internal constructor(
signalChange(config) signalChange(config)
} }
private class SaveTask(val task: BukkitTask, val saveAction: Runnable) /**
* @param task The running task, if it was scheduled
*/
private class SaveTask(val task: BukkitTask?, val saveAction: Runnable)
companion object { companion object {
private val saveTasks = HashMap<Configuration, SaveTask>() private val saveTasks = HashMap<Configuration, SaveTask>()
@ -85,10 +88,19 @@ class ConfigData<T> internal constructor(
val sa = config.saveAction val sa = config.saveAction
val root = cc.root val root = cc.root
if (root == null) { if (root == null) {
if (MainPlugin.isInitialized) {
MainPlugin.instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}") MainPlugin.instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}")
} else {
println("Attempted to save config with no root! Name: ${config.config.name}")
}
return return
} }
if (!saveTasks.containsKey(cc.root)) { if (!MainPlugin.isInitialized) {
// If the plugin isn't initilized, we can't schedule a task - do it when the plugin is enabled
synchronized(saveTasks) {
saveTasks.put(root, SaveTask(null, sa))
}
} else if (!saveTasks.containsKey(cc.root)) {
synchronized(saveTasks) { synchronized(saveTasks) {
saveTasks.put( saveTasks.put(
root, root,
@ -108,7 +120,7 @@ class ConfigData<T> internal constructor(
synchronized(saveTasks) { synchronized(saveTasks) {
val st = saveTasks[config] val st = saveTasks[config]
if (st != null) { if (st != null) {
st.task.cancel() st.task?.cancel()
saveTasks.remove(config) saveTasks.remove(config)
st.saveAction.run() st.saveAction.run()
return true return true

View file

@ -176,10 +176,11 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand) TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
bukkitCommand bukkitCommand
} catch (e: Exception) { } catch (e: Exception) {
if (command.component == null) val component = command.component
if (component == null)
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.plugin) TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.plugin)
else else
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.component) TBMCCoreAPI.SendException("Failed to register command in command map!", e, component)
shouldRegisterOfficially = false shouldRegisterOfficially = false
null null
} }
@ -302,7 +303,9 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
if (converter == null) { if (converter == null) {
val msg = "Could not find a suitable converter for type " + cl.simpleName val msg = "Could not find a suitable converter for type " + cl.simpleName
val exception: Exception = NullPointerException("converter is null") val exception: Exception = NullPointerException("converter is null")
if (command2MC.component == null) TBMCCoreAPI.SendException(msg, exception, command2MC.plugin) else TBMCCoreAPI.SendException(msg, exception, command2MC.component) val component = command2MC.component
if (component == null) TBMCCoreAPI.SendException(msg, exception, command2MC.plugin)
else TBMCCoreAPI.SendException(msg, exception, component)
return null return null
} }
return converter return converter