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