From 7de325e5246142dad59ffcfd035d39757518b003 Mon Sep 17 00:00:00 2001 From: Martin Hustoles <martin.hustoles@student.reutlingen-university.de> Date: Sun, 8 Oct 2023 19:34:20 +0200 Subject: [PATCH] PixelCampusEssentials created --- .gitignore | 113 ++++++ pom.xml | 84 +++++ src/main/java/Plots/PlayerSelection.java | 80 +++++ src/main/java/Plots/Plots.java | 330 ++++++++++++++++++ src/main/java/Plots/PlotsTabComplete.java | 52 +++ .../PixelcampusEssentials.java | 36 ++ src/main/resources/config.yml | 2 + src/main/resources/plugin.yml | 10 + 8 files changed, 707 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/Plots/PlayerSelection.java create mode 100644 src/main/java/Plots/Plots.java create mode 100644 src/main/java/Plots/PlotsTabComplete.java create mode 100644 src/main/java/fsi/pixelcampusessentials/PixelcampusEssentials.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..75e8712 --- /dev/null +++ b/pom.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>FSI</groupId> + <artifactId>PixelcampusEssentials</artifactId> + <version>1.0</version> + <packaging>jar</packaging> + + <name>PixelcampusEssentials</name> + + <properties> + <java.version>1.8</java.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>16</source> + <target>16</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.2.4</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + </build> + + <repositories> + <repository> + <id>sk89q-repo</id> + <url>https://maven.enginehub.org/repo/</url> + </repository> + <repository> + <id>papermc-repo</id> + <url>https://repo.papermc.io/repository/maven-public/</url> + </repository> + <repository> + <id>sonatype</id> + <url>https://oss.sonatype.org/content/groups/public/</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>com.sk89q.worldguard</groupId> + <artifactId>worldguard-bukkit</artifactId> + <version>7.0.9</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>io.papermc.paper</groupId> + <artifactId>paper-api</artifactId> + <version>1.20.1-R0.1-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/Plots/PlayerSelection.java b/src/main/java/Plots/PlayerSelection.java new file mode 100644 index 0000000..6e1689a --- /dev/null +++ b/src/main/java/Plots/PlayerSelection.java @@ -0,0 +1,80 @@ +package Plots; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.util.BlockVector; +import org.jetbrains.annotations.NotNull; + +public class PlayerSelection implements Listener { + + @EventHandler + public void playerLeaveEvent(@NotNull PlayerQuitEvent e){ + removeFromQeue(e.getPlayer()); + } + + private boolean removeFromQeue(Player p){ + + String uuidToRemove = p.getUniqueId().toString(); + + for (PlayerSelection i : Plots.getSelectionsQeue()) { + if (i.getUUID().equals(uuidToRemove)) { + Plots.getSelectionsQeue().remove(i); + return true; + } + } + + return false; + } + + public static PlayerSelection getfromQeue(Player p){ + + String uuid = p.getUniqueId().toString(); + + for (PlayerSelection i : Plots.getSelectionsQeue()) { + if (i.getUUID().equals(uuid)) { + return i; + } + } + + return null; + } + + public PlayerSelection(){ + } + public PlayerSelection(String UUID, BlockVector pos1, BlockVector pos2) { + this.UUID = UUID; + this.pos1 = pos1; + this.pos2 = pos2; + } + + public void setUUID(String UUID) { + this.UUID = UUID; + } + + public void setPos1(BlockVector pos1) { + this.pos1 = pos1; + } + + public void setPos2(BlockVector pos2) { + this.pos2 = pos2; + } + + public String getUUID() { + return UUID; + } + + public BlockVector getPos1() { + return pos1; + } + + public BlockVector getPos2() { + return pos2; + } + + private String UUID = null; + private BlockVector pos1 = null; + private BlockVector pos2 = null; + +} \ No newline at end of file diff --git a/src/main/java/Plots/Plots.java b/src/main/java/Plots/Plots.java new file mode 100644 index 0000000..3f44450 --- /dev/null +++ b/src/main/java/Plots/Plots.java @@ -0,0 +1,330 @@ +package Plots; + +import com.google.common.collect.Lists; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import fsi.pixelcampusessentials.PixelcampusEssentials; +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.command.TabCompleter; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; +import org.checkerframework.checker.units.qual.A; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class Plots implements CommandExecutor, Listener { + + private static final ArrayList<PlayerSelection> selectionsQeue = new ArrayList<>(); + private static Integer MAX_PLOT_SIZE; + private static final String[] cmds = {"pos1","pos2","claim","unclaim","info","addmember","removemember"}; + + public Plots(){ + + try { + MAX_PLOT_SIZE = Integer.parseInt(String.valueOf(PixelcampusEssentials.getPlugin().getConfig().getList("maxPlotSize").get(0))); + }catch (Exception e){ + + //if the value from the config fails to load, set default MAX_PLOT_SIZE to 62500; + e.printStackTrace(); + MAX_PLOT_SIZE = 62500; + } + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + + if(!(sender instanceof Player)){ + sender.sendMessage("§cYou must be a Player to do this!"); + return true; + } + + Player p = (Player)sender; + int cmd; + + try{ + cmd = chosenCommand(args[0]); + }catch (Exception e){ + return false; + } + + if(cmd == -1){ + return false; + } + + if(!sender.hasPermission("suwupremeEssentials.plot")){ + sender.sendMessage("§cYou dont have the nessecary Permissions to use Plots"); + return true; + } + + //fetching Player that will be added to the region + Player p2 = null; + if(cmd == 6 || cmd == 7){ + + try { + p2 = Bukkit.getPlayer(args[1]); + if(p2 == null)throw new Exception("§cPlayer " + args[1] + " not found! Player has to be online"); + + }catch (Exception e){ + p.sendMessage(e.getMessage()); + return true; + } + } + + + try { + switch (cmd) { + case 1 -> setPosition(p, 1); + case 2 -> setPosition(p, 2); + case 3 -> claimPlot(p); + case 4 -> unclaimPLot(p); + case 5 -> info(p, p.getWorld(), p.getLocation()); + case 6 -> addMember(p, p2); + case 7 -> removeMember(p, p2); + default -> { + return false; + } + } + + }catch (Exception e){ + e.printStackTrace(); + return true; + } + + return true; + } + + private void setPosition(Player p, Integer posNumber){ + + BlockVector playerPos = p.getLocation().toVector().toBlockVector(); + PlayerSelection sel = PlayerSelection.getfromQeue(p); + + //if player hasnt already started a selection + if(sel == null){ + + sel = new PlayerSelection( + p.getUniqueId().toString(), + posNumber == 1 ? playerPos : null, + posNumber == 2 ? playerPos : null + ); + }else{ + + if (posNumber == 1) { + sel.setPos1(playerPos); + } else { + sel.setPos2(playerPos); + } + } + + + selectionsQeue.add(sel); + + p.sendMessage("§aPos " + posNumber + " set."); + } + + private void claimPlot(Player p){ + + PlayerSelection sel = PlayerSelection.getfromQeue(p); + + if(sel == null){ + p.sendMessage("§cMake a Selection first using /plot [pos1,pos2]"); + return; + }else{ + + if(sel.getPos1() == null){ + p.sendMessage("§cPosition 1 is missing. Use /plot pos1"); + return; + }else if(sel.getPos2() == null){ + p.sendMessage("§cPosition 2 is missing. Use /plot pos2"); + return; + } + } + + RegionManager regionManager = getRegionManager(p.getWorld()); + + //Region Bounds - Expands Vertically to Build limits. (make configurable?) + BlockVector3 corner1 = BlockVector3.at( + sel.getPos1().getBlockX(), + p.getWorld().getMaxHeight(), + sel.getPos1().getBlockZ() + ); + BlockVector3 corner2 = BlockVector3.at( + sel.getPos2().getBlockX(), + p.getWorld().getMinHeight(), + sel.getPos2().getBlockZ() + ); + + //creates Plot + ProtectedRegion newRegion = new ProtectedCuboidRegion(p.getName() + "'s_Plot", corner1, corner2); + + //convert Regions Map to List of ProtectedRegions + List<ProtectedRegion> otherRegions = new ArrayList<>(regionManager.getRegions().values()); + + //Checks for any intersecting regions + List<String> overlappingRegions = new LinkedList<>(); + newRegion.getIntersectingRegions(otherRegions).forEach(i -> { + overlappingRegions.add(i.getId()); + }); + + if(overlappingRegions.size() > 0){ + p.sendMessage("§cYour Plot is overlapping with " + overlappingRegions); + return; + } + + if(getRegionSize(newRegion) > MAX_PLOT_SIZE){ + p.sendMessage("§cYour Plot cannot exeed " + MAX_PLOT_SIZE + " Blocks in Size"); + return; + } + + + //adds Player to its newly created Plot as owner + DefaultDomain plotOwner = newRegion.getOwners(); + plotOwner.addPlayer(p.getUniqueId()); + newRegion.setOwners(plotOwner); + + regionManager.addRegion(newRegion); + + p.sendMessage("§aYour Plot from " + corner1 + " to " + corner2 + " is now protected!"); + + } + + private void unclaimPLot(Player p){ + + ProtectedRegion regionFromOwner = getRegionFromOwner(p, p.getLocation()); + + if(regionFromOwner != null){ + getRegionManager(p.getWorld()).removeRegion(regionFromOwner.getId()); + p.sendMessage("§aSuccessfully removed " + regionFromOwner.getId()); + return; + } + + p.sendMessage("§cCouldn't find a Plot that belongs to you here"); + } + + private void addMember(Player owner, Player newMember){ + + ProtectedRegion regionFromOwner = getRegionFromOwner(owner, owner.getLocation()); + + if(regionFromOwner != null){ + regionFromOwner.getMembers().addPlayer(newMember.getUniqueId()); + owner.sendMessage("§aAdded " + newMember.getName() + " to " + regionFromOwner.getId()); + return; + } + + owner.sendMessage("§cYou have to be standing in your Plot as Owner"); + } + + private void removeMember(Player owner, Player oldMember){ + + ProtectedRegion regionFromOwner = getRegionFromOwner(owner, owner.getLocation()); + + if(regionFromOwner != null){ + + if(regionFromOwner.getMembers().contains(oldMember.getUniqueId())){ + + regionFromOwner.getMembers().removePlayer(oldMember.getUniqueId()); + owner.sendMessage("§aSuccessfully removed Member " + oldMember.getName() + " from " + regionFromOwner.getId()); + return; + }else{ + owner.sendMessage("§c" + oldMember.getName() + " isn't Member of your PLot"); + } + + return; + } + + owner.sendMessage("§cYou have to be standing in your Plot as Owner"); + } + + private void info(Player p, World w, Location location){ + + ApplicableRegionSet regionAt = getRegionAt(w, location); + for (ProtectedRegion region : regionAt) { + p.sendMessage("§aYou are in " + region.getId() + ". It goes from " + region.getMinimumPoint() + " to " + region.getMaximumPoint()); + return; + } + + p.sendMessage("§cThere is currently no Plot at your Location"); + } + + //get all regions the player is Standing in + private ApplicableRegionSet getRegionAt(World w, Location location){ + + + BlockVector3 playerPos = BlockVector3.at( + location.getX(), + location.getY(), + location.getZ() + ); + + RegionManager regionManager = getRegionManager(w); + ApplicableRegionSet applicableRegions = regionManager.getApplicableRegions(playerPos); + + return applicableRegions; + } + + //returns the Region the player is standing in, if he is the owner. Otherwise null + private ProtectedRegion getRegionFromOwner(Player owner, Location location){ + ApplicableRegionSet region = getRegionAt(owner.getWorld(), location); + + for (ProtectedRegion protectedRegion : region) { + if (protectedRegion.getOwners().contains(owner.getUniqueId())) { + return protectedRegion; + } + } + + return null; + } + + private int chosenCommand(String arg){ + + for (int i = 0; i < cmds.length; i++) { + if(arg.equalsIgnoreCase(cmds[i]))return i + 1; + } + + return -1; + } + + private RegionManager getRegionManager(World w){ + RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); + return regionContainer.get(BukkitAdapter.adapt(w)); + } + + private int getRegionSize(ProtectedRegion region){ + BlockVector3 maximumPoint = region.getMaximumPoint(); + BlockVector3 minimumPoint = region.getMinimumPoint(); + + // Calculate the width and height of the rectangle + int width = Math.abs(minimumPoint.getBlockX() - maximumPoint.getBlockX()) + 1; + int height = Math.abs(minimumPoint.getBlockZ() - maximumPoint.getBlockZ()) + 1; + + // Calculate the area of the rectangle + int area = width * height; + + return area; + } + + public static ArrayList<PlayerSelection> getSelectionsQeue(){ + return selectionsQeue; + } + public static String[] getCmds(){ + return cmds; + } + +} diff --git a/src/main/java/Plots/PlotsTabComplete.java b/src/main/java/Plots/PlotsTabComplete.java new file mode 100644 index 0000000..559a323 --- /dev/null +++ b/src/main/java/Plots/PlotsTabComplete.java @@ -0,0 +1,52 @@ +package Plots; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class PlotsTabComplete implements TabCompleter { + + @Nullable + @Override + public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + + if(!(sender instanceof Player)){ + return null; + } + + String[] cmds = Plots.getCmds(); + + Player p = (Player)sender; + List<String> tabCompletePhrases = new LinkedList<>(); + + switch (args.length){ + case 0: + break; + case 1: + return Arrays.stream(cmds).toList(); + case 2: + + //players should be only listed when trying to add or remove member + if(!(args[1].equalsIgnoreCase(cmds[5]) || args[1].equalsIgnoreCase(cmds[6])))break; + + //adds all players to tab complete + Bukkit.getServer().getOnlinePlayers().forEach(player -> { + tabCompletePhrases.add(player.getName()); + }); + + break; + default: + } + + return tabCompletePhrases; + } +} diff --git a/src/main/java/fsi/pixelcampusessentials/PixelcampusEssentials.java b/src/main/java/fsi/pixelcampusessentials/PixelcampusEssentials.java new file mode 100644 index 0000000..c5fc369 --- /dev/null +++ b/src/main/java/fsi/pixelcampusessentials/PixelcampusEssentials.java @@ -0,0 +1,36 @@ +package fsi.pixelcampusessentials; + +import Plots.PlayerSelection; +import Plots.Plots; +import Plots.PlotsTabComplete; +import org.bukkit.plugin.java.JavaPlugin; + +public final class PixelcampusEssentials extends JavaPlugin { + + static private PixelcampusEssentials thisPlugin; + @Override + public void onEnable() { + + thisPlugin = this; + saveDefaultConfig(); + getConfig(); + + getServer().getPluginManager().registerEvents(new PlayerSelection(),this); + + try { + getCommand("plot").setExecutor(new Plots()); + getCommand("plot").setTabCompleter(new PlotsTabComplete()); + }catch (Exception e){ + e.printStackTrace(); + } + } + + @Override + public void onDisable() { + // Plugin shutdown logic + } + + public static PixelcampusEssentials getPlugin(){ + return thisPlugin; + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..4285431 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,2 @@ +maxPlotSize: + - 62500 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..794c507 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,10 @@ +name: PixelcampusEssentials +version: '${project.version}' +main: fsi.pixelcampusessentials.PixelcampusEssentials +api-version: 1.20 +depend: [WorldGuard] +commands: + plot: + usage: '§c/plot [pos1,pos2,claim,unclaim,addmember,removemember,info]' + permission: pixelcampusEssentials.plot + permission-message: '§cYou dont have Permissions to use Plots' \ No newline at end of file -- GitLab