Player, config and player data improvements

This commit is contained in:
Norbi Peti 2023-04-17 03:11:36 +02:00
parent 8104b1a8d4
commit 42fd0dfeac
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
15 changed files with 383 additions and 463 deletions

View file

@ -80,14 +80,14 @@ class MainPlugin : ButtonPlugin() {
ChromaGamerBase.addConverter { commandSender: CommandSender -> ChromaGamerBase.addConverter { commandSender: CommandSender ->
Optional.ofNullable( Optional.ofNullable(
if (commandSender is ConsoleCommandSender || commandSender is BlockCommandSender) if (commandSender is ConsoleCommandSender || commandSender is BlockCommandSender)
TBMCPlayer.getPlayer(UUID(0, 0), TBMCPlayer::class.java) TBMCPlayerBase.getPlayer(UUID(0, 0), TBMCPlayer::class.java)
else null else null
) )
} }
//Players, has higher priority //Players, has higher priority
ChromaGamerBase.addConverter { sender: CommandSender -> ChromaGamerBase.addConverter { sender: CommandSender ->
Optional.ofNullable( Optional.ofNullable(
if (sender is Player) TBMCPlayer.getPlayer(sender.uniqueId, TBMCPlayer::class.java) else null if (sender is Player) TBMCPlayerBase.getPlayer(sender.uniqueId, TBMCPlayer::class.java) else null
) )
} }
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase::class.java) { TBMCPlayer() } TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase::class.java) { TBMCPlayer() }
@ -114,7 +114,7 @@ class MainPlugin : ButtonPlugin() {
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,
{ name -> Bukkit.getOfflinePlayer(name) }, { name -> @Suppress("DEPRECATION") Bukkit.getOfflinePlayer(name) },
"Player not found!", "Player not found!",
playerSupplier playerSupplier
) )
@ -125,13 +125,13 @@ class MainPlugin : ButtonPlugin() {
playerSupplier playerSupplier
) )
if (server.pluginManager.isPluginEnabled("Essentials")) ess = getPlugin(Essentials::class.java) if (server.pluginManager.isPluginEnabled("Essentials")) ess = getPlugin(Essentials::class.java)
logger!!.info(pdf.name + " has been Enabled (V." + pdf.version + ") Test: " + test.get() + ".") logger.info(pdf.name + " has been Enabled (V." + pdf.version + ") Test: " + test.get() + ".")
} }
public override fun pluginDisable() { public override fun pluginDisable() {
logger!!.info("Saving player data...") logger.info("Saving player data...")
ChromaGamerBase.saveUsers() ChromaGamerBase.saveUsers()
logger!!.info("Player data saved.") logger.info("Player data saved.")
} }
private fun setupPermissions(): Boolean { private fun setupPermissions(): Boolean {

View file

@ -87,7 +87,7 @@ class PlayerListener(val plugin: MainPlugin) : Listener {
@EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest @EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest
fun onPlayerChat(event: AsyncPlayerChatEvent) { fun onPlayerChat(event: AsyncPlayerChatEvent) {
if (event.isCancelled) return //The chat plugin should cancel it after this handler if (event.isCancelled) return //The chat plugin should cancel it after this handler
val cp = TBMCPlayer.getPlayer(event.player.uniqueId, TBMCPlayer::class.java) val cp = TBMCPlayerBase.getPlayer(event.player.uniqueId, TBMCPlayer::class.java)
TBMCChatAPI.SendChatMessage(ChatMessage.builder(event.player, cp, event.message).build()) TBMCChatAPI.SendChatMessage(ChatMessage.builder(event.player, cp, event.message).build())
//Not cancelling the original event here, it's cancelled in the chat plugin //Not cancelling the original event here, it's cancelled in the chat plugin
//This way other plugins can deal with the MC formatting if the chat plugin isn't present, but other platforms still get the message //This way other plugins can deal with the MC formatting if the chat plugin isn't present, but other platforms still get the message

View file

@ -1,86 +1,95 @@
package buttondevteam.core.component.restart; package buttondevteam.core.component.restart
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget.Companion.add
import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget.Companion.remove
import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.Component
import buttondevteam.lib.chat.IFakePlayer; import buttondevteam.lib.architecture.ComponentMetadata
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.IFakePlayer
import lombok.Getter; import buttondevteam.lib.chat.TBMCChatAPI
import org.bukkit.Bukkit; import lombok.Getter
import org.bukkit.ChatColor; import org.bukkit.Bukkit
import org.bukkit.event.EventHandler; import org.bukkit.ChatColor
import org.bukkit.event.Listener; import org.bukkit.event.EventHandler
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerQuitEvent
import java.time.ZoneId; import java.time.ZoneId
import java.time.ZoneOffset; import java.time.ZoneOffset
import java.time.ZonedDateTime; import java.time.ZonedDateTime
/** /**
* Provides commands such as /schrestart (restart after a countdown) and /primerestart (restart when nobody is online). * Provides commands such as /schrestart (restart after a countdown) and /primerestart (restart when nobody is online).
* Also can automatically restart at a given time. * Also can automatically restart at a given time.
*/ */
@ComponentMetadata(enabledByDefault = false) @ComponentMetadata(enabledByDefault = false)
public class RestartComponent extends Component<MainPlugin> implements Listener { class RestartComponent : Component<MainPlugin>(), Listener {
@Override public override fun enable() {
public void enable() { val scheduledRestartCommand = ScheduledRestartCommand(this)
var scheduledRestartCommand = new ScheduledRestartCommand(this); registerCommand(scheduledRestartCommand)
registerCommand(scheduledRestartCommand); registerCommand(PrimeRestartCommand(this))
registerCommand(new PrimeRestartCommand(this)); registerListener(this)
registerListener(this); restartBroadcast = add("restartCountdown")
restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown"); val restartAt = restartAt.get()
if (restartAt < 0) return
val restart = syncStart(restartAt)
log("Scheduled restart " + restart / 3600.0 / 20.0 + " hours from now")
Bukkit.getScheduler().runTaskLater(
plugin,
Runnable { scheduledRestartCommand.def(Bukkit.getConsoleSender(), 0) },
restart.toLong()
)
}
int restartAt = this.restartAt.get(); public override fun disable() {
if (restartAt < 0) return; remove(restartBroadcast)
int restart = syncStart(restartAt); }
log("Scheduled restart " + (restart / 3600. / 20.) + " hours from now");
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> scheduledRestartCommand.def(Bukkit.getConsoleSender(), 0), restart);
}
@Override /**
public void disable() { * Specifies the hour of day when the server should be restarted. Set to -1 to disable.
TBMCSystemChatEvent.BroadcastTarget.remove(restartBroadcast); */
} private val restartAt = config.getData("restartAt", 12)
private var lasttime: Long = 0
/** @Getter
* Specifies the hour of day when the server should be restarted. Set to -1 to disable. private var restartBroadcast: BroadcastTarget? = null
*/ private fun syncStart(hour: Int): Int {
private final ConfigData<Integer> restartAt = getConfig().getData("restartAt", 12); val now = ZonedDateTime.now(ZoneId.ofOffset("", ZoneOffset.UTC))
val secs = now.hour * 3600 + now.minute * 60 + now.second
var diff = secs - hour * 3600
if (diff < 0) {
diff += 24 * 3600
}
val count = diff / (24 * 3600)
val intervalPart = diff - count * 24 * 3600
val remaining = 24 * 3600 - intervalPart
return remaining * 20
}
private long lasttime = 0; @EventHandler
@Getter fun onPlayerLeave(event: PlayerQuitEvent) {
private TBMCSystemChatEvent.BroadcastTarget restartBroadcast; if (PrimeRestartCommand.isPlsrestart()
&& !event.quitMessage.equals("Server closed", ignoreCase = true)
private int syncStart(int hour) { && !event.quitMessage.equals("Server is restarting", ignoreCase = true)
var now = ZonedDateTime.now(ZoneId.ofOffset("", ZoneOffset.UTC)); ) {
int secs = now.getHour() * 3600 + now.getMinute() * 60 + now.getSecond(); if (Bukkit.getOnlinePlayers().size <= 1) {
int diff = secs - hour * 3600; if (PrimeRestartCommand.isLoud()) TBMCChatAPI.SendSystemMessage(
if (diff < 0) { Channel.globalChat,
diff += 24 * 3600; Channel.RecipientTestResult.ALL,
} "§cNobody is online anymore. Restarting.",
int count = diff / (24 * 3600); restartBroadcast
int intervalPart = diff - count * 24 * 3600; )
int remaining = 24 * 3600 - intervalPart; Bukkit.spigot().restart()
return remaining * 20; } else if (event.player !is IFakePlayer && System.nanoTime() - 10 * 60 * 1000000000L - lasttime > 0) { //10 minutes passed since last reminder
} lasttime = System.nanoTime()
if (PrimeRestartCommand.isLoud()) TBMCChatAPI.SendSystemMessage(
@EventHandler Channel.globalChat,
public void onPlayerLeave(PlayerQuitEvent event) { Channel.RecipientTestResult.ALL,
if (PrimeRestartCommand.isPlsrestart() ChatColor.DARK_RED.toString() + "The server will restart as soon as nobody is online.",
&& !event.getQuitMessage().equalsIgnoreCase("Server closed") restartBroadcast
&& !event.getQuitMessage().equalsIgnoreCase("Server is restarting")) { )
if (Bukkit.getOnlinePlayers().size() <= 1) { }
if (PrimeRestartCommand.isLoud()) }
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast); }
Bukkit.spigot().restart();
} else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 60 * 1000000000L - lasttime > 0) { //10 minutes passed since last reminder
lasttime = System.nanoTime();
if (PrimeRestartCommand.isLoud())
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast);
}
}
}
} }

