diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..22d1c4f --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..680f95b --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6546f14 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..787efec --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CivilCore.iml b/CivilCore.iml new file mode 100644 index 0000000..9cb719e --- /dev/null +++ b/CivilCore.iml @@ -0,0 +1,13 @@ + + + + + + + BUKKIT + + 1 + + + + \ No newline at end of file diff --git a/db.ps1 b/db.ps1 new file mode 100644 index 0000000..01fddb1 --- /dev/null +++ b/db.ps1 @@ -0,0 +1,2 @@ +docker-compose up +docker ps \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5765784 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3' +services: + civil_test_db: + environment: + - POSTGRES_PASSWORD=TEST123 + image: postgres:16.3 + ports: + - 5432:5432 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8fd1e50..976bac2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ rip.iwakura CivilCore - 0.1a + 0.1c @@ -24,9 +24,25 @@ papermc https://repo.papermc.io/repository/maven-public/ + + jitpack.io + https://jitpack.io + + + net.dv8tion + JDA + 5.0.1 + compile + + + club.minnced + discord-webhooks + 0.1.6 + compile + org.postgresql postgresql diff --git a/run.ps1 b/run.ps1 new file mode 100644 index 0000000..3260453 --- /dev/null +++ b/run.ps1 @@ -0,0 +1,9 @@ +if (-not (Test-Path -Path test_server)) { + New-Item -ItemType Directory -Path test_server +} + +# mvn clean package dependency:copy -DskipTests -DoutputDirectory=test_server/plugins + +Set-Location -Path test_server +java -jar server.jar -nogui +Set-Location -Path .. \ No newline at end of file diff --git a/src/main/java/rip/iwakura/civilcore/CivilCore.java b/src/main/java/rip/iwakura/civilcore/CivilCore.java index efa312e..137e0e1 100644 --- a/src/main/java/rip/iwakura/civilcore/CivilCore.java +++ b/src/main/java/rip/iwakura/civilcore/CivilCore.java @@ -2,20 +2,34 @@ package rip.iwakura.civilcore; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import org.bukkit.Bukkit; +import org.bukkit.WorldCreator; +import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import net.md_5.bungee.api.ChatColor; +import rip.iwakura.civilcore.commands.TeamChatCommand; import rip.iwakura.civilcore.commands.TeamCommand; +import rip.iwakura.civilcore.discord.Discord; public class CivilCore extends JavaPlugin { public Database db = new Database(); + public Discord bot; + public ArrayList teamChatRegister = new ArrayList<>(); @Override public void onEnable() { this.saveDefaultConfig(); + this.bot = new Discord(getConfig().getString("discord.token"), getConfig().getString("discord.channel"), this); + this.bot.initialize(); + + if (Bukkit.getWorld("court") == null) { + new WorldCreator("court").createWorld(); + } + try { Class.forName("org.postgresql.Driver"); } catch (ClassNotFoundException e) { @@ -28,6 +42,9 @@ public class CivilCore extends JavaPlugin { e.printStackTrace(); } getServer().getPluginManager().registerEvents(new PlayerHandler(this), this); + getServer().getPluginManager().registerEvents(new Court(), this); this.getCommand("team").setExecutor(new TeamCommand(this)); + this.getCommand("court").setExecutor(new Court()); + this.getCommand("tc").setExecutor(new TeamChatCommand(this)); } } diff --git a/src/main/java/rip/iwakura/civilcore/Court.java b/src/main/java/rip/iwakura/civilcore/Court.java new file mode 100644 index 0000000..b0e723a --- /dev/null +++ b/src/main/java/rip/iwakura/civilcore/Court.java @@ -0,0 +1,89 @@ +package rip.iwakura.civilcore; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.player.PlayerBucketEmptyEvent; + +public class Court implements Listener,CommandExecutor{ + private World court_world = Bukkit.getWorld("court"); + + private Location court_location = new Location(court_world, 4.5, 70.0, 0.5, -90f, 1.0f); + + @EventHandler + public void PlayerPlaceWater(PlayerBucketEmptyEvent e) { + Player p = (Player) e.getPlayer(); + if (!p.getLocation().getWorld().getName().equals("court")) return; + e.setCancelled(true); + } + + @EventHandler + public void PlayerHurt(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + + if (!p.getLocation().getWorld().getName().equals("court")) return; + + e.setCancelled(true); + + if (e.getCause() == DamageCause.VOID) { + p.teleport(court_location); + return; + } + } + + @EventHandler + public void PlayerBreak(BlockBreakEvent e) { + if (e.getPlayer().isOp()) return; + if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true); + } + @EventHandler + public void PlayerPlace(BlockPlaceEvent e) { + if (e.getPlayer().isOp()) return; + if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true); + } + @EventHandler + public void PlayerBurn(BlockBurnEvent e) { + if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true); + } + @EventHandler + public void FireSpread(BlockSpreadEvent e) { + if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true); + } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Non-player type."); + return true; + } + + Player p = (Player) sender; + + switch (args[0]) { + case "enter": + p.teleport(court_location); + break; + case "leave": + Location loc = p.getBedSpawnLocation(); + if (loc == null) { + loc = Bukkit.getWorld("world").getSpawnLocation(); + } + p.teleport(loc); + break; + } + return true; + } + +} diff --git a/src/main/java/rip/iwakura/civilcore/Database.java b/src/main/java/rip/iwakura/civilcore/Database.java index 4fe2e86..044fc05 100644 --- a/src/main/java/rip/iwakura/civilcore/Database.java +++ b/src/main/java/rip/iwakura/civilcore/Database.java @@ -82,6 +82,17 @@ public class Database { rs.close(); } + public List getPlayersInTeam(String team_name) { + List filtered = players.stream().filter( + s -> { + if (s.team.name == null) return false; + return s.team.name.equals(team_name); + }) + .collect(Collectors.toList()); + if(filtered.isEmpty()) return null; + return filtered; + } + public void refreshPlayers() throws SQLException { players.clear(); String sql = "SELECT players.NAME AS player_name, teams.NAME AS team_name, teams.TEAM_ID as t_team_id, teams.PREFIX AS team_prefix " + diff --git a/src/main/java/rip/iwakura/civilcore/PlayerHandler.java b/src/main/java/rip/iwakura/civilcore/PlayerHandler.java index 15cc9ac..e2eebb2 100644 --- a/src/main/java/rip/iwakura/civilcore/PlayerHandler.java +++ b/src/main/java/rip/iwakura/civilcore/PlayerHandler.java @@ -1,6 +1,7 @@ package rip.iwakura.civilcore; import java.sql.SQLException; +import java.util.List; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -14,7 +15,7 @@ import rip.iwakura.civilcore.types.DbPlayer; public class PlayerHandler implements Listener { - private CivilCore civilCore = null; + private final CivilCore civilCore; public PlayerHandler(CivilCore civilCore) { this.civilCore = civilCore; @@ -26,12 +27,34 @@ public class PlayerHandler implements Listener { Player p = e.getPlayer(); String player_name = p.getName(); + boolean inTeamChat = civilCore.teamChatRegister.contains(p); DbPlayer dbPlayer = civilCore.db.getPlayerByName(player_name); - String prefix = dbPlayer.team.prefix == null ? "" : ChatColor.translateAlternateColorCodes('&', dbPlayer.team.prefix) + " "; + String prefix = (inTeamChat ? ChatColor.DARK_GREEN + "TEAM >" + ChatColor.RESET : "") + + (dbPlayer.team.prefix == null ? + "" : + ChatColor.translateAlternateColorCodes('&', inTeamChat ? "" : "[" + dbPlayer.team.prefix + "]")); + String message = String.format("%s %s: %s", prefix, player_name, e.getMessage()); + + if (!inTeamChat) { + Bukkit.broadcastMessage(message); + civilCore.bot.sendMessage(e); + } else { + List players = civilCore.db.getPlayersInTeam(dbPlayer.team.name); + + if (players == null) return; + + for (DbPlayer team_dbp : players) { + Player team_p = Bukkit.getPlayer(team_dbp.name); + + if (team_p == null) continue; + + team_p.sendMessage(message); + } + } - Bukkit.broadcastMessage(String.format("%s%s: %s", prefix, player_name, e.getMessage())); } + @EventHandler public void PlayerJoinEvent(PlayerJoinEvent e) { Player p = e.getPlayer(); diff --git a/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java b/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java new file mode 100644 index 0000000..33b66e1 --- /dev/null +++ b/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java @@ -0,0 +1,83 @@ +package rip.iwakura.civilcore.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import net.md_5.bungee.api.ChatColor; +import org.jetbrains.annotations.NotNull; +import rip.iwakura.civilcore.Utils; +import rip.iwakura.civilcore.CivilCore; +import rip.iwakura.civilcore.types.DbPlayer; + +import java.util.List; +import java.util.Objects; + +public class TeamChatCommand implements CommandExecutor { + private final CivilCore civilCore; + + public TeamChatCommand(CivilCore civilCore) { + this.civilCore = civilCore; + } + + private String getPrefix(String name, boolean inTeamChat) { + DbPlayer dbPlayer = civilCore.db.getPlayerByName(name); + return (inTeamChat ? ChatColor.DARK_GREEN + "TEAM >" + ChatColor.RESET : "") + + (dbPlayer.team.prefix == null ? + "" : + ChatColor.translateAlternateColorCodes('&', inTeamChat ? "" : "[" + dbPlayer.team.prefix + "]")); + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + args = Objects.requireNonNull(Utils.parser(args)).toArray(new String[0]); + + if (!(sender instanceof Player p)) return true; + if (args.length == 0) { + if (civilCore.teamChatRegister.contains(p)) { + civilCore.teamChatRegister.remove(p); + p.sendMessage(ChatColor.GREEN + "You are now in the " + ChatColor.GOLD + "GLOBAL" + ChatColor.GREEN + " channel"); + } else { + civilCore.teamChatRegister.add(p); + p.sendMessage(ChatColor.GREEN + "You are now in the " + ChatColor.GOLD + "TEAM" + ChatColor.GREEN + " channel"); + } + return true; + } + + String message = String.join(" ", args); + + String player_name = p.getName(); + boolean inTeamChat = civilCore.teamChatRegister.contains(p); + + DbPlayer dbPlayer = civilCore.db.getPlayerByName(player_name); + + if (inTeamChat) { + // Send message to global chat + civilCore.teamChatRegister.remove(p); + String formatted = String.format("%s%s: %s", getPrefix(player_name, false) + " ", player_name, message); + Bukkit.broadcastMessage(formatted); + civilCore.bot.sendMessage(p, message); + civilCore.teamChatRegister.add(p); + } else { + // Send message to team chat + civilCore.teamChatRegister.add(p); + String formatted = String.format("%s%s: %s", getPrefix(player_name, true) + " ", player_name, message); + List players = civilCore.db.getPlayersInTeam(dbPlayer.team.name); + + if (players == null) return true; + + for (DbPlayer team_dbp : players) { + Player team_p = Bukkit.getPlayer(team_dbp.name); + + if (team_p == null) continue; + + team_p.sendMessage(formatted); + } + civilCore.teamChatRegister.remove(p); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java b/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java index 0e1a282..66040dd 100644 --- a/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java +++ b/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java @@ -22,7 +22,7 @@ public class TeamCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - args = Utils.parser(args).toArray(new String[args.length]); + args = Utils.parser(args).toArray(new String[0]); if(args.length == 0) { sender.sendMessage(ChatColor.RED + "Usage: /team [options]"); diff --git a/src/main/java/rip/iwakura/civilcore/discord/Discord.java b/src/main/java/rip/iwakura/civilcore/discord/Discord.java new file mode 100644 index 0000000..be440ee --- /dev/null +++ b/src/main/java/rip/iwakura/civilcore/discord/Discord.java @@ -0,0 +1,108 @@ +package rip.iwakura.civilcore.discord; + +import java.util.EnumSet; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.checkerframework.common.reflection.qual.GetConstructor; + +import club.minnced.discord.webhook.WebhookClient; +import club.minnced.discord.webhook.WebhookClientBuilder; +import club.minnced.discord.webhook.send.WebhookMessage; +import club.minnced.discord.webhook.send.WebhookMessageBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.GatewayIntent; +import rip.iwakura.civilcore.CivilCore; +import net.dv8tion.jda.api.events.session.ReadyEvent; +import rip.iwakura.civilcore.types.DbPlayer; + +public class Discord extends ListenerAdapter { + private String token; + private CivilCore civilCore; + + public String channelId; + + private TextChannel channel; + private WebhookClient webhook; + + public JDA bot; + + private EnumSet intents = EnumSet.of(GatewayIntent.GUILD_MESSAGES, GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_WEBHOOKS); + + public Discord(String token, String channelId, CivilCore civilCore){ + this.civilCore = civilCore; + this.channelId = channelId; + this.token = token; + } + + public void initialize() { + this.bot = JDABuilder.createLight(token, intents) + .addEventListeners(new MessageReceive(civilCore)) + .addEventListeners(this) + .build(); + } + + public void sendMessage(AsyncPlayerChatEvent e) { + DbPlayer dbPlayer = civilCore.db.getPlayerByName(e.getPlayer().getName()); + String prefix = (dbPlayer.team.prefix == null ? + "" : + "[" + dbPlayer.team.prefix + "]"); + + String player_name = e.getPlayer().getName(); + WebhookMessageBuilder builder = new WebhookMessageBuilder() + .setUsername(prefix + " " + player_name) + .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", player_name)) + .setContent(e.getMessage()); + webhook.send(builder.build()); + //channel.sendMessage(String.format("%s: %s", e.getPlayer().getName(), e.getMessage())).complete(); + } + + public void sendMessage(Player p, String message) { + DbPlayer dbPlayer = civilCore.db.getPlayerByName(p.getName()); + String prefix = (dbPlayer.team.prefix == null ? + "" : + "[" + dbPlayer.team.prefix + "]"); + + WebhookMessageBuilder builder = new WebhookMessageBuilder() + .setUsername(prefix + " " + p.getName()) + .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", p.getName())) + .setContent(message); + webhook.send(builder.build()); + //channel.sendMessage(String.format("%s: %s", p.getName(), message)).complete(); + } + + public void sendMessage(String message, String name) { + DbPlayer dbPlayer = civilCore.db.getPlayerByName(name); + String prefix = (dbPlayer.team.prefix == null ? + "" : + "[" + dbPlayer.team.prefix + "]"); + + WebhookMessageBuilder builder = new WebhookMessageBuilder() + .setUsername(prefix + " " + name) + .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", name)) + .setContent(message); + webhook.send(builder.build()); + //channel.sendMessage(String.format("%s: %s", name, message)).complete(); + } + + @Override + public void onReady(ReadyEvent e) { + this.channel = bot.getTextChannelById(channelId); + String webhook_token = civilCore.getConfig().getString("discord.webhook.token"); + if (webhook_token != null) { + Long webhook_id = Long.parseLong(civilCore.getConfig().getString("discord.webhook.id")); + this.webhook = new WebhookClientBuilder(webhook_id, webhook_token).build(); + } else { + channel.createWebhook("CivilBot").queue((hook) -> { + civilCore.getConfig().set("discord.webhook.id", hook.getId()); + civilCore.getConfig().set("discord.webhook.token", hook.getToken()); + civilCore.saveConfig(); + this.webhook = new WebhookClientBuilder(Long.parseLong(hook.getId()), hook.getToken()).build(); + }); + } + } +} diff --git a/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java b/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java new file mode 100644 index 0000000..a2a5617 --- /dev/null +++ b/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java @@ -0,0 +1,22 @@ +package rip.iwakura.civilcore.discord; + +import org.bukkit.Bukkit; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.md_5.bungee.api.ChatColor; +import rip.iwakura.civilcore.CivilCore; + +public class MessageReceive extends ListenerAdapter { + private CivilCore civilCore; + + public MessageReceive (CivilCore civilCore) { + this.civilCore = civilCore; + } + + @Override + public void onMessageReceived(MessageReceivedEvent e) { + if (e.getAuthor().isBot()) return; + if(e.getChannel().getId().equals(civilCore.bot.channelId)) Bukkit.broadcastMessage(String.format("%sDiscord%s %s: %s", ChatColor.BLUE, ChatColor.RESET, e.getAuthor().getName(), e.getMessage())); + } +} diff --git a/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java b/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java new file mode 100644 index 0000000..9f24454 --- /dev/null +++ b/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java @@ -0,0 +1,7 @@ +package rip.iwakura.civilcore.types; + +import org.bukkit.command.CommandSender; + +public interface CoreCommand { + public boolean run(CommandSender sender); +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7152f6b..529c1fa 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -8,3 +8,10 @@ commands: team: description: Team Manager usage: / + permission: "civil.teams" + court: + description: Court Manager + usage: / + tc: + description: Team Chat + usage: /