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 ->
Optional.ofNullable(
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
)
}
//Players, has higher priority
ChromaGamerBase.addConverter { sender: CommandSender ->
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() }
@ -114,7 +114,7 @@ class MainPlugin : ButtonPlugin() {
val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj -> obj.name }.asIterable() }
command2MC.addParamConverter(
OfflinePlayer::class.java,
{ name -> Bukkit.getOfflinePlayer(name) },
{ name -> @Suppress("DEPRECATION") Bukkit.getOfflinePlayer(name) },
"Player not found!",
playerSupplier
)
@ -125,13 +125,13 @@ class MainPlugin : ButtonPlugin() {
playerSupplier
)
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() {
logger!!.info("Saving player data...")
logger.info("Saving player data...")
ChromaGamerBase.saveUsers()
logger!!.info("Player data saved.")
logger.info("Player data saved.")
}
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
fun onPlayerChat(event: AsyncPlayerChatEvent) {
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())
//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

View file

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

View file

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

View file

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

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 [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 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
@ -40,10 +40,6 @@ class ConfigData<T> internal constructor(
return "ConfigData{path='$path', value=$value}"
}
override fun reset() {
value = null
}
override fun get(): T {
val cachedValue = value
if (cachedValue != null) return cachedValue //Speed things up
@ -96,7 +92,7 @@ class ConfigData<T> internal constructor(
synchronized(saveTasks) {
saveTasks.put(
root,
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.instance, {
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.instance, Runnable {
synchronized(saveTasks) {
saveTasks.remove(root)
sa.run()

View file

@ -1,16 +1,9 @@
package buttondevteam.lib.architecture
import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.config.IConfigData
import org.bukkit.Bukkit
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.plugin.java.JavaPlugin
import java.lang.reflect.InvocationTargetException
import java.util.*
import java.util.function.Function
import java.util.function.Predicate
import java.util.stream.Collectors
/**
* A config system
@ -111,14 +104,6 @@ class IHaveConfig(
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 {
/**
* Generates the config YAML.
@ -127,84 +112,7 @@ class IHaveConfig(
* @param configMap The result from [Component.getConfigMap]. May be null.
*/
fun pregenConfig(obj: Any, configMap: Map<String, IHaveConfig?>?) {
val ms = obj.javaClass.declaredMethods
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) }
}
}
// TODO: The configs are generated by ConfigData on creation
}
}
}

View file

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

View file

@ -1,7 +1,6 @@
package buttondevteam.lib.architecture.config
interface IConfigData<T> {
fun reset()
fun get(): 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 lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import org.bukkit.command.CommandSender;
import buttondevteam.lib.player.ChromaGamerBase
import org.bukkit.command.CommandSender
import java.util.*
@Builder
@Getter
public class ChatMessage {
/**
* The sender which sends the message.
*/
private final CommandSender sender;
/**
* The Chroma user which sends the message.
*/
private final ChromaGamerBase user;
/**
* The message to send as the user.
*/
@Setter
private String message;
/**
* Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord.
*/
private boolean fromCommand;
/**
* The sender which we should check for permissions. Same as {@link #sender} by default.
*/
private CommandSender permCheck;
/**
* The origin of the message, "Minecraft" or "Discord" for example. May be displayed to the user.<br>
* <b>This is the user class capitalized folder name.</b>
*/
private final String origin;
class ChatMessage internal constructor(
/**
* The sender which sends the message.
*/
val sender: CommandSender,
/**
* The Chroma user which sends the message.
*/
val user: ChromaGamerBase,
/**
* The message to send as the user.
*/
var message: String,
/**
* Indicates whether the message comes from running a command (like /tableflip). Implemented to be used from Discord.
*/
val isFromCommand: Boolean,
/**
* The sender which we should check for permissions. Same as [.sender] by default.
*/
val permCheck: CommandSender,
/**
* The origin of the message, "Minecraft" or "Discord" for example. May be displayed to the user.<br></br>
* **This is the user class capitalized folder name by default.**
*/
val origin: String
) {
/**
* The sender which we should check for permissions. Same as {@link #sender} by default.
*
* @return The perm check or the sender
*/
public CommandSender getPermCheck() {
return permCheck == null ? sender : permCheck;
}
class ChatMessageBuilder internal constructor(
private var sender: CommandSender,
private var user: ChromaGamerBase,
private var message: String,
private var origin: String
) {
private var fromCommand = false
private var permCheck: CommandSender? = null
private static ChatMessageBuilder builder() {
return new ChatMessageBuilder();
}
fun fromCommand(fromCommand: Boolean): ChatMessageBuilder {
this.fromCommand = fromCommand
return this
}
@NonNull
public static ChatMessageBuilder builder(CommandSender sender, ChromaGamerBase user, String message) {
return builder().sender(sender).user(user).message(message).origin(user.getFolder().substring(0, 1).toUpperCase() + user.getFolder().substring(1));
}
}
fun permCheck(permCheck: CommandSender): ChatMessageBuilder {
this.permCheck = permCheck
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 lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import buttondevteam.core.component.channel.Channel
import org.bukkit.command.CommandSender
@RequiredArgsConstructor
public class Command2MCSender implements Command2Sender {
private @Getter final CommandSender sender;
private @Getter final Channel channel;
private @Getter final CommandSender permCheck;
class Command2MCSender(val sender: CommandSender, val channel: Channel, val permCheck: CommandSender) : Command2Sender {
@Override
public void sendMessage(String message) {
sender.sendMessage(message);
}
override fun sendMessage(message: String) {
sender.sendMessage(message)
}
@Override
public void sendMessage(String[] message) {
sender.sendMessage(message);
}
override fun sendMessage(message: Array<String>) {
sender.sendMessage(*message)
}
@Override
public String getName() {
return sender.getName();
}
}
override fun getName(): String {
return sender.name
}
}

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.RecipientTestResult;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatPreprocessEvent;
import buttondevteam.lib.TBMCSystemChatEvent;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import buttondevteam.core.component.channel.Channel
import buttondevteam.core.component.channel.Channel.Companion.channelList
import buttondevteam.core.component.channel.Channel.Companion.registerChannel
import buttondevteam.core.component.channel.Channel.RecipientTestResult
import buttondevteam.lib.ChromaUtils.callEventAsync
import buttondevteam.lib.ChromaUtils.doItAsync
import buttondevteam.lib.TBMCChatEvent
import buttondevteam.lib.TBMCChatPreprocessEvent
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;
import java.util.function.Supplier;
object TBMCChatAPI {
/**
* 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 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 cm The message to send
* @return The event cancelled state
*/
public static boolean SendChatMessage(ChatMessage cm) {
return SendChatMessage(cm, cm.getUser().channel.get());
}
/**
* Sends a regular message to Minecraft. Make sure that the channel is registered with [.RegisterChatChannel].
*
* @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 message The message to send
* @param exceptions Platforms where this message shouldn't be sent (same as [ChatMessage.getOrigin]
* @return The event cancelled state
*/
@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)
}
/**
* 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 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
}
private fun getScoreOrSendError(channel: Channel, sender: CommandSender): RecipientTestResult {
val result = channel.getRTR(sender)
if (result.errormessage != null) sender.sendMessage("§c" + result.errormessage)
return result
}
/**
* Sends a regular message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.
*
* @param channel The channel to send to
* @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
* @param exceptions Platforms where this message shouldn't be sent (same as {@link ChatMessage#getOrigin()}
* @return The event cancelled state
*/
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);
}
}
/**
* Register a chat channel. See [Channel.Channel] for details.
*
* @param channel A new [Channel] to register
*/
@JvmStatic
fun RegisterChatChannel(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.
* Only used when registering commands.
*
* @param command The command object to use
*/
class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(command: TC) {
private var commandConfig: ConfigurationSection? = null
/**
* Read the yaml file for the given command class.
*
* @param command The command object to use
*/
init {
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.")
return null
}
val mname = cs.getString("method")
val params = cs.getString("params")
val i =
mname.indexOf('(') //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
if (i != -1 && method.name == mname.substring(0, i) && params != null) {
fun fail(message: String): String? {
TBMCCoreAPI.SendException(
"Error while getting command data for $method!",
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
} else TBMCCoreAPI.SendException(
"Error while getting command data for $method!",
Exception("Method '$method' != $mname or params is $params"),
MainPlugin.instance
)
} else fail("Method '$method' != $mname")
return null
}
}

View file

@ -18,9 +18,11 @@ import java.util.function.Supplier
@ChromaGamerEnforcer
abstract class ChromaGamerBase {
lateinit var config: IHaveConfig
protected set
protected lateinit var commonUserData: CommonUserData<out ChromaGamerBase>
protected open fun init() {
config = IHaveConfig({ save() }, commonUserData.playerData)
}
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
* @return The ID or null if not found
*/
fun <T : ChromaGamerBase> getConnectedID(cl: Class<T>): String? {
val data = staticDataMap[cl] ?: throw RuntimeException("Class $cl is not registered!")
return commonUserData.playerData.getString(data.folder + "_id")
fun getConnectedID(cl: Class<out ChromaGamerBase>): String? {
return getConnectedID(getStaticData(cl).folder)
}
/**
* 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? {
@Suppress("UNCHECKED_CAST")
if (cl.simpleName == javaClass.simpleName) return this as T
val newfolder = getFolderForType(cl)
if (newfolder == folder) // If in the same folder, the same filename is used
val data = getStaticData(cl)
if (data.folder == folder) // If in the same folder, the same filename is used
return getUser(fileName, cl)
val playerData = commonUserData.playerData
return if (playerData.contains(newfolder + "_id"))
getUser(playerData.getString(newfolder + "_id")!!, cl)
else null
return getConnectedID(data.folder)?.let { getUser(it, cl) }
}
val fileName: String
/**
* 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**
*/
get() = commonUserData.playerData.getString(folder + "_id")!!
val folder: String
/**
* This method returns the folder that this player data is stored in. For example: "minecraft".
*/
get() = getFolderForType(javaClass)
/**
* 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**
*/
val fileName: String by lazy {
commonUserData.playerData.getString(folder + "_id") ?: throw RuntimeException("ID not set!")
}
/**
* Returns the folder that this player data is stored in. For example: "minecraft".
*/
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.
@ -282,7 +292,6 @@ abstract class ChromaGamerBase {
}
}
obj.commonUserData = commonUserData
obj.config = IHaveConfig({ obj.save() }, commonUserData.playerData)
obj.init()
obj.scheduleUncache()
return obj

View file

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