View file

@ -18,7 +18,7 @@ import javax.annotation.Nullable;
@Getter @Getter
public class TBMCChatEvent extends TBMCChatEventBase { public class TBMCChatEvent extends TBMCChatEventBase {
public TBMCChatEvent(Channel channel, ChatMessage cm, Channel.RecipientTestResult rtr) { public TBMCChatEvent(Channel channel, ChatMessage cm, Channel.RecipientTestResult rtr) {
super(channel, cm.getMessage(), rtr.score, rtr.groupID); super(channel, cm.message, rtr.score, rtr.groupID);
this.cm = cm; this.cm = cm;
} }
@ -28,7 +28,7 @@ public class TBMCChatEvent extends TBMCChatEventBase {
private ChatMessage cm; private ChatMessage cm;
private boolean isIgnoreSenderPermissions() { private boolean isIgnoreSenderPermissions() {
return cm.getPermCheck() != cm.getSender(); return cm.getPermCheck() != cm.sender;
} }
/** /**
@ -36,9 +36,9 @@ public class TBMCChatEvent extends TBMCChatEventBase {
*/ */
@Override @Override
public boolean shouldSendTo(CommandSender sender) { public boolean shouldSendTo(CommandSender sender) {
if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) if (isIgnoreSenderPermissions() && sender.equals(this.cm.sender))
return true; //Allow sending the message no matter what return true; //Allow sending the message no matter what
return super.shouldSendTo(sender); return super.shouldSendTo(sender);
} }
/** /**
@ -46,9 +46,9 @@ public class TBMCChatEvent extends TBMCChatEventBase {
*/ */
@Override @Override
public int getMCScore(CommandSender sender) { public int getMCScore(CommandSender sender) {
if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) if (isIgnoreSenderPermissions() && sender.equals(this.cm.sender))
return getScore(); //Send in the correct group no matter what return getScore(); //Send in the correct group no matter what
return super.getMCScore(sender); return super.getMCScore(sender);
} }
/** /**
@ -57,9 +57,9 @@ public class TBMCChatEvent extends TBMCChatEventBase {
@Nullable @Nullable
@Override @Override
public String getGroupID(CommandSender sender) { public String getGroupID(CommandSender sender) {
if (isIgnoreSenderPermissions() && sender.equals(this.cm.getSender())) if (isIgnoreSenderPermissions() && sender.equals(this.cm.sender))
return getGroupID(); //Send in the correct group no matter what return getGroupID(); //Send in the correct group no matter what
return super.getGroupID(sender); return super.getGroupID(sender);
} }
@Override @Override

View file

@ -1,74 +1,59 @@
package buttondevteam.lib; package buttondevteam.lib
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel
import lombok.AccessLevel; import org.bukkit.event.HandlerList
import lombok.Getter; import java.util.*
import lombok.RequiredArgsConstructor; import java.util.stream.Stream
import lombok.val;
import org.bukkit.command.CommandSender;
import org.bukkit.event.HandlerList;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Stream;
/** /**
* Make sure to only send the message to users who {@link #shouldSendTo(CommandSender)} returns true. * Make sure to only send the message to users who [.shouldSendTo] returns true.
* *
* @author NorbiPeti * @author NorbiPeti
* */ // TODO: Rich message
*/ class TBMCSystemChatEvent(
@Getter channel: Channel,
public class TBMCSystemChatEvent extends TBMCChatEventBase { message: String,
private final String[] exceptions; score: Int,
private final BroadcastTarget target; groupid: String,
private boolean handled; val exceptions: Array<out String>,
val target: BroadcastTarget
) : TBMCChatEventBase(channel, message, score, groupid) {
var isHandled = false
public void setHandled() { override fun getHandlers(): HandlerList {
handled = true; return handlerList
} }
public TBMCSystemChatEvent(Channel channel, String message, int score, String groupid, String[] exceptions, BroadcastTarget target) { // TODO: Rich message class BroadcastTarget private constructor(val name: String) {
super(channel, message, score, groupid);
this.exceptions = exceptions;
this.target = target;
}
private static final HandlerList handlers = new HandlerList(); companion object {
private val targets = HashSet<BroadcastTarget?>()
val ALL = BroadcastTarget("ALL")
@Override @JvmStatic
public HandlerList getHandlers() { fun add(name: String): BroadcastTarget {
return handlers; val bt = BroadcastTarget(Objects.requireNonNull(name))
} targets.add(bt)
return bt
}
public static HandlerList getHandlerList() { @JvmStatic
return handlers; fun remove(target: BroadcastTarget?) {
} targets.remove(target)
}
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) operator fun get(name: String?): BroadcastTarget? {
public static class BroadcastTarget { return targets.stream().filter { bt: BroadcastTarget? -> bt!!.name.equals(name, ignoreCase = true) }
private final @Getter String name; .findAny().orElse(null)
private static final HashSet<BroadcastTarget> targets = new HashSet<>(); }
public static final BroadcastTarget ALL = new BroadcastTarget("ALL");
public static BroadcastTarget add(String name) { fun stream(): Stream<BroadcastTarget?> {
val bt = new BroadcastTarget(Objects.requireNonNull(name)); return targets.stream()
targets.add(bt); }
return bt; }
} }
public static void remove(BroadcastTarget target) { companion object {
targets.remove(target); val handlerList = HandlerList()
} }
@Nullable
public static BroadcastTarget get(String name) {
return targets.stream().filter(bt -> bt.name.equalsIgnoreCase(name)).findAny().orElse(null);
}
public static Stream<BroadcastTarget> stream() {
return targets.stream();
}
}
} }

