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>
<includes>
<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>
</artifactSet>
<relocations>
@ -231,6 +234,21 @@
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</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>
<organization>
<name>TBMCPlugins</name>

View file

@ -11,6 +11,7 @@ import buttondevteam.core.component.towny.TownyComponent
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component.Companion.registerComponent
import buttondevteam.lib.architecture.ConfigData
import buttondevteam.lib.chat.Color
import buttondevteam.lib.chat.TBMCChatAPI
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.
*/
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
public override fun pluginEnable() {
instance = this
val pdf = description
setupPermissions()
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.")
saveConfig()
ConfigData.saveNow(config) // Run pending save tasks
registerComponent(this, RestartComponent())
registerComponent(this, ChannelComponent())
registerComponent(this, RandomTPComponent())
@ -102,7 +104,7 @@ class MainPlugin : ButtonPlugin() {
)
.also { Channel.adminChat = it })
TBMCChatAPI.registerChatChannel(Channel(
"§9MOD${ChatColor.WHITE}",
"${ChatColor.BLUE}MOD${ChatColor.WHITE}",
Color.Blue,
"mod",
Channel.inGroupFilter("mod")
@ -118,10 +120,10 @@ class MainPlugin : ButtonPlugin() {
) // TODO: Make groups configurable
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("§eYELLOW${ChatColor.WHITE}", Color.Yellow, "yellow"))
TBMCChatAPI.registerChatChannel(ChatRoom("§aGREEN${ChatColor.WHITE}", Color.Green, "green"))
TBMCChatAPI.registerChatChannel(ChatRoom("${ChatColor.YELLOW}YELLOW${ChatColor.WHITE}", Color.Yellow, "yellow"))
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("§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() }
command2MC.addParamConverter(
OfflinePlayer::class.java,
@ -173,5 +175,7 @@ class MainPlugin : ButtonPlugin() {
@JvmField
var ess: Essentials? = null
val isInitialized get() = ::instance.isInitialized
}
}

View file

@ -92,8 +92,8 @@ public class RandomTP extends ICommand2MC
&& !newLocation())
{
//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("${ChatColor.RED} (sorry bud... I did try!)");
player.sendMessage("§ccould not find a location in 10,000 attempts");
player.sendMessage("§c (sorry bud... I did try!)");
return false;
}

View file

@ -1,176 +1,176 @@
package buttondevteam.lib;
package buttondevteam.lib
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.potato.DebugPotato;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import buttondevteam.core.MainPlugin
import buttondevteam.lib.architecture.Component
import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.player.ChromaGamerBase.Companion.registerPluginUserClass
import buttondevteam.lib.potato.DebugPotato
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.entity.Player
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;
import java.io.InputStream;
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;
object TBMCCoreAPI {
val coders: List<String> = listOf("Alisolarflare", "NorbiPeti", "iie", "thewindmillman", "mayskam1995")
public class TBMCCoreAPI {
static final List<String> coders = new ArrayList<String>() {
private static final long serialVersionUID = -4462159250738367334L;
@JvmStatic
@Throws(IOException::class)
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
}
{
add("Alisolarflare");
add("NorbiPeti");
add("iie");
add("thewindmillman");
add("mayskam1995");
}
};
private val exceptionsToSend = HashMap<String, Throwable>()
private val debugMessagesToSend: MutableList<String> = ArrayList()
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;
}
/**
* 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, component: Component<*>) {
SendException(sourcemsg, e, false) { message: String? -> component.logWarn(message!!) }
}
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) }
}
/**
* Send exception to the {@link 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
*/
public static void SendException(String sourcemsg, Throwable e, Component<?> component) {
SendException(sourcemsg, e, false, component::logWarn);
}
@JvmStatic
fun SendException(sourcemsg: String, e: Throwable, debugPotato: Boolean, logWarn: Consumer<String?>) {
try {
SendUnsentExceptions()
val event = TBMCExceptionEvent(sourcemsg, e)
Bukkit.getPluginManager().callEvent(event)
synchronized(exceptionsToSend) { if (!event.isHandled) exceptionsToSend[sourcemsg] = e }
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()
}
}
/**
* Send exception to the {@link 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
*/
public static void SendException(String sourcemsg, Throwable e, JavaPlugin plugin) {
SendException(sourcemsg, e, false, plugin.getLogger()::warning);
}
@JvmStatic
fun sendDebugMessage(debugMessage: String) {
SendUnsentDebugMessages()
val event = TBMCDebugMessageEvent(debugMessage)
Bukkit.getPluginManager().callEvent(event)
synchronized(debugMessagesToSend) { if (!event.isSent) debugMessagesToSend.add(debugMessage) }
}
public static void SendException(String sourcemsg, Throwable e, boolean debugPotato, Consumer<String> logWarn) {
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();
}
}
private var eventExceptionCoreHandler: EventExceptionCoreHandler? = null
public static void sendDebugMessage(String debugMessage) {
SendUnsentDebugMessages();
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(debugMessage);
Bukkit.getPluginManager().callEvent(event);
synchronized (debugMessagesToSend) {
if (!event.isSent())
debugMessagesToSend.add(debugMessage);
}
}
/**
* Registers Bukkit events, handling the exceptions occurring in those events
*
* @param listener The class that handles the events
* @param plugin The plugin which the listener belongs to
*/
@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
*
* @param listener The class that handles the events
* @param plugin The plugin which the listener belongs to
*/
public static void RegisterEventsForExceptions(Listener listener, Plugin plugin) {
if (eventExceptionCoreHandler == null) eventExceptionCoreHandler = new EventExceptionCoreHandler();
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler);
}
/**
* Send exceptions that haven't been sent (their events didn't get handled). This method is used by the DiscordPlugin's ready event
*/
@JvmStatic
fun 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!")
}
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) {
ChromaGamerBase.registerPluginUserClass(userclass, constructor);
}
@JvmStatic
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()
}
}
}
/**
* 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() {
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();
}
@JvmStatic
fun IsTestServer(): Boolean {
return MainPlugin.instance.test.get()
}
}

View file

@ -76,7 +76,10 @@ class ConfigData<T> internal constructor(
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 {
private val saveTasks = HashMap<Configuration, SaveTask>()
@ -85,10 +88,19 @@ class ConfigData<T> internal constructor(
val sa = config.saveAction
val root = cc.root
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
}
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) {
saveTasks.put(
root,
@ -108,7 +120,7 @@ class ConfigData<T> internal constructor(
synchronized(saveTasks) {
val st = saveTasks[config]
if (st != null) {
st.task.cancel()
st.task?.cancel()
saveTasks.remove(config)
st.saveAction.run()
return true

View file

@ -176,10 +176,11 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
bukkitCommand
} 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)
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
null
}
@ -302,7 +303,9 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
if (converter == null) {
val msg = "Could not find a suitable converter for type " + cl.simpleName
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 converter