View file

@ -10,7 +10,7 @@ import java.util.function.Function
/** /**
* Use the getter/setter constructor if [T] isn't a primitive type or String.<br></br> * Use the getter/setter constructor if [T] isn't a primitive type or String.<br></br>
* Use [Component.getConfig] or [ButtonPlugin.getIConfig] then [IHaveConfig.getData] to get an instance. * Use [Component.config] or [ButtonPlugin.iConfig] then [IHaveConfig.getData] to get an instance.
* @param config May be null for testing * @param config May be null for testing
* @param getter The parameter is of a primitive type as returned by [Configuration.get] * @param getter The parameter is of a primitive type as returned by [Configuration.get]
* @param setter The result should be a primitive type or string that can be retrieved correctly later * @param setter The result should be a primitive type or string that can be retrieved correctly later
@ -40,10 +40,6 @@ class ConfigData<T> internal constructor(
return "ConfigData{path='$path', value=$value}" return "ConfigData{path='$path', value=$value}"
} }
override fun reset() {
value = null
}
override fun get(): T { override fun get(): T {
val cachedValue = value val cachedValue = value
if (cachedValue != null) return cachedValue //Speed things up if (cachedValue != null) return cachedValue //Speed things up
@ -96,7 +92,7 @@ class ConfigData<T> internal constructor(
synchronized(saveTasks) { synchronized(saveTasks) {
saveTasks.put( saveTasks.put(
root, root,
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.instance, { SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.instance, Runnable {
synchronized(saveTasks) { synchronized(saveTasks) {
saveTasks.remove(root) saveTasks.remove(root)
sa.run() sa.run()

View file

@ -1,16 +1,9 @@
package buttondevteam.lib.architecture package buttondevteam.lib.architecture
import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.config.IConfigData import buttondevteam.lib.architecture.config.IConfigData
import org.bukkit.Bukkit
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
import org.bukkit.plugin.java.JavaPlugin
import java.lang.reflect.InvocationTargetException
import java.util.* import java.util.*
import java.util.function.Function import java.util.function.Function
import java.util.function.Predicate
import java.util.stream.Collectors
/** /**
* A config system * A config system
@ -111,14 +104,6 @@ class IHaveConfig(
ConfigData.signalChange(this) ConfigData.signalChange(this)
} }
/**
* Clears all caches and loads everything from yaml.
*/
fun reset(config: ConfigurationSection?) { // TODO: Simply replace the object
this.config = config
datamap.forEach { (_, data) -> data.reset() }
}
companion object { companion object {
/** /**
* Generates the config YAML. * Generates the config YAML.
@ -127,84 +112,7 @@ class IHaveConfig(
* @param configMap The result from [Component.getConfigMap]. May be null. * @param configMap The result from [Component.getConfigMap]. May be null.
*/ */
fun pregenConfig(obj: Any, configMap: Map<String, IHaveConfig?>?) { fun pregenConfig(obj: Any, configMap: Map<String, IHaveConfig?>?) {
val ms = obj.javaClass.declaredMethods // TODO: The configs are generated by ConfigData on creation
for (m in ms) {
if (m.returnType.name != ConfigData::class.java.name) continue
val mName: String
run {
val name = m.name
val ind = name.lastIndexOf('$')
mName = if (ind == -1) name else name.substring(ind + 1)
}
try {
m.isAccessible = true
var configList: List<ConfigData<*>>
configList = if (m.parameterCount == 0) {
listOf(m.invoke(obj) as ConfigData<*>)
} else if (m.parameterCount == 1 && m.parameterTypes[0] == IHaveConfig::class.java) {
if (configMap == null) continue //Hope it will get called with the param later
configMap.entries.stream().map { (key, value): Map.Entry<String, IHaveConfig?> ->
try {
return@map m.invoke(obj, value) as ConfigData<*>
} catch (e: IllegalAccessException) {
val msg = "Failed to pregenerate $mName for $obj using config $key!"
if (obj is Component<*>) TBMCCoreAPI.SendException(
msg,
e,
obj
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(
msg,
e,
obj
) else TBMCCoreAPI.SendException(msg, e, false) { msg: String? ->
Bukkit.getLogger().warning(msg)
}
return@map null
} catch (e: InvocationTargetException) {
val msg = "Failed to pregenerate $mName for $obj using config $key!"
if (obj is Component<*>) TBMCCoreAPI.SendException(
msg,
e,
obj
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(
msg,
e,
obj
) else TBMCCoreAPI.SendException(msg, e, false) { msg: String? ->
Bukkit.getLogger().warning(msg)
}
return@map null
}
}
.filter(Predicate<ConfigData<Any?>> { obj: ConfigData<Any?>? -> Objects.nonNull(obj) })
.collect(Collectors.toList())
} else {
if (TBMCCoreAPI.IsTestServer()) MainPlugin.instance.logger.warning(
"Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(
m.parameterTypes
)
)
continue
}
for (c in configList) {
if (c.path.length == 0) c.setPath(mName) else if (c.path != mName) MainPlugin.instance.logger.warning(
"Config name does not match: " + c.path + " instead of " + mName
)
c.get() //Saves the default value if needed - also checks validity
}
} catch (e: Exception) {
val msg = "Failed to pregenerate $mName for $obj!"
if (obj is Component<*>) TBMCCoreAPI.SendException(
msg,
e,
obj
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(msg, e, obj) else TBMCCoreAPI.SendException(
msg,
e,
false
) { msg: String? -> Bukkit.getLogger().warning(msg) }
}
}
} }
} }
} }

View file

@ -19,10 +19,6 @@ class ListConfigData<T> internal constructor(
override val path: String get() = listConfig.path override val path: String get() = listConfig.path
override fun reset() {
listConfig.reset()
}
override fun get(): List { override fun get(): List {
return listConfig.get() return listConfig.get()
} }

View file

@ -1,7 +1,6 @@
package buttondevteam.lib.architecture.config package buttondevteam.lib.architecture.config
interface IConfigData<T> { interface IConfigData<T> {
fun reset()
fun get(): T? fun get(): T?
fun set(value: T?) fun set(value: T?)

View file

@ -1,57 +1,76 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase
import lombok.Builder; import org.bukkit.command.CommandSender
import lombok.Getter; import java.util.*
import lombok.NonNull;
import lombok.Setter;
import org.bukkit.command.CommandSender;
@Builder class ChatMessage internal constructor(
@Getter /**
public class ChatMessage { * The sender which sends the message.
/** */
* The sender which sends the message. val sender: CommandSender,
*/ /**
private final CommandSender sender; * The Chroma user which sends the message.
/** */
* The Chroma user which sends the message. val user: ChromaGamerBase,
*/ /**
private final ChromaGamerBase user; * The message to send as the user.
/** */
* The message to send as the user. var message: String,
*/ /**
@Setter * Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord.
private String message; */
/** val isFromCommand: Boolean,
* Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord. /**
*/ * The sender which we should check for permissions. Same as [.sender] by default.
private boolean fromCommand; */
/** val permCheck: CommandSender,
* The sender which we should check for permissions. Same as {@link #sender} by default. /**
*/ * The origin of the message, "Minecraft" or "Discord" for example. May be displayed to the user.<br></br>
private CommandSender permCheck; * **This is the user class capitalized folder name by default.**
/** */
* The origin of the message, "Minecraft" or "Discord" for example. May be displayed to the user.<br> val origin: String
* <b>This is the user class capitalized folder name.</b> ) {
*/
private final String origin;
/** class ChatMessageBuilder internal constructor(
* The sender which we should check for permissions. Same as {@link #sender} by default. private var sender: CommandSender,
* private var user: ChromaGamerBase,
* @return The perm check or the sender private var message: String,
*/ private var origin: String
public CommandSender getPermCheck() { ) {
return permCheck == null ? sender : permCheck; private var fromCommand = false
} private var permCheck: CommandSender? = null
private static ChatMessageBuilder builder() { fun fromCommand(fromCommand: Boolean): ChatMessageBuilder {
return new ChatMessageBuilder(); this.fromCommand = fromCommand
} return this
}
@NonNull fun permCheck(permCheck: CommandSender): ChatMessageBuilder {
public static ChatMessageBuilder builder(CommandSender sender, ChromaGamerBase user, String message) { this.permCheck = permCheck
return builder().sender(sender).user(user).message(message).origin(user.getFolder().substring(0, 1).toUpperCase() + user.getFolder().substring(1)); return this
} }
fun origin(origin: String): ChatMessageBuilder {
this.origin = origin
return this
}
fun build(): ChatMessage {
return ChatMessage(sender, user, message, fromCommand, permCheck ?: sender, origin)
}
override fun toString(): String {
return "ChatMessage.ChatMessageBuilder(sender=$sender, user=$user, message=$message, fromCommand=$fromCommand, permCheck=$permCheck, origin=$origin)"
}
}
companion object {
fun builder(sender: CommandSender, user: ChromaGamerBase, message: String): ChatMessageBuilder {
return ChatMessageBuilder(
sender, user, message,
user.folder.substring(0, 1).uppercase(Locale.getDefault()) + user.folder.substring(1)
)
}
}
} }

View file

@ -1,28 +1,19 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel
import lombok.Getter; import org.bukkit.command.CommandSender
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
@RequiredArgsConstructor class Command2MCSender(val sender: CommandSender, val channel: Channel, val permCheck: CommandSender) : Command2Sender {
public class Command2MCSender implements Command2Sender {
private @Getter final CommandSender sender;
private @Getter final Channel channel;
private @Getter final CommandSender permCheck;
@Override override fun sendMessage(message: String) {
public void sendMessage(String message) { sender.sendMessage(message)
sender.sendMessage(message); }
}
@Override override fun sendMessage(message: Array<String>) {
public void sendMessage(String[] message) { sender.sendMessage(*message)
sender.sendMessage(message); }
}
@Override override fun getName(): String {
public String getName() { return sender.name
return sender.getName(); }
}
} }

View file

@ -1,97 +1,99 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel
import buttondevteam.core.component.channel.Channel.RecipientTestResult; import buttondevteam.core.component.channel.Channel.Companion.channelList
import buttondevteam.lib.ChromaUtils; import buttondevteam.core.component.channel.Channel.Companion.registerChannel
import buttondevteam.lib.TBMCChatEvent; import buttondevteam.core.component.channel.Channel.RecipientTestResult
import buttondevteam.lib.TBMCChatPreprocessEvent; import buttondevteam.lib.ChromaUtils.callEventAsync
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.ChromaUtils.doItAsync
import lombok.val; import buttondevteam.lib.TBMCChatEvent
import org.bukkit.Bukkit; import buttondevteam.lib.TBMCChatPreprocessEvent
import org.bukkit.command.CommandSender; import buttondevteam.lib.TBMCSystemChatEvent
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import java.util.*
import java.util.function.Supplier
import java.util.Arrays; object TBMCChatAPI {
import java.util.function.Supplier; /**
* Sends a chat message to Minecraft. Make sure that the channel is registered with [.RegisterChatChannel].<br></br>
* This will also send the error message to the sender, if they can't send the message.
*
* @param cm The message to send
* @param channel The MC channel to send in
* @return The event cancelled state
*/
/**
* Sends a chat message to Minecraft. Make sure that the channel is registered with [.RegisterChatChannel].<br></br>
* This will also send the error message to the sender, if they can't send the message.
*
* @param cm The message to send
* @return The event cancelled state
*/
@JvmOverloads
fun SendChatMessage(cm: ChatMessage, channel: Channel = cm.user.channel.get()): Boolean {
if (!channelList.contains(channel)) throw RuntimeException(
"Channel " + channel.displayName.get() + " not registered!"
)
if (!channel.isEnabled.get()) {
cm.sender.sendMessage("§cThe channel '" + channel.displayName.get() + "' is disabled!")
return true //Cancel sending if channel is disabled
}
val task = Supplier {
val permcheck = cm.getPermCheck()
val rtr = getScoreOrSendError(channel, permcheck)
val score = rtr.score
if (score == Channel.SCORE_SEND_NOPE || rtr.groupID == null) return@Supplier true
val eventPre = TBMCChatPreprocessEvent(cm.sender, channel, cm.message)
Bukkit.getPluginManager().callEvent(eventPre)
if (eventPre.isCancelled) return@Supplier true
cm.message = eventPre.message
val event = TBMCChatEvent(channel, cm, rtr)
Bukkit.getPluginManager().callEvent(event)
event.isCancelled
}
return doItAsync(task, false) //Not cancelled if async
}
public class TBMCChatAPI { /**
/** * Sends a regular message to Minecraft. Make sure that the channel is registered with [.RegisterChatChannel].
* Sends a chat message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.<br> *
* This will also send the error message to the sender, if they can't send the message. * @param channel The channel to send to
* * @param rtr The score&group to use to find the group - use [RecipientTestResult.ALL] if the channel doesn't have scores
* @param cm The message to send * @param message The message to send
* @return The event cancelled state * @param exceptions Platforms where this message shouldn't be sent (same as [ChatMessage.getOrigin]
*/ * @return The event cancelled state
public static boolean SendChatMessage(ChatMessage cm) { */
return SendChatMessage(cm, cm.getUser().channel.get()); @JvmStatic
} fun SendSystemMessage(
channel: Channel,
rtr: RecipientTestResult,
message: String,
target: BroadcastTarget?,
vararg exceptions: String
): Boolean {
if (!channelList.contains(channel)) throw RuntimeException("Channel " + channel.displayName.get() + " not registered!")
if (!channel.isEnabled.get()) return true //Cancel sending
if (!exceptions.contains("Minecraft")) Bukkit.getConsoleSender()
.sendMessage("[" + channel.displayName.get() + "] " + message)
val event = TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID!!, exceptions, target!!)
return callEventAsync(event)
}
/** private fun getScoreOrSendError(channel: Channel, sender: CommandSender): RecipientTestResult {
* Sends a chat message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.<br> val result = channel.getRTR(sender)
* This will also send the error message to the sender, if they can't send the message. if (result.errormessage != null) sender.sendMessage("§c" + result.errormessage)
* return result
* @param cm The message to send }
* @param channel The MC channel to send in
* @return The event cancelled state
*/
public static boolean SendChatMessage(ChatMessage cm, Channel channel) {
if (!Channel.getChannelList().contains(channel))
throw new RuntimeException("Channel " + channel.getDisplayName().get() + " not registered!");
if (!channel.isEnabled.get()) {
cm.getSender().sendMessage("§cThe channel '" + channel.displayName.get() + "' is disabled!");
return true; //Cancel sending if channel is disabled
}
Supplier<Boolean> task = () -> {
val permcheck = cm.getPermCheck();
RecipientTestResult rtr = getScoreOrSendError(channel, permcheck);
int score = rtr.score;
if (score == Channel.SCORE_SEND_NOPE || rtr.groupID == null)
return true;
TBMCChatPreprocessEvent eventPre = new TBMCChatPreprocessEvent(cm.getSender(), channel, cm.getMessage());
Bukkit.getPluginManager().callEvent(eventPre);
if (eventPre.isCancelled())
return true;
cm.setMessage(eventPre.getMessage());
TBMCChatEvent event;
event = new TBMCChatEvent(channel, cm, rtr);
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
};
return ChromaUtils.doItAsync(task, false); //Not cancelled if async
}
/** /**
* Sends a regular message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}. * Register a chat channel. See [Channel.Channel] for details.
* *
* @param channel The channel to send to * @param channel A new [Channel] to register
* @param rtr The score&group to use to find the group - use {@link RecipientTestResult#ALL} if the channel doesn't have scores */
* @param message The message to send @JvmStatic
* @param exceptions Platforms where this message shouldn't be sent (same as {@link ChatMessage#getOrigin()} fun RegisterChatChannel(channel: Channel) {
* @return The event cancelled state registerChannel(channel)
*/ }
public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message, TBMCSystemChatEvent.BroadcastTarget target, String... exceptions) {
if (!Channel.getChannelList().contains(channel))
throw new RuntimeException("Channel " + channel.displayName.get() + " not registered!");
if (!channel.enabled.get())
return true; //Cancel sending
if (!Arrays.asList(exceptions).contains("Minecraft"))
Bukkit.getConsoleSender().sendMessage("[" + channel.displayName.get() + "] " + message);
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions, target);
return ChromaUtils.callEventAsync(event);
}
private static RecipientTestResult getScoreOrSendError(Channel channel, CommandSender sender) {
RecipientTestResult result = channel.getRTR(sender);
if (result.errormessage != null)
sender.sendMessage("§c" + result.errormessage);
return result;
}
/**
* Register a chat channel. See {@link Channel#Channel(String, Color, String, java.util.function.Function)} for details.
*
* @param channel A new {@link Channel} to register
*/
public static void RegisterChatChannel(Channel channel) {
Channel.registerChannel(channel);
}
} }

View file

@ -13,14 +13,14 @@ import java.lang.reflect.Method
/** /**
* Deals with reading the commands.yml file from the plugin. The file is generated by ButtonProcessor at compile-time. * Deals with reading the commands.yml file from the plugin. The file is generated by ButtonProcessor at compile-time.
* Only used when registering commands. * Only used when registering commands.
*
* @param command The command object to use
*/ */
class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(command: TC) { class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(command: TC) {
private var commandConfig: ConfigurationSection? = null private var commandConfig: ConfigurationSection? = null
/** /**
* Read the yaml file for the given command class. * Read the yaml file for the given command class.
*
* @param command The command object to use
*/ */
init { init {
val commandClass = command.javaClass val commandClass = command.javaClass
@ -59,17 +59,23 @@ class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(comman
MainPlugin.instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.") MainPlugin.instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.")
return null return null
} }
val mname = cs.getString("method")
val params = cs.getString("params") fun fail(message: String): String? {
val i = TBMCCoreAPI.SendException(
mname.indexOf('(') //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful "Error while getting command data for $method!",
if (i != -1 && method.name == mname.substring(0, i) && params != null) { Exception(message),
MainPlugin.instance
)
return null
}
val mname = cs.getString("method") ?: return fail("Method is null")
val params = cs.getString("params") ?: return fail("Params is null")
//Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
val i = mname.indexOf('(')
if (i != -1 && method.name == mname.substring(0, i)) {
return params return params
} else TBMCCoreAPI.SendException( } else fail("Method '$method' != $mname")
"Error while getting command data for $method!",
Exception("Method '$method' != $mname or params is $params"),
MainPlugin.instance
)
return null return null
} }
} }

View file

@ -18,9 +18,11 @@ import java.util.function.Supplier
@ChromaGamerEnforcer @ChromaGamerEnforcer
abstract class ChromaGamerBase { abstract class ChromaGamerBase {
lateinit var config: IHaveConfig lateinit var config: IHaveConfig
protected set
protected lateinit var commonUserData: CommonUserData<out ChromaGamerBase> protected lateinit var commonUserData: CommonUserData<out ChromaGamerBase>
protected open fun init() { protected open fun init() {
config = IHaveConfig({ save() }, commonUserData.playerData)
} }
protected fun updateUserConfig() {} // TODO: Use this instead of reset() protected fun updateUserConfig() {} // TODO: Use this instead of reset()
@ -96,9 +98,19 @@ abstract class ChromaGamerBase {
* @param cl The player class to get the ID from * @param cl The player class to get the ID from
* @return The ID or null if not found * @return The ID or null if not found
*/ */
fun <T : ChromaGamerBase> getConnectedID(cl: Class<T>): String? { fun getConnectedID(cl: Class<out ChromaGamerBase>): String? {
val data = staticDataMap[cl] ?: throw RuntimeException("Class $cl is not registered!") return getConnectedID(getStaticData(cl).folder)
return commonUserData.playerData.getString(data.folder + "_id")
}
/**
* Returns the ID for the T typed player object connected with this one or null if no connection found.
*
* @param cl The player class to get the ID from
* @return The ID or null if not found
*/
fun getConnectedID(folder: String): String? {
return commonUserData.playerData.getString(folder + "_id")
} }
/** /**
@ -111,26 +123,24 @@ abstract class ChromaGamerBase {
fun <T : ChromaGamerBase> getAs(cl: Class<T>): T? { fun <T : ChromaGamerBase> getAs(cl: Class<T>): T? {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
if (cl.simpleName == javaClass.simpleName) return this as T if (cl.simpleName == javaClass.simpleName) return this as T
val newfolder = getFolderForType(cl) val data = getStaticData(cl)
if (newfolder == folder) // If in the same folder, the same filename is used if (data.folder == folder) // If in the same folder, the same filename is used
return getUser(fileName, cl) return getUser(fileName, cl)
val playerData = commonUserData.playerData return getConnectedID(data.folder)?.let { getUser(it, cl) }
return if (playerData.contains(newfolder + "_id"))
getUser(playerData.getString(newfolder + "_id")!!, cl)
else null
} }
val fileName: String /**
/** * Returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br></br>
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br></br> * **Does not include .yml**
* **Does not include .yml** */
*/ val fileName: String by lazy {
get() = commonUserData.playerData.getString(folder + "_id")!! commonUserData.playerData.getString(folder + "_id") ?: throw RuntimeException("ID not set!")
val folder: String }
/**
* This method returns the folder that this player data is stored in. For example: "minecraft". /**
*/ * Returns the folder that this player data is stored in. For example: "minecraft".
get() = getFolderForType(javaClass) */
val folder: String by lazy { getStaticData(javaClass).folder }
/** /**
* Get player information. This method calls the [TBMCPlayerGetInfoEvent] to get all the player information across the TBMC plugins. * Get player information. This method calls the [TBMCPlayerGetInfoEvent] to get all the player information across the TBMC plugins.
@ -282,7 +292,6 @@ abstract class ChromaGamerBase {
} }
} }
obj.commonUserData = commonUserData obj.commonUserData = commonUserData
obj.config = IHaveConfig({ obj.save() }, commonUserData.playerData)
obj.init() obj.init()
obj.scheduleUncache() obj.scheduleUncache()
return obj return obj

View file

@ -1,12 +1,13 @@
package buttondevteam.lib.player package buttondevteam.lib.player
import buttondevteam.lib.architecture.IHaveConfig
import org.bukkit.Bukkit import org.bukkit.Bukkit
import java.util.* import java.util.*
@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer::class) @AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer::class)
@TBMCPlayerEnforcer @TBMCPlayerEnforcer
abstract class TBMCPlayerBase : ChromaGamerBase() { abstract class TBMCPlayerBase : ChromaGamerBase() {
val uniqueId: UUID get() = UUID.fromString(fileName) val uniqueId: UUID by lazy { UUID.fromString(fileName) }
@JvmField @JvmField
val playerName = super.config.getData("PlayerName", "") val playerName = super.config.getData("PlayerName", "")
@ -17,9 +18,8 @@ abstract class TBMCPlayerBase : ChromaGamerBase() {
else else
throw RuntimeException("Class not defined as player class! Use @PlayerClass") throw RuntimeException("Class not defined as player class! Use @PlayerClass")
val playerData = commonUserData.playerData val playerData = commonUserData.playerData
var section = playerData.getConfigurationSection(pluginName) val section = playerData.getConfigurationSection(pluginName) ?: playerData.createSection(pluginName)
if (section == null) section = playerData.createSection(pluginName) config = IHaveConfig({ save() }, section)
config.reset(section)
} }
/** /**