diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index f27fe61..ed52b4e --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# addon-bskyblock -BSkyBlock add-on for BentoBox +BSkyBlock +========== + +[![Build Status](https://travis-ci.org/tastybento/bskyblock.svg?branch=master)](https://travis-ci.org/tastybento/bskyblock) +![Lines Of Code](https://sonarcloud.io/api/project_badges/measure?project=us.tastybento%3Abskyblock&metric=ncloc) +![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=us.tastybento%3Abskyblock&metric=sqale_rating) +![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=us.tastybento%3Abskyblock&metric=reliability_rating) +![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=us.tastybento%3Abskyblock&metric=security_rating) +![Bugs](https://sonarcloud.io/api/project_badges/measure?project=us.tastybento%3Abskyblock&metric=bugs) + + +A skyblock Bukkit plugin for Minecraft derived from the well-known ASkyBlock! +This is a survival game where the player starts with an island in the sky. + +BSkyBlock (Better SkyBlock) represents a turning point on ASkyBlock's history : Tastybento and Poslovitch thought and designed together this complete rewrite in order to provide a whole new way to play Skyblock. + +**Discover today BSkyBlock, its gameplay overhaul, and enjoy the Skyblock revival!** + +Add-ons +======= +BSkyBlock is built to enable add-ons (plugins that use the BSkyBlock API). Here is a list of the current ones: + +* Level - provides island level calculation and a top ten +* Welcome Warps - provides the warp sign feature + +You can find the projects on tastybento's GitHub. + +Bugs and Feature requests +========================= +File bug and feature requests here: https://github.com/tastybento/bskyblock/issues + +Note for developers +=================== +This is an actively developed project but we are not currently accepting Pull Requests from non-collaborators. Once we have finished the main development, we'll be open to PR's. + +Development Builds +================== +Jenkins: https://ci.codemc.org/job/Tastybento/job/bskyblock/ + +API +=== +Maven dependency: +```xml + + + codemc-repo + https://repo.codemc.org/repository/maven-public/ + + + + + + us.tastybento + bskyblock + FC-0.74 + provided + + +``` + +You can find the javadoc here: https://ci.codemc.org/job/Tastybento/job/bskyblock/javadoc/ diff --git a/REFERENCES.md b/REFERENCES.md new file mode 100644 index 0000000..2a07a99 --- /dev/null +++ b/REFERENCES.md @@ -0,0 +1,300 @@ +# Locale References + +This file contains a list of the locale references in the source code. See the commit date/time for the last update. + +## References + +/!\ Most command-related references are not listed at the moment. + +``` +commands.admin.setrange.range-updated +commands.help.description +commands.help.end +commands.help.header +commands.help.parameters +commands.help.syntax +commands.island.about.description +commands.island.create.creating-island +commands.island.create.unable-create-island +commands.island.go.description +commands.island.go.parameters +commands.island.go.teleport +commands.island.go.teleported +commands.island.reset.must-remove-members +commands.island.sethome.home-set +commands.island.sethome.must-be-on-your-island +commands.island.sethome.num-homes +commands.island.setname.too-long +commands.island.setname.too-short +commands.island.team.demote.failure +commands.island.team.demote.success +commands.island.team.invite.accept.name-joined-your-island +commands.island.team.invite.accept.you-joined-island +commands.island.team.invite.already-on-team +commands.island.team.invite.cannot-invite-self +commands.island.team.invite.cooldown +commands.island.team.invite.errors.invalid-invite +commands.island.team.invite.errors.island-is-full +commands.island.team.invite.errors.none-invited-you +commands.island.team.invite.errors.you-already-are-in-team +commands.island.team.invite.invitation-sent +commands.island.team.invite.name-has-invited-you +commands.island.team.invite.reject.name-rejected-your-invite +commands.island.team.invite.reject.you-rejected-invite +commands.island.team.invite.removing-invite +commands.island.team.invite.to-accept-or-reject +commands.island.team.invite.you-can-invite +commands.island.team.invite.you-will-lose-your-island +commands.island.team.kick.leader-kicked +commands.island.team.kick.type-again +commands.island.team.leave.left-your-island +commands.island.team.leave.type-again +commands.island.team.promote.failure +commands.island.team.promote.success +commands.island.team.setowner.errors.cant-transfer-to-yourself +commands.island.team.setowner.errors.target-is-not-member +commands.island.team.setowner.name-is-the-owner +commands.island.team.setowner.you-are-the-owner +commands.help.end +commands.help.description +commands.help.header +commands.help.parameters +commands.help.syntax +general.confirm +general.errors.already-have-island +general.errors.command-cancelled +general.errors.general +general.errors.no-island +general.errors.no-permission +general.errors.no-team +general.errors.not-in-team +general.errors.not-leader +general.errors.offline-player +general.errors.player-has-island +general.errors.player-has-no-island +general.errors.unknown-command +general.errors.unknown-player +general.errors.unknown-player-name +general.errors.use-in-game +general.errors.warp-not-safe +general.errors.wrong-world +general.errors.you-must-wait +general.errors.you-need +general.previous-request-cancelled +general.request-cancelled +general.success +general.tips.changing-obsidian-to-lava +language.edited +language.panel-title +language.selected +protection.command-is-banned +protection.flags.ANIMAL_SPAWN.description +protection.flags.ANIMAL_SPAWN.name +protection.flags.ANVIL.description +protection.flags.ANVIL.hint +protection.flags.ANVIL.name +protection.flags.ARMOR_STAND.description +protection.flags.ARMOR_STAND.hint +protection.flags.ARMOR_STAND.name +protection.flags.BEACON.description +protection.flags.BEACON.hint +protection.flags.BEACON.name +protection.flags.BED.description +protection.flags.BED.hint +protection.flags.BED.name +protection.flags.BREAK_BLOCKS.description +protection.flags.BREAK_BLOCKS.hint +protection.flags.BREAK_BLOCKS.name +protection.flags.BREEDING.description +protection.flags.BREEDING.hint +protection.flags.BREEDING.name +protection.flags.BREWING.description +protection.flags.BREWING.hint +protection.flags.BREWING.name +protection.flags.BUCKET.description +protection.flags.BUCKET.hint +protection.flags.BUCKET.name +protection.flags.BUTTON.description +protection.flags.BUTTON.hint +protection.flags.BUTTON.name +protection.flags.CHEST.description +protection.flags.CHEST.hint +protection.flags.CHEST.name +protection.flags.CHEST_DAMAGE.description +protection.flags.CHEST_DAMAGE.name +protection.flags.CHORUS_FRUIT.description +protection.flags.CHORUS_FRUIT.hint +protection.flags.CHORUS_FRUIT.name +protection.flags.CLEAN_SUPER_FLAT.description +protection.flags.CLEAN_SUPER_FLAT.name +protection.flags.COLLECT_LAVA.description +protection.flags.COLLECT_LAVA.hint +protection.flags.COLLECT_LAVA.name +protection.flags.COLLECT_WATER.description +protection.flags.COLLECT_WATER.hint +protection.flags.COLLECT_WATER.name +protection.flags.CRAFTING.description +protection.flags.CRAFTING.hint +protection.flags.CRAFTING.name +protection.flags.CREEPER_DAMAGE.description +protection.flags.CREEPER_DAMAGE.name +protection.flags.CREEPER_GRIEFING.description +protection.flags.CREEPER_GRIEFING.name +protection.flags.CROP_TRAMPLE.description +protection.flags.CROP_TRAMPLE.hint +protection.flags.CROP_TRAMPLE.name +protection.flags.DOOR.description +protection.flags.DOOR.hint +protection.flags.DOOR.name +protection.flags.EGGS.description +protection.flags.EGGS.hint +protection.flags.EGGS.name +protection.flags.ELYTRA.description +protection.flags.ELYTRA.hint +protection.flags.ELYTRA.name +protection.flags.ENCHANTING.description +protection.flags.ENCHANTING.hint +protection.flags.ENCHANTING.name +protection.flags.ENDERMAN_DEATH_DROP.description +protection.flags.ENDERMAN_DEATH_DROP.name +protection.flags.ENDERMAN_GRIEFING.description +protection.flags.ENDERMAN_GRIEFING.name +protection.flags.ENDER_PEARL.description +protection.flags.ENDER_PEARL.hint +protection.flags.ENDER_PEARL.name +protection.flags.ENTER_EXIT_MESSAGES.description +protection.flags.ENTER_EXIT_MESSAGES.island +protection.flags.ENTER_EXIT_MESSAGES.name +protection.flags.ENTER_EXIT_MESSAGES.now-entering +protection.flags.ENTER_EXIT_MESSAGES.now-leaving +protection.flags.FIRE.description +protection.flags.FIRE.hint +protection.flags.FIRE.name +protection.flags.FIRE_EXTINGUISH.description +protection.flags.FIRE_EXTINGUISH.hint +protection.flags.FIRE_EXTINGUISH.name +protection.flags.FIRE_SPREAD.description +protection.flags.FIRE_SPREAD.hint +protection.flags.FIRE_SPREAD.name +protection.flags.FURNACE.description +protection.flags.FURNACE.hint +protection.flags.FURNACE.name +protection.flags.GATE.description +protection.flags.GATE.hint +protection.flags.GATE.name +protection.flags.GEO_LIMIT_MOBS.description +protection.flags.GEO_LIMIT_MOBS.name +protection.flags.HURT_ANIMALS.description +protection.flags.HURT_ANIMALS.hint +protection.flags.HURT_ANIMALS.name +protection.flags.HURT_MONSTERS.description +protection.flags.HURT_MONSTERS.hint +protection.flags.HURT_MONSTERS.name +protection.flags.HURT_VILLAGERS.description +protection.flags.HURT_VILLAGERS.hint +protection.flags.HURT_VILLAGERS.name +protection.flags.ITEM_FRAME_DAMAGE.description +protection.flags.ITEM_FRAME_DAMAGE.name +protection.flags.INVINCIBLE_VISITORS.description +protection.flags.INVINCIBLE_VISITORS.hint +protection.flags.INVINCIBLE_VISITORS.name +protection.flags.ISLAND_RESPAWN.description +protection.flags.ISLAND_RESPAWN.name +protection.flags.ITEM_DROP.description +protection.flags.ITEM_DROP.hint +protection.flags.ITEM_DROP.name +protection.flags.ITEM_PICKUP.description +protection.flags.ITEM_PICKUP.hint +protection.flags.ITEM_PICKUP.name +protection.flags.JUKEBOX.description +protection.flags.JUKEBOX.hint +protection.flags.JUKEBOX.name +protection.flags.LEASH.description +protection.flags.LEASH.hint +protection.flags.LEASH.name +protection.flags.LEVER.description +protection.flags.LEVER.hint +protection.flags.LEVER.name +protection.flags.LOCK.description +protection.flags.LOCK.name +protection.flags.MILKING.description +protection.flags.MILKING.hint +protection.flags.MILKING.name +protection.flags.MONSTER_SPAWN.description +protection.flags.MONSTER_SPAWN.name +protection.flags.MOUNT_INVENTORY.description +protection.flags.MOUNT_INVENTORY.hint +protection.flags.MOUNT_INVENTORY.name +protection.flags.NOTE_BLOCK.description +protection.flags.NOTE_BLOCK.hint +protection.flags.NOTE_BLOCK.name +protection.flags.OFFLINE_REDSTONE.description +protection.flags.OFFLINE_REDSTONE.name +protection.flags.PISTON_PUSH.description +protection.flags.PISTON_PUSH.name +protection.flags.PLACE_BLOCKS.description +protection.flags.PLACE_BLOCKS.hint +protection.flags.PLACE_BLOCKS.name +protection.flags.PORTAL.description +protection.flags.PORTAL.hint +protection.flags.PORTAL.name +protection.flags.PRESSURE_PLATE.description +protection.flags.PRESSURE_PLATE.hint +protection.flags.PRESSURE_PLATE.name +protection.flags.PVP_END.description +protection.flags.PVP_END.hint +protection.flags.PVP_END.name +protection.flags.PVP_NETHER.description +protection.flags.PVP_NETHER.hint +protection.flags.PVP_NETHER.name +protection.flags.PVP_OVERWORLD.description +protection.flags.PVP_OVERWORLD.hint +protection.flags.PVP_OVERWORLD.name +protection.flags.REDSTONE.description +protection.flags.REDSTONE.hint +protection.flags.REDSTONE.name +protection.flags.RIDING.description +protection.flags.RIDING.hint +protection.flags.RIDING.name +protection.flags.REMOVE_MOBS.description +protection.flags.REMOVE_MOBS.name +protection.flags.SHEARING.description +protection.flags.SHEARING.hint +protection.flags.SHEARING.name +protection.flags.SPAWN_EGGS.description +protection.flags.SPAWN_EGGS.hint +protection.flags.SPAWN_EGGS.name +protection.flags.TNT.description +protection.flags.TNT.hint +protection.flags.TNT.name +protection.flags.TRADING.description +protection.flags.TRADING.hint +protection.flags.TRADING.name +protection.flags.TRAPDOOR.description +protection.flags.TRAPDOOR.hint +protection.flags.TRAPDOOR.name +protection.locked +protection.panel.flag-item.allowed_rank +protection.panel.flag-item.blocked_rank +protection.panel.flag-item.description-layout +protection.panel.flag-item.name-layout +protection.panel.flag-item.menu-layout +protection.panel.flag-item.minimal_rank +protection.panel.flag-item.setting-active +protection.panel.flag-item.setting-disabled +protection.panel.flag-item.setting-layout +protection.panel.PROTECTION.description +protection.panel.PROTECTION.title +protection.panel.SETTING.description +protection.panel.SETTING.title +protection.panel.WORLD_SETTING.description +protection.panel.WORLD_SETTING.title +protection.protected +ranks.admin +ranks.banned +ranks.coop +ranks.member +ranks.mod +ranks.owner +ranks.visitor +``` diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..1cbaff0 --- /dev/null +++ b/config.yml @@ -0,0 +1,333 @@ +# BSkyBlock Configuration FC-0.7 +# This config file is dynamic and saved when the server is shutdown. +# You cannot edit it while the server is running because changes will +# be lost! Use in-game settings GUI or edit when server is offline. +# +general: + # BSkyBlock uses bStats.org to get global data about the plugin to help improving it. + # bStats has nearly no effect on your server's performance and the sent data is completely + # anonymous so please consider twice if you really want to disable it. + metrics: true + # Check for updates - this will tell Ops and the console if there is a new + # version available. It contacts dev.bukkit.org to request the latest version + # info. It does not download the latest version or change any files + check-updates: true + # Default language for new players. + # This is the filename in the locale folder without .yml. + # If this does not exist, the default en-US will be used. + default-language: en-US + # Use economy or not. If true, an economy plugin is required. If false, no money is used or give. + # If there is no economy plugin present anyway, money will be automatically disabled. + use-economy: true + # Starting money - this is how much money new players will have as their + # balance at the start of an island. + starting-money: 10.0 + purge: + # Only islands below this level will be removed if they are abandoned and admins issue the purge command + max-island-level: 50 + # Remove user data when its island gets purged. + # Helps a lot to avoid huge backups and can save some performance on startup, + # but the player settings and data will be reset. + remove-user-data: false + database: + # FLATFILE, MYSQL, MONGO + # if you use MONGO, you must also run the BSBMongo plugin (not addon) + # See https://github.com/tastybento/bsbMongo/releases/ + type: FLATFILE + host: localhost + # Port 3306 is MySQL's default. Port 27017 is MongoDB's default. + port: 3306 + name: BSkyBlock + username: username + password: password + # How often the data will be saved to file in mins. Default is 5 minutes. + # This helps prevent issues if the server crashes. + # Data is also saved at important points in the game. + backup-period: 5 + # Recover super flat - if the generator does not run for some reason, you can get + # super flat chunks (grass). To remove automatically, select this option. Turn off + # if there are no more because it may cause lag. + # This will regenerate any chunks with bedrock at y=0 when they are loaded + recover-super-flat: false + # Mute death messages + mute-death-messages: false + # Allow FTB Autonomous Activator to work (will allow a pseudo player [CoFH] to place and break blocks and hang items) + # Add other fake player names here if required + fakeplayers: + - '[CoFH]' + # Allow obsidian to be scooped up with an empty bucket back into lava + # This only works if there is a single block of obsidian (no obsidian within 10 blocks) + # Recommendation is to keep this true so that newbies don't bother you or reset their + # island unnecessarily. + allow-obsidian-scooping: true + # Time in seconds that players have to confirm sensitive commands, e.g. island reset + confirmation-time: 20 +world: + # Friendly name for this world. Used in admin commands. Must be a single word + friendly-name: BSkyBlock + # Name of the world - if it does not exist then it will be generated. + # It acts like a prefix for nether and end (e.g. BSkyBlock, BSkyBlock_nether, BSkyBlock_end) + world-name: BSkyBlock_world + # World difficulty setting - PEACEFUL, EASY, NORMAL, HARD + # Other plugins may override this setting + difficulty: NORMAL + # Radius of island in blocks. (So distance between islands is twice this) + # Will be rounded up to the nearest 16 blocks. + # It is the same for every dimension : Overworld, Nether and End. + # This value cannot be changed mid-game and the plugin will not start if it is different. + distance-between-islands: 64 + # Default protection range radius in blocks. Cannot be larger than distance. + # Admins can change protection sizes for players individually using /bsadmin setrange + # or set this permission: bskyblock.island.range. + protection-range: 50 + # Start islands at these coordinates. This is where new islands will start in the + # world. These must be a factor of your island distance, but the plugin will auto + # calculate the closest location on the grid. Islands develop around this location + # both positively and negatively in a square grid. + # If none of this makes sense, leave it at 0,0. + start-x: 0 + start-z: 0 + offset-x: 0 + offset-z: 0 + # Island height - Lowest is 5. + # It is the y coordinate of the bedrock block in the schem + island-height: 120 + # Use your own world generator for this world. In this case, the plugin will not generate + # anything. + use-own-generator: false + # Sea height (don't changes this mid-game unless you delete the world) + # Minimum is 0, which means you are playing Skyblock! + # If sea height is less than about 10, then players will drop right through it + # if it exists. Makes for an interesting variation on skyblock. + sea-height: 0 + # Maximum number of islands in the world. Set to 0 for unlimited. + # If the number of islands is greater than this number, no new island will be created. + max-islands: 0 + # The default game mode for this world. Players will be set to this mode when they create + # a new island for example. Options are SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR + default-game-mode: SURVIVAL + nether: + # Generate Nether - if this is false, the nether world will not be made and access to + # the nether will not occur. Other plugins may still enable portal usage. + # Note: Some challenges will not be possible if there is no nether. + # Note that with a standard nether all players arrive at the same portal and entering a + # portal will return them back to their islands. + generate: true + # Islands in Nether. Change to false for standard vanilla nether. + islands: true + # Nether trees are made if a player grows a tree in the nether (gravel and glowstone) + # Applies to both vanilla and islands Nether + trees: true + # Make the nether roof, if false, there is nothing up there + # Change to false if lag is a problem from the generation + # Only applies to islands Nether + roof: true + # Nether spawn protection radius - this is the distance around the nether spawn + # that will be protected from player interaction (breaking blocks, pouring lava etc.) + # Minimum is 0 (not recommended), maximum is 100. Default is 25. + # Only applies to vanilla nether + spawn-radius: 25 + end: + generate: true + islands: true + dragon-spawn: false + # Removing mobs - this kills all monsters in the vicinity. Benefit is that it helps + # players return to their island if the island has been overrun by monsters. + # This setting is toggled in world flags and set by the settings GUI. + # Mob white list - these mobs will NOT be removed when logging in or doing /island + remove-mobs-whitelist: + - ZOMBIE_VILLAGER + - WITHER + - PIG_ZOMBIE + - ENDERMAN + # World flags. These are boolean settings for various flags for this world + flags: + ENTER_EXIT_MESSAGES: true + ISLAND_RESPAWN: true + OFFLINE_REDSTONE: true + REMOVE_MOBS: true + # These are the default protection settings for new islands. + # The value is the minimum island rank required allowed to do the action + # Ranks are: Visitor = 0, Member = 900, Owner = 1000 + default-island-flags: + HURT_ANIMALS: 900 + COLLECT_WATER: 900 + REDSTONE: 900 + BUCKET: 900 + BUTTON: 900 + LOCK: 0 + FIRE_EXTINGUISH: 900 + ENDER_PEARL: 900 + DOOR: 900 + BEACON: 900 + TRAPDOOR: 900 + PRESSURE_PLATE: 900 + FURNACE: 900 + PLACE_BLOCKS: 900 + ANVIL: 900 + FIRE: 900 + CRAFTING: 900 + BREEDING: 900 + SHEARING: 900 + ENCHANTING: 900 + HURT_VILLAGERS: 900 + SPAWN_EGGS: 900 + BED: 900 + COLLECT_LAVA: 900 + MILKING: 900 + LEVER: 900 + RIDING: 900 + HURT_MONSTERS: 900 + ARMOR_STAND: 900 + GATE: 900 + TRADING: 900 + EGGS: 900 + ITEM_DROP: 900 + PORTAL: 900 + CHEST: 900 + NOTE_BLOCK: 900 + LEASH: 900 + MOUNT_INVENTORY: 900 + BREAK_BLOCKS: 900 + CHORUS_FRUIT: 900 + ITEM_PICKUP: 900 + CROP_TRAMPLE: 900 + JUKEBOX: 900 + BREWING: 900 + # These are the default settings for new islands + default-island-settings: + PVP_END: false + ANIMAL_SPAWN: true + PVP_NETHER: false + MONSTER_SPAWN: true + FIRE_SPREAD: true + PVP_OVERWORLD: false + # These are the settings visible to users. + visible-settings: [] + # Visitor banned commands - Visitors to islands cannot use these commands in this world + visitor-banned-commands: + - spawner + - spawnmob +island: + limits: + entities: {} + tile-entities: {} + # Default max team size + # Use this permission to set for specific user groups: askyblock.team.maxsize. + # Permission size cannot be less than the default below. + max-team-size: 4 + # Default maximum number of homes a player can have. Min = 1 + # Accessed via sethome or go + # Use this permission to set for specific user groups: askyblock.island.maxhomes. + max-homes: 1 + name: + # Island naming + # Only players with the TODO can name their island + # It is displayed in the top ten and enter and exit announcements + # It replaces the owner's name. Players can use & for color coding if they have the TODO permission + # These set the minimum and maximum size of a name. + min-length: 0 + max-length: 20 + # How long a player must wait until they can rejoin a team island after being + # kicked in minutes. This slows the effectiveness of players repeating challenges + # by repetitively being invited to a team island. + invite-wait: 60 + reset: + # How many resets a player is allowed (override with /asadmin clearreset ) + # Value of -1 means unlimited, 0 means hardcore - no resets. + # Example, 2 resets means they get 2 resets or 3 islands lifetime + reset-limit: -1 + # Kicked or leaving players lose resets + # Players who leave a team will lose an island reset chance + # If a player has zero resets left and leaves a team, they cannot make a new + # island by themselves and can only join a team. + # Leave this true to avoid players exploiting free islands + leavers-lose-reset: false + # Allow kicked players to keep their inventory. + # If false, kicked player's inventory will be thrown at the island leader if the + # kicked player is online and in the island world. + kicked-keep-inventory: false + on-join: + # What the plugin should reset when the player joins or creates an island + # Reset Money - if this is true, will reset the player's money to the starting money + # Recommendation is that this is set to true, but if you run multi-worlds + # make sure your economy handles multi-worlds too. + money: false + # Reset inventory - if true, the player's inventory will be cleared. + # Note: if you have MultiInv running or a similar inventory control plugin, that + # plugin may still reset the inventory when the world changes. + inventory: false + # Reset Ender Chest - if true, the player's Ender Chest will be cleared. + ender-chest: false + on-leave: + # What the plugin should reset when the player leaves or is kicked from an island + # Reset Money - if this is true, will reset the player's money to the starting money + # Recommendation is that this is set to true, but if you run multi-worlds + # make sure your economy handles multi-worlds too. + money: false + # Reset inventory - if true, the player's inventory will be cleared. + # Note: if you have MultiInv running or a similar inventory control plugin, that + # plugin may still reset the inventory when the world changes. + inventory: false + # Reset Ender Chest - if true, the player's Ender Chest will be cleared. + ender-chest: false + require-confirmation: + reset: true + # Ask the player to confirm the command he is using by typing it again. + # The 'wait' value is the number of seconds to wait for confirmation. + kick: true + kick-wait: 10 + leave: true + leave-wait: 10 + # How long a player must wait before they can reset their island again in second + reset-wait: 300 + make-island-if-none: false + # Immediately teleport player to their island (home 1 if it exists) when entering the world + immediate-teleport-on-island: false + # Have player's respawn on their island if they die + respawn-on-island: false + deaths: + # Maximum number of deaths to count. The death count can be used by add-ons. + max: 10 + sum-team: false + # When a player joins a team, reset their death count + team-join-reset: true + customranks: {} +protection: + # Geo restrict mobs. + # Mobs that exit the island space where they were spawned will be removed. + geo-limit-settings: + - GHAST + - BAT + - BLAZE + # Invincible visitors. List of damages that will not affect visitors. + # Make list blank if visitors should receive all damages + invincible-visitors: + - BLOCK_EXPLOSION + - CONTACT + - CUSTOM + - DROWNING + - ENTITY_ATTACK + - ENTITY_EXPLOSION + - FALL + - FALLING_BLOCK + - FIRE + - FIRE_TICK + - LAVA + - LIGHTNING + - MAGIC + - POISON + - PROJECTILE + - STARVATION + - SUFFOCATION + - THORNS + - WITHER + - DRAGON_BREATH + - FLY_INTO_WALL + - HOT_FLOOR + - CRAMMING + - VOID +togglePvPCooldown: 0 +panel: + close-on-click-outside: true +uniqueId: config diff --git a/locales/de-DE.yml b/locales/de-DE.yml new file mode 100644 index 0000000..19bb9bf --- /dev/null +++ b/locales/de-DE.yml @@ -0,0 +1,583 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# Tastybento: maintainer +# Poslovitch: maintainer +# markusmarkusz: translator +# +# This translation is adapted to version : [alpha-1] + +banner: "BANNER:1:RED:STRIPE_RIGHT:BLACK:STRIPE_LEFT:YELLOW" + +not-setup: + header: |- + Es werden weitere Einstellungen benötigt, bevor das Plugin genutzt werden kann... + Bearbeite die config.yml. Starte danach den Server neu. + distance: "Stelle sicher, dass du die Insel-Abstände festgelegt hast. Solltest das Plugin aktualisiert worden sein, müssen die Insel-Abstände auf ihren alten Wert gesetzt werden." + generator: |- + Der Welt-Generator für die Insel-Welt ist nicht registriert. + Mögliche Gründe sind: + 1. Überprüfe, ob du die Welt in der bukkit.yml hinzugefügt hast, falls du die Insel-Welt als einzige Server Welt festgelegt hast. + 2. Der Server wurde reloaded und nicht restartet. Starte den Server neu und versuche es nochmal. + generator-multiverse: " 3. Dein Multiverse Plugin ist veraltet. Aktualisiere es auf die neuste Version." + world-name: |- + Der Name der Welt in der config.yml unterscheidet sich vom Namen der Welt in der islands.yml. + Sollte dies gewollt ist, nehmen wir an, dass du alles zurücksetzen willst. + Falls ja, lösche die islands.yml und die vorherige Welt. + Fallls nicht, korrigiere den Namen der Welt in der config.yml und restarte den Server. Das ist möglicherweise passiert, während ein Update durchgeführt wurde. + config-outdated: |- + Die config.yml sieht veraltet aus. + Stelle sicher, dass du die Konfiguration, nach dem Update, aktualisiert hast. + Sollte diser Fehler immer noch auftreten, hast du wahrscheinlich die alte, anstatt der neuen Konfiguration bearbeitet. + Bitte entferne die aktuelle config.yml, bearbeite die config.new.yml und nenne diese dann in config.yml um, falls dies der Fall ist. + +general: + success: "Erfolg!" + errors: + no-permission: "Du hast keine Berechtigung, um diesen Befehl zu nutzen." + use-in-game: "Dieser Befehl ist nur In-Game verfügbar." + +# TODO: These are legacy strings and should be converted to a better format but will do for now +acidBottle: "Säure Flasche" +acidBucket: "Säure Eimer" +acidLore: | + Trink + das + nicht! + Wirklich! + +adminDeleteIsland: + error: "Benutze &l/[label] deleteisland confirm &r&cum die Insel auf der du bist zu löschen." + noid: "Kann Insel nicht identifizieren." + use: "Benutze &l/[label] delete [name] &r&cum stattdessen den Spieler zu löschen." +adminHelp: + add: "Fügt einen Spieler zum Team des Leiters hinzu." + addrange: "Vergrößert bzw. verkleinert den Schutzbereich der Insel." + clearreset: "Resetlimit für Spieler zurücksetzen." + clearresetall: "Setzt das Insel-Reset-Limit von allen Spielern zurück" + cobblestats: "Zeigt Statistiken des Magic Cobble Generators" + completeChallenge: "Markiert eine Herausforderung als abgeschlossen" + delete: "Löscht eine Insel (entfernt Blöcke)." + help: "Admin Befehle:" + info: "Zeigt die Team-Infos eines Spielers." + infoisland: "Infos über die nächstgelegen Insel." + kick: "Entfernt einen Spieler von jeglichen Teams." + level: "Bietet einen detaillierten Berich über das Insel-Level eines Spielers." + listChallengeResets: "Listet Challenge-Reset Pläne, wenn vorhanden." + lock: "Verschließt/entschließt eine Insel." + purge: "Löscht inaktive Inseln älter als [TimeInDays]." + purgeallowdisallow: "Erlaubt/verbietet dass die Insel gelöscht wird wenn die Purge-Kriterien zutreffen." + purgeholes: "Lösche frei Inselstellen für neue Nutzung." + purgeunowned: "Entfernt unbewohnte Inseln." + name: "Setzt Name der Insel des Spielers." + register: "Setzt die Insel eines Spielers an deinen Standpunkt." + reload: "Lädt die Konfiguration neu." + resetAllChallenges: "Setzt alle Challenges des Spielers zurück." + resetChallenge: "Markiert eine Herausforderung als nicht abgeschlossen" + resetChallengeForAll: "Setzt Challenge für alle Spieler zurück mit optionaler Wiederholung" + resetname: "Setzt Name der Insel des Spielers zurück" + resetsign: "Macht das angesehene Schild zum Warp-Schild des Inselbesitzers." + setbiome: "Setzt das Biom des Inselbesitzers." + setdeaths: "Setzt Anzahl an Toden des Spielers" + setlanguage: "Setzt die Standard-Sprache und ändert von allen Spielern die Sprache auf diese" + setrange: "Ändert den Schutzbereich einer Insel" + setspawn: "Setzt den Insel-Welt Spawn." + settingsreset: "Setzt den Schutzbereich für alle Inseln auf die Standard-Einstellungen zurück." + teamChatSpy: "Team Chats ausspionieren (on/off)" + topTen: "Aktualisiert die Top-Liste manuell" + topbreeders: "Listet die Inseln mit den meisten Mobs welche derzeit geladen sind" + tp: "Teleportiere zu der Insel eines Spielers" + tpnether: "Teleportiere zu der Nether-Insel eines Spielers" + unregister: "Lösche einen Spieler ohne die Insel zu löschen" +adminInfo: + bannedPlayers: "Gebannte Spieler" + errorNullTeamLeader: "Teamleiter sollte null sein!" + errorTeamMembersExist: "Spieler hat Teammitglieder, obwohl er keine haben sollte!" + hoppers: "Insel hat [number] Trichter" + islandLocation: "Insel Position" + isLocked: "Insel ist abgeschlossen" + isProtected: "Insel ist Purge-geschützt" + isSpawn: "Insel ist Spawn" + isUnlocked: "Insel ist nicht abgeschlossen" + isUnprotected: "Insel ist nicht Purge-geschützt" + lastLogin: "Letzter Login" + maxSize: "Insel maximale Größe (Distanz)" + player: "Spieler" + teamLeader: "Teamleiter" + teamMembers: "Teammitglieder" + title: "Dies ist die Spawn Insel" + unowned: "Diese Insel gehört derzeit niemandem." +adminLock: + adminLockedIsland: "Deine Insel wurde von einem Admin abgeschlossen." + adminUnlockedIsland: "Deine Insel wurde von einem aufgeschlossen" + errorInGame: "Befehl muss in-game auf einer Insel benutzt werden!" +adminRegister: + hadIsland: "[name] hatte eine Insel an [location]" + leadsTeam: "[name] leitet ein Team. Entferne dessen Mitglieder zuerst." + noIsland: "In diesem Bereich ist keine bekannte Insel!" + notSpawn: "Du kannst die Herrschaft über den Spawn nicht übernehmen!" + taking: "Entferne die Herrschaft von [name]" +adminResetChallengeForAll: + error: "Format für die Zeit zum Wiederholen ist: [Integer Zahl][m/h/d] (Minuten, Stunden, Tage), z. B. 5h" + repeating: "Wiederholen [duration]" + reset: "Zurücksetzen [date]" +adminResetSign: + errorExists: "Dieses Warp Schild ist bereits aktiv und gehört [name]" + found: "Warp Schild gefunden!" + noSign: "Du musst ein Schild ansehen um diesen Befehl zu benutzen." + rescued: "Warp Schild gerettet und [name] zugewiesen" +adminReserve: + islandExists: "Hier ist bereits eine Insel. Registriere den Spieler einfach!" +adminSetHome: + homeSet: "Home gesetzt auf [location]" + noneFound: "Keine sichere Position gefunden!" + notOnPlayersIsland: "Du bist nicht auf der Insel des Spielers" + notSafe: "Diese Position ist nicht sicher" +adminSetRange: + invalid: "Ungültige Entfernung!" + set: "Setze neue Spannweite auf [number]" + tip: "Benutze 10 bis [max]" + updated: "Insel Range aktualisiert auf [number]" + warning: "Warnung - Entfernung ist größer als Insel Weite [max]" + warning2: "Überlappende Inseln werden wie der Spawn agieren!" +adminSetSpawn: + center: "Insel Zentrum [location]" + coords: "Schutzbereich Koordinaten von [min] bis [max]" + limits: "Insel Limit [min] bis [max]" + locked: "Spawn ist gesichert!" + move: "Bewege dich weiter weg oder entferne den Besitzer." + ownedBy: "Dieser Inselplatz gehört [name]" + range: "Schutzbereich = [number]" + set: "Setze Insel Spawn an deine Position." + setting: "Setze Insel Spawn zu [location]" +adminTeam: + addLeaderNoIsland: "Teamleiter hat keine eigene Insel und kann deswegen kein Team haben!" + addLeaderToOwn: "Kann Leiter nicht seinem eigenen Team hinzufügen." + addedLeader: "Leiter zu diesem Team hinzugefügt!" + addingPlayer: "Spieler zum Team hinzugefügt." + alreadyOnTeam: "Spieler war bereits in diesem Team!" + kickLeader: "Dieser Spieler ist ein Teamleiter. Entferne die Mitglieder zuerst. Benutze '/[label] info [name]' um diese zu finden." + nowUnowned: "[name] hatte eine Insel an [location] diese ist nun unbewohnt. Du möchtest sie eventuell manuell entfernen." + settingHome: "Setze Spielers Home an die Position des Inselleiters." +adminTopBreeders: + checking: "Prüfe [number] Inseln..." + finding: "Finde Inseln mit den meisten Kreaturen..." + nothing: "Keine Kreaturen gefunden." +adminTopTen: + finished: "Generieren der Top Ten Liste abgeschlossen" + generating: "Generiere die Top Ten Liste" +adminTp: + manualWarp: "Kein sicherer Platz gefunden. Bewege dich manuell in die nähe von [location]." +adminUnregister: + keepBlocks: "Entferne Spieler aus der Welt, aber behalte Insel bei [location]" + onTeam: "Spieler ist in einem Team - löse es zuerst auf." +ban: + alreadybanned: "[name] ist bereits von deiner Insel gebannt!" + banned: "Du wurdest von [name]s Insel gebannt!" + fail: "[name] kann nicht gebannt werden!" + helpBan: "banne einen Spieler von deiner Insel." + helpUnban: "entbanne einen Spieler von deiner Insel." + lifted: "Bann aufgehoben von [name]s Insel!" + liftedsuccess: "Bann aufgehoben für [name]!" + notbanned: "[name] ist nicht von deiner Insel gebannt!" + notteammember: "Ein Inselmitglied kannst du nicht bannen!" + notyourself: "Das ist keine Lösung!" + success: "[name] wurde von der Insel gebannt!" + notyourself: "Das kannst du dir nicht antun!" + success: "[name] ist von der Insel gebannt!" +biome: + help: "Öffnet das Biom Menü." + paneltitle: "Wähle ein Biom" + set: "Insel Biom geändert in [biome]!" + unknown: "Unbekanntes Biom!" + youbought: "Gekauft für [cost]!" +boats: + warning: "Es ist gefährlich, das Boot jetzt zu verlassen..." +challenges: + colors: "Challenges haben unterschiedliche Farben, je nachdem ob sie:" + complete: abgeschlossen + completeNotRepeatable: "Abgeschlossen (nicht wiederholbar)" + completeRepeatable: "Abgeschlossen (wiederholbar) sind" + completedtimes: "[donetimes]x von [maxtimes]x abgeschlossen" + errorIslandLevel: "Deine Insel muss auf Level [level] sein, um diese Herausforderung abzuschliessen!" + errorItemsNotThere: "Alle benötigten Items müssen bei deiner Insel sein!" + errorNotCloseEnough: "Du musst innerhalb von [number] Blöcken von den benötigten Items stehen." + errorNotEnoughItems: "Du hast nicht genug von den benötigten Items" + errorNotOnIsland: "Du musst auf deiner Insel sein!" + errorRewardProblem: "Ein Fehler ist aufgetreten. Bitte informiere einen Teamler!" + erroryouaremissing: "Du bist verloren!!!" + expReward: "Exp Belohnung" + firstTimeRewards: "Belohnung (Erstes mal)" + guititle: "BSkyBlock Herausforderungen" + help1: "Benutze /c um Infos über eine Herausforderung zu sehen." + help2: "Benutze /c complete um eine Herausforderung abzuschliessen." + incomplete: "Nicht abgeschlossen" + invalidChallengeName: "Ungültige Herausforderung! Benutze /c help für mehr Infos!" + itemTakeWarning: "Alle benötigten Items werden aus deinem Inventar genommen!" + level: "Level" + maxreached: "[donetimes]x von [maxtimes]x abgeschlossen" + moneyReward: "Geld" + name: "Herausforderung Name" + nameHasCompleted: "[name] hat die Herausforderung [challenge] erfolgreich abgeschlossen!" + navigation: "Click to see [level] challenges!" + notRepeatable: "Diese Challenge ist nicht wiederholbar!" + repeatRewards: "Belohnung" + rewards: "Belohnung(en)" + toComplete: "Bestehe [challengesToDo] weitere Herausforderungen als [thisLevel] um dieses Level frei zu schalten!" + toCompleteUse: "Um diese Herausforderung abzuschliessen, benutze" + unknownChallenge: "Unbekannte Herausforderung (Schreibweise beachten)!" + youHaveCompleted: "Du hast die Herausforderung [challenge] erfolgreich abgeschlossen!" + youHaveNotUnlocked: "Du hast diese Herausforderung noch nicht freigeschaltet!" + youRepeated: "Du hast die Herausforderung [challenge] erneut abgeschlossen!" +changingObsidiantoLava: "Verändere Obsidian zurück in Lava. Vorsicht!" +checkteam: + checkingTeam: "Prüfe Team von [name]" +completechallenge: + challangeCompleted: "Herausforderung: [challengename] wurde von [name] abgeschlossen!" + errorChallengeDoesNotExist: "Herausforderung existiert nicht oder wurde schon abgeschlossen!" +confirm: + errorTimeLimitExpired: "Zeit abgelaufen. Benutze den Befehl erneut." +coop: + cannotcoop: "Nur der Insel-Leiter kann mit Spielern kooperieren." + help: "Temporärer Zugriff für einen Spieler auf deiner Insel." + invited: "[name] ernannte [player] zum temporären Partner! Nutze /expel um ihn zu entfernen!" + listcoops: "Liste kooperierende Spieler" + madeyoucoopy: "[name] hat dich zum temporären Partner gemacht, bis du dich ausloggt oder er dich mit /expel entfernt!" + notincoop: "[name] ist nicht dein Partner!" + onyourteam: "Spieler ist bereits temporärer Partner!" + removed: "[name] entfernte deine Partnerschaft!" + removesuccess: "[name] ist kein Partner mehr!" + success: "[name] ist nun dein temporärer Partner bis er ausloggt oder du ihn mit /expel entfernst!" + uncoop: "entferne vollen Insel Zugriff von einem Spieler" + useexpel: "Nutze /expel um jemand entfernen!" +deaths: + deaths: "Tode" + died: "starb!" + leveldeaths: "&c[[number] Tode]" +delete: + removing: "Entferne [name]s Insel." +drankAcid: "trank Säure." +drankAcidAndDied: "trank Säure und starb." +error: + blockedbyapi: "Ein Plugin, welches die API benutzt, blockierte diese Aktion." + commandNotReady: "Du kannst diesen Befehl jetzt nicht benutzen." + maxIslands: "Die Welt ist voll mit Inseln! Versuche es später erneut!" + noIsland: "Du hast keine Insel!" + noIslandOther: "Dieser Spieler hat keine Insel!" + noPermission: "Du bist nicht berechtigt diesen Befehl zu benutzen." + notABlock: "Das ist kein Block." + noTeam: "Spieler gehört keinem Team an." + notOnIsland: "Du bist nicht in einem Inselraum!" + offlinePlayer: "Dieser Spieler ist offline oder existiert nicht." + tooLong: "Zu weit. Maximale Größe ist [length]." + tooShort: "Zu kurz. Minimale Größe ist [length]." + unknownCommand: "Unbekannter Befehl." + unknownPlayer: "Spieler unbekannt." + useInGame: "Dieser Befehl muss in-game benutzt werden." + wrongWorld: "Das ist die falsche Welt dafür." + minishopDisabled: "Minishop ist deaktiviert." +expel: + expelled: "Du wurdest von der Insel entfernt!" + fail: "[name] kann nicht entfernt werden!" + notonisland: "Spieler ist nicht auf deiner Insel anwesend!" + notyourself: "Du kannst dich nicht selber von deiner Insel vertreiben!" + success: "Du hast [name] entfernt!" +invite: + errorCantJoinIsland: "Du konntest der Insel nicht beitreten. Vielleicht ist sie voll." + errorCoolDown: "Du kannst diesen Spieler in [time] Minuten erneut einladen." + errorThatPlayerIsAlreadyInATeam: "Dieser Spieler ist schon in einem Team." + errorYouCannotInviteYourself: "Du kannst nicht dich selbst einladen!" + errorYouMustHaveIslandToInvite: "Du musst eine Insel haben, um andere einladen zu können!" + errorYourIslandIsFull: "Deine Insel ist voll. Du kannst niemanden mehr einladen." + hasJoinedYourIsland: "[name] ist deiner Insel beigetreten!" + help: "Benutze [/[label] invite ] um einen Spieler zu deiner Insel einzuladen." + inviteSentTo: "Einladung verschickt an [name]" + nameHasInvitedYou: "[name] hat dich eingeladen, seiner Insel beizutreten!" + onlyIslandOwnerCanInvite: "Nur der Leiter kann einladen!" + removingInvite: "Entferne deine vorherige Einladung" + toAcceptOrReject: "um die Einladung anzunehmen oder abzulehnen." + warningYouWillLoseIsland: "WARNUNG: Du wirst deine jetzige Insel verlieren, wenn du akzeptierst!" + youCanInvite: "Du kannst noch [number] weitere Spieler einladen." + youCannotInvite: "Du kannst keine weiteren Spieler einladen." + youHaveJoinedAnIsland: "Du bist einer Insel beigetreten! Benutze /[label] team um die Mitglieder zu sehen." +island: + blockValue: "[name] ist an dieser Position [value] wert." + blockWorthless: "[name] ist wertlos." + cannotTeleport: "Im Fall kannst du dich nicht teleportieren!" + donate: "BSkyBlock von tastybento." + donatecolor: "aqua" + error: + CouldNotCreateIsland: "Insel konnte nicht erstellt werden, bitte melde das einem Moderator." + InvalidPlayer: "Dieser Spieler ist ungültig oder hat keine Insel!" + LevelNotReady: "Du kannst diesen Befehl gerade nicht nutzen! Versuch es in einigen Sekunden." + YouDoNotHavePermission: "Du besitzt keine Rechte dazu!" + YouAlreadyHaveAnIsland: "Du besitzt bereits eine Insel!" + help: + AcceptReject: "Einladung annehmen oder ablehnen." + Challenges: "/challenges: &fZeigt die Herausforderungen" + Color: "&e" + ControlPanel: "Öffnet das Insel GUI." + Expel: "Beendet die Partnerschaft mit einem Spieler." + Invite: "Lade Spieler auf deine Insel ein." + Island: "Teleportiert dich zu deiner Insel, oder erstellt eine für dich." + IslandSpawn: "Teleportiert dich zum Spawn." + Kick: "Entferne einen Spieler von deiner Insel." + Leave: "Verlasse die Insel eines anderen Spielers." + Level: "Berechne dein Insel-Level" + LevelPlayer: "Siehe das Level einer anderen Insel." + Lock: "Sperre deine Insel für Besucher" + MakeLeader: "Übergebe die Insel an ." + Name: "Setze einen Namen für deine Insel." + ResetName: "Setze Name deiner Insel zurück." + Restart: "Starte Insel neu und lösche die alte." + SelectLanguage: "Wähle Sprache" + SetHome: "Setze deinen Teleport-Punkt für /[label]." + Settings: "Siehe Insel-Einstellungen" + Team: "Zeigt deine Team-Informationen." + Teleport: "Teleportiert dich auf deine Insel." + Top: "Siehe die besten Inseln." + Value: "Zeige Insel Level Wert von dem Block in deiner Hand" + Warp: "Warpe zu 's Insel." + Warps: "Liste aller verfügbaren Insel-Warps." + islandDeletedLifeboats: "Insel gelöscht! Zu den Rettungskapseln!!!" + islandLevelis: "Insel-Level: " + new: "Es wird eine neue Insel für dich erstellt..." + protected: "Island geschützt." + requiredPointsToNextLevel: "Du brauchst noch [points] Punkte, um das Insel-Level [next] zu erreichen!" + reset: + Confirm: "&4Deine Insel und alles was du besitzt wird gelöscht. Wenn du das wirklich möchtest dann gib &c/[label] confirm&4 innerhalb von [seconds] Sekunden ein!" + MustRemovePlayers: "Du musst alle Mitglieder von der Insel entfernen, um sie neu zu starten (/[label] kick ). Eine Liste aller Mitglieder zeigt dir /[label] team." + NoMore: "Du hast alle Resets aufgebraucht!" + OnlyOwner: "Nur der Teamleiter darf diese Insel neu starten. Verlasse diese Insel, um eine eigene zu starten (/[label] leave)." + PleaseWait: "Bitte warten, neue Insel wird erstellt..." + Left: "Resets übrig" + To: "Resetlimit zurückgesetzt!" + Wait: "Du musst noch [time] Sekunden warten bevor du das wieder nutzen kannst." + YouHave: "Es sind noch [number] weitere Resets möglich." + subtitle: "von tastybento" + subtitlecolor: "blue" + teleport: "Du wurdest auf deine Insel teleportiert. (/[label] help für mehr Infos)" + title: "BSkyBlock" + titlecolor: "gold" + unlimited: "Unbegrenzt" + url: "" +islandguardsettings: + TNTdamage: "TNT Schaden" + allowed: "Erlaubt" + aciddamage: "Säure Schaden" + animalspawning: "Erlaube/Verbiete Spawnen von Tieren" + anvil: "Amboss Verwendung" + armorstand: "Rüstungsständer Interaktion" + beacon: "Leuchtfeuer Interaktion" + bed: "Schlafen" + breakblocks: "Blöcke zerstören" + breeding: "Paaren" + brewingstand: "Tränke brauen" + bucket: "Eimer benutzen" + collectlava: "Lava sammeln" + collectwater: "Water sammeln" + chest: "Kisten benutzen" + chestdamage: "Kistenschaden durch TNT" + chorusfruit: "Chorus benutzen" + creeperdamage: "Creeper Schaden" + creepergriefing: "Creeper griefing" + creeperpain: "Creeper Explosionsschaden" + croptrample: "Felder zertrampeln" + disallowed: "Verboten" + door: "Türen benutzen" + eggs: "Eier werfen" + enchantingtable: "Verzaubern" + enderpearl: "Enderperlen benutzen" + fire: "Feuer" + fireextinguish: "Feuer löschen" + firespread: "Feuer verbreiten" + furnace: "Öfen benutzen" + gate: "Zauntore benutzen" + horseinventoryaccess: "Besucher Pferde-Inventar Zugriff" + horseriding: "Besucher Pferde reiten" + hurtanimals: "Tiere verletzen" + hurtmonsters: "Monster verletzen" + joinleaveislandmessages: "Entritte/Ausfahrten Beiträge anzeigen" + jukebox: "Plattenspieler benutzen" + leash: "Leinen benutzen" + lever: "Knöpfe/Hebel benutzen" + milking: "Melken" + monsterspawning: "Spawnen von Monstern umschalten" + netherpvp: "PvP im Nether" + placeblocks: "Blöcke platzieren" + portaluse: "Portale benutzen" + pressureplate: "Besucher aktivieren Druckplatten" + pvp: "PvP" + redstone: "Redstone benutzen" + settingsgeneraltitle: "Generelle Insel-Welt Einstellungen" + settingsgeneraldesc: "Generelle Insel-Welt Einstellungen" + settingsislandtitle: "Insel Einstellungen" + settingsislanddesc: "Diese Regeln greifen für Besucher auf dieser Insel" + settingsspawntitle: "Spawn Einstellungen" + settingsspawndesc: "Diese Regeln greifen am Spawn" + shears: "Scheren benutzen" + spawnegg: "Spawneier benutzen" + teleportwhenfalling: "Beim Fall teleportieren" + title: "Insel Einstellungen" + villagertrading: "Villager trading" + visitordrop: "Besucher Item-drop" + visitorkeepitems: "Items beim Tod auf fremden Inseln behalten" + visitorpickup: "Besucher Item-Aufnahme" + withergriefing: "Wither griefing" + workbench: "Werkbank Benutzung" +kick: + errorNoTeam: "Du hast kein Team!" + errorNotPartOfTeam: "Dieser Spieler ist nicht in deinem Insel-Team!" + errorOnlyLeaderCan: "Nur der Leiter kann Leute von der Insel werfen!" + errorPlayerNotInTeam: "Dieser Spieler ist nicht in deinem Team!" + nameRemoved: "[name] wurde von der Insel entfernt." + nameRemovedYou: "[name] hat dich von der Insel entfernt!" +lavaTip: "Tipp: Falls du kein Obsidian wolltest, führe einen Rechtsklick mit einem Eimer auf das Obsidian aus. Du hast danach Lava im Eimer!" +leave: + canceled: "Leave canceled" + errorLeadersCannotLeave: "Teamleiter können eine Insel nicht verlassen. Ernenne erst jemand anderen zum Teamleiter mit /[label] makeleader " + errorYouAreTheLeader: "Du bist der Teamleiter, benutze stattdessen /[label] remove ." + errorYouCannotLeaveIsland: "Du kannst deine Insel nicht verlassen, wenn du die einzige Person bist. Versuche sie neu zu starten, wenn du eine neue willst!" + errorYouMustBeInWorld: "Du musst in der Insel-Welt sein, um dein Team zu verlassen!" + nameHasLeftYourIsland: "[name] hat deine Insel verlassen!" + warning: "Bist du dir sicher, dass du das Team verlassen willst? Nutze '/[label] leave' erneut, um die Entscheidung zu bestätigen." + youHaveLeftTheIsland: "Du hast die Insel verlassen und bist zum Spawn zurückgekehrt." +level: + calculating: "Dein Insel Level wird berechnet. Das kann einen Moment dauern..." + errornotYourIsland: "Nur der Teamleiter kann das." + islandLevel: "Insel-Level" +lock: + enteringspawn: "Du betrittst den Spawn" + islandlocked: "Insel is für Besucher gesperrt" + leavingspawn: "Du verlässt den Spawn" + locking: "Insel für Besucher gesperrt" + nowentering: "Du betrittst nun die Insel von: [name]" + nowleaving: "Du verlässt nun die Insel von: [name]" + unlocking: "Insel für Besucher wieder geöffnet" +makeleader: + errorGeneralError: "Konnte Leiter nicht ändern." + errorNotYourIsland: "Das ist nicht deine Insel. Du kannst sie niemandem geben!" + errorPlayerMustBeOnline: "Der Spieler muss online sein, um die Insel zu übernehmen." + errorRemoveAllPlayersFirst: "Entferne alle Mitglieder ausser die Person, der du die Insel übergeben willst." + errorThatPlayerIsNotInTeam: "Dieser Spieler ist nicht in deinem Insel-Team!" + errorYouMustBeInTeam: "Du musst in einem Team sein, um deine Insel abzugeben." + nameIsNowTheOwner: "[name] ist jetzt der Besitzer deiner Insel!" + youAreNowTheOwner: "Du bist jetzt der Besitzer deiner Insel." +minishop: + buy: "Kaufen (Linksklick)" + buyproblem: "Es gab ein Problem beim Kauf von [description]!" + islandhelpMiniShop: "Öffnet den MiniShop" + outofstock: "Nicht genügend Items!" + sell: "Verkaufen (Rechtsklick)" + sellproblem: "Du hast nicht genügend von [description] um es zu verkaufen!" + title: MiniShop + youbought: "&aDu hast &c[number] &b[description] &afür &c[price] §aEuro gekauft!" + youcannnotafford: "Du hast nicht genügend Geld für [description]!" + yousold: "Du hast [number] [description] für [price] Euro verkauft!" +moblimits: + entity: "Island [entity] limit of [number] reached!" + error: "Insel Paarungslimit von [number] erreicht!" + hopper: "Insel Trichter limit von [number] erreicht!" + villager: "Insel Dorfbewohner Paarungslimit von [number] erreicht!" +nether: + spawnisprotected: "Der Netherspawn-Bereich ist geschützt." +news: + headline: "[BSkyblock News] Während du offline warst..." +purge: + acidFound: "Es gibt [number] unbewohnte Inseln. Tippe '/acid purge unowned confirm' zum entfernen in den nächsten 20 Sekunden." + allowPurge: "Purge-Schutz entfernt" + alreadyRunning: "Löschung läuft bereits! Bitte warten!" + calculating: "Berechne welche Inseln inaktiv waren für mehr als [time] Tage." + countingUnowned: "Zähle unbewohnte Inseln und prüfe Spielerdateien. Das kann ein wenig dauern..." + finished: "Löschen von inaktiven Inseln abgeschlossen." + maxPurge: "Max purge is [number] islands. Run purge again afterwards to purge more." + noneFound: "Keine inaktiven Inseln zu löschen." + nowWaiting: "Warte nun..." + preventPurge: "Insel ist vor dem Löschen geschützt" + purgeCancelled: "Löschen abgebrochen." + removingAt: "Entferne Inseln bei Position [location]" + removingName: "Löschung: Entferne [name]'s Insel" + skyblockFound: "Es gibt [number] unbewohnte Inseln. Tippe '/asadmin purge unowned confirm' zum entfernen in den nächsten 20 Sekunden." + stillChecking: "Prüfe weiterhin Spielerdateien..." + thisWillRemove: "Dies wird [number] inaktive Inseln löschen!" + typeConfirm: "Schreibe /[label] confirm während 10 Sekunden um fortzufahren" + usage: "Benutze: /[label] purge [TimeInDays]" + warning: "GEFAHR! Nicht benutzen, wenn Spieler auf dem Server sind! MACHE BACKUPS DER WELT!" +register: + errorBedrockNotFound: "Insel kann nicht registriert werden!" + settingIsland: "Setze [name]s Insel zu dem nächsten Grundstein." +reject: + nameHasRejectedInvite: "[name] hat deine Insel-Einladung abgelehnt!" + youHaveNotBeenInvited: "Du wurdest nicht in ein Team eingeladen." + youHaveRejectedInvitation: "Du hast die Insel-Einladung abgelehnt." +reload: + configReloaded: "Konfiguration neu geladen." +resetallchallenges: + success: "[name]s Challenges wurden alle zurückgesetzt." +resetchallenge: + challengeReset: "Herausforderung: [challengename] wurde zurückgesetzt für [name]" + errorChallengeDoesNotExist: "Herausforderung existiert nicht oder wurde noch nicht abgeschlossen!" +schematics: + title: "Wähle deine Insel..." +sethome: + errorNoIsland: "Du gehörst zu keiner Insel. Kehre zurück zum Spawn!" + errorNotOnIsland: "Du musst dich auf deiner Insel befinden!" + errorNumHomes: "Die Anzahl deiner homes kann 1 bis [max] betragen." + homeSet: "Dein Insel-Home wurde auf deine aktuelle Position gesetzt." +settingsReset: + done: "Done." + inprogress: "Schutzbereich Einstellungen werden zurückgesetzt. Bitte warte..." +sign: + line1: "&1[BSkyblock]" + line2: "[player]" + line3: "Fall nicht!" + line4: "Pass auf!" +sign-acidisland: + line1: "&1[Acid Insel]" + line2: "[player]" + line3: "Wasser ist Säure!!" + line4: "Pass auf!" +targetInPVPArea: "Ziel befindet sich in einer PvP freien Zone!" +team: + listingMembers: "Liste deiner Insel-Mitglieder" +teamchat: + helpChat: "aktiviere/deaktiviere den Team chat" + noTeam: "Du bist in keinem Team!" + noTeamAround: "Keines deiner Teammitglieder ist online!" + prefix: "[Team Chat]<{ISLAND_PLAYER}> " + spyoff: "Team Chat Spionage aktiviert" + spyon: "Team Chat Spionage deaktviert" + statusOff: "Team Chat deaktiviert" + statusOn: "Team Chat aktiviert" +topTen: + errorExcluded: "Info: Du bist von den Top 10 ausgeschlossen, da dir die Permission [perm] fehlt." + errorNotReady: "Die Top 10 Liste wurde noch nicht generiert!" + guiHeading: "&E&L Island: &6&N[name]&7 (#[rank])" + guiTitle: "Top 10 Inseln" + header: "Das sind die Top 10 Inseln:" +warps: + deactivate: "Das alte Insel-Warp-Schild wurde deaktiviert!" + error: + DoesNotExist: "Dieser Insel-Warp existiert nicht!" + Duplicate: "Sorry! Es gibt schon ein Warp-Schild in diesem Bereich!" + NoPerm: "Du darfst noch kein Insel-Warp-Schild erstellen!" + NoPlace: "Du musst auf deiner Insel sein um ein Insel-Warp-Schild zu erstellen!" + NoRemove: "Du kannst nur dein eigenes Insel-Warp-Schild entfernen!" + NoWarpsYet: "Es gibt noch keine Insel-Warps!" + NotReadyYet: "Dieser Insel-Warp ist noch nicht bereit. Versuch es später erneut." + NotSafe: "Der Insel-Warp ist nicht sicher. Versuch es später erneut." + NotEnoughLevel: "Dein Insel-Level ist zu niedrig, um ein Warp-Schild zu erstellen!" + next: "Nächste" + playerWarped: "[name] &2hat sich zu deinem Warp teleportiert!" + previous: "Vorherige" + removed: "Insel-Warp-Schild entfernt!" + signRemoved: "Dein Insel-Warp-Schild wurde entfernt!" + success: "Insel-Warp-Schild erfolgreich erstellt!" + title: "Insel Warps" + warpTip: "&aErstelle ein Warp-Schild mit &c[WELCOME] &ain der obersten Zeile." + warpToPlayersSign: "Teleportiere zu 's Insel-Warp." + warpsAvailable: "Folgende Insel-Warps sind verfügbar" + welcomeLine: "[WELCOME]" diff --git a/locales/en-GB.yml b/locales/en-GB.yml new file mode 100644 index 0000000..22f6e9f --- /dev/null +++ b/locales/en-GB.yml @@ -0,0 +1,19 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# Tastybento: maintainer +# Poslovitch: maintainer +# +# This translation is adapted to version : [alpha-1] + +banner: "BANNER:1:BLUE:STRIPE_DOWNLEFT:WHITE:STRIPE_DOWNRIGHT:WHITE:STRIPE_CENTER:WHITE:STRIPE_MIDDLE:WHITE:STRAIGHT_CROSS:RED:CROSS:RED" + +protection: + flags: + ARMOR_STAND: + description: "Toggle interaction" + name: "Armour stands" + hint: "Armour stand use disabled" diff --git a/locales/en-US.yml b/locales/en-US.yml new file mode 100644 index 0000000..37e1f7a --- /dev/null +++ b/locales/en-US.yml @@ -0,0 +1,633 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# tastybento: maintainer +# Poslovitch: maintainer +# +# This translation is adapted to version : [alpha-2] + +banner: "BANNER:1:WHITE:STRIPE_SMALL:RED:SQUARE_TOP_RIGHT:CYAN:SQUARE_TOP_RIGHT:BLUE" + +general: + success: "&aSuccess!" + request-cancelled: "&cConfirmation timeout - &brequest cancelled" + previous-request-cancelled: "&6Previous confirmation request cancelled" + confirm: "&cType command again within &b[seconds]s&c to confirm" + errors: + command-cancelled: "&cCommand cancelled" + no-permission: "&cYou don't have permission to execute this command." + use-in-game: "&cThis command is only available in game." + no-team: "&cYou do not have a team!" + no-island: "&cYou do not have an island!" + player-has-island: "&cPlayer already has an island!" + player-has-no-island: "&cThat player has no island!" + already-have-island: "&cYou already have an island!" + no-safe-location: "&cNo safe location found on island!" + not-leader: "&cYou are not the leader of your island!" + not-in-team: "&cThat player is not in your team!" + offline-player: "&cThat player is offline or doesn't exist." + unknown-player: "&cUnknown player!" + unknown-player-name: "&c[name] is an unknown player!" + general: "&cThat command is not ready yet - contact admin" + unknown-command: "&cUnknown command. Do &b/[label] help &cfor help." + warp-not-safe: "&cThat warp is not safe right now!" + wrong-world: "&cYou are not in the right world to do that!" + you-must-wait: "&cYou must wait [number]s before you can do that command again" + you-need: "&cYou need [permission]" + tips: + changing-obsidian-to-lava: "Changing obsidian back into lava. Be careful!" + +commands: + # Parameters in <> are required, parameters in [] are optional + help: + header: "&7=========== &c[label] help &7===========" + syntax: "&b[usage] &a[parameters]&7: &e[description]" + end: "&7=================================" + parameters: "[command]" + description: "help command" + admin: + help: + parameters: "" + description: "admin command" + clearresets: + parameters: "" + description: "clears player reset count for this world" + cleared: "&2Resets cleared" + clearresetsall: + description: "clears all player reset counts for this world" + team: + add: + parameters: " " + description: "add player to leader's team" + name-not-leader: "&c[name] is not the leader" + name-has-island: "&c[name] has an island. Unregister or delete them first!" + disband: + parameters: "" + description: "disband team leader's team" + user-disband-leader: "&cNot leader! Use disband [leader]" + disbanded: "&cAdmin disbanded your team!" + kick: + parameters: "" + description: "kick a player from a team" + cannot-kick-leader: "&cYou cannot kick the team leader. Kick members first" + admin-kicked: "&cThe admin kicked you from the team." + makeleader: + parameters: "" + description: "make player the team's leader" + already-leader: "&cPlayer is already the leader!" + range: + description: "Admin island range command" + display: + already-off: "&cIndicators are already off" + already-on: "&cIndicators are already on" + description: "Show/hide island range indicators" + hiding: "&2Hiding range indicators" + hint: |- + &cRed Barrier icons &fshow the current island protected range limit. + &7Gray Particles &fshow the max island limit. + &aGreen Particles &fshow the default protected range if the island protection range differs from it. + showing: "&2Showing range indicators" + set: + parameters: " " + description: "Sets the island protected range" + invalid-value: + not-numeric: "&c[number] is not a whole number!" + too-low: "&cThe protection range must be greater than 1!" + too-high: "&cThe protection range should be equal or less than [number]!" + same-as-before: "&cThe protection range is already set to [number]!" + success: "&2Set island protection range to [number]" + reset: + parameters: "" + description: "Resets the island protected range to the world default" + success: "&2Reset island protection range to [number]" + register: + parameters: "" + description: "register player to unowned island you are on" + registered-island: "&aRegistered player to island at [xyz]." + already-owned: "&cIsland is already owned by another player!" + no-island-here: "&cThere is no island here. Confirm to make one." + unregister: + parameters: "" + description: "unregister owner from island, but keep island blocks" + unregistered-island: "&aUnregistered player from island at [xyz]." + info: + parameters: "" + description: "get info on where you are or player's island" + no-island: "&cYou are not in an island right now..." + title: "========== Island Info ============" + owner: "Owner: [owner] ([uuid])" + last-login: "Last login: [date]" + deaths: "Deaths: [number]" + resets-left: "Resets: [number] (Max: [total])" + team-members-title: "Team members:" + team-owner-format: "&a[name] [rank]" + team-member-format: "&b[name] [rank]" + island-location: "Island location: [xyz]" + island-coords: "Island coordinates: [xz1] to [xz2]" + protection-range: "Protection range: [range]" + protection-coords: "Protection coordinates: [xz1] to [xz2]" + is-spawn: "Island is a spawn island" + banned-players: "Banned players:" + banned-format: "&c[name]" + unowned: "&cUnonwed" + version: + description: "display %bsb_plugin_name% and addons versions" + setrange: + parameters: " " + description: "set the range of player's island" + range-updated: "Island range updated to [number]" + reload: + description: "reload the plugin" + tp: + parameters: "" + description: "teleport to a player's island" + manual: "&cNo safe warp found! Manually tp near to &b[location] &cand check it out" + getrank: + parameters: "" + description: "get a player's rank on their island" + rank-is: "&aRank is [rank] on their island." + setrank: + parameters: " " + description: "set a player's rank on their island" + unknown-rank: "&cUnknown rank!" + rank-set: "&aRank set from [from] to [to]." + schem: + parameters: "" + description: "manipulate schems" + copy-first: "&cCopy a schem first!" + file-exists: "&cFile already exists, overwrite?" + no-such-file: "&cNo such file!" + could-not-load: "&cCould not load that file!" + could-not-save: "&cHmm, something went wrong saving that file: [message]" + set-pos1: "&aPosition 1 set at [vector]" + set-pos2: "&aPosition 2 set at [vector]" + set-different-pos: "&cSet a different location - this pos is already set!" + need-pos1-pos2: "&cSet pos1 and pos2 first!" + copied-blocks: "&bCopied [number] blocks to clipboard" + look-at-a-block: "&cLook at block within 20 blocks to set" + world: + description: "Manage world settings" + island: + about: + description: "display copyright and license info" + go: + parameters: "[home number]" + description: "teleport you to your island" + teleport: "&aTeleporting you to your island." + teleported: "&aTeleported you to home &e#[number]." + tip: "&bType /[label] help &afor help." + help: + description: "The main island command" + pick-world: "&cSpecify world from [worlds]" + spawn: + description: "teleport you to the spawn" + create: + description: "create an island" + unable-create-island: "Your island could not be generated, please contact an administrator." + creating-island: "Creating your island..." + pick-world: "&cPick a world from [worlds]" + info: + description: "display info about your island" + reset: + description: "restart your island and remove the old one" + must-remove-members: "You must remove all members from your island before you can restart it (/island kick )." + none-left: "&cYou have no more resets left!" + resets-left: "&cYou have [number] resets left" + sethome: + description: "set your teleport point for /island" + must-be-on-your-island: "You must be on your island to set home!" + num-homes: "Homes can be 1 to [number]." + home-set: "Your island home has been set to your current location." + parameters: "[home number]" + setname: + description: "set a name for your island" + name-too-short: "&cToo short. Minimum size is [number] characters." + name-too-long: "&cToo long. Maximum size is [number] characters." + parameters: "" + resetname: + description: "reset your island name" + team: + description: "manage your team" + info: + description: "display detailed info about your team" + invite: + description: "invite a player to join your island" + invitation-sent: "Invitation sent to [name]" + removing-invite: "Removing invite" + name-has-invited-you: "[name] has invited you to join their island." + to-accept-or-reject: "Do /island team accept to accept, or /island team reject to reject" + you-will-lose-your-island: "&cWARNING! You will lose your island if you accept!" + errors: + cannot-invite-self: "&cYou cannot invite yourself!" + cooldown: "&cYou cannot invite that person for another [number] seconds" + island-is-full: "&cYour island is full, you can't invite anyone else." + none-invited-you: "&cNo one invited you :c." + you-already-are-in-team: "&cYou are already on a team!" + already-on-team: "&cThat player is already on a team!" + invalid-invite: "&cThat invite is no longer valid, sorry." + parameters: "" + you-can-invite: "You can invite [number] more players." + accept: + description: "accept an invitation" + you-joined-island: "&aYou joined an island! Use /[label] team info to see the other members." + name-joined-your-island: "&a[name] joined your island!" + reject: + description: "reject an invitation" + you-rejected-invite: "&aYou rejected the invitation to join an island." + name-rejected-your-invite: "&c[name] rejected your island invite!" + cancel: + description: "cancel the pending invite to join your island" + leave: + cannot-leave: "&cTeamleaders cannot leave! Become a member first, or kick all members." + description: "leave your island" + left-your-island: "&c[name] left your island" + kick: + description: "remove a member from your island" + parameters: "" + leader-kicked: "&cThe leader kicked you from the island!" + cannot-kick: "&cYou cannot kick yourself!" + demote: + description: "demote a player on your island down a rank" + parameters: "" + failure: "&cPlayer cannot be demoted any further!" + success: "Demoted [name] to [rank]" + promote: + description: "promote a player on your island up a rank" + parameters: "" + failure: "&cPlayer cannot be promoted any further!" + success: "Promoted [name] to [rank]" + setowner: + description: "transfer your island ownership to a member" + errors: + cant-transfer-to-yourself: "&cYou can't transfer ownership to yourself! Well, infact, you could... But we don't want you to. 'Cause it's bad." + target-is-not-member: "&cThat player is not part of your island team!" + name-is-the-owner: "&a[name] is now the island owner!" + parameters: "" + you-are-the-owner: "&aYou are now the island owner!" + ban: + description: "ban a player from your island" + parameters: "" + cannot-ban-yourself: "&cYou cannot ban yourself!" + cannot-ban: "&cThat player cannot be banned." + cannot-ban-member: "&cKick the team member first, then ban." + player-already-banned: "&cPlayer is already banned" + owner-banned-you: "&b[name]&c banned you from their island!" + you-are-banned: "&bYou are banned from this island!" + unban: + description: "unban a player from your island" + parameters: "" + cannot-unban-yourself: "&cYou cannot unban yourself!" + player-not-banned: "&cPlayer is not banned" + you-are-unbanned: "&b[name]&a unbanned you from their island!" + banlist: + description: "list banned players" + noone: "&aNo one is banned on this island" + the-following: "&bThe following players are banned:" + names: "&c[line]" + settings: + description: "display island settings" + language: + description: "select language" + +ranks: + owner: "Owner" + member: "Member" + coop: "Coop" + visitor: "Visitor" + banned: "Banned" + admin: "Admin" + mod: "Mod" + +protection: + command-is-banned: "Command is banned for visitors" + flags: + ANIMAL_SPAWN: + description: "Toggle spawning" + name: "Animal spawning" + ANVIL: + description: "Toggle interaction" + name: "Anvils" + hint: "Anvil use disabled" + ARMOR_STAND: + description: "Toggle interaction" + name: "Armor stands" + hint: "Armor stand use disabled" + BEACON: + description: "Toggle interaction" + name: "Beacons" + hint: "Beacon use disabled" + BED: + description: "Toggle interaction" + name: "Beds" + hint: "Bed use disabled" + BREAK_BLOCKS: + description: "Toggle breaking" + name: "Break blocks" + hint: "Block breaking disabled" + BREEDING: + description: "Toggle breeding" + name: "Breed animals" + hint: "Animal breeding protected" + BREWING: + description: "Toggle interaction" + name: "Brewing stands" + hint: "No brewing allowed" + BUCKET: + description: "Toggle interaction" + name: "Buckets" + hint: "No bucket use allowed" + BUTTON: + description: "Toggle button use" + name: "Buttons" + hint: "No button use allowed" + CHEST: + description: "Toggle chest access" + name: "Chests" + hint: "Chest access disabled" + CHEST_DAMAGE: + description: "Toggle chest damage from explosions" + name: "Chest Damage" + CHORUS_FRUIT: + description: "Toggle teleportation" + name: "Chorus fruits" + hint: "No teleporting" + CLEAN_SUPER_FLAT: + description: |- + &aEnable to clean any + &asuper-flat chunks in + &aisland worlds + name: "Clean Super Flat" + COLLECT_LAVA: + description: |- + &aToggle collecting lava + &a(override Buckets) + name: "Collect lava" + hint: "No lava collection" + COLLECT_WATER: + description: |- + &aToggle collecting water + &a(override Buckets) + name: "Collect water" + hint: "No water collection" + CRAFTING: + description: "Toggle use" + name: "Workbenches" + hint: "No workbench use" + CREEPER_DAMAGE: + description: "Toggle creeper damage" + name: "Creeper damage" + CREEPER_GRIEFING: + description: "Toggle creeper griefing" + name: "Creeper griefing" + hint: "No creeper griefing allowed" + CROP_TRAMPLE: + description: "Toggle crop trampling" + name: "Trample crops" + hint: "Crops are protected" + DOOR: + description: "Toggle door usage" + name: "Use doors" + hint: "No door use" + EGGS: + description: "Toggle egg throwing" + name: "Egg throwing" + hint: "No egg throwing" + ELYTRA: + description: "Toggle use on island" + name: "Elytra" + hint: "No elytra flying allowed" + ENCHANTING: + description: "Toggle use" + name: "Enchanting table" + hint: "No table use" + ENDER_CHEST: + description: "Toggle use/crafting" + name: "Ender Chests" + hint: "Ender chests are disabled in this world" + ENDERMAN_DEATH_DROP: + description: | + Endermen will drop + any block they are + holding if killed. + name: "Enderman Death Drop" + ENDERMAN_GRIEFING: + description: | + Endermen can remove + blocks from islands + name: "Enderman griefing" + ENDER_PEARL: + description: "Toggle use" + name: "EnderPearls" + hint: "No enderpearl use" + ENTER_EXIT_MESSAGES: + description: "Display entry and exit messages" + island: "[name]'s island" + name: "Enter/Exit messages" + now-entering: "Now entering [name]" + now-leaving: "Now leaving [name]" + FIRE: + description: "Allow fire to exist or not" + name: "Fire" + hint: "No fire allowed" + FIRE_EXTINGUISH: + description: "Toggle extinguishing" + name: "Fire extinguish" + hint: "No fire extinguishing allowed" + FIRE_SPREAD: + description: "Toggle spread" + name: "Fire spread" + hint: "No fire spread allowed" + FURNACE: + description: "Toggle use" + name: "Furnace" + hint: "No furnace use" + GATE: + description: "Toggle use" + name: "Gates" + hint: "No gate use" + GEO_LIMIT_MOBS: + description: | + &eRemove mobs that go + &eoutside protected + &eisland space + name: "Limit mobs to island" + HURT_ANIMALS: + description: "Toggle hurting" + name: "Hurt animals" + hint: "No animal hurting allowed" + HURT_MONSTERS: + description: "Toggle hurting" + name: "Hurt monsters" + hint: "No monster hurting" + HURT_VILLAGERS: + description: "Toggle hurting" + name: "Hurt villagers" + hint: "No villager hurting" + ITEM_FRAME_DAMAGE: + description: |- + &aMobs can damage + &aitem frames + name: "Item Frame Damage" + INVINCIBLE_VISITORS: + description: |- + &aConfigure invincible visitor + &asettings. + name: "Invincible Visitors" + hint: "&cVisitors protected" + ISLAND_RESPAWN: + description: |- + &aPlayers respawn + &aon island + name: "Island respawn" + ITEM_DROP: + description: "Toggle dropping" + name: "Item drop" + hint: "Items cannot be dropped" + ITEM_PICKUP: + description: "Toggle pickup" + name: "Item pickup" + hint: "Items cannot be picked up" + JUKEBOX: + description: "Toggle usage" + name: "Jukebox use" + hint: "No jukebox use allowed" + LEASH: + description: "Toggle use" + name: "Leash use" + LEVER: + description: "Toggle use" + name: "Lever use" + hint: "No lever use" + LOCK: + description: "Toggle lock" + name: "Lock island" + MILKING: + description: "Toggle cow milking" + name: "Milking" + hint: "No milking allowed" + MONSTER_SPAWN: + description: "Toggle spawning" + name: "Monster spawning" + MOUNT_INVENTORY: + description: |- + &aToggle access + &ato mount inventory + name: "Mount inventory" + hint: "No access to mount inventory" + NOTE_BLOCK: + description: "Toggle use" + name: "Note block" + hint: "No note block use" + OFFLINE_REDSTONE: + description: |- + &aWhen disabled, redstone + &awill not operate on islands + &awhere all members are offline. + &aMay help reduce lag. + name: "Offline Redstone" + PISTON_PUSH: + description: |- + &aAllow pistons to push + &ablocks outside island + name: "Piston Push" + PLACE_BLOCKS: + description: "Toggle placing" + name: "Place blocks" + hint: "Not allowed to place blocks" + PORTAL: + description: "Toggle use" + name: "Portal" + hint: "Portal use is disallowed" + PRESSURE_PLATE: + description: "Toggle usage" + name: "Pressure Plates" + hint: "No pressure plate use" + PVP_END: + description: |- + &cEnable/Disable PVP + &cin the End. + name: "End PVP" + hint: "No PVP allowed in the End" + PVP_NETHER: + description: |- + &cEnable/Disable PVP + &cin the Nether. + name: "Nether PVP" + hint: "No PVP allowed in the Nether" + PVP_OVERWORLD: + description: |- + &cEnable/Disable PVP + &con island. + name: "Overworld PVP" + hint: "&cPVP is not allowed" + REDSTONE: + description: "Toggle use" + name: "Redstone items" + hint: "No redstone item use" + RIDING: + description: "Toggle riding" + name: "Animal riding" + hint: "No animal riding allowed" + REMOVE_MOBS: + description: |- + &aRemove monsters when + &ateleporting to island + name: "Remove monsters" + SHEARING: + description: "Toggle sheering" + name: "Shearing" + hint: "No shearing" + SPAWN_EGGS: + description: "Toggle use" + name: "Spawn eggs" + hint: "No throwing spawn eggs" + TNT: + description: "Toggle TNT damage" + name: "TNT damage" + TRADING: + description: "Toggle trading" + name: "Village trading" + hint: "No villager trading" + TRAPDOOR: + description: "Toggle access" + name: "Trap doors" + hint: "No trapdoor use" + locked: "&cThis island is locked!" + protected: "&cIsland protected: [description]" + + panel: + PROTECTION: + title: "Protection" + description: |- + Protection settings + for this location + SETTING: + title: "Settings" + description: "General settings" + WORLD_SETTING: + title: "[world_name] Settings" + description: "Settings for this game world" + flag-item: + name-layout: "&a[name]" + description-layout: | + &a[description] + + &7Allowed for: + allowed_rank: "&3- &a" + blocked_rank: "&3- &c" + minimal_rank: "&3- &2" + menu-layout: "&a[description]" + setting-layout: | + &a[description] + + &7Current setting: [setting] + setting-active: "&aActive" + setting-disabled: "&cDisabled" + +language: + panel-title: "Select your language" + selected: "&aCurrently selected." + edited: "&aEdited your language to &e[lang]&a." \ No newline at end of file diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml new file mode 100644 index 0000000..4b6c506 --- /dev/null +++ b/locales/fr-FR.yml @@ -0,0 +1,271 @@ +########################################################################################### +# Ceci est fichier YAML. Soyez prudents lorsque vous l'éditez et vérifiez la syntaxe en # +# utilisant un parser YAML, tel que http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# MrSheepSheep: translater +# Poslovitch: maintainer +# +# Cette traduction est adaptée pour la version : [alpha-2] + +banner: "BANNER:1:WHITE:STRIPE_BOTTOM:RED:STRIPE_TOP:BLUE" + +general: + deaths: "morts" + unlimited: "Illimité" + success: "&aAction effectuée!" + errors: + command-cancelled: "&cCommande annulée." + no-permission: "&cVous n'avez pas la permission d'utiliser cette commande." + use-in-game: "&cCette commande doit être exécutée en jeu." + no-team: "&cVous n'avez pas d'équipe!" + no-island: "&cVous n'avez pas d'île!" + player-has-no-island: "&cCe joueur n'a pas d'île!" + already-have-island: "&cVous possédez déjà une île!" + no-safe-location: "&cAucune position sécurisée n'a été trouvée sur cette île!" + not-leader: "&cVous n'êtes oas le leader de votre île!" + not-in-team: "&cCe joueur n'est pas dans votre équipe!" + offline-player: "&cCe joueur est déconnecté ou n'existe pas." + unknown-player: "&cCe joueur est inconnu." + general: "&cImpossible d'exécuter la commande. Contactez un administrateur." + warp-not-safe: "&cCe Warp n'est pas sécurisé." + wrong-world: "&cVous n'êtes pas de le bon monde pour effectuer cette action." + tips: + changing-ob-to-lava: "Transformation de l'obsidienne en lave. Faites attention!" + +commands: + help: + header: "&7=========== &c%bsb_plugin_name% &7===========" + syntax: "&b[usage] &a[parameters]&7: &e[description]" + end: "&7=================================" + parameters: "[command]" + description: "help command" + admin: + help: + description: "admin command" + version: + description: "display %bsb_plugin_name% and addons versions" + setrange: + parameters: "[player] [range]" + description: "set the range of player's island" + range-updated: "Island range updated to [number]" + reload: + description: "reload the plugin" + tp: + parameters: "[player]" + description: "teleport to a player's island" + manual: "&cNo safe warp found! Manually tp near to &b[location] &cand check it out" + island: + about: + description: "display info about %bsb_plugin_name%" + go: + parameters: "" + description: "teleport you to your island" + teleport: "&aTeleporting you to your island. &b/[label] help &afor help." + teleported: "&aTeleported you to home &e#[number]." + help: + description: "The main island command" + spawn: + description: "teleport you to the spawn" + create: + description: "create an island" + unable-create-island: "Your island could not be generated, please contact an administrator." + creating-island: "Creating your island..." + info: + description: "display info about your island" + reset: + description: "restart your island and remove the old one" + must-remove-members: "You must remove all members from your island before you can restart it (/island kick )." + sethome: + description: "set your teleport point for /island" + must-be-on-your-island: "You must be on your island to set home!" + num-homes: "Homes can be 1 to [max]." + home-set: "Your island home has been set to your current location." + parameters: "" + setname: + description: "set a name for your island" + name-too-short: "&cToo short. Minimum size is [length] characters." + name-too-long: "&cToo long. Maximum size is [length] characters." + parameters: "" + resetname: + description: "reset your island name" + team: + description: "manage your team" + info: + description: "display detailed info about your team" + invite: + description: "invite a player to join your island" + invitation-sent: "Invitation sent to [name]" + removing-invite: "Removing invite" + name-has-invited-you: "[name] has invited you to join their island." + to-accept-or-reject: "Do /island team accept to accept, or /island team reject to reject" + you-will-lose-your-island: "&cWARNING! You will lose your island if you accept!" + errors: + cannot-invite-self: "&cYou cannot invite yourself!" + cooldown: "&cYou cannot invite that person for another [time] seconds" + island-is-full: "&cYour island is full, you can't invite anyone else." + none-invited-you: "&cNo one invited you :c." + you-already-are-in-team: "&cYou are already on a team!" + already-on-team: "&cThat player is already on a team!" + invalid-invite: "&cThat invite is no longer valid, sorry." + parameters: "" + you-can-invite: "You can invite [number] more players." + accept: + description: "accept an invitation" + you-joined-island: "&aYou joined an island! Use /[label] team info to see the other members." + name-joined-your-island: "&a[name] joined your island!" + reject: + description: "reject an invitation" + you-rejected-invite: "&aYou rejected the invitation to join an island." + name-rejected-your-invite: "&c[name] rejected your island invite!" + cancel: + description: "cancel the pending invite to join your island" + leave: + description: "leave your island" + type-again: "&cEnter the leave command again to confirm" + left-your-island: "&c[player] left your island" + kick: + description: "remove a member from your island" + parameters: "" + type-again: "&cEnter the kick command again to confirm" + leader-kicked: "&cThe leader kicked you from the island!" + demote: + description: "demote a player on your island down a rank" + parameters: "" + failure: "&cPlayer cannot be demoted any further!" + success: "Demoted [name] to [rank]" + promote: + description: "promote a player on your island up a rank" + parameters: "" + failure: "&cPlayer cannot be promoted any further!" + success: "Promoted [name] to [rank]" + setowner: + description: "transfer your island ownership to a member" + errors: + cant-transfer-to-yourself: "&cYou can't transfer ownership to yourself! Well, infact, you could... But we don't want you to. 'Cause it's bad." + target-is-not-member: "&cThat player is not part of your island team!" + name-is-the-owner: "&a[name] is now the island owner!" + parameters: "" + you-are-the-owner: "&aYou are now the island owner!" + ban: + description: "ban a player from your island" + parameters: "" + unban: + description: "unban a player from your island" + parameters: "" + banlist: + description: "list banned players" + lock: + description: "lock/unlock your island so visitors cannot enter it" + settings: + description: "display island settings" + language: + description: "select language" + +ranks: + owner: "Owner" + member: "Member" + coop: "Coop" + visitor: "Visitor" + banned: "Banned" + +protection: + protected: "&cIsland protected!" + flags: + ANVIL: + name: "Anvils" + description: "Toggle interaction with anvils" + ARMOR_STAND: + name: "Armor stands" + description: "Toggle interaction with armor stands" + BEACON: + name: "Beacons" + description: "Toggle interaction with beacons" + BED: + name: "Beds" + description: "Toggle interaction with beds" + BREAK_BLOCKS: + name: "Break blocks" + description: "Toggle block breaking" + BREEDING: + name: "Breed animals" + description: "Toggle animal breeding" + BREWING: + name: "Brewing stands" + description: "Toggle interaction with brewing stands" + BUCKET: + name: "Buckets" + description: "Toggle interaction with buckets" + COLLECT_WATER: + name: "Collect water" + description: | + Toggle collecting water using buckets + (override Buckets) + COLLECT_LAVA: + name: "Collect lava" + description: | + Toggle collecting lava using buckets + (override Buckets) + CHORUS_FRUIT: + name: "Chorus fruits" + description: "Toggle teleportation using Chorus fruits" + CRAFTING: "Use crafting table" + CROP_TRAMPLE: "Trample crops" + DOOR: "Use doors" + ELYTRA: "Use elytras" + ENCHANTING: "Use enchanting table" + ENTER_EXIT_MESSAGES: "Display entry and exit messages" + FIRE: "FIRE" + FIRE_EXTINGUISH: "FIRE_EXTINGUISH" + FIRE_SPREAD: "FIRE_SPREAD" + FURNACE: "FURNACE" + GATE: "GATE" + HURT_ANIMALS: "HURT_ANIMALS" + HURT_MONSTERS: "HURT_MONSTERS" + HURT_VILLAGERS: "HURT_VILLAGERS" + ITEM_DROP: "ITEM_DROP" + ITEM_PICKUP: "ITEM_PICKUP" + LEASH: "LEASH" + MILKING: "MILKING" + MOB_SPAWN: "MOB_SPAWN" + MONSTER_SPAWN: "MONSTER_SPAWN" + MOUNT_INVENTORY: "MOUNT_INVENTORY" + RIDING: "RIDING" + MUSIC: "MUSIC" + CHEST: "CHEST" + PLACE_BLOCKS: "PLACE_BLOCKS" + PORTAL: "PORTAL" + PRESSURE_PLATE: "PRESSURE_PLATE" + PVP_OVERWORLD: "PVP_OVERWORLD" + PVP_NETHER: "PVP_NETHER" + PVP_END: "PVP_END" + REDSTONE: "REDSTONE" + SPAWN_EGGS: "SPAWN_EGGS" + SHEARING: "SHEARING" + EGGS: "EGGS" + TRADING: "TRADING" + panel: + title: "Island flags" + flag-item: + name-layout: "&a[name]" + description-layout: |+ + &a[description] + + &7Allowed for: &f[rank] + help-item: + name: "&aNeed some help?" + island: + locked: "&cCette île est verrouillée!" + +language: + panel-title: "Choisissez votre langue" + selected: "&aActuellement sélectionné." + edited: "&aLangue modifiée en &e[lang]&a." + +new-island: + sign: + line0: "&1%bsb_plugin_name%" + line1: "[player]" + line2: "Ne tombez pas!" + line3: "Bon jeu! &c<3" \ No newline at end of file diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml new file mode 100644 index 0000000..d229e8e --- /dev/null +++ b/locales/pl-PL.yml @@ -0,0 +1,551 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# RikoDEV: maintainer +# +# This translation is adapted to version : [alpha-2] + +banner: "BANNER:1:WHITE:HALF_VERTICAL:RED" + +general: + success: "&aSukces!" + request-cancelled: "&cUpłynął limit czasu na potwierdzenie - &bżądanie anulowane" + previous-request-cancelled: "&6Poprzednie żądanie potwierdzenia zostało anulowane" + confirm: "&cWpisz ponownie polecenie w ciągu &b[seconds]s&c aby potwierdzić." + errors: + command-cancelled: "&cKomenda anulowana" + no-permission: "&cNie masz uprawnień do wykonywania tego polecenia." + use-in-game: "&cTo polecenie jest dostępne tylko w grze." + no-team: "&cNie masz drużyny!" + no-island: "&cNie masz wyspy!" + player-has-island: "&cGracz ma już wyspę!" + player-has-no-island: "&cTen gracz nie ma wyspy!" + already-have-island: "&cMasz już wyspę!" + no-safe-location: "&cNa wyspie nie znaleziono bezpiecznej lokalizacji!" + not-leader: "&cNie jesteś liderem swojej wyspy!" + not-in-team: "&cTen gracz nie jest w twojej drużynie." + offline-player: "&cTen gracz jest offline lub nie istnieje." + unknown-player: "&cNieznany gracz!" + unknown-player-name: "&c[name] jest nieznanym graczem!" + general: "&cTo polecenie nie jest jeszcze gotowe - skontaktuj się z administratorem" + unknown-command: "&cNieznane polecenie. Wpisz &b/[label] help &caby wyświetlić pomoc." + warp-not-safe: "&cTen warp nie jest teraz bezpieczny!" + wrong-world: "&cNie jesteś we właściwym świecie, aby to zrobić!" + you-must-wait: "&cMusisz poczekać [number]s, zanim będziesz mógł wykonać to polecenie ponownie." + you-need: "&cPotrzebujesz [permission]" + tips: + changing-obsidian-to-lava: "Zmiana obsydianu z powrotem w lawę. Bądź ostrożny!" + +commands: + # Parameters in <> are required, parameters in [] are optional + help: + header: "&7=========== &cpomoc [label] &7===========" + syntax: "&b[usage] &a[parameters]&7: &e[description]" + end: "&7=================================" + parameters: "[command]" + description: "komenda help" + admin: + help: + parameters: "" + description: "komenda admin" + team: + add: + parameters: " " + description: "dodaj gracza do drużyny lidera" + name-not-leader: "&c[name] nie jest liderem" + name-has-island: "&c[name] ma wyspę. Najpierw musi ją wyrejestrować lub usunąć." + disband: + parameters: "" + description: "rozwiąż drużynę lidera wyspy" + user-disband-leader: "&cNie jesteś liderem! Użyj disband [leader]" + disbanded: "&cAdministrator rozwiązał Twoją drużyne!" + kick: + parameters: "" + description: "wyrzuć gracza z drużyny" + cannot-kick-leader: "&cNie możesz wyrzucić lidera drużyny. Najpierw wyrzuć członków." + admin-kicked: "&cAdministrator wyrzucił Cię z drużyny." + makeleader: + parameters: "" + description: "uczyń gracza liderem wyspy" + already-leader: "&cGracz jest już liderem!" + register: + parameters: "" + description: "zarejestruj gracza na niezamieszkanej wyspie, na której jesteś" + registered-island: "&aZarejestrowano gracza na wyspie na [xyz]." + already-owned: "&cWyspa jest już własnością innego gracza!" + no-island-here: "&cTu nie ma wyspy. Potwierdź, aby ją utworzyć." + unregister: + parameters: "" + description: "wyrejestruj właściciela z wyspy, ale zachowaj bloki wyspy" + unregistered-island: "&aWyrejestrowano gracza z wyspy [xyz]." + info: + parameters: "" + description: "zdobądź informacje o tym, gdzie jesteś lub wyspie gracza" + no-island: "&cNie jesteś teraz na wyspie..." + title: "======= Informacje o wyspie =========" + owner: "Lider: [owner] ([uuid])" + last-login: "Ostatnie logowanie: [date]" + deaths: "Śmierci: [number]" + resets-left: "Pozostałe resety: [number]/[total]" + team-members-title: "Członkowie drużyny:" + team-owner-format: "&a[name] [rank]" + team-member-format: "&b[name] [rank]" + island-location: "Lokalizacja wyspy: [xyz]" + island-coords: "Współrzędne wyspy: [xz1] to [xz2]" + protection-range: "Rozmiar ochrony: [range]" + protection-coords: "Współrzędne ochrony: [xz1] to [xz2]" + is-spawn: "Wyspa jest wyspą odradzania" + banned-players: "Zablokowani gracze:" + banned-format: "&c[name]" + unowned: "&cNiezamieszkana" + version: + description: "wyświetl %bsb_plugin_name% i wersje dodatków" + setrange: + parameters: " " + description: "ustaw zasięg wyspy gracza" + range-updated: "Zaktualizowano zasięg wyspy do [number]" + reload: + description: "ponownie załaduj plugin" + tp: + parameters: "" + description: "teleportuj się na wyspę gracza" + manual: "&cNie znaleziono bezpiecznego warpu. Teleportuj się ręcznie do lokalizacji &b[location] &ci sprawdź go." + getrank: + parameters: "" + description: "zdobądź role gracza na jego wyspie" + rank-is: "&aRola [rank] na jego wyspie." + setrank: + parameters: " " + description: "ustawia role gracza na swojej wyspie" + unknown-rank: "&cNieznana rola!" + rank-set: "&aRola ustawiona z [from] na [to]." + schem: + parameters: "" + description: "manipuluj schematem" + copy-first: "&cNajpierw skopiuj schemat!" + file-exists: "&cPlik już istnieje, nadpisać?" + no-such-file: "&cNie ma takiego pliku!" + could-not-load: "&cNie można załadować tego pliku!" + could-not-save: "&cHmm, something went wrong saving that file: [message]" + set-pos1: "&aPozycja 1 ustawiona na [vector]" + set-pos2: "&aPozycja 2 ustawiona na [vector]" + set-different-pos: "&cUstaw inną lokalizację - ta pozycja jest już ustawiona!" + need-pos1-pos2: "&cNajpierw ustaw pozycje 1, potem pozycje 2!" + copied-blocks: "&bSkopiowano [number] bloków do schowka." + look-at-a-block: "&cSpójrz na blok w ciągu 20 bloków, aby ustawić" + world: + description: "Zarządzaj ustawieniami świata" + island: + about: + description: "wyświetl informacje o prawach autorskich i licencji" + go: + parameters: "[home number]" + description: "teleportuj się na wyspe" + teleport: "&aTeleportacja na Twoją wyspe..." + teleported: "&aTeleportowano do domu &e#[number]." + tip: "&bWpisz /[label] help&a, aby otrzymać pomoc." + help: + description: "Główne komendy wyspy" + pick-world: "&cWybierz świat z dostępnych: [worlds]" + spawn: + description: "teleportacja na spawn" + create: + description: "stwórz wyspe" + unable-create-island: "Twoja wyspa nie mogła zostać wygenerowana, skontaktuj się z administratorem." + creating-island: "Tworzenie Twojej wyspy..." + pick-world: "&cWybierz świat z [worlds]" + info: + description: "wyświetl informacje o swojej wyspie" + reset: + description: "zresetuj swoją wyspę i usuń starą" + must-remove-members: "Musisz usunąć wszystkich członków ze swojej wyspy, zanim będzie można ją zresetować (/island kick )." + none-left: "&cNie pozostało więcej resetów wyspy!" + resets-left: "&cPozostało Ci [number] resetów wyspy." + sethome: + description: "ustaw swój punkt teleportacji dla komendy /island" + must-be-on-your-island: "Musisz być na swojej wyspie, aby ustawić dom!" + num-homes: "Domy mogą być od 1 do [number]." + home-set: "Twój dom na wyspie został ustawiony w bieżącej lokalizacji." + parameters: "[home number]" + setname: + description: "ustaw nazwę swojej wyspy" + name-too-short: "&cZbyt krótki. Minimalna długość to [number] znaków." + name-too-long: "&cZbyt krótki. Minimalna długość to [number] znaków." + parameters: "" + resetname: + description: "zresetuj swoją nazwę wyspy" + team: + description: "zarządzaj swoją drużynom" + info: + description: "wyświetl szczegółowe informacje o swojej drużynie" + invite: + description: "zaproś gracza, by dołączył do Twojej wyspy" + invitation-sent: "Zaproszenie wysłane do [name]" + removing-invite: "Usuwanie zaproszenia" + name-has-invited-you: "[name] zaprosił cię do przyłączenia się do jego wyspy." + to-accept-or-reject: "Wpisz /island team accept, aby zaakceptować zaproszenie lub /island team reject aby odmówić." + you-will-lose-your-island: "&cUWAGA! Jeśli zaakceptujesz zaproszenie, stracisz swoją wyspę!" + errors: + cannot-invite-self: "&cNie możesz zaprosić samego siebie!" + cooldown: "&cNie możesz zaprosić tej osoby przez [number] sekund." + island-is-full: "&cTwoja wyspa jest pełna, nie możesz zaprosić nikogo innego." + none-invited-you: "&cNikt Cię nie zaprosił :(." + you-already-are-in-team: "&cJesteś już w drużynie." + already-on-team: "&cTen gracz jest już w drużynie." + invalid-invite: "&cTo zaproszenie nie jest już ważne, przepraszam." + parameters: "" + you-can-invite: "Możesz zaprosić jeszcze [number] graczy." + accept: + description: "akceptuj zaproszenie" + you-joined-island: "&aDołączyłeś do wyspy! Wpisz /[label] team info, aby zobaczyć liste innych graczy na wyspie." + name-joined-your-island: "&a[name] dołączył do Twojej wyspy!" + reject: + description: "odrzuć zaproszenie" + you-rejected-invite: "&aOdrzuciłeś zaproszenie do dołączenia do wyspy." + name-rejected-your-invite: "&c[name] odrzucił zaproszenie do wyspy." + cancel: + description: "anuluj oczekujące zaproszenie do dołączenia do Twojej wyspy" + leave: + cannot-leave: "&cLider drużyny nie może opuścić wyspy. Zostań jej członkiem, lub wyrzuć wszystkich graczy z wyspy." + description: "opuść swoją wyspę" + left-your-island: "&c[name] opuścił Twoją wyspę." + kick: + description: "wyrzuć gracza z swojej wyspy" + parameters: "" + leader-kicked: "&cLider wyrzucił Cię z jego wyspy." + cannot-kick: "&cNie możesz wyrzucić samego siebie." + demote: + description: "degraduj gracza na swojej wyspie do roli" + parameters: "" + failure: "&cGracza nie można zdegradować niżej." + success: "Zdegradowano [name] do roli [rank]" + promote: + description: "awansuj gracza na swojej wyspie do roli" + parameters: "" + failure: "&cGracza nie można awansować wyżej!" + success: "Awansowano [name] do roli [rank]" + setowner: + description: "przenieś własność wyspy na innego członka" + errors: + cant-transfer-to-yourself: "&cNie możesz przenieść własności na siebie! Cóż, w rzeczywistości, możesz... Ale nie chcemy możemy na to pozwolić. Bo to jest złe." + target-is-not-member: "&cTen gracz nie jest częścią Twojej drużyny!" + name-is-the-owner: "&a[name] jest teraz liderem wyspy!" + parameters: "" + you-are-the-owner: "&aJesteś teraz liderem wyspy!" + ban: + description: "zbanuj gracza na swojej wyspie" + parameters: "" + cannot-ban-yourself: "&cNie możesz się zbanować!" + cannot-ban: "&cTego gracza nie można zbanować." + cannot-ban-member: "&cNajpierw wyrzuć członka drużyny, a następnie zablokuj." + player-already-banned: "&cGracz jest już zbanowany." + owner-banned-you: "&b[name]&c zbanował Cię na swojej wyspy!" + you-are-banned: "&bJesteś zablokowany na tej wyspie." + unban: + description: "odblokuj gracza na swojej wyspie" + parameters: "" + cannot-unban-yourself: "&cNie możesz się odblokować!" + player-not-banned: "&cGracz nie jest zbanowany na wyspie." + you-are-unbanned: "&b[name]&a odblokował Cię na swojej wyspie." + banlist: + description: "lista zablokowanych graczy" + noone: "&aNikt nie jest zbanowany na tej wyspie." + the-following: "&bNastępujący gracze są zbanowani:" + names: "&c[line]" + settings: + description: "wyświetl ustawienia wyspy" + language: + description: "wybierz język" + +ranks: + owner: "Lider" + member: "Członek" + coop: "Partner" + visitor: "Visitor" + banned: "Zbanowany" + admin: "Admin" + mod: "Mod" + +protection: + flags: + ANIMAL_SPAWN: + description: "Przełącz spawnowanie" + name: "Spawnowanie zwierząt." + ANVIL: + description: "Przełącz użycie" + name: Kowadła + hint: "Używanie kowadeł wyłączone." + ARMOR_STAND: + description: "Przełącz użycie" + name: "Stojaki na zbroje" + hint: "Używanie stojaków na zbroje wyłączone." + BEACON: + description: "Przełącz interakcje" + name: "Magiczne latarnie" + hint: "Używanie magicznych latarni wyłączone." + BED: + description: "Przełącz interakcje" + name: "Łóżka" + hint: "Używanie łóżka wyłączone." + BREAK_BLOCKS: + description: "Przełącz interakcje" + name: "Niszczenie bloków" + hint: "Niszczenie bloków wyłączone." + BREEDING: + description: "Przełącz interakcje" + name: "Rozmnażanie zwierząt" + hint: "Rozmnażanie zwierząt wyłączone." + BREWING: + description: "Przełącz interakcje" + name: "Statywy alchemiczne" + hint: "Używanie statywów alchemicznych wyłączone." + BUCKET: + description: "Przełącz interakcje" + name: Wiaderka + hint: "Używanie wiaderek wyłączone." + CHEST: + description: "Przełącz otwieranie skrzyń" + name: Skrzynki + hint: "Otwieranie skrzyń wyłączone." + CHORUS_FRUIT: + description: "Przełącz teleportacje" + name: "Owoce refrenusu" + hint: "Używanie teleportacji wyłączone." + COLLECT_LAVA: + description: | + Przełącz zbieranie lawy + (nadpisuje wiaderka) + name: "Zbieranie lawy" + hint: "Zbieranie lawy wyłączone." + COLLECT_WATER: + description: | + Przełącz zbieranie wody + (nadpisuje wiaderka) + name: "Zbieranie wody" + hint: "Zbieranie wody wyłączone." + CRAFTING: + description: "Przełącz użycie" + name: "Stół rzemieślniczy" + hint: "Używanie stołu rzemieślniczego wyłączone." + CROP_TRAMPLE: + description: "Przełącz deptanie roślin" + name: "Deptanie roślin" + hint: "Rośliny są chronione przed deptaniem." + DOOR: + description: "Przełącz używanie drzwi" + name: "Używanie drzwi" + hint: "Używanie drzwi wyłączone." + EGGS: + description: "Przełącz rzucanie jajkami" + name: "Rzucanie jajkami" + hint: "Rzucanie jajkami wyłączone." + ELYTRA: + description: "Przełącz użycie" + name: "Elytry" + hint: "Latanie na elytrze wyłączone." + ENCHANTING: + description: "Przełącz użycie" + name: "Stół do zaklęć" + hint: "Używanie stołu do zaklęć wyłączone." + ENDER_CHEST: + description: "Przełącz użycie/tworzenie" + name: "Skrzynia kresu" + hint: "Skrzynie kresu są wyłączone w tym świecie." + ENDER_PEARL: + description: "Przełącz użycie" + name: "Perła kresu" + hint: "Używanie pereł kresu wyłączone." + ENTER_EXIT_MESSAGES: + description: "Wyświetlaj komunikaty wejścia i wyjścia" + island: "Wyspa [name]" + name: "Wiadomości wejścia / wyjścia" + now-entering: "Wchodzisz na wyspe gracza [name]" + now-leaving: "Wychodzisz z wyspy gracza [name]" + FIRE: + description: "Pozwól, by ogień istniał lub nie" + name: "Ogień" + hint: "Ogień jest niedozwolony." + FIRE_EXTINGUISH: + description: "Przełącz gaszenie ognia" + name: "Gaszenie ognia" + hint: "Gaszenie ognia jest wyłączone." + FIRE_SPREAD: + description: "Przełącz rozprzestrzenianie" + name: "Rozprzestrzenianie ognia" + hint: "Ogień nie rozprzestrzenia się." + FURNACE: + description: "Przełącz użycie" + name: "Piec" + hint: "Używanie piecy jest wyłączone." + GATE: + description: "Przełącz użycie" + name: "Furtki" + hint: "Używanie furtek jest wyłączone." + HURT_ANIMALS: + description: "Przełącz zabijanie zwierząt" + name: "Zabijanie zwierząt" + hint: "Zabijanie zwierząt jest wyłączone." + HURT_MONSTERS: + description: "Przełącz zabijanie potworów" + name: "Zabijanie potworów" + hint: "Zabijanie potworów jest wyłączone." + HURT_VILLAGERS: + description: "Przełącz zabijanie osadników" + name: "Zabijanie osadników" + hint: "Zabijanie osadników jest wyłączone." + INVINCIBLE_VISITORS: + description: | + &aUstawienia nieśmiertelnych + &agości na wyspie. + name: "Nieśmiertelni goście" + hint: "&cGoście są chronieni przez obrażeniami." + ISLAND_RESPAWN: + description: | + &aOdradzanie graczy + &apo śmierci na wyspie. + name: "Odradzanie na wyspie" + ITEM_DROP: + description: "Przełącz wyrzucanie przedmiotów" + name: "Wyrzucanie przedmiotów" + hint: "Przedmioty nie mogą być wyrzucone." + ITEM_PICKUP: + description: "Przełącz podnoszenie przedmiotów" + name: "Podnoszenie przedmiotów" + hint: "Przedmioty nie mogą być podnoszone." + LEASH: + description: "Przełącz użycie" + name: "Używanie smyczy" + LEVER_BUTTON: + description: "Przełącz użycie" + name: "Wykorzystanie dźwigni lub przycisku" + hint: "Używanie smyczy wyłączone." + LOCK: + description: "Przełącz dostęp do wyspy" + name: "Blokada wyspy" + MILKING: + description: "Przełącz dojenie krów" + name: "Dojenie" + hint: "Dojenie krów jest wyłączone." + MONSTER_SPAWN: + description: "Przełącz spawnowanie" + name: "Sprawnowanie potworów" + MOUNT_INVENTORY: + description: | + &aPrzełącz dostęp + &ado ekwipunku mobów. + name: "Ekwipunek zwierząt" + hint: "Używanie ekwipunku zwierząt wyłączone." + MUSIC: + description: "Przełącz użycie" + name: "Muzyka" + hint: "Używanie szafy grającej wyłączone." + PISTON_PUSH: + description: | + &aPozwól tłokom przepychać + &abloki poza teren wyspy. + name: "Przepychanie pistonem" + PLACE_BLOCKS: + description: "Przełącz interakcje" + name: "Stawianie bloków" + hint: "Stawianie bloków wyłączone." + PORTAL: + description: "Przełącz użycie" + name: Portal + hint: "Używanie portalu jest wyłączone." + PRESSURE_PLATE: + description: "Przełącz użycie" + name: "Płytki naciskowe" + hint: "Używanie płytek naciskowych wyłączone." + PVP_END: + description: | + &cWłącz / wyłącz pvp + &cw świecie kresu. + name: "PVP w kresie" + hint: "&cPVP jest wyłączone w świecie kresu." + PVP_NETHER: + description: | + &cWłącz / wyłącz pvp + &cw piekle. + name: "PVP w piekle" + hint: "&cPVP jest wyłączone w piekle." + PVP_OVERWORLD: + description: | + &cWłącz / wyłącz pvp + &cna wyspie. + name: "PVP na wyspie" + hint: "&cPVP jest wyłączone na wyspie." + REDSTONE: + description: "Przełącz dostęp" + name: "Przedmioty redstone" + hint: "Używanie przedmiotów redstone wyłączone" + RIDING: + description: "Przełącz ujeżdzanie zwierząt" + name: "Ujeżdzanie zwierząt" + hint: "Ujeżdzanie zwierząt jest wyłączone." + REMOVE_MOBS: + description: | + &aUsuwanie potworów + &apodczas teleportacji na wyspe. + name: "Usuwanie potworów" + SHEARING: + description: "Przełącz strzyżenie owiec" + name: "Strzyżenie owiec" + hint: "Strzyżenie owiec wyłączone." + SPAWN_EGGS: + description: "Przełącz spawnowanie" + name: "Jajka spawnujące" + hint: "Używanie jajek spawnujących wyłączone." + TRADING: + description: "Przełącz handlowanie" + name: "Handlowanie z osadnikami" + hint: "Handlowanie z osadnikami wyłączone." + TRAPDOOR: + description: "Przełącz użycie" + name: "Klapy" + hint: "Używanie klap wyłączone." + locked: "&cTa wyspa jest zamknięta!" + protected: "&cWyspa chroniona: [description]" + + panel: + PROTECTION: + title: "Ochrona" + description: | + Ustawienia ochrony + dla tej lokalizacji + SETTING: + title: "Ustawienia" + description: "Ustawienia główne" + WORLD_SETTING: + title: "Ustawienia [world_name]" + description: "Ustawienia dla tego świata gry" + flag-item: + name-layout: "&a[name]" + description-layout: |+ + &a[description] + + &7Dozwolony dla: + allowed_rank: "&3- &a" + blocked_rank: "&3- &c" + minimal_rank: "&3- &2" + menu-layout: "&a[description]" + setting-layout: |+ + &a[description] + + &7Obecne ustawienie: [setting] + setting-active: "&aAktywny" + setting-disabled: "&cNieaktywny" + +language: + panel-title: "Wybierz swój język" + selected: "&aObecnie wybrany." + edited: "&aZmieniono język na &e[lang]&a." + +new-island: + sign: + line0: "&1%bsb_plugin_name%" + line1: "[name]" + line2: "Nie spadnij!" + line3: "Baw się dobrze! &c<3" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml new file mode 100644 index 0000000..135161b --- /dev/null +++ b/locales/vi-VN.yml @@ -0,0 +1,269 @@ +########################################################################################### +# Đây là 1 file YML, hãy cẩn thận khi chỉnh sửa, nên dùng trang web dể kiểm tra n # +# cái này http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# tastybento: Người cung cấp bản YML gốc +# Poslovitch: Người cung cấp bản YML gốc +# +# Banbeucmas: Người dịch bản YML gốc +# This translation is adapted to version : [alpha-2] + +banner: "BANNER:1:RED:CREEPER:YELLOW:RHOMBUS_MIDDLE:YELLOW:TRIANGLES_BOTTOM:RED:SQUARE_BOTTOM_LEFT:RED:SQUARE_BOTTOM_RIGHT:RED" + +general: + deaths: "Chết" + unlimited: "Vô hạn" + success: "&aThành công!" + errors: + command-cancelled: "&cĐã hủy lệnh" + no-permission: "&cBạn không có quyền thực thi lệnh này." + use-in-game: "&cLệnh này chỉ dùng được trong game." + no-team: "&cBạn không ở trong 1 đội nào cả!" + no-island: "&cBạn không có đảo!" + player-has-no-island: "&cNgười chơi đó không có đảo!" + already-have-island: "&cBạn đã có đảo!" + no-safe-location: "&cKhông có địa điểm an toàn nào trên đảo!" + not-leader: "&cBạn không phải đội trưởng!" + not-in-team: "&cNgười chơi không có trong đội!" + offline-player: "&cNgười chơi đang offline hoặc không tồn tại." + unknown-player: "&cNgười chơi không tìm thấy!" + general: "&cLệnh chưa sẵn sàng - hãy liên hệ admin" + warp-not-safe: "&cCổng dịch chuyển không an toàn!" + wrong-world: "&cBạn đang không ở trong thế giới có thể làm được việc này!" + tips: + changing-ob-to-lava: "Đang chuyển Hắc Diện Thạch về Dung Nham, hãy cẩn thận" + +commands: + help: + header: "&7=========== &c%bsb_plugin_name% &7===========" + syntax: "&b[usage] &a[parameters]&7: &e[description]" + end: "&7=================================" + parameters: "[command]" + description: "help command" + admin: + help: + description: "xem lệnh dành cho admin" + parameters: "" + version: + description: "Hiện phiên bản %bsb_plugin_name% và các bổ sung" + setrange: + parameters: "[player] [range]" + description: "Đặt giới hạn đảo người chơi" + range-updated: "Giới hạn đảo đã chuyển sang [number]" + reload: + description: "Nạp lại plugin" + tp: + parameters: "[player]" + description: "Dịch chuyển đến đảo người chơi" + manual: "&cKhông tìm thấy cổng dịch chuyển an toàn! Hãy tp tới địa điểm &b[location] &cvà kiểm tra" + island: + about: + description: "hiện thông tin về %bsb_plugin_name%" + go: + parameters: "" + description: "dịch chuyển tới đảo" + teleport: "&aĐang dịch chuyển. &b/[label] help &ađể được giúp đỡ." + teleported: "&aĐã dịch chuyển bạn tới khu vực nhà số &e#[number]." + help: + description: "Các lệnh chính" + spawn: + description: "dịch chuyển ra spawn" + create: + description: "tạo đảo" + unable-create-island: "Không thể khởi tạo đảo, hãy liên hệ admin." + creating-island: "Đang tạo đảo..." + info: + description: "hiện thông tin đảo của bạn" + reset: + description: "làm lại đảo của bạn" + must-remove-members: "Hãy đuổi toàn bộ thành viên trước khi khởi tạo lại đảo (/island kick )." + sethome: + description: "đặt bị trí dịch chuyển cho /island" + must-be-on-your-island: "Bạn phải ở trên đảo của bạn!" + num-homes: "Số nhà có thể đặt từ 1 đến [max]." + home-set: "Đã đặt vị trí nhà tại địa điểm hiện tại của bạn." + parameters: "" + setname: + description: "đặt tên đảo" + name-too-short: "&cQuá ngắn. Giới hạn thấp nhất là [length] kí tự." + name-too-long: "&cQuá dài. Giới hạn tối đa là [length] kí tự." + parameters: "" + resetname: + description: "làm lại tên đảo" + team: + description: "quản lý đội" + info: + description: "hiện thông tin chi tiết về đội" + invite: + description: "mới người chơi đến đảo" + invitation-sent: "Đã gửi lời mời tới [name]" + removing-invite: "Đang bỏ lời bời" + name-has-invited-you: "[name] đã mời bạn vào đảo." + to-accept-or-reject: "Ghi /island team accept để chấp nhận, hay /island team reject để từ chối" + you-will-lose-your-island: "&cCẢNH BÁO! Bạn sẽ mất đảo nếu chấp nhận" + errors: + cannot-invite-self: "&cBạn không thể mới chính mình" + cooldown: "&cBạn không thế mời người khác trong [time] giây" + island-is-full: "&cĐảo đã đầy, không thể mời thêm" + none-invited-you: "&cKhông ai mời bạn :c." + you-already-are-in-team: "&cBạn đã ở trong 1 đội!" + already-on-team: "&cNgười chơi đó đã ở trong 1 đội!" + invalid-invite: "&cLời mời vô hiệu, xin lỗi." + parameters: "" + you-can-invite: "Bạn có thể mời thêm [number] người." + accept: + description: "chấp nhận lời mời" + you-joined-island: "&aBạn đã tham gia đảo! Sử dụng /[label] team info để biết thêm thông tin." + name-joined-your-island: "&a[name] đã vào đảo của bạn!" + reject: + description: "từ chối lời mời" + you-rejected-invite: "&aBạn đã từ chối lời mời vào đảo." + name-rejected-your-invite: "&c[name] đã từ chối lời mời của bạn!" + cancel: + description: "hủy lời mời hiện tại" + leave: + description: "rời đảo" + type-again: "&cGõ lệnh rời đảo 1 lần nữa để xác nhận" + left-your-island: "&c[player] đã rời đảo" + kick: + description: "loại bỏ 1 người chơi khỏi đảo" + parameters: "" + type-again: "&cHãy đánh lệnh loại bỏ 1 lần nữa để xác nhận" + leader-kicked: "&cĐội trưởng đã đuổi bạn ra khỏi đội!" + demote: + description: "hạ cấp bậc người chơi" + parameters: "" + failure: "&cNgười chơi không thể hạ cấp thêm nữa" + success: "Đã hạ cấp [name] đến bậc [rank]" + promote: + description: "thăng chức người chơi" + parameters: "" + failure: "&cKhông thể thăng chức nữa!" + success: "Đã thăng chức [name] lên bậc [rank]" + setowner: + description: "chuyển quyền sở hữu đảo" + errors: + cant-transfer-to-yourself: "&cBạn không thể chuyển quyền sở hữu cho chính mình" + target-is-not-member: "&cNgười chơi đó không phải là người trong đội!" + name-is-the-owner: "&a[name] giờ là chủ đảo!" + parameters: "" + you-are-the-owner: "&aBạn giờ là chủ đảo!" + ban: + description: "cấm 1 người chơi khỏi đảo" + parameters: "" + unban: + description: "bỏ cấm 1 người chơi khỏi đảo" + parameters: "" + banlist: + description: "những người chơi bị cấm" + lock: + description: "khóa/bỏ khóa khả năng truy cập đảo của bạn" + settings: + description: "hiện cài đặt đảo" + language: + description: "chọn ngôn ngữ" + parameters: "" + +ranks: + owner: "Chủ" + member: "Thành Viên" + coop: "Đối tác" + visitor: "Khách" + banned: "Bị cấm" + +protection: + protected: "&cĐã bảo vệ đảo!" + flags: + ANVIL: + name: "Đe" + description: "Tắt/bật tương tác với Đe" + ARMOR_STAND: + name: "Giá để giáp" + description: "Tắt/bật tương tác với giá để giáp" + BEACON: + name: "Beacons" + description: "Tắt/bật tương tác với beacons" + BED: + name: "Giường" + description: "Tắt/bật tương tác với giường" + BREAK_BLOCKS: + name: "Phá khối" + description: "Tắt/bật khả năng phá khối" + BREEDING: + name: "Giao phối" + description: "Tắt/bật khả năng giao phối của dộng vật" + BREWING: + name: "Brewing stands" + description: "Tắt/bật tương tác với brewing stands" + BUCKET: + name: "Xô" + description: "Tắt/bật tương tác với xô" + COLLECT_WATER: + name: "Lấy nước" + description: | + Tắt/bật khả năng lấy nước bằng xô + COLLECT_LAVA: + name: "Lấy dung nham" + description: | + Tắt/bật khả năng lấy nước bằng xô + CHORUS_FRUIT: + name: "Chorus fruits" + description: "Toggle teleportation using Chorus fruits" + CRAFTING: "Use crafting table" + CROP_TRAMPLE: "Trample crops" + DOOR: "Use doors" + ELYTRA: "Use elytras" + ENCHANTING: "Use enchanting table" + ENTER_EXIT_MESSAGES: "Display entry and exit messages" + FIRE: "FIRE" + FIRE_EXTINGUISH: "FIRE_EXTINGUISH" + FIRE_SPREAD: "FIRE_SPREAD" + FURNACE: "FURNACE" + GATE: "GATE" + HURT_ANIMALS: "HURT_ANIMALS" + HURT_MONSTERS: "HURT_MONSTERS" + HURT_VILLAGERS: "HURT_VILLAGERS" + ITEM_DROP: "ITEM_DROP" + ITEM_PICKUP: "ITEM_PICKUP" + LEASH: "LEASH" + MILKING: "MILKING" + MOB_SPAWN: "MOB_SPAWN" + MONSTER_SPAWN: "MONSTER_SPAWN" + MOUNT_INVENTORY: "MOUNT_INVENTORY" + RIDING: "RIDING" + MUSIC: "MUSIC" + CHEST: "CHEST" + PLACE_BLOCKS: "PLACE_BLOCKS" + PORTAL: "PORTAL" + PRESSURE_PLATE: "PRESSURE_PLATE" + PVP_OVERWORLD: "PVP_OVERWORLD" + PVP_NETHER: "PVP_NETHER" + PVP_END: "PVP_END" + REDSTONE: "REDSTONE" + SPAWN_EGGS: "SPAWN_EGGS" + SHEARING: "SHEARING" + EGGS: "EGGS" + TRADING: "TRADING" + panel: + title: "Island flags" + flag-item: + name-layout: "&a[name]" + description-layout: |+ + &a[description] + + &7Allowed for: &f[rank] + help-item: + name: "&aNeed some help?" + + + island: + locked: "&cThis island is locked!" + +new-island: + sign: + line0: "&1%bsb_plugin_name%" + line1: "[player]" + line2: "Đừng rơi" + line3: "Vui vẻ &c<3" \ No newline at end of file diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml new file mode 100644 index 0000000..94d6121 --- /dev/null +++ b/locales/zh-CN.yml @@ -0,0 +1,582 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +### Credits ### +# Tastybento: maintainer +# Poslovitch: maintainer +# DuckSoft: translator +# This translation is adapted to version : [alpha-1] + +banner: "BANNER:1:RED:SQUARE_TOP_RIGHT:YELLOW:CROSS:RED:CURLY_BORDER:RED:MOJANG:YELLOW:HALF_HORIZONTAL_MIRROR:RED:HALF_VERTICAL:RED" + +not-setup: + header: |- + 在此插件能正常运作之前, 您需要更多设置... + 请编辑 config.yml, 然后重启服务器. + distance: "确保您已设定过岛屿距离. 若要升级, 请设为原值." + generator: |- + 岛屿世界生成器注册失败. + 可能的潜在原因: + 1. 若您将岛屿世界作为服务器的唯一世界 + 确保您已将此世界添加到 bukkit.yml 中. + 2. 您重载了服务器而并未重启. 请重启服务器重试. + generator-multiverse: " 3. 您的 Multiverse 插件已过期. 请升级到最新版本." + world-name: |- + config.yml 中的世界名与 islands.yml 中的世界名不符. + 若您这样做是有目的的, 系统将假设您想完全重设岛屿世界. + 若是, 请删除 islands.yml 和原世界. + 若否, 请修正 config.yml 中的世界名并重启. 若您正在升级, 您很可能是这种情况. + config-outdated: |- + config.yml 似乎已过期. + 请核实您的配置文件是否经过更新. + 若此错误持续出现, 您很可能是编辑了旧的配置文件, 而非新的配置文件. + 如果是这样, 请删除当前的 config.yml, 参照 config.new.yml 创建一个新的 config.yml. + +general: + success: "成功!" + errors: + no-permission: "您无权执行此命令." + use-in-game: "本命令仅在游戏中有效." + +# TODO: These are legacy strings and should be converted to a better format but will do for now +acidBottle: "受到辐射的水瓶" +acidBucket: "污染的水桶" +acidLore: |- + 小心! 小心! 小心! + 重要的事情说三遍! + 千万不能喝! + +adminDeleteIsland: + error: "使用 &ldeleteisland confirm &r&c来删除你所在的岛屿." + noid: "无法识别岛屿." + use: "或者使用 &ldelete [name] &r&c来删除一个玩家." +adminHelp: + add: "向领导者的队伍增加玩家" + addrange: "增加或减少岛屿的保护区域大小." + clearreset: "重置一个玩家的岛屿初始化次数." + clearresetall: "为所有玩家重置岛屿的初始化次数." + cobblestats: "显示magic cobble的生成统计数据." + completeChallenge: "标记一个挑战为已完成" + delete: "删除一个玩家的岛屿 (移除方块)." + help: "管理员指令:" + info: "显示指定玩家信息." + infoisland: "显示离你最近的岛屿信息." + kick: "从任何队伍中踢出玩家." + level: "为玩家提供岛屿等级详情." + listChallengeResets: "列出挑战重置时间表(若有)" + lock: "锁定或解锁玩家的岛屿." + purge: "删除超过 [TimeInDays] 天的不活跃岛屿." + purgeallowdisallow: "允许/禁止一个岛屿被删除(若它们达到删除标准)." + purgeholes: "重置空余的岛屿,以便重复使用." + purgeunowned: "移除无主岛屿." + name: "为玩家岛屿命名." + register: "设置玩家的岛屿在你的位置" + reload: "配置重读." + resetAllChallenges: "重置该玩家的的所有挑战" + resetChallenge: "标记一个挑战为未完成" + resetChallengeForAll: "重设全服务器每位玩家的挑战(可选重复)" + resetname: "重设玩家岛屿名称" + resetsign: "将你盯着看的牌子重设给岛主" + setbiome: "设置领导者岛屿的生物群系." + setdeaths: "设置玩家的死亡计数." + setrange: "改变岛屿的保护范围." + setlanguage: "设定默认语言并应用于所有玩家." + setspawn: "设置岛屿世界的重生点在你的附近." + settingsreset: "重设所有岛屿到默认保护设定" + teamChatSpy: "偷听岛屿team聊天(开启/关闭)" + topTen: "手动更新前十列表" + topbreeders: "列出目前载入的最活跃的岛屿" + tp: "传送到玩家岛屿" + tpnether: "传送到玩家的下界岛屿" + unregister: "删除一个玩家的岛屿信息,但不清除岛屿内方块" +adminInfo: + bannedPlayers: "已被封禁的玩家" + errorNullTeamLeader: "团队领导必须为空!" + errorTeamMembersExist: "玩家拥有团队成员,但不应该!" + hoppers: "岛上有 [number] 个漏斗" + islandLocation: "岛屿坐标" + isLocked: "岛屿被锁定" + isProtected: "岛屿被保护免于清除" + isSpawn: "岛屿是出生地" + isUnlocked: "岛屿被解锁" + isUnprotected: "岛屿未被保护免于清除" + lastLogin: "最后登录" + maxSize: "岛屿最大大小(距离)" + player: "玩家" + teamLeader: "队伍领袖" + teamMembers: "队伍成员" + title: "这是出生地岛屿" + unowned: "本岛暂时无主." +adminLock: + adminLockedIsland: "管理员锁定了你的岛屿" + adminUnlockedIsland: "管理员解锁了你的岛屿" + errorInGame: "必须在游戏内使用该命令!" +adminRegister: + hadIsland: "[name] 在 [location] 处拥有一个岛屿" + leadsTeam: "[name] 拥有一个队伍. 请先踢光队里的人." + noIsland: "本区域内无已知空岛!" + notSpawn: "你不能占领出生地!" + taking: "正从 [name] 玩家接管岛屿" +adminResetChallengeForAll: + error: "重复次数的格式必须是整数 [integer number][m/h/d] (minutes, hours, days), 例如. 5h" + repeating: "重复 [duration]" + reset: "重设 [date]" +adminResetSign: + errorExists: "传送牌已被激活过并属于 [name]" + found: "发现传送牌!" + noSign: "在执行此命令时你必须盯着传送牌杆看." + rescued: "已拯救传送牌并指派给 [name]" +adminReserve: + islandExists: "此地已有一岛屿! Register the player instead!" +adminSetHome: + homeSet: "已设家于 [location]" + noneFound: "未发现安全位置!" + notOnPlayersIsland: "你不在玩家岛屿上" + notSafe: "这位置不安全" +adminSetRange: + invalid: "范围无效!" + set: "设定新范围为 [number]" + tip: "范围必须在 10 到 [max] 之间" + updated: "岛屿范围更新为 [number]" + warning: "警告 - 范围比岛屿范围 [max] 更大" + warning2: "重叠的岛屿将像出生地一样(无法被拥有)!" +adminSetSpawn: + center: "岛屿中心 [location]" + coords: "保护坐标从 [min] 到 [max]" + limits: "岛屿限制从 [min] 到 [max]" + locked: "出生地被锁定!" + move: "再走远点或解除所有者的注册." + ownedBy: "本岛空间由 [name] 拥有" + range: "保护区域大小 = [number]" + set: "设定岛屿出生点到你的位置." + setting: "设定岛屿出生点到你的位置 [location]." +adminTeam: + addLeaderNoIsland: "团队领袖没有自己的岛屿所以不能拥有队员!" + addLeaderToOwn: "无法让领袖再次加入同一团队." + addedLeader: "已向团队加入领袖!" + addingPlayer: "正向团队加入玩家." + alreadyOnTeam: "玩家已经属于本队!" + kickLeader: "该玩家是一名团队领袖. 先踢光队员. 使用 '/[label] info [name]' 来查找队员." + nowUnowned: "[name] 在 [location] 的岛屿现将成为无主之岛. 你可以手动删除它了." + settingHome: "设定玩家的家到领袖的家的位置" +adminTopBreeders: + checking: "正核对 [number] 个岛屿..." + finding: "正查找名列前茅的岛屿..." + nothing: "未发现生物." +adminTopTen: + finished: "已生成前十名单列表" + generating: "生成前十名单列表" +adminTp: + manualWarp: "未发现安全地点. 在 [location] 附近手动传送." +adminUnregister: + keepBlocks: "从世界中删除一个玩家, 但在 [location] 处仍保留原有空岛. " + onTeam: "玩家隶属于一个队伍——先把他赶出去." +ban: + alreadybanned: "[name] 早就被列入了黑名单!" + banned: "你被列入 [name] 的岛屿的黑名单!" + fail: "[name] 无法被列入黑名单!" + helpBan: "将一个玩家列入岛屿黑名单." + helpUnban: "将一个玩家从岛屿黑名单中移除" + helpBanList: "列出被封禁的玩家." + lifted: "被从 [name] 的岛屿黑名单中解除!" + liftedsuccess: "被 [name] 解除黑名单!" + none: "无" + notbanned: "[name] 未被列入黑名单!" + notteammember: "你无法将岛屿成员列入黑名单!" + notyourself: "你不能那样对自己做!" + success: "[name] 被岛屿列入黑名单!" +biome: + help: "打开生物群系界面." + paneltitle: "选择一个生物群系" + set: "岛屿生物群系设置为: [biome]!" + unknown: "未知生物群系!" + youbought: "购买花费: [cost]!" +boats: + warning: "现在离开船是一件危险的事情..." +challenges: + colors: "任务将根据不同难度显示不同颜色:" + complete: "已完成" + completeNotRepeatable: "已完成(不可重复)" + completeRepeatable: "已完成(可重复)" + completedtimes: "完成次数 [donetimes] / [maxtimes]" + errorIslandLevel: "你的岛屿等级必须达到 [level] 级才能够完成这个任务!" + errorItemsNotThere: "所有任务所需物品必须在你的岛上!" + errorNotCloseEnough: "任务所需物品必须在你 [number] 格范围内." + errorNotEnoughItems: "你没有足够的所需物品" + errorNotOnIsland: "你只能够在你的岛屿上那样做!" + errorRewardProblem: "奖励你物品的时候发生了未知错误,请联系管理员!" + erroryouaremissing: "未找到你的信息" + expReward: "经验奖励" + firstTimeRewards: "首次奖励" + guititle: "ASkyBlock 挑战" + help1: "输入 /c <挑战名字> 来查询一个挑战的信息." + help2: "输入 /c complete <挑战名字> 完成一个挑战." + incomplete: "未完成" + invalidChallengeName: "未知挑战名! 输入 /c help 获取更多信息" + itemTakeWarning: "当你完成挑战的时候所有挑战所需物品将会消失!" + level: "等级" + maxreached: "最大完成 [donetimes] / [maxtimes]" + moneyReward: "经济奖励" + name: "挑战名称" + nameHasCompleted: "恭喜玩家 [name] 完成了 [challenge] 挑战!" + navigation: "点击查看 [level] 级的挑战!" + notRepeatable: "这个挑战不可重复!" + repeatRewards: "重复奖励" + rewards: 奖励 + toComplete: "请至少再完成 [challengesToDo] 个等级为 [thisLevel] 的挑战来解锁当前等级挑战!" + toCompleteUse: "完成这个挑战,输入" + unknownChallenge: "未知挑战名 (请检查拼写)!" + youHaveCompleted: "你完成了 [challenge] 挑战!" + youHaveNotUnlocked: "你还没有解锁当前挑战!" + youRepeated: "你重复完成了 [challenge] 挑战!" +changingObsidiantoLava: "成功将黑曜石变为岩浆,请当心!" +checkteam: + checkingTeam: "检查 [name] 的团队" +completechallenge: + challangeCompleted: "[挑战系统]: [name] 完成了 [challengename] " + errorChallengeDoesNotExist: "挑战不存在或者已经完成" +confirm: + errorTimeLimitExpired: "超时,请重新运行指令." +coop: + cannotcoop: "只有岛屿领袖才能赋予其他玩家岛屿完全访问权限." + help: "暂时允许玩家完全访问你的岛屿" + invited: "[name] 囚禁了 [player] !" + listcoops: "列出协作的玩家" + madeyoucoopy: "[name] 把你囚禁了,直到你下线或者他们驱逐你." + notincoop: "[name] 未在你的团队中!" + onyourteam: "玩家早就在你的团队中了!" + removed: "[name] 取消了你的锁定状态!" + removesuccess: "[name] 不再是一个囚禁玩家." + success: "[name] 现在被你囚禁了,直到他们下线或者你驱逐他们." + uncoop: "移除玩家对您岛屿的完全访问" + useexpel: "输入 /expel 移除." +deaths: + deaths: "死亡次数" + died: "死了!" + leveldeaths: "&c[[number] 死]" +delete: + removing: "移除了 [name] 的岛屿." +drankAcid: "饮用辐射水源." +drankAcidAndDied: "饮用受到辐射的水而死亡." +error: + blockedbyapi: "此行为被某个使用API的插件阻止." + commandNotReady: "你现在不能使用此命令." + maxIslands: "已达到世界最大容许岛屿数! 请稍后再试!" + noIsland: "你还没有获得一个岛屿!" + noIslandOther: "玩家未拥有岛屿!" + noPermission: "你没有权限那样做!" + notABlock: "这不是一个方块" + noTeam: "玩家不再团队中." + notOnIsland: "你必须在你的岛屿上使用这个指令." + offlinePlayer: "玩家不在线或不存在." + tooLong: "太长. 最大长度为 [length]." + tooShort: "太短. 最小长度为 [length]." + unknownCommand: "未知指令." + unknownPlayer: "未知玩家." + useInGame: "这个指令必须在游戏内使用." + wrongWorld: "你无法在当前世界这样操作,请返回你的岛屿." + minishopDisabled: "迷你商店未启用." +expel: + expelled: "你被驱逐出了岛屿!" + fail: "[name] 无法被驱逐!" + notonisland: "玩家没有入侵你的岛屿!" + notyourself: "你无法驱逐你自己!" + success: "你驱逐了玩家: [name]!" +invite: + errorCantJoinIsland: "你不能加入这个岛屿, 可能岛屿已满员." + errorCoolDown: "你需要等待 [time] 分钟后才能再次邀请那个玩家" + errorThatPlayerIsAlreadyInATeam: "这个玩家已经被被其他岛屿邀请了, 或者已经拥有自己的岛屿." + errorYouCannotInviteYourself: "你不能邀请你自己入住!" + errorYouMustHaveIslandToInvite: "你必须拥有一个岛屿, 才能邀请其他玩家入住!" + errorYourIslandIsFull: "你的岛屿住满了人, 你没办法再邀请其他玩家了." + hasJoinedYourIsland: "[name] 加入了你的岛屿!" + help: "输入/[label] invite <玩家ID> 邀请玩家加入你的岛屿." + inviteSentTo: "邀请已发送给 [name]" + nameHasInvitedYou: "[name] 邀请你入住他的岛屿!" + onlyIslandOwnerCanInvite: "只有岛屿主人才能邀请其他玩家!" + removingInvite: "移除了你之前邀请的玩家." + toAcceptOrReject: "来接受或拒绝岛屿入住邀请." + warningYouWillLoseIsland: "警告: 如果你接受, 你将失去当前这个岛屿!" + youCanInvite: "你还能邀请 [number] 个玩家." + youCannotInvite: "你无法邀请更多的玩家了." + youHaveJoinedAnIsland: "你加入了一个岛屿! 使用 /[label] team 来查看其他成员." +island: + blockValue: "置于此处的 [name] 可能价值[value]" + blockWorthless: "[name] 一毛不值" + cannotTeleport: "你无法在下落过程中进行传送!" + donate: "本插件由tastybento提供" + donatecolor: "aqua" + error: + CouldNotCreateIsland: "无法为你创建岛屿. 请联系管理员." + InvalidPlayer: "玩家不存在或者他不曾拥有一个岛屿!" + LevelNotReady: "请稍等数秒后再输入此指令!" + YouDoNotHavePermission: "你没有权限那样做!" + YouAlreadyHaveAnIsland: "你已经有一个岛屿了!" + help: + AcceptReject: "接受或者拒绝别人的邀请." + Challenges: "输入指令/challenges: &7显示挑战任务" + Color: "&e" + ControlPanel: "打开岛屿帮助界面." + Expel: "强制从你的岛屿上请走一个玩家." + Invite: "邀请玩家加入你的岛屿." + Island: "开始岛屿生涯,或者回到你的岛屿." + IslandSpawn: "回到岛屿出生点." + Kick: "将玩家从你的岛屿移除." + Leave: "离开了当前岛屿." + Level: "计算你的岛屿等级" + LevelPlayer: "查询玩家的岛屿等级." + Lock: "岛屿已锁定, 非岛屿成员无法传送至岛屿上" + MakeLeader: "将岛屿赠送给玩家." + Name: "给你的岛屿起个名" + ResetName: "重置你的岛屿名称." + Restart: "重新开始你的岛屿生涯." + SelectLanguage: "语言选择(Select Language)" + SetHome: "设置返回岛屿的传送坐标(需要在自己岛屿内设置)." + Settings: "查看岛屿保护和游戏设置" + Team: "显示你团队的信息." + Teleport: "传送到你的岛屿." + Top: "查询岛屿等级排行榜." + Value: "查看手中方块的岛屿价值" + Warp: "传送到玩家的传送点." + Warps: "列出所有可用的传送点." + islandDeletedLifeboats: "岛屿已删除! 火速前往救生船!" + islandLevelis: "岛屿等级为" + new: "正在为你创建一个新的岛屿..." + requiredPointsToNextLevel: "你还需要 [points] 点数来达到 [next] 级!" + reset: + Confirm: "请在 [seconds] 秒内输入/[label] confirm 来确认你的操作,然后重新开始!" + MustRemovePlayers: "你只有将岛屿所有成员踢出才能重新开始 (/[label] kick <玩家ID>). 输入/[label] team 可以查询成员列表." + NoMore: "你无法再重新开始了!" + OnlyOwner: "只有岛屿的主人才能那样做. 请输入/[label] leave离开当前团队,然后重新你的岛屿生涯." + PleaseWait: "请稍等, 正在创建新的岛屿..." + Left: "重设剩余次数" + To: "清除重新开始限制" + Wait: "你需要等待 [time] 秒才能那样做." + YouHave: "你还有 [number] 次重新开始的机会." + subtitle: "by tastybento" + subtitlecolor: "blue" + teleport: "[岛屿娘]:欢饮回来~我的主人. (输入/[label] help 获取更多帮助)" + title: "BSkyBlock" + titlecolor: "gold" + unlimited: "无限" + url: "" +islandProtected: "岛屿保护." +islandguardsettings: + TNTdamage: "是否开启TNT爆炸伤害" + allowed: "允许" + aciddamage: "酸雨伤害" + animalspawning: "允许/禁止动物出生" + anvil: "是否允许使用铁毡" + armorstand: "是否允许使用盔甲架" + beacon: "是否允许使用信标" + bed: "是否允许使用床" + breakblocks: "是否允许破坏" + breeding: "是否允许喂养" + brewingstand: "是否允许酿造药水" + bucket: "是否允许使用桶" + collectlava: "是否允许收集岩浆" + collectwater: "是否允许收集水" + chest: "是否允许使用箱子" + chestdamage: "TNT能否破坏箱子" + chorusfruit: "是否允许使用紫颂果" + creeperdamage: "苦力怕伤害" + creepergriefing: "苦力怕破坏" + creeperpain: "苦力怕爆炸伤害" + croptrample: "是否允许践踏农作物" + disallowed: "不允许" + door: "是否允许使用门" + eggs: "是否允许扔鸡蛋" + enchantingtable: "是否允许使用附魔台" + enderpearl: "是否允许使用末影珍珠" + fire: "是否允许使用打火石" + fireextinguish: "熄灭火焰" + firespread: "火焰传播" + furnace: "是否允许使用熔炉" + gate: "是否允许使用栅栏门" + horseinventoryaccess: "是否允许参观者查看马的物品栏" + horseriding: "是否允许参观者骑马" + hurtanimals: "是否允许攻击动物" + hurtmonsters: "是否允许攻击怪物" + joinleaveislandmessages: "启用岛屿进入与离开消息" + jukebox: "是否允许使用唱片机" + leash: "是否允许使用栓绳" + lever: "是否允许使用开关按钮" + milking: "挤奶" + monsterspawning: "允许/禁止岛屿刷怪" + netherpvp: "是否允许地狱PVP" + placeblocks: "是否允许放置方块" + portaluse: "是否允许使用传送门" + pressureplate: "是否允许参观者激活压力盘" + pvp: "是否允许PVP" + redstone: "是否允许使用红石" + settingsgeneraltitle: "一般岛屿世界设定" + settingsgeneraldesc: "一般岛屿世界设定" + settingsislandtitle: "岛屿设定" + settingsislanddesc: "这些规则适用于岛屿的参观者" + settingsspawntitle: "出生地设定" + settingsspawndesc: "这些规则适用于出生地" + shears: "是否允许使用剪刀" + spawnegg: "是否允许使用刷怪蛋" + teleportwhenfalling: "下落时是否允许传送" + title: "岛屿保护设置" + villagertrading: "是否允许村民交易" + visitordrop: "是否允许访问者掉落物品" + visitorkeepitems: "访问者死亡是否保存背包" + visitorpickup: "是否允许访问者捡起物品" + withergriefing: "是否开启凋零破坏" + workbench: "是否允许使用合成台" +kick: + errorNoTeam: "你没有加入任何岛屿的团队!" + errorNotPartOfTeam: "这个玩家不是你的岛屿成员之一!" + errorOnlyLeaderCan: "只有岛屿主人才能踢人!" + errorPlayerNotInTeam: "这个玩家不在你的岛屿团队中!" + nameRemoved: "[name] 刚才已被驱逐出岛." + nameRemovedYou: "[name] 将你驱逐出了他的岛屿!" +lavaTip: "温馨提示: 如果岩浆变成了黑曜石,可以使用铁桶右键变回岩浆." +leave: + canceled: "离开已取消." + errorLeadersCannotLeave: "空岛主人无法离开岛屿.请输入 /[label] makeleader <玩家ID> 来转让岛屿." + errorYouAreTheLeader: "因为你是岛屿主人所以不能离开, 使用 /[label] makeleader <玩家> 来改变岛屿主人." + errorYouCannotLeaveIsland: "你无法离开岛屿当只剩下你一个人的时候.如果你想重新开始, 请输入 /[label] restart!" + errorYouMustBeInWorld: "你只能在岛屿世界离开你的队伍!" + nameHasLeftYourIsland: "[name] 离开了你的岛屿!" + warning: "你确定要离开队伍吗? 再次键入 '/[label] leave' 以确认." + youHaveLeftTheIsland: "你离开了岛屿, 并返回到了玩家出生地." +level: + calculating: "正在计算岛屿等级, 需要等待一段时间. 请稍候..." + errornotYourIsland: "只有岛屿成员才能使用." + islandLevel: "岛屿等级" +lock: + enteringspawn: "进入出生点" + islandlocked: "岛屿禁止游客访问" + leavingspawn: "离开出生点" + locking: "锁定岛屿" + nowentering: "您所访问的是 [name] 的岛屿" + nowleaving: "你已经离开了 [name] 的岛屿" + unlocking: "解锁岛屿" +makeleader: + errorGeneralError: "不能改变岛屿主人." + errorNotYourIsland: "这不是你的岛屿, 所以你没有权限!" + errorPlayerMustBeOnline: "玩家只有在线才能接受你的岛屿." + errorRemoveAllPlayersFirst: "将所有玩家踢出团队, 才能进行团队转让." + errorThatPlayerIsNotInTeam: "这个玩家不是你的岛屿成员之一!" + errorYouMustBeInTeam: "你必须在一个团队中才能转让岛屿." + nameIsNowTheOwner: "[name] 现在是你的岛屿主人了!" + youAreNowTheOwner: "你现在是岛屿主人了." +minishop: + buy: "购买" + buyproblem: "购买出错" + islandhelpMiniShop: "打开迷你商店" + outofstock: "物品卖完了哟" + sell: "出售" + sellproblem: "你没有足够的物品." + title: "迷你商店-左键购买右键出售" + youbought: "你购买了 [number] 个[description], 花费了 [price]" + youcannotafford: "余额不足!" + yousold: "你出售了 [number] 个 [description], 获得了 [price]" +moblimits: + entity: "岛屿实体 [entity] 的数量达到极限([number])!" + error: "岛屿繁殖数量 [number] 达到极限!" + hopper: "岛屿漏斗放置数量 [number] 达到极限!" + villager: "岛屿村民繁殖数量 [number] 达到极限!" +nether: + spawnisprotected: "当前位置为地狱保护区,请勿破坏." +news: + headline: "[岛屿快讯]: 你离线的时候发生了..." +purge: + acidFound: "已有 [number] 个无主岛屿. 使用 '/acid purge unowned confirm' 来在20s内删除它们." + allowPurge: "清除保护已移除" + alreadyRunning: "正在清理中, 请等待清理完毕!" + calculating: "计算不活跃天数超过 [time] 天的岛屿." + countingUnowned: "正在计数无主岛屿并检查玩家文件. 可能会花一点时间..." + maxPurge: "一次最大清理数为 [number] 个岛屿. 再次运行purge命令以清理更多." + finished: "清理不活跃岛屿完成." + noneFound: "没有不活跃的岛屿被移除." + nowWaiting: "请等待..." + preventPurge: "岛屿是免受清除的" + purgeCancelled: "取消清理." + removingAt: "正删除位于 [location] 的岛屿" + removingName: "清理: 移除了 [name] 的岛屿" + skyblockFound: "还有 [number] 个无主岛屿. 使用 '/asadmin purge unowned confirm' 来在20s内删除它们." + stillChecking: "仍在检查玩家文件..." + thisWillRemove: "这将会移除 [number] 个不活跃的岛屿!" + typeConfirm: "请在10秒内输入 /[label] confirm 进行确定" + usage: "使用方式: /[label] purge [TimeInDays]" + warning: "危险! 当服务器内还有玩家的时候请不要运行这个命令! 请将你的服务器进行备份!!!" +register: + errorBedrockNotFound: "基岩未找到: 无法设置岛屿!" + settingIsland: "设置 [name] 岛屿的基岩在你当前位置." +reject: + nameHasRejectedInvite: "[name] 拒绝了你的岛屿入驻邀请!" + youHaveNotBeenInvited: "你没有收到任何岛屿入住邀请." + youHaveRejectedInvitation: "你拒绝了岛屿入住邀请." +reload: + configReloaded: "配置重读." +resetallchallenges: + success: "[name] 的所有挑战已经重置." +resetchallenge: + challengeReset: "[挑战系统]: [name] 的挑战: [challengename] 已经重置" + errorChallengeDoesNotExist: "挑战不存在或尚未完成" +schematics: + title: "请选择你想要的岛屿..." +sethome: + errorNoIsland: "你不是岛屿的一员. 请返回你的岛屿!" + errorNotOnIsland: "你只能在你的岛屿范围内设置!" + errorNumHomes: "家只能设置 1-[max] 个" + homeSet: "已经设置家在你当前位置." +settingsReset: + done: "完成." + inprogress: "重设保护设置中, 请稍待..." +sign: + line1: "&1[末日岛屿]" + line2: "[player]" + line3: "不要掉下去!" + line4: "努力活下去!" +sign-acidisland: + line1: "&1[酸雨岛&7]" + line2: "[player]" + line3: "水有酸性!" + line4: "千万小心!" +targetInPVPArea: "目标处于禁止PVP区域!" +team: + listingMembers: "显示你岛屿的成员" +teamchat: + helpChat: "开启/关闭团队聊天" + noTeam: "你不在一个团队中!" + noTeamAround: "没有团队成员在线!" + prefix: "[团队聊天]<{ISLAND_PLAYER}> " + spyoff: "关闭团队聊天窃听" + spyon: "打开团队聊天窃听" + statusOff: "团队聊天已经关闭" + statusOn: "团队聊天已经开启" +topTen: + errorExcluded: "仅供参考: 你被逐出了前十名, 因为你没有 [perm]" + errorNotReady: "前十名单尚未生成!" + guiHeading: "&E&L 岛屿: &6&N[name]&7 (#[rank])" + guiTitle: "前10岛屿" + header: "排名前10的岛屿:" +warps: + deactivate: "旧的传送点被替换!" + errorDoesNotExist: "传送点不存在!" + errorDuplicate: "当前位置已经存在传送点!" + errorNoPerm: "你没有权限创建岛屿传送点!" + errorNoPlace: "你只能够在你的岛屿上创建传送点!" + errorNoRemove: "你只能移除你自己的传送点!" + errorNoWarpsYet: "没有任何传送点!" + errorNotReadyYet: "传送点未准备好.请稍后再试." + errorNotSafe: "传送点不安全.请稍后再试." + errorNotEnoughLevel: "你的岛屿等级不够, 无法创建欢迎牌!" + next: "下一页" + playerWarped: "[name] &e传送到了你的岛屿!" + previous: "上一页" + removed: "传送点已经移除!" + signRemoved: "你的岛屿传送点已经移除!" + success: "空岛传送点成功创建!" + title: "岛屿传送点" + warpTip: "放置一个木牌,并且在木牌的第一行输入“[WELCOME]”以设置一个岛屿传送点." + warpToPlayersSign: "传送到玩家: 的岛屿传送点." + warpsAvailable: "以下的传送点可用" + welcomeLine: "[WELCOME]" + diff --git a/plugin.yml b/plugin.yml new file mode 100755 index 0000000..1d44d1c --- /dev/null +++ b/plugin.yml @@ -0,0 +1,150 @@ +name: BSkyBlock +main: us.tastybento.bskyblock.BSkyBlock +version: ${version} + +authors: [tastybento, Poslovitch] + +softdepend: [PlaceholderAPI, MVdWPlaceholderAPI] + +load: startup +loadbefore: [Multiverse-Core] + +permissions: + bskyblock.*: + default: false + children: + bskyblock.island.*: + children: + bskyblock.island: + description: Allow island command usage + default: true + bskyblock.island.create: + description: Allow island creation + default: true + bskyblock.island.home: + description: Allow teleporting to player island + default: true + bskyblock.island.reset: + description: Player can use the island reset or restart command + default: true + bskyblock.island.sethome: + description: Let the player use the sethome command + default: true + bskyblock.island.info: + description: Let the player check their island level + default: true + bskyblock.island.sethome: + description: Let the player set their island teleport point + default: true + bskyblock.island.controlpanel: + description: Allows usage of the island GUI + default: true + bskyblock.island.lock: + description: Allows island locking + default: true + bskyblock.island.expel: + description: Allows expelling of visitors + default: true + bskyblock.island.ban: + description: Allows banning of visitors + default: true + bskyblock.island.settings: + description: Player can see server settings + default: true + bskyblock.island.lang: + description: Player can select a language + default: true + bskyblock.island.name: + description: Player can set the name of their island + default: true + bskyblock.island.spawn: + description: Player can use the island spawn command if spawn exists + default: true + bskyblock.island.reset: + description: Player can use the island reset or restart command + default: true + bskyblock.island.team: + description: Let a player use team commands + default: true + bskyblock.mod.*: + children: + bskyblock.mod.info: + description: Let a moderator see info on a player + default: op + bskyblock.mod.resethome: + description: Allows setting or reseting of a player's home position + default: op + bskyblock.mod.clearreset: + description: Allow clearing of island reset limit + default: false + bskyblock.mod.tp: + description: Allows teleport to an island + default: op + bskyblock.mod.bypassprotect: + description: Allow moderator to bypass island protection + default: op + bskyblock.mod.bypassexpel: + description: Allow moderator to bypass island expulsion + default: op + bskyblock.mod.topbreeders: + description: Lists most populated islands current loaded + default: op + bskyblock.mod.lock: + description: Locks or unlocks an island + default: op + bskyblock.mod.bypasslock: + description: Bypasses an island lock + default: op + bskyblock.mod.team: + description: Enables modification of teams via kick and add commands + default: false + bskyblock.mod.name: + description: Enables naming of player's islands + default: false + bskyblock.mod.resetname: + description: Enables reset of player's island names + default: false + bskyblock.admin.*: + children: + bskyblock.admin.clearresetall: + description: Allow clearing of island reset limit of all players + default: op + bskyblock.admin.reload: + description: Reload the config.yml + default: op + bskyblock.admin.delete: + description: Let a player completely remove a player (including island) + default: op + bskyblock.admin.deleteisland: + description: Let a player completely remove the island the player is on + default: op + bskyblock.admin.register: + description: Let a player register the nearest island to another player. + default: op + bskyblock.admin.unregister: + description: Removes a player from an island without deleting the island blocks. + default: op + bskyblock.admin.purge: + description: Let a player purge old islands. + default: op + bskyblock.admin.setspawn: + description: Allows use of spawn tools + default: op + bskyblock.admin.setrange: + description: Allows setting of island protection range + default: op + bskyblock.admin.topbreeders: + description: Lists most populated islands current loaded + default: op + bskyblock.admin.reserve: + description: Reserves an empty spot for a player's next island + default: op + bskyblock.admin.settingsreset: + description: Resets all the islands to default protection settings + default: op + bskyblock.admin.noban: + description: Player cannot be banned from an island + default: op + bskyblock.admin.setlanguage: + description: Resets all player languages and sets the default language + default: op \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..28412a1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,183 @@ + + 4.0.0 + us.tastybento + bskyblock + jar + FC-0.79 + BSkyBlock + The next generation of ASkyBlock + + 1.8 + 1.8 + UTF-8 + UTF-8 + 1.7.4 + + + clean package install + + + . + true + ${basedir} + + *.yml + + + + schems + false + ${basedir}/schems + + *.schem + + + + locales + false + ${basedir}/locales + + *.yml + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + schematic + + + + + maven-compiler-plugin + 3.5 + + 1.8 + 1.8 + + + + + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + 3.3.0.603 + + + verify + + sonar + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.1 + + true + + + + prepare-agent + + prepare-agent + + + + prepare-agent-integration + + prepare-agent-integration + + + + jacoco-site + verify + + report + + + + + + + + + + sonar + + https://sonarcloud.io + tastybento-github + + + + + org.jacoco + jacoco-maven-plugin + + + org.sonarsource.scanner.maven + sonar-maven-plugin + + + + + + + + org.spigotmc + spigot-api + 1.12.2-R0.1-SNAPSHOT + provided + + + org.mockito + mockito-all + 1.10.19 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + org.mongodb + mongodb-driver + 3.6.3 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots + + + diff --git a/schems/end-island.schem b/schems/end-island.schem new file mode 100644 index 0000000..796334e Binary files /dev/null and b/schems/end-island.schem differ diff --git a/schems/island.schem b/schems/island.schem new file mode 100644 index 0000000..55a0e2f Binary files /dev/null and b/schems/island.schem differ diff --git a/schems/nether-island.schem b/schems/nether-island.schem new file mode 100644 index 0000000..5cd7841 Binary files /dev/null and b/schems/nether-island.schem differ diff --git a/src/main/java/us/tastybento/bskyblock/BSkyBlock.java b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java new file mode 100755 index 0000000..efa163b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java @@ -0,0 +1,365 @@ +package us.tastybento.bskyblock; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import us.tastybento.bskyblock.api.configuration.BSBConfig; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.events.BSBReadyEvent; +import us.tastybento.bskyblock.api.placeholders.PlaceholderHandler; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.BSBDbSetup; +import us.tastybento.bskyblock.listeners.BannedVisitorCommands; +import us.tastybento.bskyblock.listeners.BlockEndDragon; +import us.tastybento.bskyblock.listeners.JoinLeaveListener; +import us.tastybento.bskyblock.listeners.NetherPortals; +import us.tastybento.bskyblock.listeners.ObsidianToLava; +import us.tastybento.bskyblock.listeners.PanelListenerManager; +import us.tastybento.bskyblock.listeners.protection.FlyingMobEvents; +import us.tastybento.bskyblock.managers.AddonsManager; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.managers.SchemsManager; +import us.tastybento.bskyblock.util.HeadGetter; + +/** + * Main BSkyBlock class - provides an island minigame in the sky + * @author tastybento + * @author Poslovitch + */ +public class BSkyBlock extends JavaPlugin { + + private static BSkyBlock instance; + + // Databases + private PlayersManager playersManager; + private IslandsManager islandsManager; + + // Metrics + private Metrics metrics; + + // Managers + private CommandsManager commandsManager; + private LocalesManager localesManager; + private AddonsManager addonsManager; + private FlagsManager flagsManager; + private IslandWorldManager islandWorldManager; + private RanksManager ranksManager; + private SchemsManager schemsManager; + + // Settings + private Settings settings; + + // Notifier + private Notifier notifier; + + private HeadGetter headGetter; + + private boolean isLoaded; + + @Override + public void onEnable(){ + // Not loaded + isLoaded = false; + // Store the current millis time so we can tell how many ms it took for BSB to fully load. + final long startMillis = System.currentTimeMillis(); + + // Save the default config from config.yml + saveDefaultConfig(); + setInstance(this); + + // Load Flags + flagsManager = new FlagsManager(instance); + + // Load settings from config.yml. This will check if there are any issues with it too. + settings = new BSBConfig<>(this, Settings.class).loadConfigObject(""); + // Start Database managers + playersManager = new PlayersManager(this); + // Check if this plugin is now disabled (due to bad database handling) + if (!this.isEnabled()) { + return; + } + islandsManager = new IslandsManager(this); + ranksManager = new RanksManager(this); + + // Start head getter + headGetter = new HeadGetter(this); + + // Load metrics + metrics = new Metrics(instance); + registerCustomCharts(); + + // Load Notifier + notifier = new Notifier(); + + // Set up command manager + commandsManager = new CommandsManager(); + + // These items have to be loaded when the server has done 1 tick. + // Note Worlds are not loaded this early, so any Locations or World reference will be null + // at this point. Therefore, the 1 tick scheduler is required. + getServer().getScheduler().runTask(this, () -> { + // Create the world if it does not exist + islandWorldManager = new IslandWorldManager(instance); + // Load schems manager + schemsManager = new SchemsManager(instance); + // Load the default island schems + schemsManager.loadIslands(getIWM().getBSBIslandWorld()); + + // Set up commands + new IslandCommand(); + new AdminCommand(); + + // Locales manager must be loaded before addons + localesManager = new LocalesManager(instance); + PlaceholderHandler.register(instance); + + // Load addons. Addons may load worlds, so they must go before islands are loaded. + addonsManager = new AddonsManager(instance); + addonsManager.loadAddons(); + // Enable addons + addonsManager.enableAddons(); + + getServer().getScheduler().runTask(instance, () -> { + // Register Listeners + registerListeners(); + + // Load islands from database - need to wait until all the worlds are loaded + islandsManager.load(); + + // Save islands & players data asynchronously every X minutes + instance.getServer().getScheduler().runTaskTimer(instance, () -> { + playersManager.save(true); + islandsManager.save(true); + }, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L); + isLoaded = true; + flagsManager.registerListeners(); + instance.log("#############################################"); + instance.log(instance.getDescription().getFullName() + " has been fully enabled."); + instance.log("It took: " + (System.currentTimeMillis() - startMillis + "ms")); + instance.log("Thanks for using our plugin !"); + instance.log("- Tastybento and Poslovitch, 2017-2018"); + instance.log("#############################################"); + + // Fire plugin ready event + Bukkit.getServer().getPluginManager().callEvent(new BSBReadyEvent()); + }); + }); + } + + /** + * Register listeners + */ + private void registerListeners() { + PluginManager manager = getServer().getPluginManager(); + // Player join events + manager.registerEvents(new JoinLeaveListener(this), this); + // Panel listener manager + manager.registerEvents(new PanelListenerManager(), this); + // Nether portals + manager.registerEvents(new NetherPortals(this), this); + // Obsidian to lava helper + manager.registerEvents(new ObsidianToLava(this), this); + // Flying mobs protection + manager.registerEvents(new FlyingMobEvents(this), this); + // End dragon blocking + manager.registerEvents(new BlockEndDragon(this), this); + // Banned visitor commands + manager.registerEvents(new BannedVisitorCommands(this), this); + } + + @Override + public void onDisable() { + if (addonsManager != null) { + addonsManager.disableAddons(); + } + // Save data + if (playersManager != null) { + playersManager.shutdown(); + } + if (islandsManager != null) { + islandsManager.shutdown(); + } + // Save settings + if (settings != null) { + new BSBConfig<>(this, Settings.class).saveConfigObject(settings); + } + } + + private void registerCustomCharts(){ + metrics.addCustomChart(new Metrics.SingleLineChart("islands_count") { + + @Override + public int getValue() { + return islandsManager.getCount(); + } + }); + + metrics.addCustomChart(new Metrics.SingleLineChart("created_islands") { + + @Override + public int getValue() { + int created = islandsManager.metrics_getCreatedCount(); + islandsManager.metrics_setCreatedCount(0); + return created; + } + }); + + metrics.addCustomChart(new Metrics.SimplePie("default_locale") { + + @Override + public String getValue() { + return getSettings().getDefaultLanguage(); + } + }); + + metrics.addCustomChart(new Metrics.SimplePie("database") { + + @Override + public String getValue() { + return BSBDbSetup.getDatabase().toString(); + } + }); + } + + /** + * Returns the player database + * @return the player database + */ + public PlayersManager getPlayers(){ + return playersManager; + } + + /** + * Returns the island database + * @return the island database + */ + public IslandsManager getIslands(){ + return islandsManager; + } + + private static void setInstance(BSkyBlock plugin) { + BSkyBlock.instance = plugin; + } + + public static BSkyBlock getInstance() { + return instance; + } + + /** + * @return the Commands manager + */ + public CommandsManager getCommandsManager() { + return commandsManager; + } + + /** + * @return the Locales manager + */ + public LocalesManager getLocalesManager() { + return localesManager; + } + + /** + * @return the Addons manager + */ + public AddonsManager getAddonsManager() { + return addonsManager; + } + + /** + * @return the Flags manager + */ + public FlagsManager getFlagsManager() { + return flagsManager; + } + + /** + * @return the ranksManager + */ + public RanksManager getRanksManager() { + return ranksManager; + } + + /** + * @return the Island World Manager + */ + public IslandWorldManager getIWM() { + return islandWorldManager; + } + + /** + * @return the settings + */ + public Settings getSettings() { + return settings; + } + + /** + * @return the notifier + */ + public Notifier getNotifier() { + return notifier; + } + + /** + * @return the headGetter + */ + public HeadGetter getHeadGetter() { + return headGetter; + } + + public void log(String string) { + getLogger().info(() -> string); + } + + public void logDebug(Object object) { + getLogger().info(() -> "DEBUG: " + object); + } + + public void logError(String error) { + getLogger().severe(() -> error); + } + + public void logWarning(String warning) { + getLogger().warning(warning); + } + + /** + * Registers a world as a world to be covered by this plugin + * @param world - Bukkit over world + * @param worldSettings - settings for this world + */ + public void registerWorld(World world, WorldSettings worldSettings) { + islandWorldManager.addWorld(world, worldSettings); + } + + + + /** + * @return the schemsManager + */ + public SchemsManager getSchemsManager() { + return schemsManager; + } + + + + /** + * True if the plugin is loaded and ready + * @return the isLoaded + */ + public boolean isLoaded() { + return isLoaded; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/Constants.java b/src/main/java/us/tastybento/bskyblock/Constants.java new file mode 100644 index 0000000..1223358 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/Constants.java @@ -0,0 +1,17 @@ +package us.tastybento.bskyblock; + +/** + * Contains the plugin constants. + * @author tastybento + */ +public class Constants { + // ----------------- Constants ----------------- + // Game Type BSKYBLOCK or ACIDISLAND + public enum GameType { + BSKYBLOCK, ACIDISLAND, BOTH + } + + public static final GameType GAMETYPE = GameType.BSKYBLOCK; + // The spawn command (Essentials spawn for example) + public static final String SPAWNCOMMAND = "spawn"; +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/Metrics.java b/src/main/java/us/tastybento/bskyblock/Metrics.java new file mode 100755 index 0000000..53ad3d9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/Metrics.java @@ -0,0 +1,1027 @@ +package us.tastybento.bskyblock; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +import javax.net.ssl.HttpsURLConnection; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.java.JavaPlugin; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + * + * @author BtoBastian + */ +@SuppressWarnings("unchecked") +public class Metrics { + + static { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' }); + final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' }); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bukkit"; + + // Should failed requests be logged? + private static boolean logFailedRequests; + + // The uuid of the server + private static String serverUUID; + + // The plugin + private final JavaPlugin plugin; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + /** + * Class constructor. + * + * @param plugin - BSkyBlock plugin object The plugin which stats should be submitted. + */ + public Metrics(JavaPlugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + this.plugin = plugin; + + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + + // Check if the config file exists + if (!config.isSet("serverUuid")) { + + // Add default values + config.addDefault("enabled", true); + // Every server gets it's unique random id. + config.addDefault("serverUuid", UUID.randomUUID().toString()); + // Should failed request be logged? + config.addDefault("logFailedRequests", false); + + // Inform the server owners about bStats + config.options().header( + "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + + "To honor their work, you should not disable it.\n" + + "This has nearly no effect on the server performance!\n" + + "Check out https://bStats.org/ to learn more :)" + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { } + } + + // Load the data + serverUUID = config.getString("serverUuid"); + logFailedRequests = config.getBoolean("logFailedRequests", false); + if (config.getBoolean("enabled", true)) { + boolean found = false; + // Search for all other bStats Metrics classes to see if we are the first one + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + found = true; // We aren't the first + break; + } catch (NoSuchFieldException ignored) { } + } + // Register our service + Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); + if (!found) { + // We are the first! + startSubmitting(); + } + } + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if (!plugin.isEnabled()) { // Plugin was disabled + timer.cancel(); + return; + } + // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler + // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) + Bukkit.getScheduler().runTask(plugin, () -> submitData()); + } + }, 1000*60*5L, 1000*60*30L); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.put("pluginName", pluginName); // Append the name of the plugin + data.put("pluginVersion", pluginVersion); // Append the version of the plugin + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // Minecraft specific data + int playerAmount = Bukkit.getOnlinePlayers().size(); + int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; + String bukkitVersion = org.bukkit.Bukkit.getVersion(); + bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("playerAmount", playerAmount); + data.put("onlineMode", onlineMode); + data.put("bukkitVersion", bukkitVersion); + + data.put("javaVersion", javaVersion); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + } catch (NoSuchFieldException ignored) { + continue; // Continue "searching" + } + // Found one! + try { + pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service))); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + + data.put("plugins", pluginData); + + // Create a new thread for the connection to the bStats server + new Thread(() -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); + } + } + }).start(); + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + if (Bukkit.isPrimaryThread()) { + throw new IllegalAccessException("This method must not be called from the main thread!"); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + if (compressedData == null) { + throw new Exception(); + } + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + protected final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + protected JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JSONObject getChartData(); + + } + + /** + * Represents a custom simple pie. + */ + public static abstract class SimplePie extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public SimplePie(String chartId) { + super(chartId); + } + + /** + * Gets the value of the pie. + * + * @return The value of the pie. + */ + public abstract String getValue(); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + String value = getValue(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static abstract class AdvancedPie extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public AdvancedPie(String chartId) { + super(chartId); + } + + /** + * Gets the values of the pie. + * + * @param valueMap Just an empty map. The only reason it exists is to make your life easier. + * You don't have to create a map yourself! + * @return The values of the pie. + */ + public abstract HashMap getValues(HashMap valueMap); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + HashMap map = getValues(new HashMap<>()); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static abstract class SingleLineChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public SingleLineChart(String chartId) { + super(chartId); + } + + /** + * Gets the value of the chart. + * + * @return The value of the chart. + */ + public abstract int getValue(); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + int value = getValue(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static abstract class MultiLineChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public MultiLineChart(String chartId) { + super(chartId); + } + + /** + * Gets the values of the chart. + * + * @param valueMap Just an empty map. The only reason it exists is to make your life easier. + * You don't have to create a map yourself! + * @return The values of the chart. + */ + public abstract HashMap getValues(HashMap valueMap); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + HashMap map = getValues(new HashMap<>()); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static abstract class SimpleBarChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public SimpleBarChart(String chartId) { + super(chartId); + } + + /** + * Gets the value of the chart. + * + * @param valueMap Just an empty map. The only reason it exists is to make your life easier. + * You don't have to create a map yourself! + * @return The value of the chart. + */ + public abstract HashMap getValues(HashMap valueMap); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + HashMap map = getValues(new HashMap<>()); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static abstract class AdvancedBarChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public AdvancedBarChart(String chartId) { + super(chartId); + } + + /** + * Gets the value of the chart. + * + * @param valueMap Just an empty map. The only reason it exists is to make your life easier. + * You don't have to create a map yourself! + * @return The value of the chart. + */ + public abstract HashMap getValues(HashMap valueMap); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + HashMap map = getValues(new HashMap<>()); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple map chart. + */ + public static abstract class SimpleMapChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public SimpleMapChart(String chartId) { + super(chartId); + } + + /** + * Gets the value of the chart. + * + * @return The value of the chart. + */ + public abstract Country getValue(); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + Country value = getValue(); + + if (value == null) { + // Null = skip the chart + return null; + } + data.put("value", value.getCountryIsoTag()); + return data; + } + + } + + /** + * Represents a custom advanced map chart. + */ + public static abstract class AdvancedMapChart extends CustomChart { + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + public AdvancedMapChart(String chartId) { + super(chartId); + } + + /** + * Gets the value of the chart. + * + * @param valueMap Just an empty map. The only reason it exists is to make your life easier. + * You don't have to create a map yourself! + * @return The value of the chart. + */ + public abstract HashMap getValues(HashMap valueMap); + + @Override + protected JSONObject getChartData() { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + HashMap map = getValues(new HashMap<>()); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey().getCountryIsoTag(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * A enum which is used for custom maps. + */ + public enum Country { + + /** + * bStats will use the country of the server. + */ + AUTO_DETECT("AUTO", "Auto Detected"), + + ANDORRA("AD", "Andorra"), + UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"), + AFGHANISTAN("AF", "Afghanistan"), + ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"), + ANGUILLA("AI", "Anguilla"), + ALBANIA("AL", "Albania"), + ARMENIA("AM", "Armenia"), + NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"), + ANGOLA("AO", "Angola"), + ANTARCTICA("AQ", "Antarctica"), + ARGENTINA("AR", "Argentina"), + AMERICAN_SAMOA("AS", "American Samoa"), + AUSTRIA("AT", "Austria"), + AUSTRALIA("AU", "Australia"), + ARUBA("AW", "Aruba"), + ALAND_ISLANDS("AX", "Åland Islands"), + AZERBAIJAN("AZ", "Azerbaijan"), + BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"), + BARBADOS("BB", "Barbados"), + BANGLADESH("BD", "Bangladesh"), + BELGIUM("BE", "Belgium"), + BURKINA_FASO("BF", "Burkina Faso"), + BULGARIA("BG", "Bulgaria"), + BAHRAIN("BH", "Bahrain"), + BURUNDI("BI", "Burundi"), + BENIN("BJ", "Benin"), + SAINT_BARTHELEMY("BL", "Saint Barthélemy"), + BERMUDA("BM", "Bermuda"), + BRUNEI("BN", "Brunei"), + BOLIVIA("BO", "Bolivia"), + BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"), + BRAZIL("BR", "Brazil"), + BAHAMAS("BS", "Bahamas"), + BHUTAN("BT", "Bhutan"), + BOUVET_ISLAND("BV", "Bouvet Island"), + BOTSWANA("BW", "Botswana"), + BELARUS("BY", "Belarus"), + BELIZE("BZ", "Belize"), + CANADA("CA", "Canada"), + COCOS_ISLANDS("CC", "Cocos Islands"), + THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"), + CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"), + CONGO("CG", "Congo"), + SWITZERLAND("CH", "Switzerland"), + COTE_D_IVOIRE("CI", "Côte d'Ivoire"), + COOK_ISLANDS("CK", "Cook Islands"), + CHILE("CL", "Chile"), + CAMEROON("CM", "Cameroon"), + CHINA("CN", "China"), + COLOMBIA("CO", "Colombia"), + COSTA_RICA("CR", "Costa Rica"), + CUBA("CU", "Cuba"), + CAPE_VERDE("CV", "Cape Verde"), + CURACAO("CW", "Curaçao"), + CHRISTMAS_ISLAND("CX", "Christmas Island"), + CYPRUS("CY", "Cyprus"), + CZECH_REPUBLIC("CZ", "Czech Republic"), + GERMANY("DE", "Germany"), + DJIBOUTI("DJ", "Djibouti"), + DENMARK("DK", "Denmark"), + DOMINICA("DM", "Dominica"), + DOMINICAN_REPUBLIC("DO", "Dominican Republic"), + ALGERIA("DZ", "Algeria"), + ECUADOR("EC", "Ecuador"), + ESTONIA("EE", "Estonia"), + EGYPT("EG", "Egypt"), + WESTERN_SAHARA("EH", "Western Sahara"), + ERITREA("ER", "Eritrea"), + SPAIN("ES", "Spain"), + ETHIOPIA("ET", "Ethiopia"), + FINLAND("FI", "Finland"), + FIJI("FJ", "Fiji"), + FALKLAND_ISLANDS("FK", "Falkland Islands"), + MICRONESIA("FM", "Micronesia"), + FAROE_ISLANDS("FO", "Faroe Islands"), + FRANCE("FR", "France"), + GABON("GA", "Gabon"), + UNITED_KINGDOM("GB", "United Kingdom"), + GRENADA("GD", "Grenada"), + GEORGIA("GE", "Georgia"), + FRENCH_GUIANA("GF", "French Guiana"), + GUERNSEY("GG", "Guernsey"), + GHANA("GH", "Ghana"), + GIBRALTAR("GI", "Gibraltar"), + GREENLAND("GL", "Greenland"), + GAMBIA("GM", "Gambia"), + GUINEA("GN", "Guinea"), + GUADELOUPE("GP", "Guadeloupe"), + EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"), + GREECE("GR", "Greece"), + SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"), + GUATEMALA("GT", "Guatemala"), + GUAM("GU", "Guam"), + GUINEA_BISSAU("GW", "Guinea-Bissau"), + GUYANA("GY", "Guyana"), + HONG_KONG("HK", "Hong Kong"), + HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"), + HONDURAS("HN", "Honduras"), + CROATIA("HR", "Croatia"), + HAITI("HT", "Haiti"), + HUNGARY("HU", "Hungary"), + INDONESIA("ID", "Indonesia"), + IRELAND("IE", "Ireland"), + ISRAEL("IL", "Israel"), + ISLE_OF_MAN("IM", "Isle Of Man"), + INDIA("IN", "India"), + BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"), + IRAQ("IQ", "Iraq"), + IRAN("IR", "Iran"), + ICELAND("IS", "Iceland"), + ITALY("IT", "Italy"), + JERSEY("JE", "Jersey"), + JAMAICA("JM", "Jamaica"), + JORDAN("JO", "Jordan"), + JAPAN("JP", "Japan"), + KENYA("KE", "Kenya"), + KYRGYZSTAN("KG", "Kyrgyzstan"), + CAMBODIA("KH", "Cambodia"), + KIRIBATI("KI", "Kiribati"), + COMOROS("KM", "Comoros"), + SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"), + NORTH_KOREA("KP", "North Korea"), + SOUTH_KOREA("KR", "South Korea"), + KUWAIT("KW", "Kuwait"), + CAYMAN_ISLANDS("KY", "Cayman Islands"), + KAZAKHSTAN("KZ", "Kazakhstan"), + LAOS("LA", "Laos"), + LEBANON("LB", "Lebanon"), + SAINT_LUCIA("LC", "Saint Lucia"), + LIECHTENSTEIN("LI", "Liechtenstein"), + SRI_LANKA("LK", "Sri Lanka"), + LIBERIA("LR", "Liberia"), + LESOTHO("LS", "Lesotho"), + LITHUANIA("LT", "Lithuania"), + LUXEMBOURG("LU", "Luxembourg"), + LATVIA("LV", "Latvia"), + LIBYA("LY", "Libya"), + MOROCCO("MA", "Morocco"), + MONACO("MC", "Monaco"), + MOLDOVA("MD", "Moldova"), + MONTENEGRO("ME", "Montenegro"), + SAINT_MARTIN("MF", "Saint Martin"), + MADAGASCAR("MG", "Madagascar"), + MARSHALL_ISLANDS("MH", "Marshall Islands"), + MACEDONIA("MK", "Macedonia"), + MALI("ML", "Mali"), + MYANMAR("MM", "Myanmar"), + MONGOLIA("MN", "Mongolia"), + MACAO("MO", "Macao"), + NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"), + MARTINIQUE("MQ", "Martinique"), + MAURITANIA("MR", "Mauritania"), + MONTSERRAT("MS", "Montserrat"), + MALTA("MT", "Malta"), + MAURITIUS("MU", "Mauritius"), + MALDIVES("MV", "Maldives"), + MALAWI("MW", "Malawi"), + MEXICO("MX", "Mexico"), + MALAYSIA("MY", "Malaysia"), + MOZAMBIQUE("MZ", "Mozambique"), + NAMIBIA("NA", "Namibia"), + NEW_CALEDONIA("NC", "New Caledonia"), + NIGER("NE", "Niger"), + NORFOLK_ISLAND("NF", "Norfolk Island"), + NIGERIA("NG", "Nigeria"), + NICARAGUA("NI", "Nicaragua"), + NETHERLANDS("NL", "Netherlands"), + NORWAY("NO", "Norway"), + NEPAL("NP", "Nepal"), + NAURU("NR", "Nauru"), + NIUE("NU", "Niue"), + NEW_ZEALAND("NZ", "New Zealand"), + OMAN("OM", "Oman"), + PANAMA("PA", "Panama"), + PERU("PE", "Peru"), + FRENCH_POLYNESIA("PF", "French Polynesia"), + PAPUA_NEW_GUINEA("PG", "Papua New Guinea"), + PHILIPPINES("PH", "Philippines"), + PAKISTAN("PK", "Pakistan"), + POLAND("PL", "Poland"), + SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"), + PITCAIRN("PN", "Pitcairn"), + PUERTO_RICO("PR", "Puerto Rico"), + PALESTINE("PS", "Palestine"), + PORTUGAL("PT", "Portugal"), + PALAU("PW", "Palau"), + PARAGUAY("PY", "Paraguay"), + QATAR("QA", "Qatar"), + REUNION("RE", "Reunion"), + ROMANIA("RO", "Romania"), + SERBIA("RS", "Serbia"), + RUSSIA("RU", "Russia"), + RWANDA("RW", "Rwanda"), + SAUDI_ARABIA("SA", "Saudi Arabia"), + SOLOMON_ISLANDS("SB", "Solomon Islands"), + SEYCHELLES("SC", "Seychelles"), + SUDAN("SD", "Sudan"), + SWEDEN("SE", "Sweden"), + SINGAPORE("SG", "Singapore"), + SAINT_HELENA("SH", "Saint Helena"), + SLOVENIA("SI", "Slovenia"), + SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"), + SLOVAKIA("SK", "Slovakia"), + SIERRA_LEONE("SL", "Sierra Leone"), + SAN_MARINO("SM", "San Marino"), + SENEGAL("SN", "Senegal"), + SOMALIA("SO", "Somalia"), + SURINAME("SR", "Suriname"), + SOUTH_SUDAN("SS", "South Sudan"), + SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"), + EL_SALVADOR("SV", "El Salvador"), + SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"), + SYRIA("SY", "Syria"), + SWAZILAND("SZ", "Swaziland"), + TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"), + CHAD("TD", "Chad"), + FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"), + TOGO("TG", "Togo"), + THAILAND("TH", "Thailand"), + TAJIKISTAN("TJ", "Tajikistan"), + TOKELAU("TK", "Tokelau"), + TIMOR_LESTE("TL", "Timor-Leste"), + TURKMENISTAN("TM", "Turkmenistan"), + TUNISIA("TN", "Tunisia"), + TONGA("TO", "Tonga"), + TURKEY("TR", "Turkey"), + TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"), + TUVALU("TV", "Tuvalu"), + TAIWAN("TW", "Taiwan"), + TANZANIA("TZ", "Tanzania"), + UKRAINE("UA", "Ukraine"), + UGANDA("UG", "Uganda"), + UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"), + UNITED_STATES("US", "United States"), + URUGUAY("UY", "Uruguay"), + UZBEKISTAN("UZ", "Uzbekistan"), + VATICAN("VA", "Vatican"), + SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"), + VENEZUELA("VE", "Venezuela"), + BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"), + U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"), + VIETNAM("VN", "Vietnam"), + VANUATU("VU", "Vanuatu"), + WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"), + SAMOA("WS", "Samoa"), + YEMEN("YE", "Yemen"), + MAYOTTE("YT", "Mayotte"), + SOUTH_AFRICA("ZA", "South Africa"), + ZAMBIA("ZM", "Zambia"), + ZIMBABWE("ZW", "Zimbabwe"); + + private String isoTag; + private String name; + + Country(String isoTag, String name) { + this.isoTag = isoTag; + this.name = name; + } + + /** + * Gets the name of the country. + * + * @return The name of the country. + */ + public String getCountryName() { + return name; + } + + /** + * Gets the iso tag of the country. + * + * @return The iso tag of the country. + */ + public String getCountryIsoTag() { + return isoTag; + } + + /** + * Gets a country by it's iso tag. + * + * @param isoTag The iso tag of the county. + * @return The country with the given iso tag or null if unknown. + */ + public static Country byIsoTag(String isoTag) { + for (Country country : Country.values()) { + if (country.getCountryIsoTag().equals(isoTag)) { + return country; + } + } + return null; + } + + /** + * Gets a country by a locale. + * + * @param locale The locale. + * @return The country from the giben locale or null if unknown country or + * if the locale does not contain a country. + */ + public static Country byLocale(Locale locale) { + return byIsoTag(locale.getCountry()); + } + + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/Settings.java b/src/main/java/us/tastybento/bskyblock/Settings.java new file mode 100644 index 0000000..c264d7f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/Settings.java @@ -0,0 +1,1503 @@ +package us.tastybento.bskyblock; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.entity.EntityType; + +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.configuration.ConfigComment; +import us.tastybento.bskyblock.api.configuration.ConfigEntry; +import us.tastybento.bskyblock.api.configuration.StoreAt; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.BSBDbSetup.DatabaseType; +import us.tastybento.bskyblock.database.objects.DataObject; +import us.tastybento.bskyblock.database.objects.adapters.Adapter; +import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer; +import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer2; + +/** + * All the plugin settings are here + * @author Tastybento + */ +@StoreAt(filename="config.yml") // Explicitly call out what name this should have. +@ConfigComment("BSkyBlock Configuration [version]") +@ConfigComment("This config file is dynamic and saved when the server is shutdown.") +@ConfigComment("You cannot edit it while the server is running because changes will") +@ConfigComment("be lost! Use in-game settings GUI or edit when server is offline.") +@ConfigComment("") +public class Settings implements DataObject, WorldSettings { + + // --------------------------------------------- + + /* GENERAL */ + @ConfigComment("BSkyBlock uses bStats.org to get global data about the plugin to help improving it.") + @ConfigComment("bStats has nearly no effect on your server's performance and the sent data is completely") + @ConfigComment("anonymous so please consider twice if you really want to disable it.") + @ConfigEntry(path = "general.metrics") + private boolean metrics = true; + + @ConfigComment("Check for updates - this will tell Ops and the console if there is a new") + @ConfigComment("version available. It contacts dev.bukkit.org to request the latest version") + @ConfigComment("info. It does not download the latest version or change any files") + @ConfigEntry(path = "general.check-updates") + private boolean checkUpdates = true; + + @ConfigComment("Default language for new players.") + @ConfigComment("This is the filename in the locale folder without .yml.") + @ConfigComment("If this does not exist, the default en-US will be used.") + @ConfigEntry(path = "general.default-language") + private String defaultLanguage = "en-US"; + + @ConfigComment("Use economy or not. If true, an economy plugin is required. If false, no money is used or give.") + @ConfigComment("If there is no economy plugin present anyway, money will be automatically disabled.") + @ConfigEntry(path = "general.use-economy") + private boolean useEconomy = true; + + @ConfigComment("Starting money - this is how much money new players will have as their") + @ConfigComment("balance at the start of an island.") + @ConfigEntry(path = "general.starting-money") + private double startingMoney = 10.0; + + // Purge + @ConfigComment("Only islands below this level will be removed if they are abandoned and admins issue the purge command") + @ConfigEntry(path = "general.purge.max-island-level") + private int purgeMaxIslandLevel = 50; + + @ConfigComment("Remove user data when its island gets purged.") + @ConfigComment("Helps a lot to avoid huge backups and can save some performance on startup,") + @ConfigComment("but the player settings and data will be reset.") + @ConfigEntry(path = "general.purge.remove-user-data") + private boolean purgeRemoveUserData = false; + + // Database + @ConfigComment("FLATFILE, MYSQL, MONGO") + @ConfigComment("if you use MONGO, you must also run the BSBMongo plugin (not addon)") + @ConfigComment("See https://github.com/tastybento/bsbMongo/releases/") + @ConfigEntry(path = "general.database.type") + private DatabaseType databaseType = DatabaseType.FLATFILE; + + @ConfigEntry(path = "general.database.host") + private String dbHost = "localhost"; + + @ConfigComment("Port 3306 is MySQL's default. Port 27017 is MongoDB's default.") + @ConfigEntry(path = "general.database.port") + private int dbPort = 3306; + + @ConfigEntry(path = "general.database.name") + private String dbName = "BSkyBlock"; + + @ConfigEntry(path = "general.database.username") + private String dbUsername = "username"; + + @ConfigEntry(path = "general.database.password") + private String dbPassword = "password"; + + @ConfigComment("How often the data will be saved to file in mins. Default is 5 minutes.") + @ConfigComment("This helps prevent issues if the server crashes.") + @ConfigComment("Data is also saved at important points in the game.") + @ConfigEntry(path = "general.database.backup-period") + private int databaseBackupPeriod = 5; + + @ConfigComment("Recover super flat - if the generator does not run for some reason, you can get") + @ConfigComment("super flat chunks (grass). To remove automatically, select this option. Turn off") + @ConfigComment("if there are no more because it may cause lag.") + @ConfigComment("This will regenerate any chunks with bedrock at y=0 when they are loaded") + @ConfigEntry(path = "general.recover-super-flat") + private boolean recoverSuperFlat = false; + + @ConfigComment("Mute death messages") + @ConfigEntry(path = "general.mute-death-messages") + private boolean muteDeathMessages = false; + + @ConfigComment("Allow FTB Autonomous Activator to work (will allow a pseudo player [CoFH] to place and break blocks and hang items)") + @ConfigComment("Add other fake player names here if required") + @ConfigEntry(path = "general.fakeplayers") + private Set fakePlayers = new HashSet<>(); + + @ConfigComment("Allow obsidian to be scooped up with an empty bucket back into lava") + @ConfigComment("This only works if there is a single block of obsidian (no obsidian within 10 blocks)") + @ConfigComment("Recommendation is to keep this true so that newbies don't bother you or reset their") + @ConfigComment("island unnecessarily.") + @ConfigEntry(path = "general.allow-obsidian-scooping") + private boolean allowObsidianScooping = true; + + @ConfigComment("Time in seconds that players have to confirm sensitive commands, e.g. island reset") + @ConfigEntry(path = "general.confirmation-time") + private int confirmationTime = 20; + + // --------------------------------------------- + + /* WORLD */ + @ConfigComment("Friendly name for this world. Used in admin commands. Must be a single word") + @ConfigEntry(path = "world.friendly-name", needsReset = true) + private String friendlyName = "BSkyBlock"; + + @ConfigComment("Name of the world - if it does not exist then it will be generated.") + @ConfigComment("It acts like a prefix for nether and end (e.g. BSkyBlock, BSkyBlock_nether, BSkyBlock_end)") + @ConfigEntry(path = "world.world-name", needsReset = true) + private String worldName = "BSkyBlock-world"; + + @ConfigComment("World difficulty setting - PEACEFUL, EASY, NORMAL, HARD") + @ConfigComment("Other plugins may override this setting") + @ConfigEntry(path = "world.difficulty") + private Difficulty difficulty; + + @ConfigComment("Radius of island in blocks. (So distance between islands is twice this)") + @ConfigComment("Will be rounded up to the nearest 16 blocks.") + @ConfigComment("It is the same for every dimension : Overworld, Nether and End.") + @ConfigComment("This value cannot be changed mid-game and the plugin will not start if it is different.") + @ConfigEntry(path = "world.distance-between-islands", needsReset = true) + private int islandDistance = 200; + + @ConfigComment("Default protection range radius in blocks. Cannot be larger than distance.") + @ConfigComment("Admins can change protection sizes for players individually using /bsadmin setrange") + @ConfigComment("or set this permission: bskyblock.island.range.") + @ConfigEntry(path = "world.protection-range", overrideOnChange = true) + private int islandProtectionRange = 100; + + @ConfigComment("Start islands at these coordinates. This is where new islands will start in the") + @ConfigComment("world. These must be a factor of your island distance, but the plugin will auto") + @ConfigComment("calculate the closest location on the grid. Islands develop around this location") + @ConfigComment("both positively and negatively in a square grid.") + @ConfigComment("If none of this makes sense, leave it at 0,0.") + @ConfigEntry(path = "world.start-x", needsReset = true) + private int islandStartX = 0; + + @ConfigEntry(path = "world.start-z", needsReset = true) + private int islandStartZ = 0; + + @ConfigEntry(path = "world.offset-x") + private int islandXOffset; + @ConfigEntry(path = "world.offset-z") + private int islandZOffset; + + @ConfigComment("Island height - Lowest is 5.") + @ConfigComment("It is the y coordinate of the bedrock block in the schem") + @ConfigEntry(path = "world.island-height") + private int islandHeight = 100; + + @ConfigComment("Use your own world generator for this world. In this case, the plugin will not generate") + @ConfigComment("anything.") + @ConfigEntry(path = "world.use-own-generator") + private boolean useOwnGenerator; + + @ConfigComment("Sea height (don't changes this mid-game unless you delete the world)") + @ConfigComment("Minimum is 0, which means you are playing Skyblock!") + @ConfigComment("If sea height is less than about 10, then players will drop right through it") + @ConfigComment("if it exists. Makes for an interesting variation on skyblock.") + @ConfigEntry(path = "world.sea-height") + private int seaHeight = 0; + + @ConfigComment("Maximum number of islands in the world. Set to 0 for unlimited. ") + @ConfigComment("If the number of islands is greater than this number, no new island will be created.") + @ConfigEntry(path = "world.max-islands") + private int maxIslands = -1; + + @ConfigComment("The default game mode for this world. Players will be set to this mode when they create") + @ConfigComment("a new island for example. Options are SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR") + @ConfigEntry(path = "world.default-game-mode") + private GameMode defaultGameMode = GameMode.SURVIVAL; + + // Nether + @ConfigComment("Generate Nether - if this is false, the nether world will not be made and access to") + @ConfigComment("the nether will not occur. Other plugins may still enable portal usage.") + @ConfigComment("Note: Some challenges will not be possible if there is no nether.") + @ConfigComment("Note that with a standard nether all players arrive at the same portal and entering a") + @ConfigComment("portal will return them back to their islands.") + @ConfigEntry(path = "world.nether.generate") + private boolean netherGenerate = true; + + @ConfigComment("Islands in Nether. Change to false for standard vanilla nether.") + @ConfigEntry(path = "world.nether.islands", needsReset = true) + private boolean netherIslands = true; + + @ConfigComment("Nether trees are made if a player grows a tree in the nether (gravel and glowstone)") + @ConfigComment("Applies to both vanilla and islands Nether") + @ConfigEntry(path = "world.nether.trees") + private boolean netherTrees = true; + + @ConfigComment("Make the nether roof, if false, there is nothing up there") + @ConfigComment("Change to false if lag is a problem from the generation") + @ConfigComment("Only applies to islands Nether") + @ConfigEntry(path = "world.nether.roof") + private boolean netherRoof = true; + + @ConfigComment("Nether spawn protection radius - this is the distance around the nether spawn") + @ConfigComment("that will be protected from player interaction (breaking blocks, pouring lava etc.)") + @ConfigComment("Minimum is 0 (not recommended), maximum is 100. Default is 25.") + @ConfigComment("Only applies to vanilla nether") + @ConfigEntry(path = "world.nether.spawn-radius") + private int netherSpawnRadius = 32; + + // End + @ConfigEntry(path = "world.end.generate") + private boolean endGenerate = true; + + @ConfigEntry(path = "world.end.islands", needsReset = true) + private boolean endIslands = true; + + @ConfigEntry(path = "world.end.dragon-spawn") + private boolean dragonSpawn = false; + + @ConfigComment("Removing mobs - this kills all monsters in the vicinity. Benefit is that it helps") + @ConfigComment("players return to their island if the island has been overrun by monsters.") + @ConfigComment("This setting is toggled in world flags and set by the settings GUI.") + @ConfigComment("Mob white list - these mobs will NOT be removed when logging in or doing /island") + @ConfigEntry(path = "world.remove-mobs-whitelist") + private Set removeMobsWhitelist = new HashSet<>(); + + @ConfigComment("World flags. These are boolean settings for various flags for this world") + @ConfigEntry(path = "world.flags") + private Map worldFlags = new HashMap<>(); + + @ConfigComment("These are the default protection settings for new islands.") + @ConfigComment("The value is the minimum island rank required allowed to do the action") + @ConfigComment("Ranks are: Visitor = 0, Member = 900, Owner = 1000") + @ConfigEntry(path = "world.default-island-flags") + @Adapter(FlagSerializer.class) + private Map defaultIslandFlags = new HashMap<>(); + + @ConfigComment("These are the default settings for new islands") + @ConfigEntry(path = "world.default-island-settings") + @Adapter(FlagSerializer2.class) + private Map defaultIslandSettings = new HashMap<>(); + + @ConfigComment("These are the settings visible to users.") + @ConfigEntry(path = "world.visible-settings") + private List visibleSettings = new ArrayList<>(); + + @ConfigComment("Visitor banned commands - Visitors to islands cannot use these commands in this world") + @ConfigEntry(path = "world.visitor-banned-commands") + private List visitorBannedCommands = new ArrayList<>(); + + // --------------------------------------------- + + /* ISLAND */ + // Entities + @ConfigEntry(path = "island.limits.entities") + private Map entityLimits = new EnumMap<>(EntityType.class); + @ConfigEntry(path = "island.limits.tile-entities") + private Map tileEntityLimits = new HashMap<>(); + + @ConfigComment("Default max team size") + @ConfigComment("Use this permission to set for specific user groups: askyblock.team.maxsize.") + @ConfigComment("Permission size cannot be less than the default below. ") + @ConfigEntry(path = "island.max-team-size") + private int maxTeamSize = 4; + @ConfigComment("Default maximum number of homes a player can have. Min = 1") + @ConfigComment("Accessed via sethome or go ") + @ConfigComment("Use this permission to set for specific user groups: askyblock.island.maxhomes.") + @ConfigEntry(path = "island.max-homes") + private int maxHomes = 5; + @ConfigComment("Island naming") + @ConfigComment("Only players with the TODO can name their island") + @ConfigComment("It is displayed in the top ten and enter and exit announcements") + @ConfigComment("It replaces the owner's name. Players can use & for color coding if they have the TODO permission") + @ConfigComment("These set the minimum and maximum size of a name.") + @ConfigEntry(path = "island.name.min-length") + private int nameMinLength = 4; + @ConfigEntry(path = "island.name.max-length") + private int nameMaxLength = 20; + @ConfigComment("How long a player must wait until they can rejoin a team island after being") + @ConfigComment("kicked in minutes. This slows the effectiveness of players repeating challenges") + @ConfigComment("by repetitively being invited to a team island.") + @ConfigEntry(path = "island.invite-wait") + private int inviteWait = 60; + + // Reset + @ConfigComment("How many resets a player is allowed (override with /asadmin clearreset )") + @ConfigComment("Value of -1 means unlimited, 0 means hardcore - no resets.") + @ConfigComment("Example, 2 resets means they get 2 resets or 3 islands lifetime") + @ConfigEntry(path = "island.reset.reset-limit") + private int resetLimit = -1; + + @ConfigEntry(path = "island.require-confirmation.reset") + private boolean resetConfirmation = true; + + @ConfigComment("How long a player must wait before they can reset their island again in second") + @ConfigEntry(path = "island.reset-wait") + private long resetWait = 300; + + @ConfigComment("Kicked or leaving players lose resets") + @ConfigComment("Players who leave a team will lose an island reset chance") + @ConfigComment("If a player has zero resets left and leaves a team, they cannot make a new") + @ConfigComment("island by themselves and can only join a team.") + @ConfigComment("Leave this true to avoid players exploiting free islands") + @ConfigEntry(path = "island.reset.leavers-lose-reset") + private boolean leaversLoseReset = false; + + @ConfigComment("Allow kicked players to keep their inventory.") + @ConfigComment("If false, kicked player's inventory will be thrown at the island leader if the") + @ConfigComment("kicked player is online and in the island world.") + @ConfigEntry(path = "island.reset.kicked-keep-inventory") + private boolean kickedKeepInventory = false; + + @ConfigComment("What the plugin should reset when the player joins or creates an island") + @ConfigComment("Reset Money - if this is true, will reset the player's money to the starting money") + @ConfigComment("Recommendation is that this is set to true, but if you run multi-worlds") + @ConfigComment("make sure your economy handles multi-worlds too.") + @ConfigEntry(path = "island.reset.on-join.money") + private boolean onJoinResetMoney = false; + + @ConfigComment("Reset inventory - if true, the player's inventory will be cleared.") + @ConfigComment("Note: if you have MultiInv running or a similar inventory control plugin, that") + @ConfigComment("plugin may still reset the inventory when the world changes.") + @ConfigEntry(path = "island.reset.on-join.inventory") + private boolean onJoinResetInventory = false; + + @ConfigComment("Reset Ender Chest - if true, the player's Ender Chest will be cleared.") + @ConfigEntry(path = "island.reset.on-join.ender-chest") + private boolean onJoinResetEnderChest = false; + + @ConfigComment("What the plugin should reset when the player leaves or is kicked from an island") + @ConfigComment("Reset Money - if this is true, will reset the player's money to the starting money") + @ConfigComment("Recommendation is that this is set to true, but if you run multi-worlds") + @ConfigComment("make sure your economy handles multi-worlds too.") + @ConfigEntry(path = "island.reset.on-leave.money") + private boolean onLeaveResetMoney = false; + + @ConfigComment("Reset inventory - if true, the player's inventory will be cleared.") + @ConfigComment("Note: if you have MultiInv running or a similar inventory control plugin, that") + @ConfigComment("plugin may still reset the inventory when the world changes.") + @ConfigEntry(path = "island.reset.on-leave.inventory") + private boolean onLeaveResetInventory = false; + + @ConfigComment("Reset Ender Chest - if true, the player's Ender Chest will be cleared.") + @ConfigEntry(path = "island.reset.on-leave.ender-chest") + private boolean onLeaveResetEnderChest = false; + + @ConfigEntry(path = "island.make-island-if-none") + private boolean makeIslandIfNone = false; + @ConfigComment("Immediately teleport player to their island (home 1 if it exists) when entering the world") + @ConfigEntry(path = "island.immediate-teleport-on-island") + private boolean immediateTeleportOnIsland = false; + @ConfigComment("Have player's respawn on their island if they die") + @ConfigEntry(path = "island.respawn-on-island") + private boolean respawnOnIsland = true; + + // Deaths + @ConfigComment("Maximum number of deaths to count. The death count can be used by add-ons.") + @ConfigEntry(path = "island.deaths.max") + private int deathsMax = 10; + + @ConfigEntry(path = "island.deaths.sum-team") + private boolean deathsSumTeam = false; + + @ConfigComment("When a player joins a team, reset their death count") + @ConfigEntry(path = "island.deaths.team-join-reset") + private boolean teamJoinDeathReset = true; + + // Ranks + @ConfigEntry(path = "island.customranks") + private Map customRanks = new HashMap<>(); + + // --------------------------------------------- + + /* PROTECTION */ + private int togglePvPCooldown; + + @ConfigComment("Geo restrict mobs.") + @ConfigComment("Mobs that exit the island space where they were spawned will be removed.") + @ConfigEntry(path = "protection.geo-limit-settings") + private List geoLimitSettings = new ArrayList<>(); + + // Invincible visitor settings + @ConfigComment("Invincible visitors. List of damages that will not affect visitors.") + @ConfigComment("Make list blank if visitors should receive all damages") + @ConfigEntry(path = "protection.invincible-visitors") + private List ivSettings = new ArrayList<>(); + + // --------------------------------------------- + + // Timeout for team kick and leave commands + @ConfigComment("Ask the player to confirm the command he is using by typing it again.") + @ConfigComment("The 'wait' value is the number of seconds to wait for confirmation.") + @ConfigEntry(path = "island.require-confirmation.kick") + private boolean kickConfirmation = true; + + @ConfigEntry(path = "island.require-confirmation.kick-wait") + private long kickWait = 10L; + + @ConfigEntry(path = "island.require-confirmation.leave") + private boolean leaveConfirmation = true; + + @ConfigEntry(path = "island.require-confirmation.leave-wait") + private long leaveWait = 10L; + + @ConfigEntry(path = "panel.close-on-click-outside") + private boolean closePanelOnClickOutside = true; + + //---------------------------------------------------------------------------------------/ + @ConfigComment("These settings should not be edited") + @ConfigEntry(path = "do-not-edit-these-settings.reset-epoch") + private long resetEpoch = 0; + + private String uniqueId = "config"; + + // Getters and setters + + /** + * @return the metrics + */ + public boolean isMetrics() { + return metrics; + } + /** + * @param metrics the metrics to set + */ + public void setMetrics(boolean metrics) { + this.metrics = metrics; + } + /** + * @return the startingMoney + */ + public double getStartingMoney() { + return startingMoney; + } + /** + * @param startingMoney the startingMoney to set + */ + public void setStartingMoney(double startingMoney) { + this.startingMoney = startingMoney; + } + /** + * @return the recoverSuperFlat + */ + public boolean isRecoverSuperFlat() { + return recoverSuperFlat; + } + /** + * @param recoverSuperFlat the recoverSuperFlat to set + */ + public void setRecoverSuperFlat(boolean recoverSuperFlat) { + this.recoverSuperFlat = recoverSuperFlat; + } + /** + * @return the muteDeathMessages + */ + public boolean isMuteDeathMessages() { + return muteDeathMessages; + } + /** + * @param muteDeathMessages the muteDeathMessages to set + */ + public void setMuteDeathMessages(boolean muteDeathMessages) { + this.muteDeathMessages = muteDeathMessages; + } + /** + * @return the customRanks + */ + public Map getCustomRanks() { + return customRanks; + } + /** + * @return the databaseBackupPeriod + */ + public int getDatabaseBackupPeriod() { + return databaseBackupPeriod; + } + /** + * @return the databaseType + */ + public DatabaseType getDatabaseType() { + return databaseType; + } + /** + * @return the dbHost + */ + public String getDbHost() { + return dbHost; + } + /** + * @return the dbName + */ + public String getDbName() { + return dbName; + } + /** + * @return the dbPassword + */ + public String getDbPassword() { + return dbPassword; + } + /** + * @return the dbPort + */ + public int getDbPort() { + return dbPort; + } + /** + * @return the dbUsername + */ + public String getDbUsername() { + return dbUsername; + } + /** + * @return the deathsMax + */ + public int getDeathsMax() { + return deathsMax; + } + /** + * @param teamJoinDeathReset the teamJoinDeathReset to set + */ + public void setTeamJoinDeathReset(boolean teamJoinDeathReset) { + this.teamJoinDeathReset = teamJoinDeathReset; + } + /** + * @return the defaultLanguage + */ + public String getDefaultLanguage() { + return defaultLanguage; + } + /** + * @return the entityLimits + */ + @Override + public Map getEntityLimits() { + return entityLimits; + } + /** + * Number of minutes to wait + * @return the inviteWait + */ + public int getInviteWait() { + return inviteWait; + } + /** + * @return the islandDistance + */ + @Override + public int getIslandDistance() { + return islandDistance; + } + /** + * @return the islandHeight + */ + @Override + public int getIslandHeight() { + return islandHeight; + } + /** + * @return the islandProtectionRange + */ + @Override + public int getIslandProtectionRange() { + return islandProtectionRange; + } + /** + * @return the islandStartX + */ + @Override + public int getIslandStartX() { + return islandStartX; + } + /** + * @return the islandStartZ + */ + @Override + public int getIslandStartZ() { + return islandStartZ; + } + /** + * @return the islandXOffset + */ + @Override + public int getIslandXOffset() { + return islandXOffset; + } + /** + * @return the islandZOffset + */ + @Override + public int getIslandZOffset() { + return islandZOffset; + } + /** + * @return the kickWait + */ + public long getKickWait() { + return kickWait; + } + /** + * @return the leaveWait + */ + public long getLeaveWait() { + return leaveWait; + } + /** + * @return the maxHomes + */ + @Override + public int getMaxHomes() { + return maxHomes; + } + /** + * @return the maxIslands + */ + @Override + public int getMaxIslands() { + return maxIslands; + } + /** + * @return the maxTeamSize + */ + @Override + public int getMaxTeamSize() { + return maxTeamSize; + } + /** + * @return the nameMaxLength + */ + public int getNameMaxLength() { + return nameMaxLength; + } + /** + * @return the nameMinLength + */ + public int getNameMinLength() { + return nameMinLength; + } + /** + * @return the netherSpawnRadius + */ + @Override + public int getNetherSpawnRadius() { + return netherSpawnRadius; + } + /** + * @return the purgeMaxIslandLevel + */ + public int getPurgeMaxIslandLevel() { + return purgeMaxIslandLevel; + } + /** + * @return the resetLimit + */ + @Override + public int getResetLimit() { + return resetLimit; + } + /** + * @return the resetWait + */ + public long getResetWait() { + return resetWait; + } + /** + * @return the seaHeight + */ + @Override + public int getSeaHeight() { + return seaHeight; + } + /** + * @return the tileEntityLimits + */ + @Override + public Map getTileEntityLimits() { + return tileEntityLimits; + } + /** + * @return the togglePvPCooldown + */ + public int getTogglePvPCooldown() { + return togglePvPCooldown; + } + /** + * @return the uniqueId + */ + @Override + public String getUniqueId() { + return uniqueId; + } + /** + * @return the worldName + */ + @Override + public String getWorldName() { + return worldName; + } + /** + * @return the allowObsidianScooping + */ + public boolean isAllowObsidianScooping() { + return allowObsidianScooping; + } + /** + * @return the checkUpdates + */ + public boolean isCheckUpdates() { + return checkUpdates; + } + /** + * @return the deathsSumTeam + */ + public boolean isDeathsSumTeam() { + return deathsSumTeam; + } + /** + * @return the endGenerate + */ + @Override + public boolean isEndGenerate() { + return endGenerate; + } + /** + * @return the endIslands + */ + @Override + public boolean isEndIslands() { + return endIslands; + } + /** + * @return the immediateTeleportOnIsland + */ + public boolean isImmediateTeleportOnIsland() { + return immediateTeleportOnIsland; + } + /** + * @return the kickConfirmation + */ + public boolean isKickConfirmation() { + return kickConfirmation; + } + /** + * @return the kickedKeepInventory + */ + public boolean isKickedKeepInventory() { + return kickedKeepInventory; + } + /** + * @return the leaveConfirmation + */ + public boolean isLeaveConfirmation() { + return leaveConfirmation; + } + /** + * @return the leaversLoseReset + */ + public boolean isLeaversLoseReset() { + return leaversLoseReset; + } + /** + * @return the makeIslandIfNone + */ + public boolean isMakeIslandIfNone() { + return makeIslandIfNone; + } + /** + * @return the netherGenerate + */ + @Override + public boolean isNetherGenerate() { + return netherGenerate; + } + /** + * @return the netherIslands + */ + @Override + public boolean isNetherIslands() { + return netherIslands; + } + /** + * @return the netherRoof + */ + public boolean isNetherRoof() { + return netherRoof; + } + /** + * @return the netherTrees + */ + @Override + public boolean isNetherTrees() { + return netherTrees; + } + /** + * @return the purgeRemoveUserData + */ + public boolean isPurgeRemoveUserData() { + return purgeRemoveUserData; + } + /** + * @return the resetConfirmation + */ + public boolean isResetConfirmation() { + return resetConfirmation; + } + /** + * @return the respawnOnIsland + */ + public boolean isRespawnOnIsland() { + return respawnOnIsland; + } + /** + * @return the useEconomy + */ + public boolean isUseEconomy() { + return useEconomy; + } + /** + * @return the useOwnGenerator + */ + @Override + public boolean isUseOwnGenerator() { + return useOwnGenerator; + } + /** + * @param allowObsidianScooping the allowObsidianScooping to set + */ + public void setAllowObsidianScooping(boolean allowObsidianScooping) { + this.allowObsidianScooping = allowObsidianScooping; + } + /** + * @param checkUpdates the checkUpdates to set + */ + public void setCheckUpdates(boolean checkUpdates) { + this.checkUpdates = checkUpdates; + } + /** + * @param customRanks the customRanks to set + */ + public void setCustomRanks(Map customRanks) { + this.customRanks = customRanks; + } + /** + * @param databaseBackupPeriod the databaseBackupPeriod to set + */ + public void setDatabaseBackupPeriod(int databaseBackupPeriod) { + this.databaseBackupPeriod = databaseBackupPeriod; + } + /** + * @param databaseType the databaseType to set + */ + public void setDatabaseType(DatabaseType databaseType) { + this.databaseType = databaseType; + } + /** + * @param dbHost the dbHost to set + */ + public void setDbHost(String dbHost) { + this.dbHost = dbHost; + } + /** + * @param dbName the dbName to set + */ + public void setDbName(String dbName) { + this.dbName = dbName; + } + /** + * @param dbPassword the dbPassword to set + */ + public void setDbPassword(String dbPassword) { + this.dbPassword = dbPassword; + } + /** + * @param dbPort the dbPort to set + */ + public void setDbPort(int dbPort) { + this.dbPort = dbPort; + } + /** + * @param dbUsername the dbUsername to set + */ + public void setDbUsername(String dbUsername) { + this.dbUsername = dbUsername; + } + /** + * @param deathsMax the deathsMax to set + */ + public void setDeathsMax(int deathsMax) { + this.deathsMax = deathsMax; + } + /** + * @param deathsSumTeam the deathsSumTeam to set + */ + public void setDeathsSumTeam(boolean deathsSumTeam) { + this.deathsSumTeam = deathsSumTeam; + } + /** + * @param defaultLanguage the defaultLanguage to set + */ + public void setDefaultLanguage(String defaultLanguage) { + this.defaultLanguage = defaultLanguage; + } + /** + * @param endGenerate the endGenerate to set + */ + public void setEndGenerate(boolean endGenerate) { + this.endGenerate = endGenerate; + } + /** + * @param endIslands the endIslands to set + */ + public void setEndIslands(boolean endIslands) { + this.endIslands = endIslands; + } + /** + * @param entityLimits the entityLimits to set + */ + public void setEntityLimits(Map entityLimits) { + this.entityLimits = entityLimits; + } + /** + * @param immediateTeleportOnIsland the immediateTeleportOnIsland to set + */ + public void setImmediateTeleportOnIsland(boolean immediateTeleportOnIsland) { + this.immediateTeleportOnIsland = immediateTeleportOnIsland; + } + /** + * @param inviteWait the inviteWait to set + */ + public void setInviteWait(int inviteWait) { + this.inviteWait = inviteWait; + } + /** + * @param islandDistance the islandDistance to set + */ + public void setIslandDistance(int islandDistance) { + this.islandDistance = islandDistance; + } + /** + * @param islandHeight the islandHeight to set + */ + public void setIslandHeight(int islandHeight) { + this.islandHeight = islandHeight; + } + /** + * @param islandProtectionRange the islandProtectionRange to set + */ + public void setIslandProtectionRange(int islandProtectionRange) { + this.islandProtectionRange = islandProtectionRange; + } + /** + * @param islandStartX the islandStartX to set + */ + public void setIslandStartX(int islandStartX) { + this.islandStartX = islandStartX; + } + /** + * @param islandStartZ the islandStartZ to set + */ + public void setIslandStartZ(int islandStartZ) { + this.islandStartZ = islandStartZ; + } + /** + * @param islandXOffset the islandXOffset to set + */ + public void setIslandXOffset(int islandXOffset) { + this.islandXOffset = islandXOffset; + } + /** + * @param islandZOffset the islandZOffset to set + */ + public void setIslandZOffset(int islandZOffset) { + this.islandZOffset = islandZOffset; + } + /** + * @param kickConfirmation the kickConfirmation to set + */ + public void setKickConfirmation(boolean kickConfirmation) { + this.kickConfirmation = kickConfirmation; + } + /** + * @param kickedKeepInventory the kickedKeepInventory to set + */ + public void setKickedKeepInventory(boolean kickedKeepInventory) { + this.kickedKeepInventory = kickedKeepInventory; + } + /** + * @param kickWait the kickWait to set + */ + public void setKickWait(long kickWait) { + this.kickWait = kickWait; + } + /** + * @param leaveConfirmation the leaveConfirmation to set + */ + public void setLeaveConfirmation(boolean leaveConfirmation) { + this.leaveConfirmation = leaveConfirmation; + } + /** + * @param leaversLoseReset the leaversLoseReset to set + */ + public void setLeaversLoseReset(boolean leaversLoseReset) { + this.leaversLoseReset = leaversLoseReset; + } + /** + * @param leaveWait the leaveWait to set + */ + public void setLeaveWait(long leaveWait) { + this.leaveWait = leaveWait; + } + /** + * @param makeIslandIfNone the makeIslandIfNone to set + */ + public void setMakeIslandIfNone(boolean makeIslandIfNone) { + this.makeIslandIfNone = makeIslandIfNone; + } + /** + * @param maxHomes the maxHomes to set + */ + public void setMaxHomes(int maxHomes) { + this.maxHomes = maxHomes; + } + /** + * @param maxIslands the maxIslands to set + */ + public void setMaxIslands(int maxIslands) { + this.maxIslands = maxIslands; + } + /** + * @param maxTeamSize the maxTeamSize to set + */ + public void setMaxTeamSize(int maxTeamSize) { + this.maxTeamSize = maxTeamSize; + } + /** + * @param nameMaxLength the nameMaxLength to set + */ + public void setNameMaxLength(int nameMaxLength) { + this.nameMaxLength = nameMaxLength; + } + /** + * @param nameMinLength the nameMinLength to set + */ + public void setNameMinLength(int nameMinLength) { + this.nameMinLength = nameMinLength; + } + /** + * @param netherGenerate the netherGenerate to set + */ + public void setNetherGenerate(boolean netherGenerate) { + this.netherGenerate = netherGenerate; + } + /** + * @param netherIslands the netherIslands to set + */ + public void setNetherIslands(boolean netherIslands) { + this.netherIslands = netherIslands; + } + /** + * @param netherRoof the netherRoof to set + */ + public void setNetherRoof(boolean netherRoof) { + this.netherRoof = netherRoof; + } + /** + * @param netherSpawnRadius the netherSpawnRadius to set + */ + public void setNetherSpawnRadius(int netherSpawnRadius) { + this.netherSpawnRadius = netherSpawnRadius; + } + /** + * @param netherTrees the netherTrees to set + */ + public void setNetherTrees(boolean netherTrees) { + this.netherTrees = netherTrees; + } + /** + * @param purgeMaxIslandLevel the purgeMaxIslandLevel to set + */ + public void setPurgeMaxIslandLevel(int purgeMaxIslandLevel) { + this.purgeMaxIslandLevel = purgeMaxIslandLevel; + } + /** + * @param purgeRemoveUserData the purgeRemoveUserData to set + */ + public void setPurgeRemoveUserData(boolean purgeRemoveUserData) { + this.purgeRemoveUserData = purgeRemoveUserData; + } + /** + * @param resetConfirmation the resetConfirmation to set + */ + public void setResetConfirmation(boolean resetConfirmation) { + this.resetConfirmation = resetConfirmation; + } + /** + * @param resetLimit the resetLimit to set + */ + public void setResetLimit(int resetLimit) { + this.resetLimit = resetLimit; + } + /** + * @param resetWait the resetWait to set + */ + public void setResetWait(long resetWait) { + this.resetWait = resetWait; + } + /** + * @param respawnOnIsland the respawnOnIsland to set + */ + public void setRespawnOnIsland(boolean respawnOnIsland) { + this.respawnOnIsland = respawnOnIsland; + } + /** + * @param seaHeight the seaHeight to set + */ + public void setSeaHeight(int seaHeight) { + this.seaHeight = seaHeight; + } + /** + * @param tileEntityLimits the tileEntityLimits to set + */ + public void setTileEntityLimits(Map tileEntityLimits) { + this.tileEntityLimits = tileEntityLimits; + } + /** + * @param togglePvPCooldown the togglePvPCooldown to set + */ + public void setTogglePvPCooldown(int togglePvPCooldown) { + this.togglePvPCooldown = togglePvPCooldown; + } + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + /** + * @param useEconomy the useEconomy to set + */ + public void setUseEconomy(boolean useEconomy) { + this.useEconomy = useEconomy; + } + /** + * @param useOwnGenerator the useOwnGenerator to set + */ + public void setUseOwnGenerator(boolean useOwnGenerator) { + this.useOwnGenerator = useOwnGenerator; + } + /** + * @param worldName the worldName to set + */ + public void setWorldName(String worldName) { + this.worldName = worldName; + } + /** + * @return the fakePlayers + */ + public Set getFakePlayers() { + return fakePlayers; + } + /** + * @param fakePlayers the fakePlayers to set + */ + public void setFakePlayers(Set fakePlayers) { + this.fakePlayers = fakePlayers; + } + /** + * @return the confirmationTime + */ + public int getConfirmationTime() { + return confirmationTime; + } + /** + * @param confirmationTime the confirmationTime to set + */ + public void setConfirmationTime(int confirmationTime) { + this.confirmationTime = confirmationTime; + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.configuration.WorldSettings#getFriendlyName() + */ + @Override + public String getFriendlyName() { + return friendlyName; + } + + /** + * @param friendlyName the friendlyName to set + */ + public void setFriendlyName(String friendlyName) { + this.friendlyName = friendlyName; + } + /** + * @return the dragonSpawn + */ + @Override + public boolean isDragonSpawn() { + return dragonSpawn; + } + /** + * @param dragonSpawn the dragonSpawn to set + */ + public void setDragonSpawn(boolean dragonSpawn) { + this.dragonSpawn = dragonSpawn; + } + + @Override + public String getPermissionPrefix() { + return "bskyblock"; + } + + /** + * Invincible visitor settings + * @return the ivSettings + */ + @Override + public List getIvSettings() { + return ivSettings; + } + + /** + * @param ivSettings the ivSettings to set + */ + public void setIvSettings(List ivSettings) { + this.ivSettings = ivSettings; + } + + /** + * @return the worldFlags + */ + @Override + public Map getWorldFlags() { + return worldFlags; + } + /** + * @param worldFlags the worldFlags to set + */ + public void setWorldFlags(Map worldFlags) { + this.worldFlags = worldFlags; + } + + /** + * @return whether panels should close when clicked outside or not + */ + public boolean isClosePanelOnClickOutside() { + return closePanelOnClickOutside; + } + + /** + * Set panel close on click outside + * @param closePanelOnClickOutside - true means close panel when click is outside panel + */ + public void setClosePanelOnClickOutside(boolean closePanelOnClickOutside) { + this.closePanelOnClickOutside = closePanelOnClickOutside; + } + /** + * @return the defaultGameMode + */ + @Override + public GameMode getDefaultGameMode() { + return defaultGameMode; + } + /** + * @param defaultGameMode the defaultGameMode to set + */ + public void setDefaultGameMode(GameMode defaultGameMode) { + this.defaultGameMode = defaultGameMode; + } + /** + * @return the removeMobsWhitelist + */ + @Override + public Set getRemoveMobsWhitelist() { + return removeMobsWhitelist; + } + /** + * @param removeMobsWhitelist the removeMobsWhitelist to set + */ + public void setRemoveMobsWhitelist(Set removeMobsWhitelist) { + this.removeMobsWhitelist = removeMobsWhitelist; + } + /** + * @return the onJoinResetMoney + */ + @Override + public boolean isOnJoinResetMoney() { + return onJoinResetMoney; + } + /** + * @return the onJoinResetInventory + */ + @Override + public boolean isOnJoinResetInventory() { + return onJoinResetInventory; + } + /** + * @return the onJoinResetEnderChest + */ + @Override + public boolean isOnJoinResetEnderChest() { + return onJoinResetEnderChest; + } + /** + * @return the onLeaveResetMoney + */ + @Override + public boolean isOnLeaveResetMoney() { + return onLeaveResetMoney; + } + /** + * @return the onLeaveResetInventory + */ + @Override + public boolean isOnLeaveResetInventory() { + return onLeaveResetInventory; + } + /** + * @return the onLeaveResetEnderChest + */ + @Override + public boolean isOnLeaveResetEnderChest() { + return onLeaveResetEnderChest; + } + /** + * @param onJoinResetMoney the onJoinResetMoney to set + */ + public void setOnJoinResetMoney(boolean onJoinResetMoney) { + this.onJoinResetMoney = onJoinResetMoney; + } + /** + * @param onJoinResetInventory the onJoinResetInventory to set + */ + public void setOnJoinResetInventory(boolean onJoinResetInventory) { + this.onJoinResetInventory = onJoinResetInventory; + } + /** + * @param onJoinResetEnderChest the onJoinResetEnderChest to set + */ + public void setOnJoinResetEnderChest(boolean onJoinResetEnderChest) { + this.onJoinResetEnderChest = onJoinResetEnderChest; + } + /** + * @param onLeaveResetMoney the onLeaveResetMoney to set + */ + public void setOnLeaveResetMoney(boolean onLeaveResetMoney) { + this.onLeaveResetMoney = onLeaveResetMoney; + } + /** + * @param onLeaveResetInventory the onLeaveResetInventory to set + */ + public void setOnLeaveResetInventory(boolean onLeaveResetInventory) { + this.onLeaveResetInventory = onLeaveResetInventory; + } + /** + * @param onLeaveResetEnderChest the onLeaveResetEnderChest to set + */ + public void setOnLeaveResetEnderChest(boolean onLeaveResetEnderChest) { + this.onLeaveResetEnderChest = onLeaveResetEnderChest; + } + + @Override + public Optional getAddon() { + // This is a plugin, not an addon + return Optional.empty(); + } + /** + * @return the defaultIslandProtection + */ + @Override + public Map getDefaultIslandFlags() { + return defaultIslandFlags; + } + /** + * @return the visibleSettings + */ + @Override + public List getVisibleSettings() { + return visibleSettings; + } + /** + */ + public void setDefaultIslandFlags(Map defaultIslandFlags) { + this.defaultIslandFlags = defaultIslandFlags; + } + /** + * @param visibleSettings the visibleSettings to set + */ + public void setVisibleSettings(List visibleSettings) { + this.visibleSettings = visibleSettings; + } + /** + * @return the defaultIslandSettings + */ + @Override + public Map getDefaultIslandSettings() { + return defaultIslandSettings; + } + /** + * @param defaultIslandSettings the defaultIslandSettings to set + */ + public void setDefaultIslandSettings(Map defaultIslandSettings) { + this.defaultIslandSettings = defaultIslandSettings; + } + + public boolean isTeamJoinDeathReset() { + return teamJoinDeathReset; + } + /** + * @return the visitorbannedcommands + */ + @Override + public List getVisitorBannedCommands() { + return visitorBannedCommands; + } + /** + * @param visitorBannedCommands the visitorbannedcommands to set + */ + public void setVisitorBannedCommands(List visitorBannedCommands) { + this.visitorBannedCommands = visitorBannedCommands; + } + /** + * @return the difficulty + */ + @Override + public Difficulty getDifficulty() { + return difficulty; + } + /** + * @param difficulty the difficulty to set + */ + @Override + public void setDifficulty(Difficulty difficulty) { + this.difficulty = difficulty; + } + + @Override + public boolean isWaterUnsafe() { + return false; + } + /** + * @return the geoLimitSettings + */ + @Override + public List getGeoLimitSettings() { + return geoLimitSettings; + } + /** + * @param geoLimitSettings the geoLimitSettings to set + */ + public void setGeoLimitSettings(List geoLimitSettings) { + this.geoLimitSettings = geoLimitSettings; + } + /** + * @return the resetEpoch + */ + @Override + public long getResetEpoch() { + return resetEpoch; + } + /** + * @param resetEpoch the resetEpoch to set + */ + @Override + public void setResetEpoch(long resetEpoch) { + this.resetEpoch = resetEpoch; + } + + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java b/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java new file mode 100644 index 0000000..9a88e2c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java @@ -0,0 +1,297 @@ +package us.tastybento.bskyblock.api.addons; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.Listener; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * Add-on class for BSkyBlock. Extend this to create an add-on. The operation + * and methods are very similar to Bukkit's JavaPlugin. + * + * @author tastybento, ComminQ_Q + */ +public abstract class Addon implements AddonInterface { + + private static final String ADDON_CONFIG_FILENAME = "config.yml"; + private boolean enabled; + private AddonDescription description; + private FileConfiguration config; + private File dataFolder; + private File file; + + public Addon() { + enabled = false; + } + + public BSkyBlock getBSkyBlock() { + return BSkyBlock.getInstance(); + } + + /** + * @return the addon's default config file + */ + public FileConfiguration getConfig() { + if (config == null) { + config = loadYamlFile(); + } + return config; + } + + /** + * @return Addon's data folder + */ + public File getDataFolder() { + return dataFolder; + } + + /** + * @return Addon's description + */ + public AddonDescription getDescription() { + return description; + } + + /** + * @return the file + */ + public File getFile() { + return file; + } + + /** + * @return Logger + */ + public Logger getLogger() { + return getBSkyBlock().getLogger(); + } + + /** + * Convenience method to obtain the server + * + * @return the server object + */ + public Server getServer() { + return getBSkyBlock().getServer(); + } + + public boolean isEnabled() { + return enabled; + } + + /** + * Load YAML config file + * + * @return Yaml File configuration + */ + private FileConfiguration loadYamlFile() { + File yamlFile = new File(dataFolder, ADDON_CONFIG_FILENAME); + + YamlConfiguration yamlConfig = null; + if (yamlFile.exists()) { + try { + yamlConfig = new YamlConfiguration(); + yamlConfig.load(yamlFile); + } catch (Exception e) { + Bukkit.getLogger().severe(() -> "Could not load config.yml: " + e.getMessage()); + } + } + return yamlConfig; + } + + /** + * Register a listener for this addon + * + * @param listener - listener + */ + public void registerListener(Listener listener) { + Bukkit.getPluginManager().registerEvents(listener, BSkyBlock.getInstance()); + } + + /** + * Saves the FileConfiguration retrievable by getConfig(). + */ + public void saveConfig() { + try { + getConfig().save(new File(dataFolder, ADDON_CONFIG_FILENAME)); + } catch (IOException e) { + Bukkit.getLogger().severe("Could not save config!"); + } + } + + /** + * Saves the addon's config.yml file to the addon's data folder and loads it. If + * the file exists already, it will not be replaced. + */ + public void saveDefaultConfig() { + saveResource(ADDON_CONFIG_FILENAME, false); + config = loadYamlFile(); + } + + /** + * Saves a resource contained in this add-on's jar file to the addon's data + * folder. + * + * @param resourcePath + * in jar file + * @param replace + * - if true, will overwrite previous file + */ + public void saveResource(String resourcePath, boolean replace) { + saveResource(resourcePath, dataFolder, replace, false); + } + + /** + * Saves a resource contained in this add-on's jar file to the destination + * folder. + * + * @param jarResource + * in jar file + * @param destinationFolder + * on file system + * @param replace + * - if true, will overwrite previous file + * @param noPath + * - if true, the resource's path will be ignored when saving + */ + public void saveResource(String jarResource, File destinationFolder, boolean replace, boolean noPath) { + if (jarResource == null || jarResource.equals("")) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + + jarResource = jarResource.replace('\\', '/'); + try (JarFile jar = new JarFile(file)) { + JarEntry jarConfig = jar.getJarEntry(jarResource); + if (jarConfig != null) { + try (InputStream in = jar.getInputStream(jarConfig)) { + if (in == null) { + throw new IllegalArgumentException( + "The embedded resource '" + jarResource + "' cannot be found in " + jar.getName()); + } + // There are two options, use the path of the resource or not + File outFile = new File(destinationFolder, jarResource); + if (noPath) { + outFile = new File(destinationFolder, outFile.getName()); + } + // Make any dirs that need to be made + outFile.getParentFile().mkdirs(); + if (!outFile.exists() || replace) { + java.nio.file.Files.copy(in, outFile.toPath()); + } + } + } + } catch (IOException e) { + Bukkit.getLogger().severe( + "Could not save from jar file. From " + jarResource + " to " + destinationFolder.getAbsolutePath()); + } + } + + /** + * Get the resource from Jar file + * @param jarResource - jar resource filename + * @return resource or null if there is a problem + */ + public InputStream getResource(String jarResource) { + if (jarResource == null || jarResource.equals("")) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + + jarResource = jarResource.replace('\\', '/'); + try (JarFile jar = new JarFile(file)) { + JarEntry jarConfig = jar.getJarEntry(jarResource); + if (jarConfig != null) { + try (InputStream in = jar.getInputStream(jarConfig)) { + return in; + } + } + } catch (IOException e) { + Bukkit.getLogger().severe("Could not open from jar file. " + jarResource); + } + return null; + } + /** + * Set the file that contains this addon + * + * @param f + * the file to set + */ + public void setAddonFile(File f) { + file = f; + } + + /** + * Set this addon's data folder + * + * @param file - data folder + */ + public void setDataFolder(File file) { + dataFolder = file; + } + + /** + * Set this addons description + * + * @param desc - description + */ + public void setDescription(AddonDescription desc) { + description = desc; + } + + /** + * Set whether this addon is enabled or not + * + * @param enabled - true if enabled + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Get Players Manager + * @return Players manager + */ + public PlayersManager getPlayers() { + return getBSkyBlock().getPlayers(); + } + + /** + * Get Islands Manager + * @return Islands manager + */ + public IslandsManager getIslands() { + return getBSkyBlock().getIslands(); + } + + /** + * Get the Addon By Name + * @return Optional Addon + */ + public Optional getAddonByName(String name) { + return getBSkyBlock().getAddonsManager().getAddonByName(name); + } + + public void log(String string) { + getBSkyBlock().log(string); + } + + public void logWarning(String string) { + getBSkyBlock().logWarning(string); + } + + public void logError(String string) { + getBSkyBlock().logError(string); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java b/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java new file mode 100644 index 0000000..c53b9d7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java @@ -0,0 +1,138 @@ +package us.tastybento.bskyblock.api.addons; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.InvalidDescriptionException; +import org.bukkit.util.permissions.DefaultPermissions; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.AddonDescription.AddonDescriptionBuilder; +import us.tastybento.bskyblock.api.addons.exception.InvalidAddonFormatException; +import us.tastybento.bskyblock.api.addons.exception.InvalidAddonInheritException; +import us.tastybento.bskyblock.managers.AddonsManager; + +/** + * Loads addons and sets up permissions + * @author Tastybento, ComminQ + */ +public class AddonClassLoader extends URLClassLoader { + + private final Map> classes = new HashMap<>(); + private Addon addon; + private AddonsManager loader; + + public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File path, ClassLoader parent) + throws InvalidAddonInheritException, + MalformedURLException, + InvalidAddonFormatException, + InvalidDescriptionException, + InstantiationException, + IllegalAccessException { + super(new URL[]{path.toURI().toURL()}, parent); + + loader = addonsManager; + + Class javaClass; + try { + String mainClass = data.getString("main"); + javaClass = Class.forName(mainClass, true, this); + if(mainClass.contains("us.tastybento")){ + throw new InvalidAddonFormatException("Packages declaration cannot start with 'us.tastybento'"); + } + } catch (ClassNotFoundException e) { + BSkyBlock.getInstance().logError("Could not load '" + path.getName() + "' in folder '" + path.getParent() + "' - invalid addon.yml"); + throw new InvalidDescriptionException("Invalid addon.yml"); + } + + Class addonClass; + try{ + addonClass = javaClass.asSubclass(Addon.class); + } catch(ClassCastException e){ + throw new InvalidAddonInheritException("Main class doesn't not extends super class 'Addon'"); + } + + addon = addonClass.newInstance(); + addon.setDescription(asDescription(data)); + // Set permissions + if (data.isConfigurationSection("permissions")) { + ConfigurationSection perms = data.getConfigurationSection("permissions"); + perms.getKeys(true).forEach(perm -> { + if (perms.contains(perm + ".default") && perms.contains(perm + ".description")) { + registerPermission(perms, perm); + } + }); + } + } + + private void registerPermission(ConfigurationSection perms, String perm) { + PermissionDefault pd = PermissionDefault.getByName(perms.getString(perm + ".default")); + if (pd == null) { + Bukkit.getLogger().severe("Permission default is invalid : " + perms.getName()); + return; + } + String desc = perms.getString(perm + ".description"); + DefaultPermissions.registerPermission(perm, desc, pd); + } + + private AddonDescription asDescription(YamlConfiguration data){ + return new AddonDescriptionBuilder(data.getString("name")) + .withVersion(data.getString("version")) + .withAuthor(data.getString("authors")).build(); + } + + + /* (non-Javadoc) + * @see java.net.URLClassLoader#findClass(java.lang.String) + */ + @Override + protected Class findClass(String name) { + return findClass(name, true); + } + + /** + * This is a custom findClass that enables classes in other addons to be found + * @param name - class name + * @param checkGlobal - check globally or not when searching + * @return Class - class if found + */ + public Class findClass(String name, boolean checkGlobal) { + if (name.startsWith("us.tastybento.")) { + return null; + } + Class result = classes.get(name); + if (result == null) { + if (checkGlobal) { + result = loader.getClassByName(name); + } + if (result == null) { + try { + result = super.findClass(name); + } catch (ClassNotFoundException e) { + return null; + } + if (result != null) { + loader.setClass(name, result); + } + classes.put(name, result); + } + } + return result; + } + + /** + * @return the addon + */ + public Addon getAddon() { + return addon; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/AddonDescription.java b/src/main/java/us/tastybento/bskyblock/api/addons/AddonDescription.java new file mode 100644 index 0000000..22372fa --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/AddonDescription.java @@ -0,0 +1,111 @@ +package us.tastybento.bskyblock.api.addons; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Tastybento, Poslovitch + */ +public final class AddonDescription { + + private String main; + private String name; + private String version; + private String description; + private List authors; + + public AddonDescription() {} + + public AddonDescription(String main, String name, String version, String description, List authors) { + this.main = main; + this.name = name; + this.version = version; + this.description = description; + this.authors = authors; + } + + /** + * @param main the main to set + */ + public void setMain(String main) { + this.main = main; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @param authors the authors to set + */ + public void setAuthors(List authors) { + this.authors = authors; + } + + public String getName() { + return name; + } + + public String getMain() { + return main; + } + + public String getVersion() { + return version; + } + + public String getDescription() { + return description; + } + + public List getAuthors() { + return authors; + } + + public static class AddonDescriptionBuilder{ + + private AddonDescription description; + + public AddonDescriptionBuilder(String name){ + description = new AddonDescription(); + description.setName(name); + } + + public AddonDescriptionBuilder withAuthor(String... authors){ + description.setAuthors(Arrays.asList(authors)); + return this; + } + + public AddonDescriptionBuilder withDescription(String desc){ + description.setDescription(desc); + return this; + } + + public AddonDescriptionBuilder withVersion(String version){ + description.setVersion(version); + return this; + } + + public AddonDescription build(){ + return description; + } + + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/AddonInterface.java b/src/main/java/us/tastybento/bskyblock/api/addons/AddonInterface.java new file mode 100644 index 0000000..07fb825 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/AddonInterface.java @@ -0,0 +1,7 @@ +package us.tastybento.bskyblock.api.addons; + +public interface AddonInterface { + void onEnable(); + void onDisable(); + default void onLoad() {} +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/AddonState.java b/src/main/java/us/tastybento/bskyblock/api/addons/AddonState.java new file mode 100644 index 0000000..47d252e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/AddonState.java @@ -0,0 +1,34 @@ +package us.tastybento.bskyblock.api.addons; + +/** + * Represents the current run-time state of a {@link Addon}. + * + * @author Poslovitch + * @since 1.0 + */ +public enum AddonState { + /** + * The addon has been correctly enabled and is now fully working. + */ + ENABLED, + + /** + * The addon is fully disabled. + */ + DISABLED, + + /** + * The addon has not been loaded because it requires a different version of BSkyBlock or of the server software. + */ + INCOMPATIBLE, + + /** + * The addon has not been enabled because a dependency is missing. + */ + MISSING_DEPENDENCY, + + /** + * The addon loading or enabling process has been interrupted by an unhandled error. + */ + ERROR +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/exception/AddonException.java b/src/main/java/us/tastybento/bskyblock/api/addons/exception/AddonException.java new file mode 100644 index 0000000..0c15d39 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/exception/AddonException.java @@ -0,0 +1,14 @@ +package us.tastybento.bskyblock.api.addons.exception; + +public abstract class AddonException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 4203162022348693854L; + + public AddonException(String errorMessage){ + super("AddonException : " + errorMessage); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonFormatException.java b/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonFormatException.java new file mode 100644 index 0000000..761630b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonFormatException.java @@ -0,0 +1,28 @@ +package us.tastybento.bskyblock.api.addons.exception; + +import java.util.logging.Level; + +import org.bukkit.Bukkit; + +public class InvalidAddonFormatException extends AddonException { + + /** + * + */ + private static final long serialVersionUID = 7741502900847049986L; + + public InvalidAddonFormatException(String errorMessage) { + super(errorMessage); + } + + @Override + public void printStackTrace(){ + super.printStackTrace(); + + Bukkit.getLogger().log(Level.WARNING, " Basic format : (addon.yml)"); + Bukkit.getLogger().log(Level.WARNING, " main: path.to.your.MainClass"); + Bukkit.getLogger().log(Level.WARNING, " name: "); + Bukkit.getLogger().log(Level.WARNING, " authors: | "); + Bukkit.getLogger().log(Level.WARNING, " version: YourVersion"); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonInheritException.java b/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonInheritException.java new file mode 100644 index 0000000..42b56c2 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/addons/exception/InvalidAddonInheritException.java @@ -0,0 +1,14 @@ +package us.tastybento.bskyblock.api.addons.exception; + +public class InvalidAddonInheritException extends AddonException { + + /** + * + */ + private static final long serialVersionUID = -5847358994397613244L; + + public InvalidAddonInheritException(String errorMessage) { + super(errorMessage); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/commands/BSBCommand.java b/src/main/java/us/tastybento/bskyblock/api/commands/BSBCommand.java new file mode 100644 index 0000000..5f7be59 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/commands/BSBCommand.java @@ -0,0 +1,42 @@ +package us.tastybento.bskyblock.api.commands; + +import java.util.List; +import java.util.Optional; + +import us.tastybento.bskyblock.api.user.User; + +/** + * Interface for BSkyBlock Commands + * @author tastybento + * + */ +public interface BSBCommand { + + /** + * Anything that needs to be set up for this command. + * Register subcommands in this section. + */ + void setup(); + + /** + * What will be executed when this command is run + * @param user the User who is executing the command + * @param label the label which has been used to execute the command (can be the command's label OR an alias) + * @param args the command arguments + * @return true or false - true if the command executed successfully + */ + boolean execute(User user, String label, List args); + + /** + * Tab Completer for CompositeCommands. Note that any registered sub-commands will be automatically + * added to the list must not be manually added. Use this to add tab-complete for things like names. + * @param user - the User + * @param alias - alias for command + * @param args - command arguments + * @return List of strings that could be used to complete this command. + */ + default Optional> tabComplete(User user, String alias, List args) { + return Optional.empty(); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java new file mode 100644 index 0000000..ccda8f9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java @@ -0,0 +1,644 @@ +package us.tastybento.bskyblock.api.commands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.events.command.CommandEvent; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * BSB composite command + * @author tastybento + * @author Poslovitch + */ +public abstract class CompositeCommand extends Command implements PluginIdentifiableCommand, BSBCommand { + + private final BSkyBlock plugin; + + /** + * True if the command is for the player only (not for the console) + */ + private boolean onlyPlayer = false; + /** + * The parameters string for this command. It is the commands followed by a locale reference. + */ + private String parameters = ""; + /** + * The parent command to this one. If this is a top-level command it will be empty. + */ + protected final CompositeCommand parent; + /** + * The permission required to execute this command + */ + private String permission = ""; + /** + * This is the command level. 0 is the top, 1 is the first level sub command. + */ + private final int subCommandLevel; + /** + * Map of sub commands + */ + private Map subCommands; + + /** + * Map of aliases for subcommands + */ + private Map subCommandAliases; + /** + * The command chain from the very top, e.g., island team promote + */ + private String usage; + + /** + * The prefix to be used in this command + */ + private String permissionPrefix = ""; + + /** + * The world that this command operates in. This is an overworld and will cover any associated nether or end + * If the world value does not exist, then the command is general across worlds + */ + private World world; + + /** + * The addon creating this command, if any + */ + private Addon addon; + + /** + * The top level label + */ + private String topLabel = ""; + + private static Map toBeConfirmed = new HashMap<>(); + + /** + * Top level command + * @param addon - addon creating the command + * @param label - string for this command + * @param aliases - aliases + */ + public CompositeCommand(Addon addon, String label, String... aliases) { + super(label); + this.addon = addon; + this.topLabel = label; + this.plugin = BSkyBlock.getInstance(); + setAliases(new ArrayList<>(Arrays.asList(aliases))); + parent = null; + setUsage(""); + subCommandLevel = 0; // Top level + subCommands = new LinkedHashMap<>(); + subCommandAliases = new LinkedHashMap<>(); + // Register command if it is not already registered + if (plugin.getCommand(label) == null) { + plugin.getCommandsManager().registerCommand(this); + } + setup(); + if (!getSubCommand("help").isPresent() && !label.equals("help")) { + new DefaultHelpCommand(this); + } + } + + /** + * This is the top-level command constructor for commands that have no parent. + * @param label - string for this command + * @param aliases - aliases for this command + */ + public CompositeCommand(String label, String... aliases) { + this((Addon)null, label, aliases); + } + + /** + * Sub-command constructor + * @param parent - the parent composite command + * @param label - string label for this subcommand + * @param aliases - aliases for this subcommand + */ + public CompositeCommand(CompositeCommand parent, String label, String... aliases) { + super(label); + this.topLabel = parent.getTopLabel(); + this.plugin = BSkyBlock.getInstance(); + this.parent = parent; + subCommandLevel = parent.getLevel() + 1; + // Add this sub-command to the parent + parent.getSubCommands().put(label, this); + setAliases(new ArrayList<>(Arrays.asList(aliases))); + subCommands = new LinkedHashMap<>(); + subCommandAliases = new LinkedHashMap<>(); + // Add aliases to the parent for this command + for (String alias : aliases) { + parent.getSubCommandAliases().put(alias, this); + } + setUsage(""); + // Inherit permission prefix + this.permissionPrefix = parent.getPermissionPrefix(); + // Inherit world + this.world = parent.getWorld(); + setup(); + // If this command does not define its own help class, then use the default help command + if (!getSubCommand("help").isPresent() && !label.equals("help")) { + new DefaultHelpCommand(this); + } + + } + /* + * This method deals with the command execution. It traverses the tree of + * subcommands until it finds the right object and then runs execute on it. + */ + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + // Get the User instance for this sender + User user = User.getInstance(sender); + CompositeCommand cmd = getCommandFromArgs(args); + // Check for console and permissions + if (cmd.onlyPlayer && !(sender instanceof Player)) { + user.sendMessage("general.errors.use-in-game"); + return false; + } + // Check perms, but only if this isn't the console + if ((sender instanceof Player) && !sender.isOp() && !cmd.getPermission().isEmpty() && !sender.hasPermission(cmd.getPermission())) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", TextVariables.PERMISSION, cmd.getPermission()); + return false; + } + // Fire an event to see if this command should be cancelled + CommandEvent event = CommandEvent.builder() + .setCommand(this) + .setLabel(label) + .setSender(sender) + .setArgs(args) + .build(); + if (event.isCancelled()) { + return false; + } + // Execute and trim args + return cmd.execute(user, (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label, Arrays.asList(args).subList(cmd.subCommandLevel, args.length)); + } + + /** + * Get the current composite command based on the arguments + * @param args - arguments + * @return the current composite command based on the arguments + */ + private CompositeCommand getCommandFromArgs(String[] args) { + CompositeCommand subCommand = this; + // Run through any arguments + for (String arg : args) { + // get the subcommand corresponding to the arg + if (subCommand.hasSubCommands()) { + Optional sub = subCommand.getSubCommand(arg); + if (!sub.isPresent()) { + return subCommand; + } + // Step down one + subCommand = sub.orElse(subCommand); + // Set the label + subCommand.setLabel(arg); + } else { + // We are at the end of the walk + return subCommand; + } + // else continue the loop + } + return subCommand; + } + + /** + * Convenience method to get the island manager + * @return IslandsManager + */ + protected IslandsManager getIslands() { + return plugin.getIslands(); + } + + /** + * @return this command's sub-level. Top level is 0. + * Every time a command registers with a parent, their level will be set. + */ + protected int getLevel() { + return subCommandLevel; + } + + /** + * @return Logger + */ + public Logger getLogger() { + return plugin.getLogger(); + } + + /** + * Convenience method to obtain team members + * @param world - world to check + * @param user - the User + * @return set of UUIDs of all team members + */ + protected Set getMembers(World world, User user) { + return plugin.getIslands().getMembers(world, user.getUniqueId()); + } + + public String getParameters() { + return parameters; + } + + /** + * @return the parent command object + */ + public CompositeCommand getParent() { + return parent; + } + + @Override + public String getPermission() { + return permission; + } + + /** + * Convenience method to get the player manager + * @return PlayersManager + */ + protected PlayersManager getPlayers() { + return plugin.getPlayers(); + } + + @Override + public BSkyBlock getPlugin() { + return plugin; + } + + /** + * Get the island worlds manager + * @return island worlds manager + */ + public IslandWorldManager getIWM() { + return plugin.getIWM(); + } + /** + * @return Settings object + */ + public Settings getSettings() { + return plugin.getSettings(); + } + + /** + * Returns the CompositeCommand object referring to this command label + * @param label - command label or alias + * @return CompositeCommand or null if none found + */ + public Optional getSubCommand(String label) { + label = label.toLowerCase(); + if (subCommands.containsKey(label)) { + return Optional.ofNullable(subCommands.get(label)); + } + // Try aliases + if (subCommandAliases.containsKey(label)) { + return Optional.ofNullable(subCommandAliases.get(label)); + } + return Optional.empty(); + } + + /** + * @return Map of sub commands for this command + */ + public Map getSubCommands() { + return subCommands; + } + + /** + * Returns a map of sub commands for this command. + * As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #getSubCommands()} when no such distinction is needed. + * @param ignoreHelp Whether the Help subcommand should not be returned in the map or not. + * @return Map of sub commands for this command + * @see #hasSubCommands(boolean) + */ + public Map getSubCommands(boolean ignoreHelp) { + if (ignoreHelp && getSubCommand("help").isPresent()) { + Map result = subCommands; + result.remove("help"); + return result; + } + return getSubCommands(); + } + + /** + * Convenience method to obtain the user's team leader + * @param world - world to check + * @param user - the User + * @return UUID of player's team leader or null if user has no island + */ + protected UUID getTeamLeader(World world, User user) { + return plugin.getIslands().getTeamLeader(world, user.getUniqueId()); + } + + @Override + public String getUsage() { + return "/" + usage; + } + + /** + * Check if this command has a specific sub command. + * @param subCommand - sub command + * @return true if this command has this sub command + */ + protected boolean hasSubCommand(String subCommand) { + return subCommands.containsKey(subCommand) || subCommandAliases.containsKey(subCommand); + } + + /** + * Check if this command has any sub commands. + * @return true if this command has subcommands + */ + protected boolean hasSubCommands() { + return !subCommands.isEmpty(); + } + + /** + * Check if this command has any sub commands. + * As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #hasSubCommands()} when no such distinction is needed. + * @param ignoreHelp Whether the Help subcommand should not be taken into account or not. + * @return true if this command has subcommands + * @see #getSubCommands(boolean) + */ + protected boolean hasSubCommands(boolean ignoreHelp) { + return !getSubCommands(ignoreHelp).isEmpty(); + } + + /** + * Convenience method to check if a user has a team. + * @param world - the world to check + * @param user - the User + * @return true if player is in a team + */ + protected boolean inTeam(World world, User user) { + return plugin.getIslands().inTeam(world, user.getUniqueId()); + } + + /** + * Check if this command is only for players. + * @return true or false + */ + public boolean isOnlyPlayer() { + return onlyPlayer; + } + + /** + * Convenience method to check if a user is a player + * @param user - the User + * @return true if sender is a player + */ + protected boolean isPlayer(User user) { + return user.getPlayer() != null; + } + + /** + * Set whether this command is only for players + * @param onlyPlayer - true if command only for players + */ + public void setOnlyPlayer(boolean onlyPlayer) { + this.onlyPlayer = onlyPlayer; + } + + /** + * Sets the command parameters to be shown in help + * @param parameters - string of parameters + */ + public void setParameters(String parameters) { + this.parameters = parameters; + } + + /* (non-Javadoc) + * @see org.bukkit.command.Command#setPermission(java.lang.String) + */ + @Override + public void setPermission(String permission) { + this.permission = permissionPrefix + permission; + } + + /** + * Inherits the permission from parent command + */ + public void inheritPermission() { + this.permission = parent.getPermission(); + } + + /** + * This creates the full linking chain of commands + */ + @Override + public Command setUsage(String usage) { + // Go up the chain + CompositeCommand parentCommand = getParent(); + StringBuilder u = new StringBuilder().append(getLabel()).append(" ").append(usage); + while (parentCommand != null) { + u.insert(0, " "); + u.insert(0, parentCommand.getLabel()); + parentCommand = parentCommand.getParent(); + } + this.usage = u.toString().trim(); + return this; + } + + @Override + public List tabComplete(final CommandSender sender, final String alias, final String[] args) { + List options = new ArrayList<>(); + // Get command object based on args entered so far + CompositeCommand cmd = getCommandFromArgs(args); + // Check for console and permissions + if (cmd.onlyPlayer && !(sender instanceof Player)) { + return options; + } + if (!cmd.getPermission().isEmpty() && !sender.hasPermission(cmd.getPermission()) && !sender.isOp()) { + return options; + } + // Add any tab completion from the subcommand + options.addAll(cmd.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElse(new ArrayList<>())); + // Add any sub-commands automatically + if (cmd.hasSubCommands()) { + // Check if subcommands are visible to this sender + for (CompositeCommand subCommand: cmd.getSubCommands().values()) { + if (sender instanceof Player) { + // Player + if (subCommand.getPermission().isEmpty() || sender.hasPermission(subCommand.getPermission()) || sender.isOp()) { + // Permission is okay + options.add(subCommand.getLabel()); + } + } else { + // Console + if (!subCommand.onlyPlayer) { + // Not a player command + options.add(subCommand.getLabel()); + } + } + } + } + + String lastArg = args.length != 0 ? args[args.length - 1] : ""; + return Util.tabLimit(options, lastArg).stream().sorted().collect(Collectors.toList()); + } + + /** + * Show help + * @param command - command that this help is for + * @param user - the User + * @return result of help command or false if no help defined + */ + protected boolean showHelp(CompositeCommand command, User user) { + return command.getSubCommand("help").map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false); + } + + /** + * @return the subCommandAliases + */ + public Map getSubCommandAliases() { + return subCommandAliases; + } + + /** + * If the permission prefix has been set, will return the prefix plus a trailing dot. + * @return the permissionPrefix + */ + public String getPermissionPrefix() { + return permissionPrefix; + } + + /** + * Set the permission prefix. This will be added automatically to the permission + * and will apply to any sub commands too. + * Do not put a dot on the end of it. + * @param permissionPrefix the permissionPrefix to set + */ + public void setPermissionPrefix(String permissionPrefix) { + this.permissionPrefix = permissionPrefix + "."; + } + + /** + * The the world that this command applies to. + * @return the world + */ + public World getWorld() { + if (world == null) { + plugin.logError(getLabel() + " did not setWorld in setup!"); + } + return world; + } + + /** + * @param world the world to set + */ + public void setWorld(World world) { + this.world = world; + } + + /** + * @return the addon + */ + public Addon getAddon() { + return addon; + } + + /** + * @return top level label, e.g., island + */ + public String getTopLabel() { + return topLabel; + } + + /** + * Tells user to confirm command by retyping + * @param user - user + * @param confirmed - runnable to be executed if confirmed + */ + public void askConfirmation(User user, Runnable confirmed) { + // Check for pending confirmations + if (toBeConfirmed.containsKey(user)) { + if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) { + toBeConfirmed.get(user).getTask().cancel(); + Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable()); + toBeConfirmed.remove(user); + return; + } else { + // Player has another outstanding confirmation request that will now be cancelled + user.sendMessage("general.previous-request-cancelled"); + } + } + // Tell user that they need to confirm + user.sendMessage("general.confirm", "[seconds]", String.valueOf(getSettings().getConfirmationTime())); + // Set up a cancellation task + BukkitTask task = Bukkit.getScheduler().runTaskLater(getPlugin(), () -> { + user.sendMessage("general.request-cancelled"); + toBeConfirmed.remove(user); + }, getPlugin().getSettings().getConfirmationTime() * 20L); + + // Add to the global confirmation map + toBeConfirmed.put(user, new Confirmer(getTopLabel(), getLabel(), confirmed, task)); + } + + private class Confirmer { + private final String topLabel; + private final String label; + private final Runnable runnable; + private final BukkitTask task; + + /** + * @param label - command label + * @param runnable - runnable to run when confirmed + * @param task - task ID to cancel when confirmed + */ + Confirmer(String topLabel, String label, Runnable runnable, BukkitTask task) { + this.topLabel = topLabel; + this.label = label; + this.runnable = runnable; + this.task = task; + } + /** + * @return the topLabel + */ + public String getTopLabel() { + return topLabel; + } + /** + * @return the label + */ + public String getLabel() { + return label; + } + /** + * @return the runnable + */ + public Runnable getRunnable() { + return runnable; + } + /** + * @return the task + */ + public BukkitTask getTask() { + return task; + } + + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommand.java b/src/main/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommand.java new file mode 100644 index 0000000..7167f5a --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommand.java @@ -0,0 +1,107 @@ +package us.tastybento.bskyblock.api.commands; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang.math.NumberUtils; + +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +/** + * Adds a default help to every command that will show the usage of the command + * and the usage of any subcommands that the command has. + * @author tastybento + * + */ +public class DefaultHelpCommand extends CompositeCommand { + + private static final int MAX_DEPTH = 2; + private static final String USAGE_PLACEHOLDER = "[usage]"; + private static final String PARAMS_PLACEHOLDER = "[parameters]"; + private static final String DESC_PLACEHOLDER = "[description]"; + private static final String HELP_SYNTAX_REF = "commands.help.syntax"; + private static final String HELP = "help"; + + public DefaultHelpCommand(CompositeCommand parent) { + super(parent, HELP); + } + + @Override + public void setup() { + // Set the usage to what the parent's command is + setParameters(parent.getParameters()); + setDescription(parent.getDescription()); + inheritPermission(); + } + + @Override + public boolean execute(User user, String label, List args) { + int depth = 0; + if (args.size() == 1) { + if (NumberUtils.isDigits(args.get(0))) { + // Converts first argument into an int, or returns -1 if it cannot. Avoids exceptions. + depth = Optional.ofNullable(args.get(0)).map(NumberUtils::toInt).orElse(-1); + } else { + String usage = user.getTranslation(parent.getUsage()); + String params = user.getTranslation("commands.help.parameters"); + String desc = user.getTranslation("commands.help.description"); + user.sendMessage(HELP_SYNTAX_REF, USAGE_PLACEHOLDER, usage, PARAMS_PLACEHOLDER, params, DESC_PLACEHOLDER, desc); + return true; + } + } + if (depth == 0) { + user.sendMessage("commands.help.header", TextVariables.LABEL, getIWM().getFriendlyName(getWorld())); + } + if (depth < MAX_DEPTH) { + if (!parent.getLabel().equals(HELP)) { + + // Get elements + String usage = parent.getUsage().isEmpty() ? "" : user.getTranslation(parent.getUsage()); + String params = getParameters().isEmpty() ? "" : user.getTranslation(getParameters()); + String desc = getDescription().isEmpty() ? "" : user.getTranslation(getDescription()); + + if (showPrettyHelp(user, usage, params, desc)) { + // No more to show + return true; + } + } + // Increment the depth and run through any subcommands and get their help too + runSubCommandHelp(user, depth + 1); + } + if (depth == 0) { + user.sendMessage("commands.help.end"); + } + return true; + } + + private void runSubCommandHelp(User user, int newDepth) { + for (CompositeCommand subCommand : parent.getSubCommands().values()) { + // Ignore the help command + if (!subCommand.getLabel().equals(HELP)) { + // Every command should have help because every command has a default help + Optional sub = subCommand.getSubCommand(HELP); + sub.ifPresent(compositeCommand -> compositeCommand.execute(user, HELP, Collections.singletonList(String.valueOf(newDepth)))); + } + } + } + + private boolean showPrettyHelp(User user, String usage, String params, String desc) { + // Show the help + if (user.isPlayer()) { + // Player. Check perms + if (user.isOp() || user.hasPermission(parent.getPermission())) { + user.sendMessage(HELP_SYNTAX_REF, USAGE_PLACEHOLDER, usage, PARAMS_PLACEHOLDER, params, DESC_PLACEHOLDER, desc); + } else { + // No permission, nothing to see here. If you don't have permission, you cannot see any sub commands + return true; + } + } else if (!parent.isOnlyPlayer()) { + // Console. Only show if it is a console command + user.sendMessage(HELP_SYNTAX_REF, USAGE_PLACEHOLDER, usage, PARAMS_PLACEHOLDER, params, DESC_PLACEHOLDER, desc); + } + return false; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java b/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java new file mode 100644 index 0000000..9bcf1b5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java @@ -0,0 +1,93 @@ +package us.tastybento.bskyblock.api.configuration; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; + +/** + * Handy config class to store and load Java POJOs as YAML configs + * @author tastybento + * + * @param + */ +public class BSBConfig { + + private AbstractDatabaseHandler handler; + private Logger logger; + + @SuppressWarnings("unchecked") + public BSBConfig(BSkyBlock plugin, Class type) { + this.logger = plugin.getLogger(); + handler = (AbstractDatabaseHandler) new FlatFileDatabase().getHandler(type); + } + + @SuppressWarnings("unchecked") + public BSBConfig(Addon addon, Class type) { + this.logger = addon.getLogger(); + handler = (AbstractDatabaseHandler) new FlatFileDatabase().getHandler(type); + } + + /** + * Load all the config objects and supply them as a list + * @return list of config objects or an empty list if they cannot be loaded + */ + public List loadConfigObjects() { + List result = new ArrayList<>(); + try { + result = handler.loadObjects(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | ClassNotFoundException | IntrospectionException e) { + logger.severe(() -> "Could not load config! Error: " + e.getMessage()); + } + return result; + } + + /** + * Loads the config object + * @param uniqueId - unique id of the object + * @return the object or null if it cannot be loaded + */ + public T loadConfigObject(String uniqueId) { + + try { + return handler.loadObject(uniqueId); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | ClassNotFoundException | IntrospectionException e) { + logger.severe(() -> "Could not load config object! " + e.getMessage()); + } + + return null; + } + + /** + * Save config object + * @param instance to save + */ + public boolean saveConfigObject(T instance) { + try { + handler.saveObject(instance); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException + | IntrospectionException e) { + logger.severe(() -> "Could not save config! Error: " + e.getMessage()); + return false; + } + return true; + } + + /** + * Checks if a config object exists or not + * @param name - unique name of the config object + * @return true if it exists + */ + public boolean configObjectExists(String name) { + return handler.objectExists(name); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigComment.java b/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigComment.java new file mode 100644 index 0000000..75aafdb --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigComment.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.api.configuration; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Repeatable(ConfigComment.Line.class) +@Target({ FIELD, METHOD, TYPE }) +public @interface ConfigComment { + + String value(); + + @Retention(RetentionPolicy.RUNTIME) + @Target({ FIELD, METHOD, TYPE }) + @interface Line { + ConfigComment[] value(); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigEntry.java b/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigEntry.java new file mode 100644 index 0000000..b81c5a1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/ConfigEntry.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.api.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import us.tastybento.bskyblock.Constants.GameType; + +/** + * + * + * @author Poslovitch, tastybento + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ConfigEntry { + + String path(); + String since() default "1.0"; + boolean overrideOnChange() default false; + boolean experimental() default false; + boolean needsReset() default false; + GameType specificTo() default GameType.BOTH; + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/StoreAt.java b/src/main/java/us/tastybento/bskyblock/api/configuration/StoreAt.java new file mode 100644 index 0000000..f6f63b0 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/StoreAt.java @@ -0,0 +1,27 @@ +package us.tastybento.bskyblock.api.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines where this data object will be stored. + * @author tastybento + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface StoreAt { + + /** + * Path where this will be stored. If blank, it will be the BSkyBlock database folder. + */ + String path() default ""; + + /** + * Filename + */ + String filename() default ""; + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java b/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java new file mode 100644 index 0000000..9380fed --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java @@ -0,0 +1,254 @@ +package us.tastybento.bskyblock.api.configuration; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.entity.EntityType; + +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.flags.Flag; + +/** + * Contains world-specific settings. Only getters are required, but you may need setters for your own class. + * @author tastybento + * + */ +public interface WorldSettings { + + /** + * @return the Addon that registered this world + */ + Optional getAddon(); + + /** + * Get the default game mode for this game world, e.g. SURVIVAL + * @return game mode + */ + GameMode getDefaultGameMode(); + + /** + * @return default rank settings for new islands + */ + Map getDefaultIslandFlags(); + + Map getDefaultIslandSettings(); + + /** + * Get the world difficulty + * @return difficulty + */ + Difficulty getDifficulty(); + + /** + * Set the world difficulty + * @param difficulty + */ + void setDifficulty(Difficulty difficulty); + + /** + * @return the entityLimits + */ + Map getEntityLimits(); + + /** + * @return the friendly name of the world. Used in player commands + */ + String getFriendlyName(); + + /** + * @return the islandDistance + */ + int getIslandDistance(); + + /** + * @return the islandHeight + */ + int getIslandHeight(); + + /** + * @return the islandProtectionRange + */ + int getIslandProtectionRange(); + + /** + * @return the islandStartX + */ + int getIslandStartX(); + + /** + * @return the islandStartZ + */ + int getIslandStartZ(); + + /** + * @return the islandXOffset + */ + int getIslandXOffset(); + + /** + * @return the islandZOffset + */ + int getIslandZOffset(); + + /** + * @return Invincible Visitor setting list + */ + List getIvSettings(); + + /** + * @return the max homes + */ + int getMaxHomes(); + + /** + * @return the maxIslands + */ + int getMaxIslands(); + + /** + * @return the max team size for this world + */ + int getMaxTeamSize(); + + /** + * @return the netherSpawnRadius + */ + int getNetherSpawnRadius(); + + /** + * @return the permission prefix + */ + String getPermissionPrefix(); + + /** + * Get the set of entity types that should not be removed in this world when a player teleports to their island + * @return set of entity types + */ + Set getRemoveMobsWhitelist(); + + /** + * @return the seaHeight + */ + int getSeaHeight(); + + /** + * @return the tileEntityLimits + */ + Map getTileEntityLimits(); + + /** + * @return visible settings for player + */ + List getVisibleSettings(); + + /** + * @return the visitorBannedCommands + */ + List getVisitorBannedCommands(); + + /** + * Get world flags + * @return Map of world flags + */ + Map getWorldFlags(); + + /** + * @return the worldName + */ + String getWorldName(); + + /** + * @return the dragonSpawn + */ + boolean isDragonSpawn(); + + /** + * @return the endGenerate + */ + boolean isEndGenerate(); + + /** + * @return the endIslands + */ + boolean isEndIslands(); + + /** + * @return the netherGenerate + */ + boolean isNetherGenerate(); + + /** + * @return the netherIslands + */ + boolean isNetherIslands(); + + /** + * @return the netherTrees + */ + boolean isNetherTrees(); + + /** + * @return the onJoinResetEnderChest + */ + boolean isOnJoinResetEnderChest(); + + /** + * @return the onJoinResetInventory + */ + boolean isOnJoinResetInventory(); + + /** + * @return the onJoinResetMoney + */ + boolean isOnJoinResetMoney(); + + /** + * @return the onLeaveResetEnderChest + */ + boolean isOnLeaveResetEnderChest(); + + /** + * @return the onLeaveResetInventory + */ + boolean isOnLeaveResetInventory(); + + /** + * @return the onLeaveResetMoney + */ + boolean isOnLeaveResetMoney(); + + /** + * @return true if the default world generator should not operate in this world + */ + boolean isUseOwnGenerator(); + + /** + * @return true if water is not safe in this world, e.g, should not be a home location + */ + boolean isWaterUnsafe(); + + /** + * @return list of entity types that should not exit the island limits + */ + List getGeoLimitSettings(); + + /** + * @return reset limit for world + */ + int getResetLimit(); + + + /** + * Get the island reset time stamp. Any player who last logged in before this time will have resets zeroed + */ + long getResetEpoch(); + + /** + * Set the island reset time stamp. Any player who last logged in before this time will have resets zeroed + */ + void setResetEpoch(long timestamp); +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/BSBReadyEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/BSBReadyEvent.java new file mode 100644 index 0000000..41da85e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/BSBReadyEvent.java @@ -0,0 +1,9 @@ +package us.tastybento.bskyblock.api.events; + +/** + * Fired when BSkyBlock is ready to play and all files are loaded + * + * @author tastybento + * @since 1.0 + */ +public class BSBReadyEvent extends PremadeEvent {} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/IslandBaseEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/IslandBaseEvent.java new file mode 100644 index 0000000..3d93c9d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/IslandBaseEvent.java @@ -0,0 +1,89 @@ +package us.tastybento.bskyblock.api.events; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.event.Cancellable; + +import us.tastybento.bskyblock.database.objects.Island; + +/** + * + * @author Poslovitch + * @version 1.0 + */ +public class IslandBaseEvent extends PremadeEvent implements Cancellable { + private boolean cancelled; + + private final Island island; + private final UUID playerUUID; + private final boolean admin; + private final Location location; + + public IslandBaseEvent(Island island) { + super(); + this.island = island; + playerUUID = island == null ? null : island.getOwner(); + admin = false; + location = island == null ? null : island.getCenter(); + } + + /** + * @param island - island + * @param playerUUID - the player's UUID + * @param admin - true if ths is due to an admin event + * @param location - the location + */ + public IslandBaseEvent(Island island, UUID playerUUID, boolean admin, Location location) { + super(); + this.island = island; + this.playerUUID = playerUUID; + this.admin = admin; + this.location = location; + } + + /** + * @return the island involved in this event + */ + public Island getIsland(){ + return island; + } + + /** + * @return the owner of the island + */ + public UUID getOwner() { + return island.getOwner(); + } + + /** + * @return the playerUUID + */ + public UUID getPlayerUUID() { + return playerUUID; + } + + /** + * @return the admin + */ + public boolean isAdmin() { + return admin; + } + + /** + * @return the location + */ + public Location getLocation() { + return location; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + cancelled = cancel; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/PremadeEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/PremadeEvent.java new file mode 100644 index 0000000..e1630e5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/PremadeEvent.java @@ -0,0 +1,18 @@ +package us.tastybento.bskyblock.api.events; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public abstract class PremadeEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return getHandlerList(); + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonBaseEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonBaseEvent.java new file mode 100644 index 0000000..cc8c4d3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonBaseEvent.java @@ -0,0 +1,22 @@ +package us.tastybento.bskyblock.api.events.addon; + +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.events.PremadeEvent; + +/** + * @author Poslovitch + * @since 1.0 + */ +public class AddonBaseEvent extends PremadeEvent { + + private final Addon addon; + + public AddonBaseEvent(Addon addon) { + super(); + this.addon = addon; + } + + public Addon getAddon() { + return addon; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonEvent.java new file mode 100644 index 0000000..9a71f1b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/addon/AddonEvent.java @@ -0,0 +1,71 @@ +package us.tastybento.bskyblock.api.events.addon; + +import us.tastybento.bskyblock.api.addons.Addon; + +public class AddonEvent { + + public enum Reason { + ENABLE, + DISABLE, + LOAD, + UNKNOWN + } + + public static AddonEventBuilder builder() { + return new AddonEventBuilder(); + } + + public static class AddonEnableEvent extends AddonBaseEvent { + private AddonEnableEvent(Addon addon) { + // Final variables have to be declared in the constuctor + super(addon); + } + } + public static class AddonDisableEvent extends AddonBaseEvent { + private AddonDisableEvent(Addon addon) { + // Final variables have to be declared in the constuctor + super(addon); + } + } + public static class AddonLoadEvent extends AddonBaseEvent { + private AddonLoadEvent(Addon addon) { + // Final variables have to be declared in the constuctor + super(addon); + } + } + public static class AddonGeneralEvent extends AddonBaseEvent { + private AddonGeneralEvent(Addon addon) { + // Final variables have to be declared in the constuctor + super(addon); + } + } + + public static class AddonEventBuilder { + // Here field are NOT final. They are just used for the building. + private Addon addon; + private Reason reason = Reason.UNKNOWN; + + public AddonEventBuilder addon(Addon addon) { + this.addon = addon; + return this; + } + + public AddonEventBuilder reason(Reason reason) { + this.reason = reason; + return this; + } + + public AddonBaseEvent build() { + switch (reason) { + case ENABLE: + return new AddonEnableEvent(addon); + case DISABLE: + return new AddonDisableEvent(addon); + case LOAD: + return new AddonLoadEvent(addon); + default: + return new AddonGeneralEvent(addon); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/command/CommandEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/command/CommandEvent.java new file mode 100644 index 0000000..119ad86 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/command/CommandEvent.java @@ -0,0 +1,94 @@ +package us.tastybento.bskyblock.api.events.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Cancellable; + +import us.tastybento.bskyblock.api.events.PremadeEvent; + +/** + * Fired when a team event happens. + * + * @author tastybento + * @since 1.0 + */ +public class CommandEvent extends PremadeEvent implements Cancellable { + + private boolean cancelled; + + private final CommandSender sender; + private final Command command; + private final String label; + private final String[] args; + + private CommandEvent(CommandSender sender, Command command, String label, String[] args) { + super(); + this.sender = sender; + this.command = command; + this.label = label; + this.args = args; + } + + public static CommandEventBuilder builder() { + return new CommandEventBuilder(); + } + + public static class CommandEventBuilder { + // Here field are NOT final. They are just used for the building. + private CommandSender sender; + private Command command; + private String label; + private String[] args; + + public CommandEventBuilder setSender(CommandSender sender) { + this.sender = sender; + return this; + } + + public CommandEventBuilder setCommand(Command command) { + this.command = command; + return this; + } + + public CommandEventBuilder setLabel(String label) { + this.label = label; + return this; + } + + public CommandEventBuilder setArgs(String[] args) { + this.args = args; + return this; + } + + public CommandEvent build() { + return new CommandEvent(sender, command, label, args); + } + + } + + public CommandSender getSender() { + return sender; + } + + public Command getCommand() { + return command; + } + + public String getLabel() { + return label; + } + + public String[] getArgs() { + return args; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean arg0) { + cancelled = arg0; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/island/FlagChangeEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/island/FlagChangeEvent.java new file mode 100644 index 0000000..0d416b7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/island/FlagChangeEvent.java @@ -0,0 +1,55 @@ +package us.tastybento.bskyblock.api.events.island; + +import java.util.UUID; + +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * This event is fired when a player changes a flag on his island + *

+ * Canceling this event will result in canceling the change. + * + * @author Poslovitch + * @since 1.0 + */ +public class FlagChangeEvent extends IslandBaseEvent { + private final UUID player; + private final Flag editedFlag; + private final boolean setTo; + + /** + * @param island - island + * @param player - the player + * @param editedFlag - flag edited + * @param setTo - new value + */ + public FlagChangeEvent(Island island, UUID player, Flag editedFlag, boolean setTo) { + super(island); + this.player = player; + this.editedFlag = editedFlag; + this.setTo = setTo; + } + + /** + * @return the player + */ + public UUID getPlayer() { + return player; + } + + /** + * @return the edited flag + */ + public Flag getFlag() { + return editedFlag; + } + + /** + * @return enabled/disabled + */ + public boolean getSetTo() { + return setTo; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/island/IslandEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/island/IslandEvent.java new file mode 100644 index 0000000..4804272 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/island/IslandEvent.java @@ -0,0 +1,248 @@ +package us.tastybento.bskyblock.api.events.island; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * Fired when a team event happens. + * + * @author tastybento + * @since 1.0 + */ +public class IslandEvent { + + /** + * Reason for the event + * + */ + public enum Reason { + CREATE, + CREATED, + DELETE, + DELETED, + ENTER, + EXIT, + LOCK, + RESET, + RESETTED, + UNLOCK, + UNKNOWN + } + + public static IslandEventBuilder builder() { + return new IslandEventBuilder(); + } + + /** + * Fired when an island is going to be created. May be canceled. + * + */ + public static class IslandCreateEvent extends IslandBaseEvent { + private IslandCreateEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is created. + * + */ + public static class IslandCreatedEvent extends IslandBaseEvent { + private IslandCreatedEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is going to be deleted. May be canceled. + * + */ + public static class IslandDeleteEvent extends IslandBaseEvent { + private IslandDeleteEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is deleted. + * + */ + public static class IslandDeletedEvent extends IslandBaseEvent { + private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an a player enters an island + * + */ + public static class IslandEnterEvent extends IslandBaseEvent { + private IslandEnterEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when a player exits and island + * + */ + public static class IslandExitEvent extends IslandBaseEvent { + private IslandExitEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is locked + * + */ + public static class IslandLockEvent extends IslandBaseEvent { + private IslandLockEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is unlocked + * + */ + public static class IslandUnlockEvent extends IslandBaseEvent { + private IslandUnlockEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when an island is going to be reset. May be canceled. + * + */ + public static class IslandResetEvent extends IslandBaseEvent { + private IslandResetEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired after an island is reset + * + */ + public static class IslandResettedEvent extends IslandBaseEvent { + private IslandResettedEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + /** + * Fired when something happens to the island not covered by other events + * + */ + public static class IslandGeneralEvent extends IslandBaseEvent { + private IslandGeneralEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + + public static class IslandEventBuilder { + // Here field are NOT final. They are just used for the building. + private Island island; + private UUID player; + private Reason reason = Reason.UNKNOWN; + private boolean admin; + private Location location; + + public IslandEventBuilder island(Island island) { + this.island = island; + return this; + } + + /** + * True if this is an admin driven event + * @param admin - true if due to admin event + * @return TeamEvent + */ + public IslandEventBuilder admin(boolean admin) { + this.admin = admin; + return this; + } + + /** + * @param reason for the event + * @return IslandEventBuilder + */ + public IslandEventBuilder reason(Reason reason) { + this.reason = reason; + return this; + } + + /** + * @param player - the player involved in the event + * @return IslandEventBuilder + */ + public IslandEventBuilder involvedPlayer(UUID player) { + this.player = player; + return this; + } + + public IslandEventBuilder location(Location center) { + location = center; + return this; + } + + public IslandBaseEvent build() { + switch (reason) { + case CREATE: + IslandCreateEvent create = new IslandCreateEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(create); + return create; + case CREATED: + IslandCreatedEvent created = new IslandCreatedEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(created); + return created; + case DELETE: + IslandDeleteEvent delete = new IslandDeleteEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(delete); + return delete; + case DELETED: + IslandDeletedEvent deleted = new IslandDeletedEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(deleted); + return deleted; + case ENTER: + IslandEnterEvent enter = new IslandEnterEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(enter); + return enter; + case EXIT: + IslandExitEvent exit = new IslandExitEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(exit); + return exit; + case LOCK: + IslandLockEvent lock = new IslandLockEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(lock); + return lock; + case RESET: + IslandResetEvent reset = new IslandResetEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(reset); + return reset; + case RESETTED: + IslandResettedEvent resetted = new IslandResettedEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(resetted); + return resetted; + case UNLOCK: + IslandUnlockEvent unlock = new IslandUnlockEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(unlock); + return unlock; + default: + IslandGeneralEvent general = new IslandGeneralEvent(island, player, admin, location); + Bukkit.getServer().getPluginManager().callEvent(general); + return general; + } + + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeDeleteIslandEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeDeleteIslandEvent.java new file mode 100644 index 0000000..9f43602 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeDeleteIslandEvent.java @@ -0,0 +1,22 @@ +package us.tastybento.bskyblock.api.events.purge; + +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * This event is fired before an island is going to be purged. + * Canceling this event will prevent the plugin to remove the island. + * + * @author Poslovitch + * @since 1.0 + */ +public class PurgeDeleteIslandEvent extends IslandBaseEvent { + + /** + * Called to create the event + * @param island - island that will be removed + */ + public PurgeDeleteIslandEvent(Island island) { + super(island); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeStartEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeStartEvent.java new file mode 100644 index 0000000..2ecae33 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/purge/PurgeStartEvent.java @@ -0,0 +1,85 @@ +package us.tastybento.bskyblock.api.events.purge; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.event.Cancellable; + +import us.tastybento.bskyblock.api.events.PremadeEvent; + +/** + * This event is fired when islands to remove have been chosen and before starting to remove them. + * You can remove or add islands to remove. + * Canceling this event will cancel the purge + * + * @author Poslovitch + * @since 1.0 + */ +public class PurgeStartEvent extends PremadeEvent implements Cancellable { + private boolean cancelled; + + private final UUID user; + private List islandsList; + + /** + * Called to create the event + * @param user - the User - the UUID of the player who launched the purge, may be null if purge is launched using the console. + * @param islandsList - the list of islands to remove, based on their leader's UUID + */ + public PurgeStartEvent(UUID user, List islandsList) { + this.user = user; + this.islandsList = islandsList; + } + + /** + * @return the user who launched the purge, may be null if purge is launched using the console. + */ + public UUID getUser( ){ + return user; + } + + /** + * @return the list of islands to remove, based on their leader's UUID + */ + public List getIslandsList() { + return islandsList; + } + + /** + * Convenience method to directly add an island owner's UUID to the list + * @param islandOwner - the owner's UUID from the island to remove + */ + public void add(UUID islandOwner) { + if(!islandsList.contains(islandOwner)) { + islandsList.add(islandOwner); + } + } + + /** + * Convenience method to directly remove an island owner's UUID to the list + * @param islandOwner - the owner's UUID from the island to remove + */ + public void remove(UUID islandOwner) { + if(islandsList.contains(islandOwner)) { + islandsList.remove(islandOwner); + } + } + + /** + * Replace the island list + * @param islandsList - a new island owners' UUIDs list + */ + public void setIslandsList(List islandsList) { + this.islandsList = islandsList; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + cancelled = cancel; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/events/team/TeamEvent.java b/src/main/java/us/tastybento/bskyblock/api/events/team/TeamEvent.java new file mode 100644 index 0000000..c53daba --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/events/team/TeamEvent.java @@ -0,0 +1,166 @@ +package us.tastybento.bskyblock.api.events.team; + +import java.util.UUID; + +import org.bukkit.Location; + +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * Fired when a team event happens. + * + * @author tastybento + * @since 1.0 + */ +public class TeamEvent { + + public enum Reason { + INVITE, + JOIN, + REJECT, + LEAVE, + KICK, + MAKELEADER, + INFO, + DELETE, + UNKNOWN, + UNINVITE + } + + public static TeamEventBuilder builder() { + return new TeamEventBuilder(); + } + + public static class TeamJoinEvent extends IslandBaseEvent { + private TeamJoinEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamInviteEvent extends IslandBaseEvent { + private TeamInviteEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamLeaveEvent extends IslandBaseEvent { + private TeamLeaveEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamRejectEvent extends IslandBaseEvent { + private TeamRejectEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamKickEvent extends IslandBaseEvent { + private TeamKickEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamMakeLeaderEvent extends IslandBaseEvent { + private TeamMakeLeaderEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamInfoEvent extends IslandBaseEvent { + private TeamInfoEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamDeleteEvent extends IslandBaseEvent { + private TeamDeleteEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamUninviteEvent extends IslandBaseEvent { + private TeamUninviteEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + public static class TeamGeneralEvent extends IslandBaseEvent { + private TeamGeneralEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constuctor + super(island, player, admin, location); + } + } + + public static class TeamEventBuilder { + private Island island; + private UUID player; + private Reason reason = Reason.UNKNOWN; + private boolean admin; + private Location location; + + public TeamEventBuilder island(Island island) { + this.island = island; + return this; + } + + /** + * True if this is an admin driven event + * @param admin - true if due to an admin event + * @return TeamEvent + */ + public TeamEventBuilder admin(boolean admin) { + this.admin = admin; + return this; + } + + /** + * @param reason for the event + * @return TeamEventBuilder + */ + public TeamEventBuilder reason(Reason reason) { + this.reason = reason; + return this; + } + + /** + * @param player - the player involved in the event + * @return TeamEventBuilder + */ + public TeamEventBuilder involvedPlayer(UUID player) { + this.player = player; + return this; + } + + public TeamEventBuilder location(Location center) { + location = center; + return this; + } + + public IslandBaseEvent build() { + switch (reason) { + case JOIN: + return new TeamJoinEvent(island, player, admin, location); + case INVITE: + return new TeamInviteEvent(island, player, admin, location); + case LEAVE: + return new TeamLeaveEvent(island, player, admin, location); + case REJECT: + return new TeamRejectEvent(island, player, admin, location); + case KICK: + return new TeamKickEvent(island, player, admin, location); + case MAKELEADER: + return new TeamMakeLeaderEvent(island, player, admin, location); + case INFO: + return new TeamInfoEvent(island, player, admin, location); + case DELETE: + return new TeamDeleteEvent(island, player, admin, location); + case UNINVITE: + return new TeamUninviteEvent(island, player, admin, location); + default: + return new TeamGeneralEvent(island, player, admin, location); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/AbstractFlagListener.java b/src/main/java/us/tastybento/bskyblock/api/flags/AbstractFlagListener.java new file mode 100644 index 0000000..9941baa --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/AbstractFlagListener.java @@ -0,0 +1,202 @@ +package us.tastybento.bskyblock.api.flags; + +import java.lang.reflect.Method; +import java.util.Optional; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.Listener; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; + +/** + * Abstract class for flag listeners. Provides common code. + * @author tastybento + * + */ +public abstract class AbstractFlagListener implements Listener { + + private BSkyBlock plugin = BSkyBlock.getInstance(); + private User user = null; + + /** + * @return the plugin + */ + public BSkyBlock getPlugin() { + return plugin; + } + + /** + * Used for unit testing only to set the plugin + * @param plugin - BSkyBlock plugin object + */ + public void setPlugin(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * Sets the player associated with this event. + * If the user is a fake player, they are not counted. + * @param e - event + * @return true if found, otherwise false + */ + private boolean createEventUser(Event e) { + try { + // Use reflection to get the getPlayer method if it exists + Method getPlayer = e.getClass().getMethod("getPlayer"); + if (getPlayer != null) { + setUser(User.getInstance((Player)getPlayer.invoke(e))); + return true; + } + } catch (Exception e1) { // Do nothing + } + return false; + } + + /** + * Explicitly set the user for the next {@link #checkIsland(Event, Location, Flag)} or {@link #checkIsland(Event, Location, Flag, boolean)} + * @param user - the User + */ + public AbstractFlagListener setUser(User user) { + if (!plugin.getSettings().getFakePlayers().contains(user.getName())) { + this.user = user; + } + return this; + } + + /* + * The following methods cover the cancellable events and enable a simple noGo(e) to be used to cancel and send the error message + */ + + /** + * Cancels the event and sends the island public message to user + * @param e - event + * @param flag - the flag that has been checked + */ + public void noGo(Event e, Flag flag) { + noGo(e, flag, false); + } + + /** + * Cancels the event and sends the island protected message to user unless silent is true + * @param e - event + * @param flag - the flag that has been checked + * @param silent - if true, message is not sent + */ + public void noGo(Event e, Flag flag, boolean silent) { + if (e instanceof Cancellable) { + ((Cancellable)e).setCancelled(true); + } + if (user != null) { + if (!silent) { + user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(flag.getHintReference())); + } + user.updateInventory(); + } + } + + /** + * Check if flag is allowed at location + * @param e - event + * @param loc - location + * @param flag - flag {@link us.tastybento.bskyblock.lists.Flags} + * @return true if allowed, false if not + */ + public boolean checkIsland(Event e, Location loc, Flag flag) { + return checkIsland(e, loc, flag, false); + } + + /** + * Check if flag is allowed at location + * @param e - event + * @param loc - location + * @param flag - flag {@link us.tastybento.bskyblock.lists.Flags} + * @param silent - if true, no attempt is made to tell the user + * @return true if the check is okay, false if it was disallowed + */ + public boolean checkIsland(Event e, Location loc, Flag flag, boolean silent) { + // If this is not an Island World, skip + if (!plugin.getIWM().inWorld(loc)) { + return true; + } + // Get the island and if present + Optional island = getIslands().getProtectedIslandAt(loc); + // Handle Settings Flag + if (flag.getType().equals(Flag.Type.SETTING)) { + // If the island exists, return the setting, otherwise return the default setting for this flag + return island.map(x -> x.isAllowed(flag)).orElse(flag.isSetForWorld(loc.getWorld())); + } + + // Protection flag + // If the user is not set already, try to get it from the event + // Set the user associated with this event + // The user is not set, and the event does not hold a getPlayer, so return false + // TODO: is this the correct handling here? + if (user == null && !createEventUser(e)) { + plugin.logError("Check island had no associated user! " + e.getEventName()); + return false; + } + + // Ops or bypass mods can do anything + if (user.isOp() || user.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + ".mod.bypassprotect")) { + user = null; + return true; + } + + // Check if the plugin is set in User (required for testing) + User.setPlugin(plugin); + + if (island.isPresent()) { + if (!island.get().isAllowed(user, flag)) { + noGo(e, flag, silent); + // Clear the user for the next time + user = null; + return false; + } else { + user = null; + return true; + } + } + // The player is in the world, but not on an island, so general world settings apply + if (!flag.isSetForWorld(loc.getWorld())) { + noGo(e, flag, silent); + user = null; + return false; + } else { + user = null; + return true; + } + } + + /** + * Get the flag for this ID + * @param id - the flag ID + * @return Flag denoted by the id + */ + protected Flag id(String id) { + return plugin.getFlagsManager().getFlagByID(id); + } + + /** + * Get the island database manager + * @return the island database manager + */ + protected IslandsManager getIslands() { + return plugin.getIslands(); + } + + /** + * Get the island world manager + * @return Island World Manager + */ + protected IslandWorldManager getIWM() { + return plugin.getIWM(); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java b/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java new file mode 100644 index 0000000..2aba316 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java @@ -0,0 +1,250 @@ +package us.tastybento.bskyblock.api.flags; + +import java.util.Optional; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.RanksManager; + +public class Flag implements Comparable { + + public enum Type { + PROTECTION(Material.SHIELD), + SETTING(Material.COMMAND), + WORLD_SETTING(Material.GRASS); + + private Material icon; + + Type(Material icon) { + this.icon = icon; + } + + public Material getIcon() { + return icon; + } + } + + private static final String PROTECTION_FLAGS = "protection.flags."; + + private final String id; + private final Material icon; + private final Listener listener; + private final Type type; + private boolean setting; + private final int defaultRank; + private final PanelItem.ClickHandler clickHandler; + private final boolean subPanel; + + Flag(String id, Material icon, Listener listener, Type type, int defaultRank, PanelItem.ClickHandler clickListener, boolean subPanel) { + this.id = id; + this.icon = icon; + this.listener = listener; + this.type = type; + this.defaultRank = defaultRank; + this.clickHandler = clickListener; + this.subPanel = subPanel; + } + + public String getID() { + return id; + } + + public Material getIcon() { + return icon; + } + + public Optional getListener() { + return Optional.ofNullable(listener); + } + + /** + * Check if a setting is set in this world + * @param world - world + * @return world setting or default flag setting if a specific world setting is not set. + * If world is not a game world, then the result will always be false! + */ + public boolean isSetForWorld(World world) { + if (type.equals(Type.WORLD_SETTING)) { + WorldSettings ws = BSkyBlock.getInstance().getIWM().getWorldSettings(world); + if (ws != null) { + ws.getWorldFlags().putIfAbsent(getID(), setting); + return ws.getWorldFlags().get(getID()); + } + return false; + } else { + // Setting + return setting; + } + } + + /** + * Set a world setting + * @param world - world + * @param setting - true or false + */ + public void setSetting(World world, boolean setting) { + if (getType().equals(Type.WORLD_SETTING)) { + BSkyBlock.getInstance().getIWM().getWorldSettings(world).getWorldFlags().put(getID(), setting); + } + } + + /** + * Set the status of this flag for locations outside of island spaces + * @param defaultSetting - true means it is allowed. false means it is not allowed + */ + public void setDefaultSetting(boolean defaultSetting) { + this.setting = defaultSetting; + } + + /** + * @return the type + */ + public Type getType() { + return type; + } + + /** + * @return the defaultRank + */ + public int getDefaultRank() { + return defaultRank; + } + + /** + * @return whether the flag uses a subpanel or not + */ + public boolean hasSubPanel() { + return subPanel; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Flag)) { + return false; + } + Flag other = (Flag) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + + return type == other.type; + } + + public String getNameReference() { + return PROTECTION_FLAGS + this.id + ".name"; + } + + public String getDescriptionReference() { + return PROTECTION_FLAGS + this.id + ".description"; + } + + public String getHintReference() { + return PROTECTION_FLAGS + this.id + ".hint"; + } + + /** + * Converts a flag to a panel item. The content of the flag will change depending on who the user is and where they are. + * @param plugin - plugin + * @param user - user that will see this flag + * @return - PanelItem for this flag + */ + public PanelItem toPanelItem(BSkyBlock plugin, User user) { + // Start the flag conversion + PanelItemBuilder pib = new PanelItemBuilder() + .icon(new ItemStack(icon)) + .name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference()))) + .clickHandler(clickHandler); + if (hasSubPanel()) { + pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()))); + return pib.build(); + } + // Check if this is a setting or world setting + if (getType().equals(Type.WORLD_SETTING)) { + String worldDetting = this.isSetForWorld(user.getWorld()) ? user.getTranslation("protection.panel.flag-item.setting-active") + : user.getTranslation("protection.panel.flag-item.setting-disabled"); + pib.description(user.getTranslation("protection.panel.flag-item.setting-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()) + , "[setting]", worldDetting)); + return pib.build(); + } + + // Get the island this user is on or their own + Island island = plugin.getIslands().getIslandAt(user.getLocation()).orElse(plugin.getIslands().getIsland(user.getWorld(), user.getUniqueId())); + if (island != null) { + if (getType().equals(Type.SETTING)) { + String islandSetting = island.isAllowed(this) ? user.getTranslation("protection.panel.flag-item.setting-active") + : user.getTranslation("protection.panel.flag-item.setting-disabled"); + pib.description(user.getTranslation("protection.panel.flag-item.setting-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()) + , "[setting]", islandSetting)); + return pib.build(); + } + // TODO: Get the world settings - the player has no island and is not in an island location + // Dynamic rank list + if (getType().equals(Type.PROTECTION)) { + // Protection flag + String d = user.getTranslation(getDescriptionReference()); + d = user.getTranslation("protection.panel.flag-item.description-layout", TextVariables.DESCRIPTION, d); + pib.description(d); + plugin.getRanksManager().getRanks().forEach((reference, score) -> { + if (score > RanksManager.BANNED_RANK && score < island.getFlag(this)) { + pib.description(user.getTranslation("protection.panel.flag-item.blocked_rank") + user.getTranslation(reference)); + } else if (score <= RanksManager.OWNER_RANK && score > island.getFlag(this)) { + pib.description(user.getTranslation("protection.panel.flag-item.allowed_rank") + user.getTranslation(reference)); + } else if (score == island.getFlag(this)) { + pib.description(user.getTranslation("protection.panel.flag-item.minimal_rank") + user.getTranslation(reference)); + } + }); + } + } + return pib.build(); + } + + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Flag [id=" + id + ", icon=" + icon + ", listener=" + listener + ", type=" + type + ", defaultSetting=" + + setting + ", defaultRank=" + defaultRank + ", clickHandler=" + clickHandler + ", subPanel=" + subPanel + "]"; + } + + @Override + public int compareTo(Flag o) { + return getID().compareTo(o.getID()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/FlagBuilder.java b/src/main/java/us/tastybento/bskyblock/api/flags/FlagBuilder.java new file mode 100644 index 0000000..d1bc5e9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/FlagBuilder.java @@ -0,0 +1,131 @@ +package us.tastybento.bskyblock.api.flags; + +import org.bukkit.Material; +import org.bukkit.event.Listener; + +import us.tastybento.bskyblock.api.flags.Flag.Type; +import us.tastybento.bskyblock.api.flags.clicklisteners.CycleClick; +import us.tastybento.bskyblock.api.flags.clicklisteners.IslandToggleClick; +import us.tastybento.bskyblock.api.flags.clicklisteners.WorldToggleClick; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.managers.RanksManager; + +public class FlagBuilder { + + private String id; + private Material icon; + private Listener listener; + private boolean setting; + private Type type = Type.PROTECTION; + private int defaultRank = RanksManager.MEMBER_RANK; + private PanelItem.ClickHandler onClick; + private boolean subPanel = false; + + public FlagBuilder id(String string) { + id = string; + return this; + } + + /** + * The material that will become the icon for this flag + * @param icon - material + */ + public FlagBuilder icon(Material icon) { + this.icon = icon; + return this; + } + + /** + * @param listener - the Bukkit listener that will be registered to handle this flag + */ + public FlagBuilder listener(Listener listener) { + this.listener = listener; + return this; + } + + public Flag build() { + // If no onClick has been set, then apply default ones + if (onClick == null) { + switch (type){ + case PROTECTION: + onClick = new CycleClick(id); + break; + case SETTING: + onClick = new IslandToggleClick(id); + break; + case WORLD_SETTING: + onClick = new WorldToggleClick(id); + break; + default: + onClick = new CycleClick(id); + break; + } + } + + Flag f = new Flag(id, icon, listener, type, defaultRank, onClick, subPanel); + f.setDefaultSetting(setting); + return f; + } + + /** + * Sets the default setting for this flag in the world + * @param setting - true or false + * @return FlagBuilder + */ + public FlagBuilder allowedByDefault(boolean setting) { + this.setting = setting; + return this; + } + + /** + * Set the type of this flag + * @param type {@link Type} + * @return FlagBuilder + */ + public FlagBuilder type(Type type) { + this.type = type; + return this; + } + + /** + * Set the id of this flag to the name of this enum value + * @param flag - flag + * @return FlagBuilder + */ + public FlagBuilder id(Enum flag) { + id = flag.name(); + return this; + } + + /** + * Set a default rank for this flag. If not set, the value of RanksManager.MEMBER_RANK will be used + * @param rank - rank value + * @return FlagBuilder + */ + public FlagBuilder defaultRank(int rank) { + this.defaultRank = rank; + return this; + } + + /** + * Adds a listener for clicks on this flag when it is a panel item. Default is + * {@link us.tastybento.bskyblock.api.flags.clicklisteners.CycleClick} + * @param onClickListener - the listener for clicks. Must use the ClickOn interface + * @return FlagBuilder + */ + public FlagBuilder onClick(PanelItem.ClickHandler onClickListener) { + this.onClick = onClickListener; + return this; + } + + /** + * Marks this flag as "using a sub-panel" + * @param subPanel - whether the flag will use a sub-panel or not + * @return FlagBuilder + */ + public FlagBuilder subPanel(boolean subPanel) { + this.subPanel = subPanel; + return this; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClick.java b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClick.java new file mode 100644 index 0000000..f2a2607 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClick.java @@ -0,0 +1,77 @@ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import org.bukkit.Sound; +import org.bukkit.event.inventory.ClickType; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.util.Util; + +/** + * Left Clicks increase rank, right clicks lower rank + * @author tastybento + * + */ +public class CycleClick implements PanelItem.ClickHandler { + + private BSkyBlock plugin = BSkyBlock.getInstance(); + private final String id; + + /** + * @param id - the flag id that will be adjusted by this click + */ + public CycleClick(String id) { + this.id = id; + } + + @Override + public boolean onClick(Panel panel, User user, ClickType click, int slot) { + // Get the world + if (!plugin.getIWM().inWorld(user.getLocation())) { + user.sendMessage("general.errors.wrong-world"); + return true; + } + String reqPerm = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + ".settings." + id; + if (!user.hasPermission(reqPerm)) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", "[permission]", reqPerm); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + return true; + } + // Left clicking increases the rank required + // Right clicking decreases the rank required + // Get the user's island + Island island = plugin.getIslands().getIsland(user.getWorld(), user.getUniqueId()); + if (island != null && island.getOwner().equals(user.getUniqueId())) { + RanksManager rm = plugin.getRanksManager(); + Flag flag = plugin.getFlagsManager().getFlagByID(id); + int currentRank = island.getFlag(flag); + if (click.equals(ClickType.LEFT)) { + if (currentRank == RanksManager.OWNER_RANK) { + island.setFlag(flag, RanksManager.VISITOR_RANK); + } else { + island.setFlag(flag, rm.getRankUpValue(currentRank)); + } + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); + } else if (click.equals(ClickType.RIGHT)) { + if (currentRank == RanksManager.VISITOR_RANK) { + island.setFlag(flag, RanksManager.OWNER_RANK); + } else { + island.setFlag(flag, rm.getRankDownValue(currentRank)); + } + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); + } + // Apply change to panel + panel.getInventory().setItem(slot, flag.toPanelItem(plugin, user).getItem()); + } else { + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + } + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClick.java b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClick.java new file mode 100644 index 0000000..d348331 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClick.java @@ -0,0 +1,66 @@ +/* + + */ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import org.bukkit.Sound; +import org.bukkit.event.inventory.ClickType; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +/** + * Toggles a island setting on/off + * @author tastybento + * + */ +public class IslandToggleClick implements ClickHandler { + + private BSkyBlock plugin = BSkyBlock.getInstance(); + private String id; + + /** + * @param id - the flag ID that this click listener is associated with + */ + public IslandToggleClick(String id) { + this.id = id; + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler#onClick(us.tastybento.bskyblock.api.panels.Panel, us.tastybento.bskyblock.api.user.User, org.bukkit.event.inventory.ClickType, int) + */ + @Override + public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { + // Get the world + if (!plugin.getIWM().inWorld(user.getLocation())) { + user.sendMessage("general.errors.wrong-world"); + return true; + } + String reqPerm = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + ".settings." + id; + if (!user.hasPermission(reqPerm)) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", "[permission]", reqPerm); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + return true; + } + // Get the user's island + Island island = plugin.getIslands().getIsland(user.getWorld(), user); + if (island != null && island.getOwner().equals(user.getUniqueId())) { + Flag flag = plugin.getFlagsManager().getFlagByID(id); + // Toggle flag + island.toggleFlag(flag); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); + // Apply change to panel + panel.getInventory().setItem(slot, flag.toPanelItem(plugin, user).getItem()); + } else { + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + } + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClick.java b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClick.java new file mode 100644 index 0000000..e0b4742 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClick.java @@ -0,0 +1,61 @@ +/* + + */ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import org.bukkit.Sound; +import org.bukkit.event.inventory.ClickType; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +/** + * Toggles a worldwide setting on/off + * @author tastybento + * + */ +public class WorldToggleClick implements ClickHandler { + + private BSkyBlock plugin = BSkyBlock.getInstance(); + private String id; + + /** + * @param id - the flag ID that this click listener is associated with + */ + public WorldToggleClick(String id) { + this.id = id; + } + + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler#onClick(us.tastybento.bskyblock.api.panels.Panel, us.tastybento.bskyblock.api.user.User, org.bukkit.event.inventory.ClickType, int) + */ + @Override + public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { + // Get the world + if (!plugin.getIWM().inWorld(user.getLocation())) { + user.sendMessage("general.errors.wrong-world"); + return true; + } + String reqPerm = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + ".admin.world.settings." + id; + if (!user.hasPermission(reqPerm)) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", "[permission]", reqPerm); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + return true; + } + // Get flag + Flag flag = plugin.getFlagsManager().getFlagByID(id); + // Toggle flag + flag.setSetting(user.getWorld(), !flag.isSetForWorld(user.getWorld())); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); + // Apply change to panel + panel.getInventory().setItem(slot, flag.toPanelItem(plugin, user).getItem()); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/localization/BSBLocale.java b/src/main/java/us/tastybento/bskyblock/api/localization/BSBLocale.java new file mode 100644 index 0000000..0dc06fb --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/localization/BSBLocale.java @@ -0,0 +1,96 @@ +package us.tastybento.bskyblock.api.localization; + +import java.io.File; +import java.util.Locale; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.util.ItemParser; + +/** + * @author Poslovitch, tastybento + */ +public class BSBLocale { + + private Locale locale; + private YamlConfiguration config; + private ItemStack banner; + + public BSBLocale(Locale locale, YamlConfiguration config) { + this.locale = locale; + this.config = config; + + // Load the banner from the configuration + banner = ItemParser.parse(config.getString("banner")); + } + + /** + * Get text from the yml file for this locale + * @param reference - the YAML node where the text is + * @return Text for this locale reference or the reference if nothing has been found + */ + public String get(String reference) { + if (config.contains(reference)) { + return config.getString(reference); + } + return reference; // return reference in case nothing has been found + } + + /** + * Returns the locale language + * @return the locale language + */ + public String getLanguage(){ + if(locale == null) { + return "unknown"; + } + + return locale.getDisplayLanguage(); + } + + /** + * Returns the locale country + * @return the locale country + */ + public String getCountry(){ + if(locale == null) { + return "unknown"; + } + + return locale.getDisplayCountry(); + } + + /** + * Returns the locale language tag (e.g: en-GB) + * @return the locale language tag + */ + public String toLanguageTag(){ + return locale.toLanguageTag(); + } + + /** + * Returns the banner ItemStack representing this locale + * @return the banner ItemStack + */ + public ItemStack getBanner() { + return banner; + } + + /** + * Merges a language YAML file to this locale + * @param toBeMerged the YamlConfiguration of the language file + */ + public void merge(YamlConfiguration toBeMerged) { + for (String key : toBeMerged.getKeys(true)) { + if (!config.contains(key)) { + config.set(key, toBeMerged.get(key)); + } + } + } + + public boolean contains(String reference) { + return config.contains(reference); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/localization/TextVariables.java b/src/main/java/us/tastybento/bskyblock/api/localization/TextVariables.java new file mode 100644 index 0000000..3f0bcf3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/localization/TextVariables.java @@ -0,0 +1,19 @@ +package us.tastybento.bskyblock.api.localization; + +/** + * Contains the common variables that can be used in texts. + * @author Poslovitch + */ +public class TextVariables { + + private TextVariables() {} + + public static final String NAME = "[name]"; + public static final String DESCRIPTION = "[description]"; + public static final String NUMBER = "[number]"; + public static final String RANK = "[rank]"; + public static final String LABEL = "[label]"; + public static final String PERMISSION = "[permission]"; + public static final String SPAWN_HERE = "[spawn_here]"; + public static final String VERSION = "[version]"; +} diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/Panel.java b/src/main/java/us/tastybento/bskyblock/api/panels/Panel.java new file mode 100644 index 0000000..fcb2add --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/panels/Panel.java @@ -0,0 +1,147 @@ +package us.tastybento.bskyblock.api.panels; + +import java.util.Map; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.listeners.PanelListenerManager; +import us.tastybento.bskyblock.util.HeadGetter; +import us.tastybento.bskyblock.util.HeadRequester; + +public class Panel implements HeadRequester { + + private Inventory inventory; + private Map items; + private PanelListener listener; + private User user; + private final String name; + + public Panel(String name, Map items, int size, User user, PanelListener listener) { + this.name = name; + this.items = items; + // If size is undefined (0) then use the number of items + if (size == 0) { + size = items.keySet().size(); + } + // Create panel + if (size > 0) { + // Make sure size is a multiple of 9 + size = size + 8; + size -= (size % 9); + inventory = Bukkit.createInventory(null, size, name); + // Fill the inventory and return + for (Map.Entry en: items.entrySet()) { + //TODO allow multi-paging + if (en.getKey() < 54) inventory.setItem(en.getKey(), en.getValue().getItem()); + // Get player head async + if (en.getValue().isPlayerHead()) { + HeadGetter.getHead(en.getValue(), this); + } + } + } else { + inventory = Bukkit.createInventory(null, 9, name); + } + this.listener = listener; + // If the listener is defined, then run setup + if (listener != null) listener.setup(); + + // If the user is defined, then open panel immediately + this.user = user; + if (user != null) this.open(user); + } + + public Inventory getInventory() { + return inventory; + } + + public Map getItems() { + return items; + } + + /** + * @return the listener + */ + public Optional getListener() { + return Optional.ofNullable(listener); + } + + public Optional getUser() { + return Optional.ofNullable(user); + } + + public void open(Player... players) { + for (Player player : players) { + player.openInventory(inventory); + PanelListenerManager.getOpenPanels().put(player.getUniqueId(), this); + } + } + + /** + * Open the inventory panel + * @param users - users that should see the panel + */ + public void open(User... users) { + for (User u : users) { + u.getPlayer().openInventory(inventory); + PanelListenerManager.getOpenPanels().put(u.getUniqueId(), this); + } + } + + /** + * @param inventory the inventory to set + */ + public void setInventory(Inventory inventory) { + this.inventory = inventory; + } + + /** + * @param items the items to set + */ + public void setItems(Map items) { + this.items = items; + } + + /** + * @param listener the listener to set + */ + public void setListener(PanelListener listener) { + this.listener = listener; + } + + /** + * @param user - the User the user to set + */ + public void setUser(User user) { + this.user = user; + } + + @Override + public void setHead(PanelItem item) { + // Update the panel item + items.values().stream().filter(i -> i.getName().equals(item.getName())).forEach(i -> i = item); + for (int i = 0; i < inventory.getSize(); i++) { + ItemStack it = inventory.getItem(i); + if (it != null && it.getType().equals(Material.SKULL_ITEM)) { + ItemMeta meta = it.getItemMeta(); + if (item.getName().equals(meta.getLocalizedName())) { + inventory.setItem(i, item.getItem()); + return; + } + } + } + } + + /** + * @return the name + */ + public String getName() { + return name; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/PanelItem.java b/src/main/java/us/tastybento/bskyblock/api/panels/PanelItem.java new file mode 100644 index 0000000..df27748 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/panels/PanelItem.java @@ -0,0 +1,128 @@ +package us.tastybento.bskyblock.api.panels; + +import java.util.List; +import java.util.Optional; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; + +public class PanelItem { + + public static PanelItem empty() { + return new PanelItemBuilder().build(); + } + + private ItemStack icon; + private ClickHandler clickHandler; + private List description; + private String name; + private boolean glow; + private ItemMeta meta; + private boolean playerHead; + + public PanelItem(ItemStack icon, String name, List description, boolean glow, ClickHandler clickHandler, boolean playerHead) { + this.icon = icon; + this.playerHead = playerHead; + // Get the meta + meta = icon.getItemMeta(); + + this.clickHandler = clickHandler; + + // Create the final item + setName(name); + setDescription(description); + setGlow(glow); + + // Set flags to neaten up the view + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + meta.addItemFlags(ItemFlag.HIDE_DESTROYS); + meta.addItemFlags(ItemFlag.HIDE_PLACED_ON); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); + icon.setItemMeta(meta); + } + + public ItemStack getItem() { + return icon; + } + + public List getDescription() { + return description; + } + + public void setDescription(List description) { + this.description = description; + meta.setLore(description); + icon.setItemMeta(meta); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + meta.setDisplayName(name); + meta.setLocalizedName(name); //Localized name cannot be overridden by the player using an anvils + icon.setItemMeta(meta); + } + + public Optional getClickHandler() { + return Optional.ofNullable(clickHandler); + } + + public boolean isGlow() { + return glow; + } + + public void setGlow(boolean glow) { + this.glow = glow; + meta.addEnchant(Enchantment.ARROW_DAMAGE, 0, glow); + } + + /** + * @return the playerHead + */ + public boolean isPlayerHead() { + return playerHead; + } + + /** + * Click handler interface + * + */ + public interface ClickHandler { + /** + * This is executed when the icon is clicked + * @param panel - the panel that is being clicked + * @param user - the User + * @param clickType - the click type + * @param slot - the slot that was clicked + * @return true if the click event should be cancelled + */ + boolean onClick(Panel panel, User user, ClickType clickType, int slot); + } + + public void setHead(ItemStack itemStack) { + this.icon = itemStack; + // Get the meta + meta = icon.getItemMeta(); + // Create the final item + setName(name); + setDescription(description); + setGlow(glow); + + // Set flags to neaten up the view + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + meta.addItemFlags(ItemFlag.HIDE_DESTROYS); + meta.addItemFlags(ItemFlag.HIDE_PLACED_ON); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + icon.setItemMeta(meta); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/PanelListener.java b/src/main/java/us/tastybento/bskyblock/api/panels/PanelListener.java new file mode 100644 index 0000000..dbc7c59 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/panels/PanelListener.java @@ -0,0 +1,19 @@ +package us.tastybento.bskyblock.api.panels; + +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; + +import us.tastybento.bskyblock.api.user.User; + +public interface PanelListener { + + /** + * This is called when the panel is first setup + */ + void setup(); + + void onInventoryClose(InventoryCloseEvent event); + + void onInventoryClick(User user, InventoryClickEvent event); + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelBuilder.java b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelBuilder.java new file mode 100644 index 0000000..0a49fd3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelBuilder.java @@ -0,0 +1,99 @@ +package us.tastybento.bskyblock.api.panels.builders; + +import java.util.TreeMap; + +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.PanelListener; +import us.tastybento.bskyblock.api.user.User; + +public class PanelBuilder { + private String name; + private TreeMap items = new TreeMap<>(); + private int size; + private User user; + private PanelListener listener; + + public PanelBuilder name(String name) { + this.name = name; + return this; + } + + /** + * Add item to the panel in the last slot. + * @param item - Panel item + * @return PanelBuilder + */ + public PanelBuilder item(PanelItem item) { + return item(nextSlot(), item); + } + + /** + * Add item into a specific slot. If it is already occupied, it will be replaced. + * @param slot - slot + * @param item - Panel item + * @return PanelBuilder + */ + public PanelBuilder item(int slot, PanelItem item) { + items.put(slot, item); + return this; + } + + /** + * Forces panel to be a specific number of slots. + * @param size - size to be + * @return PanelBuilder - PanelBuilder + */ + public PanelBuilder size(int size) { + this.size = size; + return this; + } + + /** + * Sets the user who will get this panel. This will open it immediately when it is built + * @param user - the User + * @return PanelBuilder + */ + public PanelBuilder user(User user) { + this.user = user; + return this; + } + + /** + * Sets which PanelListener will listen for clicks + * @param listener - listener for this panel + * @return PanelBuilder + */ + public PanelBuilder listener(PanelListener listener) { + this.listener = listener; + return this; + } + + /** + * Get the next free slot number + * @return next slot number, or -1 in case none has been found. + */ + public int nextSlot() { + for (int i = 0 ; i < (size == 0 ? 54 : size) ; i++) { + if (!slotOccupied(i)) return i; + } + return -1; + } + + /** + * Checks if a slot is occupied in the panel or not + * @param slot to check + * @return true or false + */ + public boolean slotOccupied(int slot) { + return items.containsKey(slot); + } + + /** + * Build the panel + * @return Panel + */ + public Panel build() { + return new Panel(name, items, Math.max(size, items.isEmpty() ? size : items.lastKey()), user, listener); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java new file mode 100644 index 0000000..54d173f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java @@ -0,0 +1,98 @@ +package us.tastybento.bskyblock.api.panels.builders; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; + +public class PanelItemBuilder { + private ItemStack icon = new ItemStack(Material.AIR); + private String name = ""; + private List description = new ArrayList<>(); + private boolean glow = false; + private PanelItem.ClickHandler clickHandler; + private boolean playerHead; + + public PanelItemBuilder icon(Material icon) { + this.icon = new ItemStack(icon); + return this; + } + + public PanelItemBuilder icon(ItemStack icon) { + this.icon = icon; + return this; + } + + /** + * Set icon to player's head + * @param playerName - player's name + * @return PanelItemBuilder + */ + public PanelItemBuilder icon(String playerName) { + this.icon = new ItemStack(Material.SKULL_ITEM, 1, (short) 3); + this.name = playerName; + this.playerHead = true; + return this; + } + + + public PanelItemBuilder name(String name) { + this.name = name; + return this; + } + + /** + * Adds a list of strings to the descriptions + * @param description - List of strings + * @return PanelItemBuilder + */ + public PanelItemBuilder description(List description) { + this.description.addAll(description); + return this; + } + + /** + * Add any number of lines to the description + * @param description strings of lines + * @return PanelItemBuilder + */ + public PanelItemBuilder description(String... description) { + List additions = Arrays.asList(description); + ArrayList updatableList = new ArrayList<>(); + updatableList.addAll(this.description); + updatableList.addAll(additions); + this.description = updatableList; + return this; + } + + /** + * Adds a line to the description + * @param description - string + * @return PanelItemBuilder + */ + public PanelItemBuilder description(String description) { + Collections.addAll(this.description, description.split("\n")); + return this; + } + + public PanelItemBuilder glow(boolean glow) { + this.glow = glow; + return this; + } + + public PanelItemBuilder clickHandler(ClickHandler clickHandler) { + this.clickHandler = clickHandler; + return this; + } + + public PanelItem build() { + return new PanelItem(icon, name, description, glow, clickHandler, playerHead); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/placeholders/Placeholder.java b/src/main/java/us/tastybento/bskyblock/api/placeholders/Placeholder.java new file mode 100644 index 0000000..fc4addd --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/placeholders/Placeholder.java @@ -0,0 +1,29 @@ +package us.tastybento.bskyblock.api.placeholders; + +import us.tastybento.bskyblock.api.user.User; + +/** + * @author Poslovitch + */ +public class Placeholder { + + private String identifier; + private PlaceholderRequest request; + + Placeholder(String identifier, PlaceholderRequest request) { + this.identifier = identifier; + this.request = request; + } + + public String getIdentifier() { + return this.identifier; + } + + public PlaceholderRequest getRequest() { + return request; + } + + public interface PlaceholderRequest { + String request(User user); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderAPIInterface.java b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderAPIInterface.java new file mode 100644 index 0000000..1bc6f10 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderAPIInterface.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.api.placeholders; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; + +/** + * Simple interface for every Placeholder API. + * + * @author Poslovitch + */ +public interface PlaceholderAPIInterface { + + /** + * Gets the name of the Placeholder API + * @return name of the placeholder plugin + */ + String getName(); + + /** + * Registers the placeholder API + * @param plugin - BSkyBlock plugin object + * @return true if successfully registered + */ + boolean register(BSkyBlock plugin); + + /** + * Unregisters the placeholder API + * @param plugin - BSkyBlock plugin object + */ + void unregister(BSkyBlock plugin); + + /** + * Replace placeholders in the message according to the receiver + * @param receiver - user who will receive the message + * @param message - message + * @return updated message + */ + String replacePlaceholders(User receiver, String message); +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderBuilder.java b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderBuilder.java new file mode 100644 index 0000000..f6ca9f3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderBuilder.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.api.placeholders; + +public class PlaceholderBuilder { + + private String identifier; + private Placeholder.PlaceholderRequest value; + + public PlaceholderBuilder identifier(String identifier) { + this.identifier = identifier; + return this; + } + + /** + * The value this placeholder should take + * @param value - placeholder request value + * @return PlaceholderBuilder object + */ + public PlaceholderBuilder value(Placeholder.PlaceholderRequest value) { + this.value = value; + return this; + } + + public Placeholder build() { + return new Placeholder(identifier, value); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderHandler.java b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderHandler.java new file mode 100644 index 0000000..f69ba83 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/placeholders/PlaceholderHandler.java @@ -0,0 +1,98 @@ +package us.tastybento.bskyblock.api.placeholders; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; + +/** + * Handles hooks with other Placeholder APIs. + * + * @author Poslovitch, Tastybento + */ +public class PlaceholderHandler { + private static final String PACKAGE = "us.tastybento.bskyblock.api.placeholders.hooks."; + + // This class should never be instantiated (all methods are static) + private PlaceholderHandler() {} + + /** + * List of API classes in the package specified above (except the Internal one) + */ + private static final String[] HOOKS = { + //TODO + }; + + private static List apis = new ArrayList<>(); + + /** + * Register placeholders and hooks + * @param plugin - BSkyBlock plugin object + */ + public static void register(BSkyBlock plugin){ + + // Load Internal Placeholder API + try{ + Class clazz = Class.forName(PACKAGE + "InternalPlaceholderImpl"); + PlaceholderAPIInterface internal = (PlaceholderAPIInterface)clazz.newInstance(); + apis.add(internal); + } catch (Exception e){ + // Should never happen. + plugin.logError("Failed to load default placeholder API"); + } + + // Load hooks + for(String hook : HOOKS){ + if(plugin.getServer().getPluginManager().isPluginEnabled(hook)){ + try{ + Class clazz = Class.forName(PACKAGE + hook + "PlaceholderImpl"); + PlaceholderAPIInterface api = (PlaceholderAPIInterface)clazz.newInstance(); + if(api.register(plugin)){ + plugin.log("Hooked placeholders into " + hook); // since Java 8, we can use Supplier , which will be evaluated lazily + apis.add(api); + } else { + plugin.log("Failed to hook placeholders into " + hook); + } + } catch (Exception e){ + plugin.log("Failed to hook placeholders into " + hook); + } + } + } + } + + /** + * Unregister placeholder hooks + * @param plugin - BSkyBlock plugin object + */ + public static void unregister(BSkyBlock plugin){ + Iterator it = apis.iterator(); + while (it.hasNext()) { + PlaceholderAPIInterface api = it.next(); + api.unregister(plugin); + it.remove(); + } + } + + /** + * Replace placeholders in the message according to the receiver + * @param receiver - user to receive the message + * @param message - message + * @return updated message + */ + public static String replacePlaceholders(User receiver, String message){ + for(PlaceholderAPIInterface api : apis){ + message = api.replacePlaceholders(receiver, message); + } + + return message; + } + + /** + * @return true if APIs are registered (including Internal), otherwise false + */ + public static boolean hasHooks(){ + return apis != null; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/placeholders/hooks/InternalPlaceholderImpl.java b/src/main/java/us/tastybento/bskyblock/api/placeholders/hooks/InternalPlaceholderImpl.java new file mode 100644 index 0000000..f8afcad --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/placeholders/hooks/InternalPlaceholderImpl.java @@ -0,0 +1,45 @@ +package us.tastybento.bskyblock.api.placeholders.hooks; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.placeholders.Placeholder; +import us.tastybento.bskyblock.api.placeholders.PlaceholderAPIInterface; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Placeholders; + +/** + * Built-in placeholder API + * + * @author Poslovitch + */ +public class InternalPlaceholderImpl implements PlaceholderAPIInterface { + + @Override + public String getName() { + return "Internal"; + } + + @Override + public boolean register(BSkyBlock plugin) { + return true; + } + + @Override + public void unregister(BSkyBlock plugin) { + // Useless : it would disable the placeholders. + } + + @Override + public String replacePlaceholders(User receiver, String message) { + if(message == null || message.isEmpty()) { + return ""; + } + + for(Placeholder placeholder : Placeholders.values()){ + String identifier = "%" + placeholder.getIdentifier() + "%"; + message = message.replaceAll(identifier, placeholder.getRequest().request(receiver)); + } + + return message; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/api/user/Notifier.java b/src/main/java/us/tastybento/bskyblock/api/user/Notifier.java new file mode 100644 index 0000000..7209489 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/user/Notifier.java @@ -0,0 +1,71 @@ +package us.tastybento.bskyblock.api.user; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +/** + * Utilities class that helps to avoid spamming the User with potential repeated messages + * @author Poslovitch + */ +public class Notifier { + + /** + * Time in seconds before {@link #notificationCache} removes the entry related to the player. + */ + private static final int NOTIFICATION_DELAY = 4; + + private final LoadingCache notificationCache = CacheBuilder.newBuilder() + .expireAfterAccess(NOTIFICATION_DELAY, TimeUnit.SECONDS) + .maximumSize(500) + .build( + new CacheLoader() { + @Override + public Notification load(User user) { + return new Notification(null, 0); + } + } + ); + + /** + * Sends message to a user only if the message hasn't been sent recently + * @param user - user + * @param message - message to send (already translated) + * @return true if message sent successfully, false it it has been throttled + */ + public synchronized boolean notify(User user, String message) { + try { + Notification lastNotification = notificationCache.get(user); + long now = System.currentTimeMillis(); + if (now >= lastNotification.getTime() + NOTIFICATION_DELAY * 1000 || !message.equals(lastNotification.getMessage())) { + notificationCache.put(user, new Notification(message, now)); + user.sendRawMessage(message); + return true; + } + return false; + } catch (ExecutionException e) { + return false; + } + } + + public class Notification { + private final String message; + private final long time; + + public Notification(String message, long time) { + this.message = message; + this.time = time; + } + + public String getMessage() { + return message; + } + + public long getTime() { + return time; + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/api/user/User.java b/src/main/java/us/tastybento/bskyblock/api/user/User.java new file mode 100644 index 0000000..e7acff6 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/api/user/User.java @@ -0,0 +1,374 @@ +package us.tastybento.bskyblock.api.user; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.permissions.PermissionAttachmentInfo; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.placeholders.PlaceholderHandler; + +/** + * BSB's user object. Wraps Player. + * @author tastybento + * + */ +public class User { + + private static Map users = new HashMap<>(); + + /** + * Clears all users from the user list + */ + public static void clearUsers() { + users.clear(); + } + + /** + * Get an instance of User from a CommandSender + * @param sender - command sender, e.g. console + * @return user - user + */ + public static User getInstance(CommandSender sender) { + if (sender instanceof Player) { + return getInstance((Player)sender); + } + // Console + return new User(sender); + } + /** + * Get an instance of User from a Player object + * @param player - the player + * @return user - user + */ + public static User getInstance(Player player) { + if (player == null) { + return null; + } + if (users.containsKey(player.getUniqueId())) { + return users.get(player.getUniqueId()); + } + return new User(player); + } + /** + * Get an instance of User from a UUID + * @param uuid - UUID + * @return user - user + */ + public static User getInstance(UUID uuid) { + if (uuid == null) { + return null; + } + if (users.containsKey(uuid)) { + return users.get(uuid); + } + // Return player, or null if they are not online + return new User(uuid); + } + /** + * Removes this player from the User cache + * @param player - the player + */ + public static void removePlayer(Player player) { + users.remove(player.getUniqueId()); + } + + // ---------------------------------------------------- + + private static BSkyBlock plugin = BSkyBlock.getInstance(); + + private Player player; + private final UUID playerUUID; + private final CommandSender sender; + + private User(CommandSender sender) { + player = null; + playerUUID = null; + this.sender = sender; + } + + private User(Player player) { + this.player = player; + sender = player; + playerUUID = player.getUniqueId(); + users.put(player.getUniqueId(), this); + } + + private User(UUID playerUUID) { + player = Bukkit.getPlayer(playerUUID); + this.playerUUID = playerUUID; + sender = player; + } + + /** + * Used for testing + * @param p - BSkyBlock plugin + */ + public static void setPlugin(BSkyBlock p) { + plugin = p; + } + + public Set getEffectivePermissions() { + return sender.getEffectivePermissions(); + } + + public PlayerInventory getInventory() { + return player != null ? player.getInventory() : null; + } + + public Location getLocation() { + return player != null ? player.getLocation() : null; + } + + public String getName() { + return player != null ? player.getName() : plugin.getPlayers().getName(playerUUID); + } + + /** + * @return the player + */ + public Player getPlayer() { + return player; + } + + /** + * @return true if this user is a player, false if not, e.g., console + */ + public boolean isPlayer() { + return player != null; + } + + public CommandSender getSender() { + return sender; + } + + public UUID getUniqueId() { + return playerUUID; + } + + /** + * @param permission - permission string + * @return true if permission is empty or if the player has that permission + */ + public boolean hasPermission(String permission) { + return permission.isEmpty() || sender.hasPermission(permission); + } + + public boolean isOnline() { + return player != null && player.isOnline(); + } + + /** + * Checks if user is Op + * @return true if user is Op + */ + public boolean isOp() { + if (sender != null) { + return sender.isOp(); + } + if (playerUUID != null) { + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerUUID); + if (offlinePlayer != null) { + return offlinePlayer.isOp(); + } + } + return false; + } + + /** + * Gets a translation of this reference for this user. + * @param reference - reference found in a locale file + * @param variables - variables to insert into translated string. Variables go in pairs, for example + * "[name]", "tastybento" + * @return Translated string with colors converted, or the reference if nothing has been found + */ + public String getTranslation(String reference, String... variables) { + // Get translation + String translation = plugin.getLocalesManager().get(this, reference); + + // If no translation has been found, return the reference for debug purposes. + if (translation == null) { + return reference; + } + + // Then replace variables + if (variables.length > 1) { + for (int i = 0; i < variables.length; i += 2) { + translation = translation.replace(variables[i], variables[i+1]); + } + } + + // Replace placeholders + translation = PlaceholderHandler.replacePlaceholders(this, translation); + + return ChatColor.translateAlternateColorCodes('&', translation); + } + + /** + * Gets a translation of this reference for this user. + * @param reference - reference found in a locale file + * @param variables - variables to insert into translated string. Variables go in pairs, for example + * "[name]", "tastybento" + * @return Translated string with colors converted, or a blank String if nothing has been found + */ + public String getTranslationOrNothing(String reference, String... variables) { + String translation = getTranslation(reference, variables); + return translation.equals(reference) ? "" : translation; + } + + /** + * Send a message to sender if message is not empty. + * @param reference - language file reference + * @param variables - CharSequence target, replacement pairs + */ + public void sendMessage(String reference, String... variables) { + String message = getTranslation(reference, variables); + if (!ChatColor.stripColor(message).trim().isEmpty()) { + if (sender != null) { + sender.sendMessage(message); + } else { + // TODO: Offline message + // Save this message so the player can see it later + } + } + } + + /** + * Sends a message to sender without any modification (colors, multi-lines, placeholders). + * @param message - the message to send + */ + public void sendRawMessage(String message) { + if (sender != null) { + sender.sendMessage(message); + } else { + // TODO: Offline message + // Save this message so the player can see it later + } + } + + /** + * Sends a message to sender if message is not empty and if the same wasn't sent within the previous {@link Notifier#NOTIFICATION_DELAY} seconds. + * @param reference - language file reference + * @param variables - CharSequence target, replacement pairs + * + * @see Notifier + */ + public void notify(String reference, String... variables) { + String message = getTranslation(reference, variables); + if (!ChatColor.stripColor(message).trim().isEmpty() && sender != null) { + plugin.getNotifier().notify(this, message); + } + } + + /** + * Sets the user's game mode + * @param mode - GameMode + */ + public void setGameMode(GameMode mode) { + player.setGameMode(mode); + } + + /** + * Teleports user to this location. If the user is in a vehicle, they will exit first. + * @param location - the location + */ + public void teleport(Location location) { + player.teleport(location); + } + + /** + * Gets the current world this entity resides in + * @return World + */ + public World getWorld() { + return player.getWorld(); + } + + /** + * Closes the user's inventory + */ + public void closeInventory() { + player.closeInventory(); + } + + /** + * Get the user's locale + * @return Locale + */ + public Locale getLocale() { + if (sender instanceof Player && !plugin.getPlayers().getLocale(playerUUID).isEmpty()) { + return Locale.forLanguageTag(plugin.getPlayers().getLocale(playerUUID)); + } + return Locale.forLanguageTag(plugin.getSettings().getDefaultLanguage()); + + } + + /** + * Forces an update of the user's complete inventory. + * Deprecated, but there is no current alternative. + */ + public void updateInventory() { + player.updateInventory(); + + } + + /** + * Performs a command as the player + * @param cmd - command to execute + * @return true if the command was successful, otherwise false + */ + public boolean performCommand(String cmd) { + return player.performCommand(cmd); + + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((playerUUID == null) ? 0 : playerUUID.hashCode()); + return result; + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof User)) { + return false; + } + User other = (User) obj; + if (playerUUID == null) { + return other.playerUUID == null; + } else return playerUUID.equals(other.playerUUID); + } + + /** + * Checks if a user is in one of the game worlds + * @return true if user is, false if not + */ + public boolean inWorld() { + return plugin.getIWM().inWorld(getLocation()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/AdminCommand.java b/src/main/java/us/tastybento/bskyblock/commands/AdminCommand.java new file mode 100755 index 0000000..456f28e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/AdminCommand.java @@ -0,0 +1,74 @@ +package us.tastybento.bskyblock.commands; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.admin.AdminClearResetsAllCommand; +import us.tastybento.bskyblock.commands.admin.AdminClearResetsCommand; +import us.tastybento.bskyblock.commands.admin.AdminGetRankCommand; +import us.tastybento.bskyblock.commands.admin.AdminInfoCommand; +import us.tastybento.bskyblock.commands.admin.AdminRegisterCommand; +import us.tastybento.bskyblock.commands.admin.AdminReloadCommand; +import us.tastybento.bskyblock.commands.admin.AdminSchemCommand; +import us.tastybento.bskyblock.commands.admin.AdminSetRankCommand; +import us.tastybento.bskyblock.commands.admin.AdminTeleportCommand; +import us.tastybento.bskyblock.commands.admin.AdminUnregisterCommand; +import us.tastybento.bskyblock.commands.admin.AdminVersionCommand; +import us.tastybento.bskyblock.commands.admin.range.AdminRangeCommand; +import us.tastybento.bskyblock.commands.admin.team.AdminTeamAddCommand; +import us.tastybento.bskyblock.commands.admin.team.AdminTeamDisbandCommand; +import us.tastybento.bskyblock.commands.admin.team.AdminTeamKickCommand; +import us.tastybento.bskyblock.commands.admin.team.AdminTeamMakeLeaderCommand; + +public class AdminCommand extends CompositeCommand { + + public AdminCommand() { + super("bsbadmin", "bsb"); + } + + @Override + public void setup() { + setPermissionPrefix("bskyblock"); + setPermission("admin.*"); + setOnlyPlayer(false); + setParameters("commands.admin.help.parameters"); + setDescription("commands.admin.help.description"); + setWorld(getPlugin().getIWM().getBSBIslandWorld()); + new AdminVersionCommand(this); + new AdminReloadCommand(this); + new AdminTeleportCommand(this, "tp"); + new AdminTeleportCommand(this, "tpnether"); + new AdminTeleportCommand(this, "tpend"); + new AdminGetRankCommand(this); + new AdminSetRankCommand(this); + new AdminInfoCommand(this); + // Team commands + new AdminTeamAddCommand(this); + new AdminTeamKickCommand(this); + new AdminTeamDisbandCommand(this); + new AdminTeamMakeLeaderCommand(this); + // Schems + new AdminSchemCommand(this); + // Register/unregister islands + new AdminRegisterCommand(this); + new AdminUnregisterCommand(this); + // Range + new AdminRangeCommand(this); + // Resets + new AdminClearResetsCommand(this); + new AdminClearResetsAllCommand(this); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!args.isEmpty()) { + user.sendMessage("general.errors.unknown-command", TextVariables.LABEL, getTopLabel()); + return false; + } + // By default run the attached help command, if it exists (it should) + return showHelp(this, user); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/IslandCommand.java b/src/main/java/us/tastybento/bskyblock/commands/IslandCommand.java new file mode 100755 index 0000000..d7cfac9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/IslandCommand.java @@ -0,0 +1,78 @@ +package us.tastybento.bskyblock.commands; + +import java.util.ArrayList; +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.island.IslandAboutCommand; +import us.tastybento.bskyblock.commands.island.IslandBanCommand; +import us.tastybento.bskyblock.commands.island.IslandBanlistCommand; +import us.tastybento.bskyblock.commands.island.IslandCreateCommand; +import us.tastybento.bskyblock.commands.island.IslandGoCommand; +import us.tastybento.bskyblock.commands.island.IslandLanguageCommand; +import us.tastybento.bskyblock.commands.island.IslandResetCommand; +import us.tastybento.bskyblock.commands.island.IslandResetnameCommand; +import us.tastybento.bskyblock.commands.island.IslandSethomeCommand; +import us.tastybento.bskyblock.commands.island.IslandSetnameCommand; +import us.tastybento.bskyblock.commands.island.IslandSettingsCommand; +import us.tastybento.bskyblock.commands.island.IslandUnbanCommand; +import us.tastybento.bskyblock.commands.island.team.IslandTeamCommand; + +public class IslandCommand extends CompositeCommand { + + public IslandCommand() { + super("island", "is"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CompositeCommand#setup() + */ + @Override + public void setup() { + setDescription("commands.island.help.description"); + setOnlyPlayer(true); + // Permission + setPermissionPrefix("bskyblock"); + setPermission("island"); + setWorld(getPlugin().getIWM().getBSBIslandWorld()); + // Set up subcommands + new IslandAboutCommand(this); + new IslandCreateCommand(this); + new IslandGoCommand(this); + new IslandResetCommand(this); + new IslandSetnameCommand(this); + new IslandResetnameCommand(this); + new IslandSethomeCommand(this); + new IslandSettingsCommand(this); + new IslandLanguageCommand(this); + new IslandBanCommand(this); + new IslandUnbanCommand(this); + new IslandBanlistCommand(this); + // Team commands + new IslandTeamCommand(this); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + if (user == null) { + return false; + } + if (args.isEmpty()) { + // If user has an island, go + if (getPlugin().getIslands().getIsland(getWorld(), user.getUniqueId()) != null) { + return getSubCommand("go").map(goCmd -> goCmd.execute(user, goCmd.getLabel(), new ArrayList<>())).orElse(false); + } + // No islands currently + return getSubCommand("create").map(createCmd -> createCmd.execute(user, createCmd.getLabel(), new ArrayList<>())).orElse(false); + } + user.sendMessage("general.errors.unknown-command", TextVariables.LABEL, getTopLabel()); + return false; + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommand.java new file mode 100644 index 0000000..05efa4f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommand.java @@ -0,0 +1,40 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminClearResetsAllCommand extends CompositeCommand { + + public AdminClearResetsAllCommand(CompositeCommand parent) { + super(parent, "clearresetsall"); + } + + @Override + public void setup() { + setPermission("admin.clearresetsall"); + setDescription("commands.admin.clearresetsall.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (!args.isEmpty()) { + showHelp(this, user); + return false; + } + this.askConfirmation(user, () -> { + // Set the reset epoch to now + getIWM().setResetEpoch(getWorld()); + // Reset all current players + Bukkit.getOnlinePlayers().stream().map(Player::getUniqueId).filter(getPlayers()::isKnown).forEach(u -> getPlayers().setResets(getWorld(), u, 0)); + user.sendMessage("general.success"); + }); + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommand.java new file mode 100644 index 0000000..7fc7bf3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommand.java @@ -0,0 +1,59 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +public class AdminClearResetsCommand extends CompositeCommand { + + public AdminClearResetsCommand(CompositeCommand parent) { + super(parent, "clearresets"); + } + + @Override + public void setup() { + setPermission("admin.clearreset"); + setParameters("commands.admin.clearresets.parameters"); + setDescription("commands.admin.clearresets.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + // Clear resets + user.sendMessage("commands.admin.clearresets.cleared"); + getPlayers().setResets(getWorld(), targetUUID, 0); + user.sendMessage("general.success"); + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminGetRankCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminGetRankCommand.java new file mode 100644 index 0000000..ab38bf8 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminGetRankCommand.java @@ -0,0 +1,71 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.RanksManager; + +/** + * @author tastybento + * + */ +public class AdminGetRankCommand extends CompositeCommand { + + public AdminGetRankCommand(CompositeCommand adminCommand) { + super(adminCommand, "getrank"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#setup() + */ + @Override + public void setup() { + setPermission("admin.setrank"); + setOnlyPlayer(false); + setParameters("commands.admin.getrank.parameters"); + setDescription("commands.admin.getrank.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 1) { + // Show help + showHelp(this, user); + return false; + } + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + // Get rank + RanksManager rm = getPlugin().getRanksManager(); + User target = User.getInstance(targetUUID); + Island island = getIslands().getIsland(getWorld(), targetUUID); + int currentRank = island.getRank(target); + user.sendMessage("commands.admin.getrank.rank-is", TextVariables.RANK, user.getTranslation(rm.getRank(currentRank))); + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + return Optional.of(Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList())); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommand.java new file mode 100644 index 0000000..991391a --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommand.java @@ -0,0 +1,52 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminInfoCommand extends CompositeCommand { + + public AdminInfoCommand(CompositeCommand parent) { + super(parent, "info"); + } + + @Override + public void setup() { + setPermission("admin.info"); + setOnlyPlayer(false); + setParameters("commands.admin.info.parameters"); + setDescription("commands.admin.info.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.size() > 1 || (args.isEmpty() && !user.isPlayer())) { + // Show help + showHelp(this, user); + return false; + } + // If there are no args, then the player wants info on the island at this location + if (args.isEmpty()) { + if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(getPlugin(), user, getWorld())).orElse(false)) { + user.sendMessage("commands.admin.info.no-island"); + return false; + } + return true; + } + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + // Show info for this player + getIslands().getIsland(getWorld(), targetUUID).showInfo(getPlugin(), user, getWorld()); + return true; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommand.java new file mode 100644 index 0000000..356dd70 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommand.java @@ -0,0 +1,106 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.Material; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class AdminRegisterCommand extends CompositeCommand { + + public AdminRegisterCommand(CompositeCommand parent) { + super(parent, "register"); + } + + @Override + public void setup() { + setPermission("admin.register"); + setOnlyPlayer(true); + setParameters("commands.admin.register.parameters"); + setDescription("commands.admin.register.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-island"); + return false; + } + if (getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("commands.admin.register.cannot-register-team-player"); + return false; + } + + // Check if island is owned + Optional island = getIslands().getIslandAt(user.getLocation()); + if (island.map(i -> i.getOwner() != null).orElse(false)) { + user.sendMessage("commands.admin.register.already-owned"); + return false; + } + // Register island if it exists + if (!island.map(i -> { + // Island exists + getIslands().makeLeader(user, targetUUID, i, getPermissionPrefix()); + user.sendMessage("commands.admin.register.registered-island", "[xyz]", Util.xyz(i.getCenter().toVector())); + user.sendMessage("general.success"); + return true; + }).orElse(false)) { + // Island does not exist + user.sendMessage("commands.admin.register.no-island-here"); + this.askConfirmation(user, () -> { + // Make island here + Island i = getIslands().createIsland(getClosestIsland(user.getLocation()), targetUUID); + getIslands().makeLeader(user, targetUUID, i, getPermissionPrefix()); + getWorld().getBlockAt(i.getCenter()).setType(Material.BEDROCK); + user.sendMessage("commands.admin.register.registered-island", "[xyz]", Util.xyz(i.getCenter().toVector())); + user.sendMessage("general.success"); + }); + return false; + } + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } + + /** + * This returns the coordinate of where an island should be on the grid. + * + * @param location - location to check + * @return Location of where an island should be on a grid in this world + */ + public Location getClosestIsland(Location location) { + int dist = getIWM().getIslandDistance(getWorld()) * 2; + long x = Math.round((double) location.getBlockX() / dist) * dist + getIWM().getIslandXOffset(getWorld()); + long z = Math.round((double) location.getBlockZ() / dist) * dist + getIWM().getIslandZOffset(getWorld()); + long y = getIWM().getIslandHeight(getWorld()); + return new Location(location.getWorld(), x, y, z); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminReloadCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminReloadCommand.java new file mode 100644 index 0000000..b95837c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminReloadCommand.java @@ -0,0 +1,37 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author tastybento + * + */ +public class AdminReloadCommand extends CompositeCommand { + + /** + * @param parent - parent command + */ + public AdminReloadCommand(CompositeCommand parent) { + super(parent, "reload", "rl"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#setup() + */ + @Override + public void setup() { + setDescription("commands.admin.reload.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java new file mode 100644 index 0000000..e77d13c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java @@ -0,0 +1,141 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.island.builders.Clipboard; +import us.tastybento.bskyblock.util.Util; + +public class AdminSchemCommand extends CompositeCommand { + private Map clipboards; + + public AdminSchemCommand(CompositeCommand parent) { + super(parent, "schem"); + } + + @Override + public void setup() { + setPermission("admin.schem"); + setParameters("commands.admin.schem.parameters"); + setDescription("commands.admin.schem.description"); + setOnlyPlayer(true); + clipboards = new HashMap<>(); + } + + @Override + @SuppressWarnings("deprecation") + public boolean execute(User user, String label, List args) { + if (args.isEmpty()) { + showHelp(this, user); + return false; + } + File schemFolder = new File(getIWM().getDataFolder(getWorld()), "schems"); + Clipboard cb = clipboards.getOrDefault(user.getUniqueId(), new Clipboard(getPlugin(), schemFolder)); + + if (args.get(0).equalsIgnoreCase("paste")) { + if (cb.isFull()) { + cb.paste(user.getLocation()); + user.sendMessage("general.success"); + return true; + } else { + user.sendMessage("commands.admin.schem.copy-first"); + return false; + } + } + + if (args.get(0).equalsIgnoreCase("load")) { + if (args.size() == 2) { + if (cb.load(user, args.get(1))) { + clipboards.put(user.getUniqueId(), cb); + return true; + } + } else { + showHelp(this, user); + return false; + } + return false; + } + + if (args.get(0).equalsIgnoreCase("origin")) { + if (cb.getPos1() == null || cb.getPos2() == null) { + user.sendMessage("commands.admin.schem.need-pos1-pos2"); + return false; + } + // Get the block player is looking at + Block b = user.getPlayer().getLineOfSight(null, 20).stream().filter(x -> !x.getType().equals(Material.AIR)).findFirst().orElse(null); + if (b != null) { + cb.setOrigin(b.getLocation()); + user.getPlayer().sendBlockChange(b.getLocation(), Material.STAINED_GLASS,(byte)14); + Bukkit.getScheduler().runTaskLater(getPlugin(), + () -> user.getPlayer().sendBlockChange(b.getLocation(), b.getType(), b.getData()), 20L); + + user.sendMessage("general.success"); + return true; + } else { + user.sendMessage("commands.admin.schem.look-at-a-block"); + return false; + } + } + + if (args.get(0).equalsIgnoreCase("copy")) { + boolean copyAir = (args.size() == 2 && args.get(1).equalsIgnoreCase("air")); + return cb.copy(user, copyAir); + } + + if (args.get(0).equalsIgnoreCase("save")) { + if (cb.isFull()) { + if (args.size() == 2) { + // Check if file exists + File newFile = new File(schemFolder, args.get(1) + ".schem"); + if (newFile.exists()) { + user.sendMessage("commands.admin.schem.file-exists"); + this.askConfirmation(user, () -> cb.save(user, args.get(1))); + return false; + } else { + return cb.save(user, args.get(1)); + } + } else { + showHelp(this, user); + return false; + } + } else { + user.sendMessage("commands.admin.schem.copy-first"); + return false; + } + } + + if (args.get(0).equalsIgnoreCase("pos1")) { + if (user.getLocation().equals(cb.getPos2())) { + user.sendMessage("commands.admin.schem.set-different-pos"); + return false; + } + cb.setPos1(user.getLocation()); + user.sendMessage("commands.admin.schem.set-pos1", "[vector]", Util.xyz(user.getLocation().toVector())); + clipboards.put(user.getUniqueId(), cb); + return true; + } + + if (args.get(0).equalsIgnoreCase("pos2")) { + if (user.getLocation().equals(cb.getPos1())) { + user.sendMessage("commands.admin.schem.set-different-pos"); + return false; + } + cb.setPos2(user.getLocation()); + user.sendMessage("commands.admin.schem.set-pos2", "[vector]", Util.xyz(user.getLocation().toVector())); + clipboards.put(user.getUniqueId(), cb); + return true; + } + + return false; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSetRankCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSetRankCommand.java new file mode 100644 index 0000000..0ec49b4 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSetRankCommand.java @@ -0,0 +1,77 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.RanksManager; + +/** + * @author tastybento + * + */ +public class AdminSetRankCommand extends CompositeCommand { + + public AdminSetRankCommand(CompositeCommand adminCommand) { + super(adminCommand, "setrank"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#setup() + */ + @Override + public void setup() { + setPermission("admin.setrank"); + setOnlyPlayer(false); + setParameters("commands.admin.setrank.parameters"); + setDescription("commands.admin.setrank.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.BSBCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 2) { + // Show help + showHelp(this, user); + return false; + } + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getPlugin().getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + // Get rank + RanksManager rm = getPlugin().getRanksManager(); + int rankValue = rm.getRanks().entrySet().stream() + .filter(r -> user.getTranslation(r.getKey()).equalsIgnoreCase(args.get(1))).findFirst() + .map(Map.Entry::getValue).orElse(-999); + if (rankValue < RanksManager.BANNED_RANK) { + user.sendMessage("commands.admin.setrank.unknown-rank"); + return false; + } + User target = User.getInstance(targetUUID); + + Island island = getPlugin().getIslands().getIsland(getWorld(), targetUUID); + int currentRank = island.getRank(target); + island.setRank(target, rankValue); + user.sendMessage("commands.admin.setrank.rank-set", "[from]", user.getTranslation(rm.getRank(currentRank)), "[to]", user.getTranslation(rm.getRank(rankValue))); + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + return Optional.of(getPlugin().getRanksManager().getRanks().keySet().stream().map(user::getTranslation).collect(Collectors.toList())); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminTeleportCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminTeleportCommand.java new file mode 100644 index 0000000..144c8bb --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminTeleportCommand.java @@ -0,0 +1,75 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; +import us.tastybento.bskyblock.util.teleport.SafeTeleportBuilder; + +public class AdminTeleportCommand extends CompositeCommand { + + public AdminTeleportCommand(CompositeCommand parent, String tpCommand) { + super(parent, tpCommand); + } + + @Override + public void setup() { + // Permission + setPermission(getPermissionPrefix() + "admin.tp"); + setOnlyPlayer(true); + setParameters("commands.admin.tp.parameters"); + setDescription("commands.admin.tp.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.isEmpty()) { + this.showHelp(this, user); + return true; + } + + // Convert name to a UUID + final UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } else { + if (getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID)) { + Location warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getWorld()); + if (getLabel().equals("tpnether")) { + warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getPlugin().getIWM().getNetherWorld(getWorld())); + } else if (getLabel().equals("tpend")) { + warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getPlugin().getIWM().getEndWorld(getWorld())); + } + // Other wise, go to a safe spot + String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " " + + warpSpot.getBlockZ()); + new SafeTeleportBuilder(getPlugin()).entity(user.getPlayer()) + .location(warpSpot) + .failureMessage(failureMessage) + .build(); + return true; + } + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommand.java new file mode 100644 index 0000000..190a244 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommand.java @@ -0,0 +1,64 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +public class AdminUnregisterCommand extends CompositeCommand { + + public AdminUnregisterCommand(CompositeCommand parent) { + super(parent, "unregister"); + } + + @Override + public void setup() { + setPermission("admin.unregister"); + setParameters("commands.admin.unregister.parameters"); + setDescription("commands.admin.unregister.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + if (getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("commands.admin.unregister.cannot-unregister-team-player"); + return false; + } + // Unregister island + user.sendMessage("commands.admin.unregister.unregistered-island", "[xyz]", Util.xyz(getIslands().getIsland(getWorld(), targetUUID).getCenter().toVector())); + getIslands().removePlayer(getWorld(), targetUUID); + getPlayers().clearHomeLocations(getWorld(), targetUUID); + user.sendMessage("general.success"); + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminVersionCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminVersionCommand.java new file mode 100644 index 0000000..0cdd3f3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminVersionCommand.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.commands.admin; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminVersionCommand extends CompositeCommand { + + public AdminVersionCommand(CompositeCommand adminCommand) { + super(adminCommand, "version", "v"); + } + + @Override + public void setup() { + // Permission + setPermission("admin.version"); + setDescription("commands.admin.version.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + return false; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommand.java new file mode 100644 index 0000000..89a083b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommand.java @@ -0,0 +1,32 @@ +package us.tastybento.bskyblock.commands.admin.range; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author Poslovitch + */ +public class AdminRangeCommand extends CompositeCommand { + + public AdminRangeCommand(CompositeCommand parent) { + super (parent, "range"); + } + + @Override + public void setup() { + setPermission("admin.range"); + setDescription("commands.admin.range.description"); + + new AdminRangeDisplayCommand(this); + new AdminRangeSetCommand(this); + new AdminRangeResetCommand(this); + } + + @Override + public boolean execute(User user, String label, List args) { + showHelp(this, user); + return true; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommand.java new file mode 100644 index 0000000..6cf8c5f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommand.java @@ -0,0 +1,116 @@ +package us.tastybento.bskyblock.commands.admin.range; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author Poslovitch + */ +public class AdminRangeDisplayCommand extends CompositeCommand { + + private Map display = new HashMap<>(); + + public AdminRangeDisplayCommand(CompositeCommand parent) { + super(parent, "display", "show", "hide"); + } + + @Override + public void setup() { + setPermission("admin.range.display"); + setDescription("commands.admin.range.display.description"); + setOnlyPlayer(true); + } + + @Override + public boolean execute(User user, String label, List args) { + // According to the label used to execute the command, there is a different behaviour + // - display : toggle on/off + // - show : only set on - and send "error" if already on + // - hide : only set off - same if already off + + if (!display.containsKey(user)) { + switch (label) { + case "display": + case "show": + showZones(user); + break; + case "hide": + user.sendMessage("commands.admin.range.display.already-off"); + break; + } + } else { + switch (label) { + case "display": + case "hide": + hideZones(user); + break; + case "show": + user.sendMessage("commands.admin.range.display.already-on"); + break; + } + } + + return true; + } + + private void showZones(User user) { + user.sendMessage("commands.admin.range.display.showing"); + user.sendMessage("commands.admin.range.display.hint"); + display.put(user, Bukkit.getScheduler().scheduleSyncRepeatingTask(getPlugin(), () -> { + if (!user.getPlayer().isOnline()) { + hideZones(user); + } + + getIslands().getIslandAt(user.getLocation()).ifPresent(island -> { + // Draw the island protected area + drawZone(user.getPlayer(), Particle.BARRIER, island.getCenter(), island.getProtectionRange()); + + // Draw the default protected area if island protected zone is different + if (island.getProtectionRange() != getPlugin().getSettings().getIslandProtectionRange()) { + drawZone(user.getPlayer(), Particle.VILLAGER_HAPPY, island.getCenter(), getPlugin().getSettings().getIslandProtectionRange()); + } + + // Draw the island area + drawZone(user.getPlayer(), Particle.TOWN_AURA, island.getCenter(), island.getRange()); + }); + }, 20, 30)); + } + + private void hideZones(User user) { + user.sendMessage("commands.admin.range.display.hiding"); + Bukkit.getScheduler().cancelTask(display.get(user)); + display.remove(user); + } + + private void drawZone(Player player, Particle particle, Location center, int range) { + // Get player Y coordinate + int playerY = player.getLocation().getBlockY() + 1; + + // Draw 3 "stages" (one line below, at and above player's y coordinate) + for (int stage = -1 ; stage <= 1 ; stage++) { + for (int i = -range ; i <= range ; i++) { + spawnParticle(player, particle, center.getBlockX() + i, playerY + stage, center.getBlockZ() + range); + spawnParticle(player, particle, center.getBlockX() + i, playerY + stage, center.getBlockZ() - range); + spawnParticle(player, particle, center.getBlockX() + range, playerY + stage, center.getBlockZ() + i); + spawnParticle(player, particle, center.getBlockX() - range, playerY + stage, center.getBlockZ() + i); + } + } + } + + private void spawnParticle(Player player, Particle particle, int x, int y, int z) { + // Check if this particle is beyond the viewing distance of the server + if (player.getLocation().toVector().distanceSquared(new Vector(x,y,z)) < (Bukkit.getServer().getViewDistance()*256*Bukkit.getServer().getViewDistance())) { + player.spawnParticle(particle, x, y, z, 1); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommand.java new file mode 100644 index 0000000..6046d28 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommand.java @@ -0,0 +1,53 @@ +package us.tastybento.bskyblock.commands.admin.range; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; + +public class AdminRangeResetCommand extends CompositeCommand { + + public AdminRangeResetCommand(CompositeCommand parent) { + super(parent, "reset"); + } + + @Override + public void setup() { + setPermission("admin.range.reset"); + setParameters("commands.admin.range.reset.parameters"); + setDescription("commands.admin.range.reset.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 1) { + // Show help + showHelp(this, user); + return false; + } + + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getPlugin().getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + + // Get island + Island island = getIslands().getIsland(getWorld(), targetUUID); + + // Reset the protection range + int range = getIWM().getIslandProtectionRange(getWorld()); + island.setProtectionRange(range); + user.sendMessage("commands.admin.range.reset.success", TextVariables.NUMBER, String.valueOf(range)); + + return true; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommand.java new file mode 100644 index 0000000..3c9ec67 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommand.java @@ -0,0 +1,75 @@ +package us.tastybento.bskyblock.commands.admin.range; + +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; + +public class AdminRangeSetCommand extends CompositeCommand { + + public AdminRangeSetCommand(CompositeCommand parent) { + super(parent, "set"); + } + + @Override + public void setup() { + setPermission("admin.range.set"); + setParameters("commands.admin.range.set.parameters"); + setDescription("commands.admin.range.set.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 2) { + // Show help + showHelp(this, user); + return false; + } + + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getPlugin().getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + + // Get new range + if (!StringUtils.isNumeric(args.get(1))) { + user.sendMessage("commands.admin.range.set.invalid-value.not-numeric", TextVariables.NUMBER, args.get(1)); + return false; + } + int range = Integer.valueOf(args.get(1)); + + // Get island + Island island = getIslands().getIsland(getWorld(), targetUUID); + + // Do some sanity checks to make sure the new protection range won't cause problems + if (range <= 1) { + user.sendMessage("commands.admin.range.set.invalid-value.too-low", TextVariables.NUMBER, args.get(1)); + return false; + } + if (range > island.getRange()) { + user.sendMessage("commands.admin.range.set.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange())); + return false; + } + if (range == island.getProtectionRange()) { + user.sendMessage("commands.admin.range.set.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1)); + return false; + } + + // Well, now it can be applied without taking any risks ! + island.setProtectionRange(range); + user.sendMessage("commands.admin.range.set.success", TextVariables.NUMBER, String.valueOf(range)); + + return true; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommand.java new file mode 100644 index 0000000..a504b17 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommand.java @@ -0,0 +1,70 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +public class AdminTeamAddCommand extends CompositeCommand { + + public AdminTeamAddCommand(CompositeCommand parent) { + super(parent, "add"); + } + + @Override + public void setup() { + setPermission("admin.team"); + setParameters("commands.admin.team.add.parameters"); + setDescription("commands.admin.team.add.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 2) { + showHelp(this, user); + return false; + } + // Get leader and target + UUID leaderUUID = getPlayers().getUUID(args.get(0)); + if (leaderUUID == null) { + user.sendMessage("general.errors.unknown-player-name", TextVariables.NAME, args.get(0)); + return false; + } + UUID targetUUID = getPlayers().getUUID(args.get(1)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player-name", TextVariables.NAME, args.get(1)); + return false; + } + if (!getIslands().hasIsland(getWorld(), leaderUUID)) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + if (getIslands().inTeam(getWorld(), leaderUUID) && !getIslands().getTeamLeader(getWorld(), leaderUUID).equals(leaderUUID)) { + user.sendMessage("commands.admin.team.add.name-not-leader", TextVariables.NAME, args.get(0)); + getIslands().getIsland(getWorld(), leaderUUID).showMembers(getPlugin(), user, getWorld()); + return false; + } + if (getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("commands.island.team.invite.errors.already-on-team"); + return false; + } + if (getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("commands.admin.team.add.name-has-island", TextVariables.NAME, args.get(1)); + return false; + } + // Success + User target = User.getInstance(targetUUID); + User leader = User.getInstance(leaderUUID); + leader.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, getPlugin().getPlayers().getName(targetUUID)); + target.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel()); + getIslands().getIsland(getWorld(), leaderUUID).addMember(targetUUID); + user.sendMessage("general.success"); + return true; + + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommand.java new file mode 100644 index 0000000..4c8bd4e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommand.java @@ -0,0 +1,58 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminTeamDisbandCommand extends CompositeCommand { + + public AdminTeamDisbandCommand(CompositeCommand parent) { + super(parent, "disband"); + } + + @Override + public void setup() { + setPermission("admin.team"); + setParameters("commands.admin.team.disband.parameters"); + setDescription("commands.admin.team.disband.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("general.errors.not-in-team"); + return false; + } + if (!getIslands().getTeamLeader(getWorld(), targetUUID).equals(targetUUID)) { + user.sendMessage("commands.admin.team.disband.use-disband-leader", "[leader]", getPlayers().getName(getIslands().getTeamLeader(getWorld(), targetUUID))); + return false; + } + // Disband team + getIslands().getMembers(getWorld(), targetUUID).forEach(m -> { + User.getInstance(m).sendMessage("commands.admin.team.disband.disbanded"); + // The leader gets to keep the island + if (!m.equals(targetUUID)) { + getIslands().setLeaveTeam(getWorld(), m); + } + }); + user.sendMessage("general.success"); + return true; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommand.java new file mode 100644 index 0000000..040f2c5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommand.java @@ -0,0 +1,57 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminTeamKickCommand extends CompositeCommand { + + public AdminTeamKickCommand(CompositeCommand parent) { + super(parent, "kick"); + + } + + @Override + public void setup() { + setPermission("admin.team"); + setParameters("commands.admin.team.kick.parameters"); + setDescription("commands.admin.team.kick.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("general.errors.not-in-team"); + return false; + } + if (getIslands().getTeamLeader(getWorld(), targetUUID).equals(targetUUID)) { + user.sendMessage("commands.admin.team.kick.cannot-kick-leader"); + getIslands().getIsland(getWorld(), targetUUID).showMembers(getPlugin(), user, getWorld()); + return false; + } + User.getInstance(targetUUID).sendMessage("commands.admin.team.kick.admin-kicked"); + getIslands().removePlayer(getWorld(), targetUUID); + user.sendMessage("general.success"); + return true; + + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommand.java new file mode 100644 index 0000000..77733a1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommand.java @@ -0,0 +1,52 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class AdminTeamMakeLeaderCommand extends CompositeCommand { + + public AdminTeamMakeLeaderCommand(CompositeCommand parent) { + super(parent, "makeleader"); + } + + @Override + public void setup() { + setPermission("admin.team"); + setParameters("commands.admin.team.makeleader.parameters"); + setDescription("commands.admin.team.makeleader.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().inTeam(getWorld(), targetUUID)) { + user.sendMessage("general.errors.not-in-team"); + return false; + } + if (getIslands().getTeamLeader(getWorld(), targetUUID).equals(targetUUID)) { + user.sendMessage("commands.admin.team.makeleader.already-leader"); + return false; + } + // Make new leader + getIslands().makeLeader(getWorld(), user, targetUUID, getPermissionPrefix()); + user.sendMessage("general.success"); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/CustomIslandMultiHomeHelp.java b/src/main/java/us/tastybento/bskyblock/commands/island/CustomIslandMultiHomeHelp.java new file mode 100644 index 0000000..2eedd96 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/CustomIslandMultiHomeHelp.java @@ -0,0 +1,59 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +/** + * This is a custom help for the /island go and /island sethome commands. It overrides the default help sub command. + * The number of homes can change depending on the player's permissions and config.yml settings. + * This is an example of a custom help as much as anything. + * + * @author tastybento + * + */ +public class CustomIslandMultiHomeHelp extends CompositeCommand { + + public CustomIslandMultiHomeHelp(CompositeCommand parent) { + super(parent, "help"); + } + + @Override + public void setup() { + setOnlyPlayer(true); + // Inherit parameters from the respective parent class - in this case, only /island go and /island sethome + setParameters(parent.getParameters()); + setDescription(parent.getDescription()); + inheritPermission(); + } + + @Override + public boolean execute(User user, String label, List args) { + // This will only be shown if it is for a player + if (user.isPlayer()) { + // Get elements + String usage = parent.getUsage().isEmpty() ? "" : user.getTranslation(parent.getUsage()); + String params = ""; + String desc = getDescription().isEmpty() ? "" : user.getTranslation(getDescription()); + + showPrettyHelp(user, usage, params, desc); + return true; + } + return false; + } + + private void showPrettyHelp(User user, String usage, String params, String desc) { + // Player. Check perms + if (user.hasPermission(getPermission())) { + int maxHomes = Util.getPermValue(user.getPlayer(), getPermissionPrefix() + "island.maxhomes", getIWM().getMaxHomes(getWorld())); + if (maxHomes > 1) { + params = getParameters().isEmpty() ? "" : user.getTranslation(getParameters()); + } + user.sendMessage("commands.help.syntax", "[usage]", usage, "[parameters]", params, "[description]", desc); + } + } + +} + diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandAboutCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandAboutCommand.java new file mode 100644 index 0000000..1126716 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandAboutCommand.java @@ -0,0 +1,50 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class IslandAboutCommand extends CompositeCommand { + + /** + * About + * @param islandCommand - parent command + */ + public IslandAboutCommand(CompositeCommand islandCommand) { + super(islandCommand, "about", "ab"); + } + + @Override + public void setup() { + setDescription("commands.island.about.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + user.sendRawMessage("About " + BSkyBlock.getInstance().getDescription().getName() + " v" + BSkyBlock.getInstance().getDescription().getVersion() + ":"); + user.sendRawMessage("Copyright (c) 2017 - 2018 Tastybento, Poslovitch"); + user.sendRawMessage("All rights reserved."); + user.sendRawMessage(""); + user.sendRawMessage("Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:"); + + user.sendRawMessage(" * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer."); + user.sendRawMessage(" * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution."); + user.sendRawMessage(" * Neither the name of the BSkyBlock Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission."); + + user.sendRawMessage("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" " + + "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE " + + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE " + + "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE " + + "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR " + + "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF " + + "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS " + + "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN " + + "CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) " + + "ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE " + + "POSSIBILITY OF SUCH DAMAGE."); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanCommand.java new file mode 100644 index 0000000..2618a6c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanCommand.java @@ -0,0 +1,109 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class IslandBanCommand extends CompositeCommand { + + public IslandBanCommand(CompositeCommand islandCommand) { + super(islandCommand, "ban"); + } + + @Override + public void setup() { + setPermission("island.ban"); + setOnlyPlayer(true); + setParameters("commands.island.ban.parameters"); + setDescription("commands.island.ban.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 1) { + // Show help + showHelp(this, user); + return false; + } + UUID playerUUID = user.getUniqueId(); + // Player issuing the command must have an island + if (!getIslands().hasIsland(getWorld(), playerUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().isOwner(getWorld(), playerUUID)) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + // Player cannot ban themselves + if (playerUUID.equals(targetUUID)) { + user.sendMessage("commands.island.ban.cannot-ban-yourself"); + return false; + } + if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) { + user.sendMessage("commands.island.ban.cannot-ban-member"); + return false; + } + if (getIslands().getIsland(getWorld(), playerUUID).isBanned(targetUUID)) { + user.sendMessage("commands.island.ban.player-already-banned"); + return false; + } + User target = User.getInstance(targetUUID); + // Cannot ban ops + if (target.isOp()) { + user.sendMessage("commands.island.ban.cannot-ban"); + return false; + } + // Finished error checking - start the banning + return ban(user, target); + } + + private boolean ban(User user, User targetUser) { + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + if (island.addToBanList(targetUser.getUniqueId())) { + user.sendMessage("general.success"); + targetUser.sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, user.getName()); + // If the player is online, has an island and on the banned island, move them home immediately + if (targetUser.isOnline() && getIslands().hasIsland(getWorld(), targetUser.getUniqueId()) && island.onIsland(targetUser.getLocation())) { + getIslands().homeTeleport(getWorld(), targetUser.getPlayer()); + island.getWorld().playSound(targetUser.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F); + } + return true; + } + // Banning was blocked, maybe due to an event cancellation. Fail silently. + return false; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + List options = Bukkit.getOnlinePlayers().stream() + .filter(p -> !p.getUniqueId().equals(user.getUniqueId())) + .filter(p -> !island.isBanned(p.getUniqueId())) + .filter(p -> user.getPlayer().canSee(p)) + .map(Player::getName).collect(Collectors.toList()); + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + return Optional.of(Util.tabLimit(options, lastArg)); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommand.java new file mode 100644 index 0000000..42454d3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommand.java @@ -0,0 +1,70 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; + +public class IslandBanlistCommand extends CompositeCommand { + + public IslandBanlistCommand(CompositeCommand islandCommand) { + super(islandCommand, "banlist"); + } + + @Override + public void setup() { + setPermission("island.ban"); + setOnlyPlayer(true); + setDescription("commands.island.banlist.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!args.isEmpty()) { + // Show help + showHelp(this, user); + return false; + } + // Player issuing the command must have an island + if (!getIslands().hasIsland(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-island"); + return false; + } + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + // Show all the players banned on the island + if (island.getBanned().isEmpty()) { + user.sendMessage("commands.island.banlist.noone"); + return true; + } + // Title + user.sendMessage("commands.island.banlist.the-following"); + // Create a nicely formatted list + List names = island.getBanned().stream().map(u -> getPlayers().getName(u)).sorted().collect(Collectors.toList()); + List lines = new ArrayList<>(); + StringBuilder line = new StringBuilder(); + // Put the names into lines of no more than 40 characters long, separated by commas + names.forEach(n -> { + if (line.length() + n.length() < 41) { + line.append(n); + } else { + lines.add(line.toString().trim()); + line.setLength(0); + line.append(n); + } + line.append(", "); + }); + // Remove trailing comma + line.setLength(line.length() - 2); + // Add the final line if it is not empty + if (line.length() > 0) { + lines.add(line.toString()); + } + // Send the strings + lines.forEach(l -> user.sendMessage("commands.island.banlist.names", "[line]", l)); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java new file mode 100644 index 0000000..e4f58d7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java @@ -0,0 +1,60 @@ +package us.tastybento.bskyblock.commands.island; + +import java.io.IOException; +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.island.NewIsland; + +/** + * /island create - Create an island. + * + * @author Tastybento + */ +public class IslandCreateCommand extends CompositeCommand { + + /** + * Command to create an island + * @param islandCommand - parent command + */ + public IslandCreateCommand(CompositeCommand islandCommand) { + super(islandCommand, "create"); + } + + @Override + public void setup() { + setPermission("island.create"); + setOnlyPlayer(true); + setDescription("commands.island.create.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + if (getIslands().hasIsland(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.already-have-island"); + return false; + } + if (getIslands().inTeam(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.already-have-island"); + return false; + } + user.sendMessage("commands.island.create.creating-island"); + try { + NewIsland.builder() + .player(user) + .world(getWorld()) + .reason(Reason.CREATE) + .build(); + return true; + } catch (IOException e) { + getPlugin().logError("Could not create island for player. " + e.getMessage()); + user.sendMessage("commands.island.create.unable-create-island"); + return false; + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandGoCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandGoCommand.java new file mode 100644 index 0000000..c71e243 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandGoCommand.java @@ -0,0 +1,54 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; + +import org.apache.commons.lang.math.NumberUtils; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + */ +public class IslandGoCommand extends CompositeCommand { + + public IslandGoCommand(CompositeCommand islandCommand) { + super(islandCommand, "go", "home", "h"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CompositeCommand#setup() + */ + @Override + public void setup() { + setPermission("island.home"); + setOnlyPlayer(true); + setDescription("commands.island.go.description"); + new CustomIslandMultiHomeHelp(this); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + if (getIslands().getIsland(getWorld(), user.getUniqueId()) == null) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!args.isEmpty() && NumberUtils.isDigits(args.get(0))) { + int homeValue = Integer.valueOf(args.get(0)); + int maxHomes = Util.getPermValue(user.getPlayer(), "island.maxhomes", getIWM().getMaxHomes(getWorld())); + if (homeValue > 1 && homeValue <= maxHomes) { + getIslands().homeTeleport(getWorld(), user.getPlayer(), homeValue); + user.sendMessage("commands.island.go.tip", TextVariables.LABEL, getTopLabel()); + return true; + } + } + getIslands().homeTeleport(getWorld(), user.getPlayer()); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandInfoCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandInfoCommand.java new file mode 100644 index 0000000..7c5ed93 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandInfoCommand.java @@ -0,0 +1,4 @@ +package us.tastybento.bskyblock.commands.island; + +public class IslandInfoCommand { +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandLanguageCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandLanguageCommand.java new file mode 100644 index 0000000..33ba33a --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandLanguageCommand.java @@ -0,0 +1,36 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.panels.LanguagePanel; + +/** + * @author Poslovitch + */ +public class IslandLanguageCommand extends CompositeCommand { + + public IslandLanguageCommand(CompositeCommand islandCommand) { + super(islandCommand, "language", "lang"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CompositeCommand#setup() + */ + @Override + public void setup() { + setPermission("island.language"); + setOnlyPlayer(true); + setDescription("commands.island.language.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + LanguagePanel.openPanel(user); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java new file mode 100644 index 0000000..b159fa3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java @@ -0,0 +1,120 @@ +package us.tastybento.bskyblock.commands.island; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.island.NewIsland; + +public class IslandResetCommand extends CompositeCommand { + + private Map cooldown; + + public IslandResetCommand(CompositeCommand islandCommand) { + super(islandCommand, "reset", "restart"); + } + + @Override + public void setup() { + cooldown = new HashMap<>(); + setPermission("island.create"); + setOnlyPlayer(true); + setDescription("commands.island.reset.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + // Check cooldown + if (getSettings().getResetWait() > 0 && onRestartWaitTime(user) > 0 && !user.isOp()) { + user.sendMessage("general.errors.you-must-wait", TextVariables.NUMBER, String.valueOf(onRestartWaitTime(user))); + return false; + } + if (!getIslands().hasIsland(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().isOwner(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.not-leader"); + return false; + } + if (getIslands().inTeam(getWorld(), user.getUniqueId())) { + user.sendMessage("commands.island.reset.must-remove-members"); + return false; + } + if (getIWM().getResetLimit(getWorld()) >= 0 ) { + int resetsLeft = getIWM().getResetLimit(getWorld()) - getPlayers().getResets(getWorld(), user.getUniqueId()); + if (resetsLeft <= 0) { + user.sendMessage("commands.island.reset.none-left"); + return false; + } else { + // Notify how many resets are left + user.sendMessage("commands.island.reset.resets-left", TextVariables.NUMBER, String.valueOf(resetsLeft)); + } + } + // Request confirmation + if (getSettings().isResetConfirmation()) { + this.askConfirmation(user, () -> resetIsland(user)); + return true; + } else { + return resetIsland(user); + } + + } + + private boolean resetIsland(User user) { + // Reset the island + Player player = user.getPlayer(); + player.setGameMode(GameMode.SPECTATOR); + // Get the player's old island + Island oldIsland = getIslands().getIsland(getWorld(), player.getUniqueId()); + // Remove them from this island (it still exists and will be deleted later) + getIslands().removePlayer(getWorld(), player.getUniqueId()); + // Remove money inventory etc. + if (getIWM().isOnLeaveResetEnderChest(getWorld())) { + user.getPlayer().getEnderChest().clear(); + } + if (getIWM().isOnLeaveResetInventory(getWorld())) { + user.getPlayer().getInventory().clear(); + } + if (getSettings().isUseEconomy() && getIWM().isOnLeaveResetMoney(getWorld())) { + // TODO: needs Vault + } + // Add a reset + getPlayers().addReset(getWorld(), user.getUniqueId()); + // Create new island and then delete the old one + try { + NewIsland.builder() + .player(user) + .reason(Reason.RESET) + .oldIsland(oldIsland) + .build(); + } catch (IOException e) { + getPlugin().logError("Could not create island for player. " + e.getMessage()); + user.sendMessage("commands.island.create.unable-create-island"); + return false; + } + setCooldown(user); + return true; + } + + private int onRestartWaitTime(User user) { + if (!cooldown.containsKey(user.getUniqueId())) { + return 0; + } + return (int) (System.currentTimeMillis() - cooldown.get(user.getUniqueId()) / 1000); + } + + private void setCooldown(User user) { + cooldown.put(user.getUniqueId(), System.currentTimeMillis() + (getSettings().getResetLimit() * 1000L)); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetnameCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetnameCommand.java new file mode 100644 index 0000000..640e62f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetnameCommand.java @@ -0,0 +1,49 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author tastybento + * + */ +public class IslandResetnameCommand extends CompositeCommand { + + public IslandResetnameCommand(CompositeCommand islandCommand) { + super(islandCommand, "resetname"); + } + + @Override + public void setup() { + setPermission("island.name"); + setOnlyPlayer(true); + setDescription("commands.island.resetname.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + + if (!getIslands().hasIsland(getWorld(), playerUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + + if (!getIslands().isOwner(getWorld(), playerUUID)) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // Resets the island name + getIslands().getIsland(getWorld(), playerUUID).setName(null); + + user.sendMessage("general.success"); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandSethomeCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSethomeCommand.java new file mode 100644 index 0000000..31bf875 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSethomeCommand.java @@ -0,0 +1,68 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +public class IslandSethomeCommand extends CompositeCommand { + + public IslandSethomeCommand(CompositeCommand islandCommand) { + super(islandCommand, "sethome"); + } + + @Override + public void setup() { + setPermission("island.sethome"); + setOnlyPlayer(true); + setDescription("commands.island.sethome.description"); + new CustomIslandMultiHomeHelp(this); + } + + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + // Check island + if (getPlugin().getIslands().getIsland(getWorld(), user.getUniqueId()) == null) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getPlugin().getIslands().userIsOnIsland(getWorld(), user)) { + user.sendMessage("commands.island.sethome.must-be-on-your-island"); + return false; + } + if (args.isEmpty()) { + // island sethome + getPlugin().getPlayers().setHomeLocation(playerUUID, user.getLocation()); + user.sendMessage("commands.island.sethome.home-set"); + } else { + // Dynamic home sizes with permissions + int maxHomes = Util.getPermValue(user.getPlayer(), "island.maxhomes", getSettings().getMaxHomes()); + if (maxHomes > 1) { + // Check the number given is a number + int number; + try { + number = Integer.valueOf(args.get(0)); + if (number < 1 || number > maxHomes) { + user.sendMessage("commands.island.sethome.num-homes", TextVariables.NUMBER, String.valueOf(maxHomes)); + return false; + } else { + getPlugin().getPlayers().setHomeLocation(user, user.getLocation(), number); + user.sendMessage("commands.island.sethome.home-set"); + } + } catch (Exception e) { + user.sendMessage("commands.island.sethome.num-homes", TextVariables.NUMBER, String.valueOf(maxHomes)); + return false; + } + } else { + user.sendMessage("general.errors.no-permission"); + return false; + } + } + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandSetnameCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSetnameCommand.java new file mode 100644 index 0000000..af027e5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSetnameCommand.java @@ -0,0 +1,77 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.ChatColor; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author tastybento + * + */ +public class IslandSetnameCommand extends CompositeCommand { + + public IslandSetnameCommand(CompositeCommand islandCommand) { + super(islandCommand, "setname"); + } + + @Override + public void setup() { + setPermission("island.name"); + setOnlyPlayer(true); + setParameters("commands.island.setname.parameters"); + setDescription("commands.island.setname.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + + if (!getIslands().hasIsland(getWorld(), playerUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + + if (!getIslands().isOwner(getWorld(), playerUUID)) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // Explain command + if (args.isEmpty()) { + showHelp(this, user); + return false; + } + + // Naming the island - join all the arguments with spaces. + String name = args.stream().collect(Collectors.joining( " " )); + + // Check if the name isn't too short or too long + if (name.length() < getSettings().getNameMinLength()) { + user.sendMessage("commands.island.setname.too-short", TextVariables.NUMBER, String.valueOf(getSettings().getNameMinLength())); + return false; + } + if (name.length() > getSettings().getNameMaxLength()) { + user.sendMessage("commands.island.setname.too-long", TextVariables.NUMBER, String.valueOf(getSettings().getNameMaxLength())); + return false; + } + + // Set the name + if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + ".island.name.format")) { + getIslands().getIsland(getWorld(), playerUUID).setName(ChatColor.translateAlternateColorCodes('&', name)); + } else { + getIslands().getIsland(getWorld(), playerUUID).setName(name); + } + + user.sendMessage("general.success"); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandSettingsCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSettingsCommand.java new file mode 100644 index 0000000..efb3fc7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSettingsCommand.java @@ -0,0 +1,44 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.panels.SettingsPanel; +import us.tastybento.bskyblock.util.Util; + +/** + * @author Poslovitch + */ +public class IslandSettingsCommand extends CompositeCommand { + + public IslandSettingsCommand(CompositeCommand islandCommand) { + super(islandCommand, "settings", "flags"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CompositeCommand#setup() + */ + @Override + public void setup() { + setPermission("island.settings"); + setOnlyPlayer(true); + setDescription("commands.island.settings.description"); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.api.commands.CommandArgument#execute(org.bukkit.command.CommandSender, java.lang.String[]) + */ + @Override + public boolean execute(User user, String label, List args) { + // Settings are only shown if you are in the right world + if (Util.getWorld(user.getWorld()).equals(getWorld())) { + SettingsPanel.openPanel(getPlugin(), user, Flag.Type.PROTECTION, getWorld()); //TODO keep track of history? + return true; + } else { + user.sendMessage("general.errors.wrong-world"); + return false; + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandSpawnCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSpawnCommand.java new file mode 100644 index 0000000..f5fa1d3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandSpawnCommand.java @@ -0,0 +1,4 @@ +package us.tastybento.bskyblock.commands.island; + +public class IslandSpawnCommand { +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommand.java new file mode 100644 index 0000000..e77152d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommand.java @@ -0,0 +1,82 @@ +package us.tastybento.bskyblock.commands.island; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class IslandUnbanCommand extends CompositeCommand { + + public IslandUnbanCommand(CompositeCommand islandCommand) { + super(islandCommand, "unban"); + } + + @Override + public void setup() { + setPermission("island.ban"); + setOnlyPlayer(true); + setParameters("commands.island.unban.parameters"); + setDescription("commands.island.unban.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (args.size() != 1) { + // Show help + showHelp(this, user); + return false; + } + UUID playerUUID = user.getUniqueId(); + // Player issuing the command must have an island + if (!getIslands().hasIsland(getWorld(), playerUUID)) { + user.sendMessage("general.errors.no-island"); + return false; + } + if (!getIslands().isOwner(getWorld(), playerUUID)) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // Get target player + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + // Player cannot unban themselves + if (playerUUID.equals(targetUUID)) { + user.sendMessage("commands.island.unban.cannot-unban-yourself"); + return false; + } + if (!getIslands().getIsland(getWorld(), playerUUID).isBanned(targetUUID)) { + user.sendMessage("commands.island.unban.player-not-banned"); + return false; + } + // Finished error checking - start the unbanning + User targetUser = User.getInstance(targetUUID); + return unban(user, targetUser); + } + + private boolean unban(User user, User targetUser) { + if (getIslands().getIsland(getWorld(), user.getUniqueId()).removeFromBanList(targetUser.getUniqueId())) { + user.sendMessage("general.success"); + targetUser.sendMessage("commands.island.unban.you-are-unbanned", TextVariables.NAME, user.getName()); + return true; + } + // Unbanning was blocked, maybe due to an event cancellation. Fail silently. + return false; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + List options = island.getBanned().stream().map(getPlayers()::getName).collect(Collectors.toList()); + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + return Optional.of(Util.tabLimit(options, lastArg)); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamCommand.java new file mode 100644 index 0000000..0eba71c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamCommand.java @@ -0,0 +1,83 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +public class IslandTeamCommand extends CompositeCommand { + + private IslandTeamInviteCommand inviteCommand; + + public IslandTeamCommand(CompositeCommand parent) { + super(parent, "team"); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setDescription("commands.island.team.description"); + // Register commands + inviteCommand = new IslandTeamInviteCommand(this); + new IslandTeamLeaveCommand(this); + new IslandTeamSetownerCommand(this); + new IslandTeamKickCommand(this); + new IslandTeamInviteAcceptCommand(this); + new IslandTeamInviteRejectCommand(this); + } + + @Override + public boolean execute(User user, String label, List args) { + // Player issuing the command must have an island + UUID teamLeaderUUID = getTeamLeader(getWorld(), user); + if (teamLeaderUUID == null) { + user.sendMessage("general.errors.no-island"); + return false; + } + + UUID playerUUID = user.getUniqueId(); + // Fire event so add-ons can run commands, etc. + if (fireEvent(user)) { + // Cancelled + return false; + } + Set teamMembers = getMembers(getWorld(), user); + if (teamLeaderUUID.equals(playerUUID)) { + int maxSize = inviteCommand.getMaxTeamSize(user); + if (teamMembers.size() < maxSize) { + user.sendMessage("commands.island.team.invite.you-can-invite", TextVariables.NUMBER, String.valueOf(maxSize - teamMembers.size())); + } else { + user.sendMessage("commands.island.team.invite.errors.island-is-full"); + } + } + // Show members of island + getIslands().getIsland(getWorld(), playerUUID).showMembers(getPlugin(), user, getWorld()); + return true; + } + + + private boolean fireEvent(User user) { + IslandBaseEvent event = TeamEvent.builder() + .island(getIslands() + .getIsland(getWorld(), user.getUniqueId())) + .reason(TeamEvent.Reason.INFO) + .involvedPlayer(user.getUniqueId()) + .build(); + getPlugin().getServer().getPluginManager().callEvent(event); + return event.isCancelled(); + } + + /** + * @return the inviteCommand + */ + public IslandTeamInviteCommand getInviteCommand() { + return inviteCommand; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteAcceptCommand.java new file mode 100644 index 0000000..b1883a1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -0,0 +1,113 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.GameMode; +import org.bukkit.Location; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; + +public class IslandTeamInviteAcceptCommand extends CompositeCommand { + + private IslandTeamCommand itc; + + public IslandTeamInviteAcceptCommand(IslandTeamCommand islandTeamCommand) { + super(islandTeamCommand, "accept"); + this.itc = islandTeamCommand; + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setDescription("commands.island.team.invite.accept.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + + UUID playerUUID = user.getUniqueId(); + // Check if player has been invited + if (!itc.getInviteCommand().getInviteList().containsKey(playerUUID)) { + user.sendMessage("commands.island.team.invite.errors.none-invited-you"); + return false; + } + // Check if player is already in a team + if (getIslands().inTeam(getWorld(), playerUUID)) { + user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team"); + return false; + } + // Get the team leader + UUID prospectiveTeamLeaderUUID = itc.getInviteCommand().getInviteList().get(playerUUID); + if (!getIslands().hasIsland(getWorld(), prospectiveTeamLeaderUUID)) { + user.sendMessage("commands.island.team.invite.errors.invalid-invite"); + itc.getInviteCommand().getInviteList().remove(playerUUID); + return false; + } + // Fire event so add-ons can run commands, etc. + IslandBaseEvent event = TeamEvent.builder() + .island(getIslands() + .getIsland(getWorld(), prospectiveTeamLeaderUUID)) + .reason(TeamEvent.Reason.JOIN) + .involvedPlayer(playerUUID) + .build(); + getPlugin().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return true; + } + // Remove the invite + itc.getInviteCommand().getInviteList().remove(playerUUID); + // Put player into Spectator mode + user.setGameMode(GameMode.SPECTATOR); + // Get the player's island - may be null if the player has no island + Island island = getIslands().getIsland(getWorld(), playerUUID); + // Get the team's island + Island teamIsland = getIslands().getIsland(getWorld(), prospectiveTeamLeaderUUID); + // Clear the player's inventory + user.getInventory().clear(); + // Move player to team's island + User prospectiveTeamLeader = User.getInstance(prospectiveTeamLeaderUUID); + Location newHome = getIslands().getSafeHomeLocation(getWorld(), prospectiveTeamLeader, 1); + user.teleport(newHome); + // Remove player as owner of the old island + getIslands().removePlayer(getWorld(), playerUUID); + // Remove money inventory etc. for leaving + if (getIWM().isOnLeaveResetEnderChest(getWorld()) || getIWM().isOnJoinResetEnderChest(getWorld())) { + user.getPlayer().getEnderChest().clear(); + } + if (getIWM().isOnLeaveResetInventory(getWorld()) || getIWM().isOnJoinResetInventory(getWorld())) { + user.getPlayer().getInventory().clear(); + } + if (getSettings().isUseEconomy() && (getIWM().isOnLeaveResetMoney(getWorld()) || getIWM().isOnJoinResetMoney(getWorld()))) { + // TODO: needs Vault + } + // Add the player as a team member of the new island + getIslands().setJoinTeam(teamIsland, playerUUID); + // Set the player's home + getPlayers().setHomeLocation(playerUUID, user.getLocation()); + // Delete the old island + getIslands().deleteIsland(island, true); + // TODO Set the cooldown + // Reset deaths + if (getSettings().isTeamJoinDeathReset()) { + getPlayers().setDeaths(playerUUID, 0); + } + // Put player back into normal mode + user.setGameMode(getIWM().getDefaultGameMode(getWorld())); + + user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel()); + User inviter = User.getInstance(itc.getInviteCommand().getInviteList().get(playerUUID)); + if (inviter != null) { + inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName()); + } + getIslands().save(false); + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommand.java new file mode 100644 index 0000000..5f3840c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommand.java @@ -0,0 +1,159 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.OfflinePlayer; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +public class IslandTeamInviteCommand extends CompositeCommand { + + private BiMap inviteList; + + public IslandTeamInviteCommand(CompositeCommand islandCommand) { + super(islandCommand, "invite"); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setDescription("commands.island.team.invite.description"); + inviteList = HashBiMap.create(); + } + + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + // Player issuing the command must have an island + if (!getIslands().hasIsland(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-island"); + return false; + } + UUID teamLeaderUUID = getTeamLeader(getWorld(), user); + if (!(teamLeaderUUID.equals(playerUUID))) { + user.sendMessage("general.errors.not-leader"); + return false; + } + if (args.isEmpty() || args.size() > 1) { + // Invite label with no name, i.e., /island invite - tells the player who has invited them so far + if (inviteList.containsKey(playerUUID)) { + OfflinePlayer inviter = getPlugin().getServer().getOfflinePlayer(inviteList.get(playerUUID)); + user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, inviter.getName()); + return true; + } + // Show help + showHelp(this, user); + return false; + } else { + // Only online players can be invited + UUID invitedPlayerUUID = getPlayers().getUUID(args.get(0)); + if (invitedPlayerUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + User invitedPlayer = User.getInstance(invitedPlayerUUID); + if (!invitedPlayer.isOnline()) { + user.sendMessage("general.errors.offline-player"); + return false; + } + // Player cannot invite themselves + if (playerUUID.equals(invitedPlayerUUID)) { + user.sendMessage("commands.island.team.invite.cannot-invite-self"); + return false; + } + // Check if this player can be invited to this island, or + // whether they are still on cooldown + long time = getPlayers().getInviteCoolDownTime(invitedPlayerUUID, getIslands().getIslandLocation(getWorld(), playerUUID)); + if (time > 0 && !user.isOp()) { + user.sendMessage("commands.island.team.invite.cooldown", TextVariables.NUMBER, String.valueOf(time)); + return false; + } + // Player cannot invite someone already on a team + if (getIslands().inTeam(getWorld(), invitedPlayerUUID)) { + user.sendMessage("commands.island.team.invite.already-on-team"); + return false; + } + return invite(user,invitedPlayer); + } + } + + private boolean invite(User user, User invitedPlayer) { + Set teamMembers = getMembers(getWorld(), user); + // Check if player has space on their team + int maxSize = getMaxTeamSize(user); + if (teamMembers.size() < maxSize) { + // If that player already has an invite out then retract it. + // Players can only have one invite one at a time - interesting + if (inviteList.containsValue(user.getUniqueId())) { + inviteList.inverse().remove(user.getUniqueId()); + user.sendMessage("commands.island.team.invite.removing-invite"); + } + // Fire event so add-ons can run commands, etc. + IslandBaseEvent event = TeamEvent.builder() + .island(getIslands().getIsland(getWorld(), user.getUniqueId())) + .reason(TeamEvent.Reason.INVITE) + .involvedPlayer(invitedPlayer.getUniqueId()) + .build(); + getPlugin().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // Put the invited player (key) onto the list with inviter (value) + // If someone else has invited a player, then this invite will overwrite the previous invite! + inviteList.put(invitedPlayer.getUniqueId(), user.getUniqueId()); + user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, invitedPlayer.getName()); + // Send message to online player + invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName()); + invitedPlayer.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getLabel()); + if (getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) { + invitedPlayer.sendMessage("commands.island.team.invite.you-will-lose-your-island"); + } + return true; + } else { + user.sendMessage("commands.island.team.invite.errors.island-is-full"); + return false; + } + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } + + /** + * Order is Invited, Inviter + * @return the inviteList + */ + public BiMap getInviteList() { + return inviteList; + } + + /** + * Gets the maximum team size for this player in this game based on the permission or the world's setting + * @param user - user + * @return max team size of user + */ + public int getMaxTeamSize(User user) { + return Util.getPermValue(user.getPlayer(), getPermissionPrefix() + "team.maxsize.", getIWM().getMaxTeamSize(getWorld())); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteRejectCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteRejectCommand.java new file mode 100644 index 0000000..e27fae9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteRejectCommand.java @@ -0,0 +1,60 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +public class IslandTeamInviteRejectCommand extends CompositeCommand { + + private IslandTeamCommand itc; + + public IslandTeamInviteRejectCommand(IslandTeamCommand islandTeamCommand) { + super(islandTeamCommand, "reject"); + this.itc = islandTeamCommand; + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setDescription("commands.island.team.invite.reject.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + // Reject /island reject + if (itc.getInviteCommand().getInviteList().containsKey(playerUUID)) { + // Fire event so add-ons can run commands, etc. + IslandBaseEvent event = TeamEvent.builder() + .island(getIslands() + .getIsland(getWorld(), itc.getInviteCommand().getInviteList().get(playerUUID))) + .reason(TeamEvent.Reason.REJECT) + .involvedPlayer(playerUUID) + .build(); + getPlugin().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + + // Remove this player from the global invite list + itc.getInviteCommand().getInviteList().remove(user.getUniqueId()); + user.sendMessage("commands.island.team.invite.reject.you-rejected-invite"); + + User inviter = User.getInstance(itc.getInviteCommand().getInviteList().get(playerUUID)); + inviter.sendMessage("commands.island.team.invite.reject.name-rejected-your-invite", TextVariables.NAME, user.getName()); + } else { + // Someone typed /island reject and had not been invited + // TODO: make the error nicer if there are invites in other worlds + user.sendMessage("commands.island.team.invite.errors.none-invited-you"); + return false; + } + return true; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommand.java new file mode 100644 index 0000000..443c67d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommand.java @@ -0,0 +1,92 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.scheduler.BukkitRunnable; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.user.User; + +public class IslandTeamKickCommand extends CompositeCommand { + + Set kickSet; + + public IslandTeamKickCommand(CompositeCommand islandTeamCommand) { + super(islandTeamCommand, "kick"); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setParameters("commands.island.team.kick.parameters"); + setDescription("commands.island.team.kick.description"); + kickSet = new HashSet<>(); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!getIslands().inTeam(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-team"); + return false; + } + if (!getTeamLeader(getWorld(), user).equals(user.getUniqueId())) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (targetUUID.equals(user.getUniqueId())) { + user.sendMessage("commands.island.kick.cannot-kick"); + return false; + } + if (!getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) { + user.sendMessage("general.errors.not-in-team"); + return false; + } + if (!getSettings().isKickConfirmation() || kickSet.contains(targetUUID)) { + kickSet.remove(targetUUID); + User.getInstance(targetUUID).sendMessage("commands.island.team.kick.leader-kicked"); + getIslands().removePlayer(getWorld(), targetUUID); + // Remove money inventory etc. + if (getIWM().isOnLeaveResetEnderChest(getWorld())) { + user.getPlayer().getEnderChest().clear(); + } + if (getIWM().isOnLeaveResetInventory(getWorld())) { + user.getPlayer().getInventory().clear(); + } + if (getSettings().isUseEconomy() && getIWM().isOnLeaveResetMoney(getWorld())) { + // TODO: needs Vault + } + user.sendMessage("general.success"); + return true; + } else { + user.sendMessage("commands.island.team.kick.type-again"); + kickSet.add(targetUUID); + new BukkitRunnable() { + + @Override + public void run() { + if (kickSet.contains(targetUUID)) { + kickSet.remove(targetUUID); + user.sendMessage("general.errors.command-cancelled"); + } + }}.runTaskLater(getPlugin(), getSettings().getKickWait() * 20); + return false; + } + } + + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommand.java new file mode 100644 index 0000000..e2f5500 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommand.java @@ -0,0 +1,61 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.List; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +public class IslandTeamLeaveCommand extends CompositeCommand { + + public IslandTeamLeaveCommand(CompositeCommand islandTeamCommand) { + super(islandTeamCommand, "leave"); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setDescription("commands.island.team.leave.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!getIslands().inTeam(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-team"); + return false; + } + if (getIslands().hasIsland(getWorld(), user.getUniqueId())) { + user.sendMessage("commands.island.team.leave.cannot-leave"); + return false; + } + if (!getSettings().isLeaveConfirmation()) { + leave(user); + return true; + } else { + this.askConfirmation(user, () -> leave(user)); + return false; + } + } + + private void leave(User user) { + UUID leaderUUID = getIslands().getTeamLeader(getWorld(), user.getUniqueId()); + if (leaderUUID != null) { + User.getInstance(leaderUUID).sendMessage("commands.island.team.leave.left-your-island", TextVariables.NAME, user.getName()); + } + getIslands().setLeaveTeam(getWorld(), user.getUniqueId()); + // Remove money inventory etc. + if (getIWM().isOnLeaveResetEnderChest(getWorld())) { + user.getPlayer().getEnderChest().clear(); + } + if (getIWM().isOnLeaveResetInventory(getWorld())) { + user.getPlayer().getInventory().clear(); + } + if (getSettings().isUseEconomy() && getIWM().isOnLeaveResetMoney(getWorld())) { + // TODO: needs Vault + } + user.sendMessage("general.success"); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamPromoteCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamPromoteCommand.java new file mode 100644 index 0000000..a66eea8 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamPromoteCommand.java @@ -0,0 +1,85 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.List; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +public class IslandTeamPromoteCommand extends CompositeCommand { + + public IslandTeamPromoteCommand(CompositeCommand islandTeamCommand, String string) { + super(islandTeamCommand, string); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + if (this.getLabel().equals("promote")) { + setParameters("commands.island.team.promote.parameters"); + setDescription("commands.island.team.promote.description"); + } else { + setParameters("commands.island.team.demote.parameters"); + setDescription("commands.island.team.demote.description"); + } + } + + @Override + public boolean execute(User user, String label, List args) { + if (!getIslands().inTeam(getWorld(), user.getUniqueId())) { + user.sendMessage("general.errors.no-team"); + return true; + } + if (!getTeamLeader(getWorld(), user).equals(user.getUniqueId())) { + user.sendMessage("general.errors.not-leader"); + return true; + } + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + User target = getPlayers().getUser(args.get(0)); + if (target == null) { + user.sendMessage("general.errors.unknown-player"); + return true; + } + if (!inTeam(getWorld(), target) || !getTeamLeader(getWorld(), user).equals(getTeamLeader(getWorld(), target))) { + user.sendMessage("general.errors.not-in-team"); + return true; + } + + return change(user, target); + } + + private boolean change(User user, User target) { + int currentRank = getIslands().getIsland(getWorld(), user.getUniqueId()).getRank(target); + if (this.getLabel().equals("promote")) { + int nextRank = getPlugin().getRanksManager().getRankUpValue(currentRank); + if (nextRank > currentRank) { + getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, nextRank); + String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(nextRank)); + user.sendMessage("commands.island.team.promote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName); + return true; + } else { + user.sendMessage("commands.island.team.promote.failure"); + return false; + } + } else { + // Demote + int prevRank = getPlugin().getRanksManager().getRankDownValue(currentRank); + if (prevRank < currentRank) { + getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, prevRank); + String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(prevRank)); + user.sendMessage("commands.island.team.demote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName); + return true; + } else { + user.sendMessage("commands.island.team.demote.failure"); + return false; + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamSetownerCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamSetownerCommand.java new file mode 100644 index 0000000..44b7adf --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/commands/island/team/IslandTeamSetownerCommand.java @@ -0,0 +1,87 @@ +package us.tastybento.bskyblock.commands.island.team; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +public class IslandTeamSetownerCommand extends CompositeCommand { + + public IslandTeamSetownerCommand(CompositeCommand islandTeamCommand) { + super(islandTeamCommand, "setleader"); + } + + @Override + public void setup() { + setPermission("island.team"); + setOnlyPlayer(true); + setParameters("commands.island.team.setowner.parameters"); + setDescription("commands.island.team.setowner.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + UUID playerUUID = user.getUniqueId(); + // Can use if in a team + boolean inTeam = getPlugin().getIslands().inTeam(getWorld(), playerUUID); + UUID teamLeaderUUID = getTeamLeader(getWorld(), user); + if (!(inTeam && teamLeaderUUID.equals(playerUUID))) { + user.sendMessage("general.errors.not-leader"); + return false; + } + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player"); + return false; + } + if (!getIslands().inTeam(getWorld(), playerUUID)) { + user.sendMessage("general.errors.no-team"); + return false; + } + if (targetUUID.equals(playerUUID)) { + user.sendMessage("commands.island.team.setowner.errors.cant-transfer-to-yourself"); + return false; + } + if (!getPlugin().getIslands().getMembers(getWorld(), playerUUID).contains(targetUUID)) { + user.sendMessage("commands.island.team.setowner.errors.target-is-not-member"); + return false; + } + // Fire event so add-ons can run commands, etc. + IslandBaseEvent event = TeamEvent.builder() + .island(getIslands() + .getIsland(getWorld(), playerUUID)) + .reason(TeamEvent.Reason.MAKELEADER) + .involvedPlayer(targetUUID) + .build(); + getPlugin().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + getIslands().makeLeader(getWorld(), user, targetUUID, getPermissionPrefix()); + getIslands().save(true); + return true; + } + + + @Override + public Optional> tabComplete(User user, String alias, List args) { + List options = new ArrayList<>(); + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + for (UUID member : getPlugin().getIslands().getMembers(getWorld(), user.getUniqueId())) { + options.add(getPlugin().getServer().getOfflinePlayer(member).getName()); + } + return Optional.of(Util.tabLimit(options, lastArg)); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java new file mode 100644 index 0000000..56d021d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java @@ -0,0 +1,86 @@ +package us.tastybento.bskyblock.database; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import us.tastybento.bskyblock.BSkyBlock; + +/** + * An abstract class that handles insert/select-operations into/from a database + * + * @author tastybento + * + * @param + */ +public abstract class AbstractDatabaseHandler { + + /** + * The data object that should be created and filled with values + * from the database or inserted into the database + */ + protected Class dataObject; + + /** + * Contains the settings to create a connection to the database like + * host/port/database/user/password + */ + protected DatabaseConnecter databaseConnecter; + + protected BSkyBlock plugin; + + /** + * Constructor + * + * @param type + * The type of the objects that should be created and filled with + * values from the database or inserted into the database + * @param databaseConnecter + * Contains the settings to create a connection to the database + * like host/port/database/user/password + */ + protected AbstractDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter databaseConnecter) { + this.plugin = plugin; + this.databaseConnecter = databaseConnecter; + this.dataObject = type; + } + + /** + * Loads all the records in this table and returns a list of them + * @return list of + */ + public abstract List loadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException; + + /** + * Creates a filled with values from the corresponding + * database file + * @param uniqueId - unique ID + * @return + */ + public abstract T loadObject(String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException; + + /** + * Save T into the corresponding database + * + * @param instance that should be inserted into the database + */ + public abstract void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ; + + /** + * Deletes the object with the unique id from the database + * @param instance - object instance + */ + public abstract void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ; + + /** + * Checks if a unique id exists or not + * @param uniqueId - uniqueId to check + * @return true if this uniqueId exists + */ + public abstract boolean objectExists(String uniqueId); + + /** + * Closes the database + */ + public abstract void close(); +} diff --git a/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java new file mode 100644 index 0000000..158f99b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java @@ -0,0 +1,112 @@ +package us.tastybento.bskyblock.database; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.Addon; + +/** + * Handy class to store and load Java POJOs in the BSkyBlock Database + * @author tastybento + * + * @param + */ +public class BSBDatabase { + + private AbstractDatabaseHandler handler; + private Logger logger; + + @SuppressWarnings("unchecked") + public BSBDatabase(BSkyBlock plugin, Class type) { + this.logger = plugin.getLogger(); + handler = (AbstractDatabaseHandler) BSBDbSetup.getDatabase().getHandler(type); + } + + @SuppressWarnings("unchecked") + public BSBDatabase(Addon addon, Class type) { + this.logger = addon.getLogger(); + handler = (AbstractDatabaseHandler) BSBDbSetup.getDatabase().getHandler(type); + + } + + /** + * Load all the config objects and supply them as a list + * @return list of config objects or an empty list if they cannot be loaded + */ + public List loadObjects() { + List result = new ArrayList<>(); + try { + result = handler.loadObjects(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | ClassNotFoundException | IntrospectionException e) { + logger.severe(() -> "Could not load objects from database! Error: " + e.getMessage()); + } + return result; + } + + /** + * Loads the config object + * @param uniqueId - unique id of the object + * @return the object or null if it cannot be loaded + */ + public T loadObject(String uniqueId) { + T result = null; + try { + result = handler.loadObject(uniqueId); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | ClassNotFoundException | IntrospectionException e) { + logger.severe(() -> "Could not load object from database! " + e.getMessage()); + } + return result; + } + + /** + * Save config object + * @param instance to save + */ + public boolean saveObject(T instance) { + try { + handler.saveObject(instance); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException + | IntrospectionException e) { + logger.severe(() -> "Could not save object to database! Error: " + e.getMessage()); + return false; + } + return true; + } + + /** + * Checks if a config object exists or not + * @param name - unique name of the config object + * @return true if it exists + */ + public boolean objectExists(String name) { + return handler.objectExists(name); + } + + /** + * Delete object from database + * @param object - object to delete + */ + public void deleteObject(T object) { + try { + handler.deleteObject(object); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException + | IntrospectionException e) { + logger.severe(() -> "Could not delete config! Error: " + e.getMessage()); + } + } + + /** + * Close the database + */ + public void close() { + handler.close(); + } + + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/BSBDbSetup.java b/src/main/java/us/tastybento/bskyblock/database/BSBDbSetup.java new file mode 100755 index 0000000..59c2e0f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/BSBDbSetup.java @@ -0,0 +1,43 @@ +package us.tastybento.bskyblock.database; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; +import us.tastybento.bskyblock.database.mongodb.MongoDBDatabase; +import us.tastybento.bskyblock.database.mysql.MySQLDatabase; + +public abstract class BSBDbSetup { + + /** + * Gets the type of database being used. Currently supported options are + * FLATFILE and MYSQL. Default is FLATFILE + * @return Database type + */ + public static BSBDbSetup getDatabase(){ + for(DatabaseType type : DatabaseType.values()){ + if(type == BSkyBlock.getInstance().getSettings().getDatabaseType()) { + return type.database; + } + } + return DatabaseType.FLATFILE.database; + } + + public enum DatabaseType{ + FLATFILE(new FlatFileDatabase()), + MYSQL(new MySQLDatabase()), + MONGO(new MongoDBDatabase()); + + BSBDbSetup database; + + DatabaseType(BSBDbSetup database){ + this.database = database; + } + } + + /** + * Gets a database handler that will store and retrieve classes of type dataObjectClass + * @param dataObjectClass - class of the object to be stored in the database + * @return handler for this database object + */ + public abstract AbstractDatabaseHandler getHandler(Class dataObjectClass); + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/DatabaseConnecter.java b/src/main/java/us/tastybento/bskyblock/database/DatabaseConnecter.java new file mode 100644 index 0000000..85976ee --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/DatabaseConnecter.java @@ -0,0 +1,66 @@ +package us.tastybento.bskyblock.database; + +import java.util.Map; + +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * + * Creates a connection to a database. + * + */ +public interface DatabaseConnecter { + + /** + * Establishes a new connection to the database + * + * @return A new connection to the database using the settings provided + */ + Object createConnection(); + + /** + * Close the database connection + */ + void closeConnection(); + + /** + * Returns the connection url + * + * @return the connector's URL + */ + String getConnectionUrl(); + + /** + * Looks through the database (or files) and returns a known unique key + * @param tableName - name of the table + * @return a unique key for this record + */ + String getUniqueId(String tableName); + + /** + * Check if a key exists in the database in this table or not + * @param tableName - name of the table + * @param key - key to check + * @return true if it exists + */ + boolean uniqueIdExists(String tableName, String key); + + /** + * Loads a YAML file. Used by the flat file database + * @param tableName - the table name to load + * @param fileName - the filename + * @return Yaml Configuration + */ + YamlConfiguration loadYamlFile(String tableName, String fileName); + + /** + * Save the Yaml Config + * @param yamlConfig - the YAML config + * @param path - analogous to a table name in a database + * @param fileName - the name of the record. Must be unique. + * @param commentMap - map of comments, may be empty + */ + void saveYamlFile(YamlConfiguration yamlConfig, String path, String fileName, Map commentMap); + +} + diff --git a/src/main/java/us/tastybento/bskyblock/database/DatabaseConnectionSettingsImpl.java b/src/main/java/us/tastybento/bskyblock/database/DatabaseConnectionSettingsImpl.java new file mode 100644 index 0000000..a040bfc --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/DatabaseConnectionSettingsImpl.java @@ -0,0 +1,96 @@ +package us.tastybento.bskyblock.database; + +public class DatabaseConnectionSettingsImpl { + private String host; + private int port; + private String databaseName; + private String username; + private String password; + + /** + * Hosts database settings + * @param host - database host + * @param port - port + * @param databaseName - database name + * @param username - username + * @param password - password + */ + public DatabaseConnectionSettingsImpl(String host, int port, String databaseName, String username, String password) { + this.host = host; + this.port = port; + this.databaseName = databaseName; + this.username = username; + this.password = password; + } + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host the host to set + */ + public void setHost(String host) { + this.host = host; + } + + /** + * @return the port + */ + public int getPort() { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(int port) { + this.port = port; + } + + /** + * @return the databaseName + */ + public String getDatabaseName() { + return databaseName; + } + + /** + * @param databaseName the databaseName to set + */ + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java new file mode 100644 index 0000000..8051806 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java @@ -0,0 +1,38 @@ +package us.tastybento.bskyblock.database.flatfile; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.DatabaseConnecter; + +/** + * Class handles config settings saving and loading + * + * @author tastybento + * + * @param Handles config files for Class + */ + +public class ConfigHandler extends FlatFileDatabaseHandler { + + public ConfigHandler(BSkyBlock plugin, Class type, DatabaseConnecter databaseConnecter) { + super(plugin, type, databaseConnecter); + } + + public void saveSettings(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + configFlag = true; + saveObject(instance); + } + + public T loadSettings(String uniqueId, T dbConfig) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException { + if (dbConfig == null) { + return loadObject(uniqueId); + } + + // TODO: compare the loaded with the database copy + + return loadObject(uniqueId); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java new file mode 100755 index 0000000..86bfba5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java @@ -0,0 +1,23 @@ +package us.tastybento.bskyblock.database.flatfile; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.BSBDbSetup; + +public class FlatFileDatabase extends BSBDbSetup{ + + @Override + public AbstractDatabaseHandler getHandler(Class type) { + return new FlatFileDatabaseHandler<>(BSkyBlock.getInstance(), type, new FlatFileDatabaseConnecter(BSkyBlock.getInstance())); + } + + /** + * Get the config + * @param type - config object type + * @return - the config handler + */ + public AbstractDatabaseHandler getConfig(Class type) { + return new ConfigHandler<>(BSkyBlock.getInstance(), type, new FlatFileDatabaseConnecter(BSkyBlock.getInstance())); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseConnecter.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseConnecter.java new file mode 100644 index 0000000..9222b05 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseConnecter.java @@ -0,0 +1,178 @@ +package us.tastybento.bskyblock.database.flatfile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; +import java.util.UUID; + +import org.bukkit.configuration.file.YamlConfiguration; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.DatabaseConnecter; + +public class FlatFileDatabaseConnecter implements DatabaseConnecter { + + private static final int MAX_LOOPS = 100; + private static final String DATABASE_FOLDER_NAME = "database"; + private BSkyBlock plugin; + private File dataFolder; + + + public FlatFileDatabaseConnecter(BSkyBlock plugin) { + this.plugin = plugin; + dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); + } + + @Override + public Connection createConnection() { + return null; // Not used + } + + @Override + public String getConnectionUrl() { + return null; // Not used + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.DatabaseConnecter#loadYamlFile(java.lang.String, java.lang.String) + */ + @Override + public YamlConfiguration loadYamlFile(String tableName, String fileName) { + if (!fileName.endsWith(".yml")) { + fileName = fileName + ".yml"; + } + File yamlFile = new File(plugin.getDataFolder(), tableName + File.separator + fileName); + + YamlConfiguration config = null; + if (yamlFile.exists()) { + try { + config = new YamlConfiguration(); + config.load(yamlFile); + } catch (Exception e) { + plugin.logError("Could not load yaml file from database " + tableName + " " + fileName + " " + e.getMessage()); + } + } else { + // Create the missing file + config = new YamlConfiguration(); + plugin.log("No " + fileName + " found. Creating it..."); + try { + if (plugin.getResource(fileName) != null) { + plugin.log("Using default found in jar file."); + plugin.saveResource(fileName, false); + config = new YamlConfiguration(); + config.load(yamlFile); + } else { + config.save(yamlFile); + } + } catch (Exception e) { + plugin.logError("Could not create the " + fileName + " file!"); + } + } + return config; + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.DatabaseConnecter#saveYamlFile(org.bukkit.configuration.file.YamlConfiguration, java.lang.String, java.lang.String) + */ + @Override + public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, Map commentMap) { + if (!fileName.endsWith(".yml")) { + fileName = fileName + ".yml"; + } + File tableFolder = new File(plugin.getDataFolder(), tableName); + File file = new File(tableFolder, fileName); + if (!tableFolder.exists()) { + tableFolder.mkdirs(); + } + try { + // Approach is save to temp file (saving is not necessarily atomic), then move file atomically + // This has best chance of no file corruption + + File tmpFile = File.createTempFile("yaml", null, tableFolder); + yamlConfig.save(tmpFile); + if (tmpFile.exists()) { + Files.copy(tmpFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.delete(tmpFile.toPath()); + } else { + throw new IOException(); + } + } catch (Exception e) { + plugin.logError("Could not save yaml file: " + tableName + " " + fileName + " " + e.getMessage()); + return; + } + if (commentMap != null && !commentMap.isEmpty()) { + commentFile(file, commentMap); + } + } + + /** + * Adds comments to a YAML file + * @param file - file + * @param commentMap - map of comments to apply to file + */ + private void commentFile(File file, Map commentMap) { + // Run through the file and add in the comments + File commentedFile = new File(file.getPath() + ".tmp"); + List newFile = new ArrayList<>(); + try (Scanner scanner = new Scanner(file)) { + while (scanner.hasNextLine()) { + String nextLine = scanner.nextLine(); + // See if there are any comments in this line + for (Entry e : commentMap.entrySet()) { + if (nextLine.contains(e.getKey())) { + // We want the comment to start at the same level as the entry + StringBuilder commentLine = new StringBuilder(); + for (int i = 0; i < nextLine.indexOf(e.getKey()); i++){ + commentLine.append(' '); + } + commentLine.append(e.getValue()); + nextLine = commentLine.toString(); + break; + } + } + newFile.add(nextLine); + } + Files.write(commentedFile.toPath(), (Iterable)newFile.stream()::iterator); + Files.move(commentedFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e1) { + plugin.logError("Could not comment config file " + file.getName() + " " + e1.getMessage()); + } + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.DatabaseConnecter#getUniqueId(java.lang.String) + */ + @Override + public String getUniqueId(String tableName) { + UUID uuid = UUID.randomUUID(); + File file = new File(dataFolder, tableName + File.separator + uuid.toString() + ".yml"); + int limit = 0; + while (file.exists() && limit++ < MAX_LOOPS) { + uuid = UUID.randomUUID(); + file = new File(dataFolder, tableName + File.separator + uuid.toString() + ".yml"); + } + return uuid.toString(); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.DatabaseConnecter#uniqueIdExists(java.lang.String, java.lang.String) + */ + @Override + public boolean uniqueIdExists(String tableName, String key) { + File file = new File(dataFolder, tableName + File.separator + key + ".yml"); + return file.exists(); + } + + @Override + public void closeConnection() { + // Not used + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java new file mode 100644 index 0000000..484de34 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java @@ -0,0 +1,516 @@ +package us.tastybento.bskyblock.database.flatfile; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.YamlConfiguration; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Constants; +import us.tastybento.bskyblock.Constants.GameType; +import us.tastybento.bskyblock.api.configuration.ConfigComment; +import us.tastybento.bskyblock.api.configuration.ConfigEntry; +import us.tastybento.bskyblock.api.configuration.StoreAt; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.objects.adapters.Adapter; +import us.tastybento.bskyblock.database.objects.adapters.AdapterInterface; +import us.tastybento.bskyblock.util.Util; + +/** + * Class that creates a list of s filled with values from the corresponding + * database-table. + * + * @author tastybento + * + * @param Handles flat files for Class + */ + +public class FlatFileDatabaseHandler extends AbstractDatabaseHandler { + + private static final String DATABASE_FOLDER_NAME = "database"; + protected boolean configFlag; + + public FlatFileDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter dbConnecter) { + super(plugin, type, dbConnecter); + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#loadObject(java.lang.String) + */ + @Override + public T loadObject(String key) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException { + String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName(); + String fileName = key; + StoreAt storeAt = dataObject.getAnnotation(StoreAt.class); + if (storeAt != null) { + path = storeAt.path(); + fileName = storeAt.filename(); + } + YamlConfiguration config = databaseConnecter.loadYamlFile(path, fileName); + return createObject(config); + } + + @Override + public boolean objectExists(String uniqueId) { + return databaseConnecter.uniqueIdExists(dataObject.getSimpleName(), uniqueId); + } + + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#loadObjects() + */ + @Override + public List loadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException { + List list = new ArrayList<>(); + FilenameFilter ymlFilter = (dir, name) -> name.toLowerCase().endsWith(".yml"); + String path = dataObject.getSimpleName(); + StoreAt storeAt = dataObject.getAnnotation(StoreAt.class); + if (storeAt != null) { + path = storeAt.path(); + } + File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); + File tableFolder = new File(dataFolder, path); + if (!tableFolder.exists()) { + // Nothing there... + tableFolder.mkdirs(); + } + for (File file: Objects.requireNonNull(tableFolder.listFiles(ymlFilter))) { + String fileName = file.getName(); + if (storeAt != null) { + fileName = storeAt.filename(); + } + YamlConfiguration config = databaseConnecter.loadYamlFile(DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName(), fileName); + list.add(createObject(config)); + } + return list; + } + + /** + * + * Creates a list of s filled with values from the provided ResultSet + * + * @param config - YAML config file + * + * @return filled with values + */ + private T createObject(YamlConfiguration config) throws InstantiationException, IllegalAccessException, IntrospectionException, InvocationTargetException, ClassNotFoundException { + T instance = dataObject.newInstance(); + + // Run through all the fields in the object + for (Field field : dataObject.getDeclaredFields()) { + // Gets the getter and setters for this field + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); + // Get the write method + Method method = propertyDescriptor.getWriteMethod(); + String storageLocation = field.getName(); + // Check if there is an annotation on the field + ConfigEntry configEntry = field.getAnnotation(ConfigEntry.class); + // If there is a config annotation then do something + if (configEntry != null) { + if (!configEntry.path().isEmpty()) { + storageLocation = configEntry.path(); + } + if (!configEntry.specificTo().equals(GameType.BOTH) && !configEntry.specificTo().equals(Constants.GAMETYPE)) { + continue; + } + // TODO: Add handling of other ConfigEntry elements + } + Adapter adapterNotation = field.getAnnotation(Adapter.class); + if (adapterNotation != null && AdapterInterface.class.isAssignableFrom(adapterNotation.value())) { + // A conversion adapter has been defined + // Get the original value + Object value = config.get(storageLocation); + method.invoke(instance, ((AdapterInterface)adapterNotation.value().newInstance()).deserialize(value)); + // We are done here + continue; + } + + // Look in the YAML Config to see if this field exists (it should) + if (config.contains(storageLocation)) { + // Check for null values + if (config.get(storageLocation) == null) { + method.invoke(instance, (Object)null); + continue; + } + // Handle storage of maps. Check if this type is a Map + if (Map.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { + // Note that we have no idea what type this is + List collectionTypes = getCollectionParameterTypes(method); + // collectionTypes should be 2 long + Type keyType = collectionTypes.get(0); + Type valueType = collectionTypes.get(1); + Map value = new HashMap<>(); + if (config.getConfigurationSection(storageLocation) != null) { + for (String key : config.getConfigurationSection(storageLocation).getKeys(false)) { + // Map values can be null - it is allowed here + Object mapValue = deserialize(config.get(storageLocation + "." + key), Class.forName(valueType.getTypeName())); + // Keys cannot be null - skip if they exist + // Convert any serialized dots back to dots + key = key.replaceAll(":dot:", "."); + Object mapKey = deserialize(key,Class.forName(keyType.getTypeName())); + if (mapKey == null) { + continue; + } + value.put(mapKey, mapValue); + } + } + method.invoke(instance, value); + } else if (Set.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { + // Loop through the collection resultset + // Note that we have no idea what type this is + List collectionTypes = getCollectionParameterTypes(method); + // collectionTypes should be only 1 long + Type setType = collectionTypes.get(0); + Set value = new HashSet<>(); + for (Object listValue: config.getList(storageLocation)) { + value.add(deserialize(listValue,Class.forName(setType.getTypeName()))); + } + + // TODO: this may not work with all keys. Further serialization may be required. + method.invoke(instance, value); + } else if (List.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { + // Loop through the collection resultset + // Note that we have no idea what type this is + List collectionTypes = getCollectionParameterTypes(method); + // collectionTypes should be only 1 long + Type setType = collectionTypes.get(0); + List value = new ArrayList<>(); + if (config.getList(storageLocation) != null) { + for (Object listValue: config.getList(storageLocation)) { + value.add(deserialize(listValue,Class.forName(setType.getTypeName()))); + } + } + // TODO: this may not work with all keys. Further serialization may be required. + method.invoke(instance, value); + } else { + // Not a collection + Object value = config.get(storageLocation); + if (value != null && !value.getClass().equals(MemorySection.class)) { + method.invoke(instance, deserialize(value,propertyDescriptor.getPropertyType())); + } + } + } + } + + return instance; + } + + /** + * Get a list of parameter types for the collection argument in this method + * @param writeMethod - write method + * @return a list of parameter types for the collection argument in this method + */ + private List getCollectionParameterTypes(Method writeMethod) { + List result = new ArrayList<>(); + // Get the return type + // This uses a trick to extract what the arguments are of the writeMethod of the field. + // In this way, we can deduce what type needs to be written at runtime. + Type[] genericParameterTypes = writeMethod.getGenericParameterTypes(); + // There could be more than one argument, so step through them + for (Type genericParameterType : genericParameterTypes) { + // If the argument is a parameter, then do something - this should always be true if the parameter is a collection + if( genericParameterType instanceof ParameterizedType ) { + // Get the actual type arguments of the parameter + Type[] parameters = ((ParameterizedType)genericParameterType).getActualTypeArguments(); + result.addAll(Arrays.asList(parameters)); + } + } + return result; + } + + /** + * Inserts T into the corresponding database-table + * + * @param instance that should be inserted into the database + */ + @SuppressWarnings("unchecked") + @Override + public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // This is the Yaml Configuration that will be used and saved at the end + YamlConfiguration config = new YamlConfiguration(); + + // The file name of the Yaml file. + String filename = ""; + String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName(); + // Comments for the file + Map yamlComments = new HashMap<>(); + + // Only allow storing in an arbitrary place if it is a config object. Otherwise it is in the database + StoreAt storeAt = instance.getClass().getAnnotation(StoreAt.class); + if (storeAt != null) { + path = storeAt.path(); + filename = storeAt.filename(); + } + // See if there are any top-level comments + // See if there are multiple comments + ConfigComment.Line comments = instance.getClass().getAnnotation(ConfigComment.Line.class); + if (comments != null) { + for (ConfigComment comment : comments.value()) { + setComment(comment, config, yamlComments, ""); + } + } + // Handle single line comments + ConfigComment comment = instance.getClass().getAnnotation(ConfigComment.class); + if (comment != null) { + setComment(comment, config, yamlComments, ""); + } + + // Run through all the fields in the class that is being stored. EVERY field must have a get and set method + for (Field field : dataObject.getDeclaredFields()) { + + // Get the property descriptor for this field + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); + // Get the read method, i.e., getXXXX(); + Method method = propertyDescriptor.getReadMethod(); + // Invoke the read method to get the value. We have no idea what type of value it is. + Object value = method.invoke(instance); + String storageLocation = field.getName(); + // Check if there is an annotation on the field + ConfigEntry configEntry = field.getAnnotation(ConfigEntry.class); + // If there is a config path annotation then do something + if (configEntry != null) { + if (!configEntry.specificTo().equals(GameType.BOTH) && !configEntry.specificTo().equals(Constants.GAMETYPE)) { + continue; + } + if (!configEntry.path().isEmpty()) { + storageLocation = configEntry.path(); + } + // TODO: add in game-specific saving + + } + + // Get path for comments + String parent = ""; + if (storageLocation.contains(".")) { + parent = storageLocation.substring(0, storageLocation.lastIndexOf('.')) + "."; + } + // See if there are multiple comments + comments = field.getAnnotation(ConfigComment.Line.class); + if (comments != null) { + for (ConfigComment bodyComment : comments.value()) { + setComment(bodyComment, config, yamlComments, parent); + } + } + // Handle single line comments + comment = field.getAnnotation(ConfigComment.class); + if (comment != null) { + setComment(comment, config, yamlComments, parent); + } + + // Adapter + Adapter adapterNotation = field.getAnnotation(Adapter.class); + if (adapterNotation != null && AdapterInterface.class.isAssignableFrom(adapterNotation.value())) { + // A conversion adapter has been defined + try { + config.set(storageLocation, ((AdapterInterface)adapterNotation.value().newInstance()).serialize(value)); + } catch (InstantiationException e) { + plugin.logError("Could not instatiate adapter " + adapterNotation.value().getName() + " " + e.getMessage()); + } + // We are done here + continue; + } + + // Depending on the vale type, it'll need serializing differently + // Check if this field is the mandatory UniqueId field. This is used to identify this instantiation of the class + if (method.getName().equals("getUniqueId")) { + // If the object does not have a unique name assigned to it already, one is created at random + String id = (String)value; + if (value == null || id.isEmpty()) { + id = databaseConnecter.getUniqueId(dataObject.getSimpleName()); + // Set it in the class so that it will be used next time + propertyDescriptor.getWriteMethod().invoke(instance, id); + } + // Save the name for when the file is saved + if (filename.isEmpty()) { + filename = id; + } + } + // Collections need special serialization + if (Map.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { + // Maps need to have keys serialized + if (value != null) { + Map result = new HashMap<>(); + for (Entry object : ((Map)value).entrySet()) { + // Serialize all key and values + String key = (String)serialize(object.getKey()); + key = key.replaceAll("\\.", ":dot:"); + result.put(key, serialize(object.getValue())); + } + // Save the list in the config file + config.set(storageLocation, result); + } + } else if (Set.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { + // Sets need to be serialized as string lists + if (value != null) { + List list = new ArrayList<>(); + for (Object object : (Set)value) { + list.add(serialize(object)); + } + // Save the list in the config file + config.set(storageLocation, list); + } + } else { + // For all other data that doesn't need special serialization + config.set(storageLocation, serialize(value)); + } + } + if (filename.isEmpty()) { + throw new IllegalArgumentException("No uniqueId in class"); + } + + databaseConnecter.saveYamlFile(config, path, filename, yamlComments); + } + + private void setComment(ConfigComment comment, YamlConfiguration config, Map yamlComments, String parent) { + String random = "comment-" + UUID.randomUUID().toString(); + // Store placeholder + config.set(parent + random, " "); + // Create comment + yamlComments.put(random, "# " + comment.value().replace(TextVariables.VERSION, plugin.getDescription().getVersion())); + } + + /** + * Serialize an object if required + * @param object - object to serialize + * @return - serialized object + */ + private Object serialize(Object object) { + if (object == null) { + return "null"; + } + if (object instanceof UUID) { + return ((UUID)object).toString(); + } + if (object instanceof World) { + return ((World)object).getName(); + } + if (object instanceof Location) { + return Util.getStringLocation((Location)object); + } + if (object instanceof Enum) { + //Custom enums are a child of the Enum class. Just get the names of each one. + return ((Enum)object).name(); + } + return object; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private Object deserialize(Object value, Class clazz) { + // If value is already null, then it can be nothing else + if (value == null) { + return null; + } + if (value instanceof String && value.equals("null")) { + // If the value is null as a string, return null + return null; + } + // Bukkit may have deserialized the object already + if (clazz.equals(value.getClass())) { + return value; + } + // Types that need to be deserialized + if (clazz.equals(Long.class) && value.getClass().equals(Integer.class)) { + return Long.valueOf((Integer) value); + } + if (clazz.equals(Integer.class) && value.getClass().equals(String.class)) { + return Integer.valueOf((String)value); + } + if (clazz.equals(Long.class) && value.getClass().equals(String.class)) { + return Long.valueOf((String)value); + } + if (clazz.equals(Double.class) && value.getClass().equals(String.class)) { + return Double.valueOf((String)value); + } + if (clazz.equals(Float.class) && value.getClass().equals(String.class)) { + return Float.valueOf((String)value); + } + if (clazz.equals(UUID.class)) { + value = UUID.fromString((String)value); + } + // Bukkit Types + if (clazz.equals(Location.class)) { + // Get Location from String - may be null... + value = Util.getLocationString(((String)value)); + } + if (clazz.equals(World.class)) { + // Get world by name - may be null... + value = plugin.getServer().getWorld((String)value); + } + // Enums + if (Enum.class.isAssignableFrom(clazz)) { + //Custom enums are a child of the Enum class. + // Find out the value + Class enumClass = (Class)clazz; + try { + value = Enum.valueOf(enumClass, (String)value); + } catch (Exception e) { + // This value does not exist - probably admin typed it wrongly + // Show what is available and pick one at random + plugin.logError("Error in YML file: " + value + " is not a valid value in the enum " + clazz.getCanonicalName() + "!"); + plugin.logError("Options are : "); + boolean isSet = false; + for (Field fields : enumClass.getFields()) { + plugin.logError(fields.getName()); + if (!isSet && !((String)value).isEmpty() && fields.getName().substring(0, 1).equals(((String)value).substring(0, 1))) { + value = Enum.valueOf(enumClass, fields.getName()); + plugin.logError("Setting to " + fields.getName() + " because it starts with the same letter"); + isSet = true; + } + } + } + } + return value; + } + + @Override + public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // The file name of the Yaml file. + PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); + Method method = propertyDescriptor.getReadMethod(); + String fileName = (String) method.invoke(instance); + if (!fileName.endsWith(".yml")) { + fileName = fileName + ".yml"; + } + File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); + File tableFolder = new File(dataFolder, dataObject.getSimpleName()); + if (tableFolder.exists()) { + + File file = new File(tableFolder, fileName); + try { + Files.delete(file.toPath()); + } catch (IOException e) { + plugin.logError("Could not delete yaml database object! " + file.getName() + " - " + e.getMessage()); + } + } + } + + @Override + public void close() { + // Not used + + } +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabase.java b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabase.java new file mode 100755 index 0000000..0ce08b2 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabase.java @@ -0,0 +1,29 @@ +package us.tastybento.bskyblock.database.mongodb; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.BSBDbSetup; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; + +public class MongoDBDatabase extends BSBDbSetup{ + + @Override + public AbstractDatabaseHandler getHandler(Class type) { + BSkyBlock plugin = BSkyBlock.getInstance(); + // Check if the MongoDB plugin exists + if (plugin.getServer().getPluginManager().getPlugin("BsbMongo") == null) { + plugin.logError("You must install BsbMongo plugin for MongoDB support!"); + plugin.logError("See: https://github.com/tastybento/bsbMongo/releases/"); + plugin.getServer().getPluginManager().disablePlugin(plugin); + return null; + } + return new MongoDBDatabaseHandler<>(plugin, type, new MongoDBDatabaseConnecter(new DatabaseConnectionSettingsImpl( + plugin.getSettings().getDbHost(), + plugin.getSettings().getDbPort(), + plugin.getSettings().getDbName(), + plugin.getSettings().getDbUsername(), + plugin.getSettings().getDbPassword() + ))); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseConnecter.java b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseConnecter.java new file mode 100644 index 0000000..8e0b5c5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseConnecter.java @@ -0,0 +1,75 @@ +package us.tastybento.bskyblock.database.mongodb; + +import java.util.Map; + +import org.bukkit.configuration.file.YamlConfiguration; + +import com.mongodb.MongoClient; +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.client.MongoDatabase; + +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; + +public class MongoDBDatabaseConnecter implements DatabaseConnecter { + + private MongoClient client; + private DatabaseConnectionSettingsImpl dbSettings; + + /** + * Class for MySQL database connections using the settings provided + * @param dbSettings - database settings + */ + public MongoDBDatabaseConnecter(DatabaseConnectionSettingsImpl dbSettings) { + this.dbSettings = dbSettings; + MongoCredential credential = MongoCredential.createCredential(dbSettings.getUsername(), + dbSettings.getDatabaseName(), + dbSettings.getPassword().toCharArray()); + MongoClientOptions options = MongoClientOptions.builder().sslEnabled(false).build(); + client = new MongoClient(new ServerAddress(dbSettings.getHost(), dbSettings.getPort()), credential,options); + } + + @Override + public MongoDatabase createConnection() { + return client.getDatabase(dbSettings.getDatabaseName()); + } + + @Override + public String getConnectionUrl() { + return ""; + } + + @Override + public String getUniqueId(String tableName) { + // Not used + return ""; + } + + @Override + public YamlConfiguration loadYamlFile(String string, String key) { + // Not used + return null; + } + + @Override + public boolean uniqueIdExists(String tableName, String key) { + // Not used + return false; + } + + @Override + public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, + Map commentMap) { + // Not used + + } + + @Override + public void closeConnection() { + client.close(); + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseHandler.java new file mode 100644 index 0000000..d439064 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mongodb/MongoDBDatabaseHandler.java @@ -0,0 +1,165 @@ +package us.tastybento.bskyblock.database.mongodb; + +import java.util.ArrayList; +import java.util.List; + +import org.bson.Document; +import org.bson.conversions.Bson; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.potion.PotionEffectType; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.FindOneAndReplaceOptions; +import com.mongodb.client.model.IndexOptions; +import com.mongodb.client.model.Indexes; +import com.mongodb.util.JSON; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.mysql.adapters.FlagAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.LocationAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.PotionEffectTypeAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.WorldAdapter; +import us.tastybento.bskyblock.database.objects.DataObject; + +/** + * + * Class that inserts a into the corresponding database-table. + * + * @author tastybento + * + * @param + */ +@SuppressWarnings("deprecation") +public class MongoDBDatabaseHandler extends AbstractDatabaseHandler { + + private static final String UNIQUEID = "uniqueId"; + private static final String MONGO_ID = "_id"; + + private MongoCollection collection; + private DatabaseConnecter dbConnecter; + + private BSkyBlock bskyblock; + + /** + * Handles the connection to the database and creation of the initial database schema (tables) for + * the class that will be stored. + * @param plugin - BSkyBlock plugin object + * @param type - the type of class to be stored in the database. Must inherit DataObject + * @param dbConnecter - authentication details for the database + */ + public MongoDBDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter dbConnecter) { + super(plugin, type, dbConnecter); + this.bskyblock = plugin; + this.dbConnecter = dbConnecter; + /* + Connection to the database + */ + MongoDatabase database = (MongoDatabase) dbConnecter.createConnection(); + collection = database.getCollection(dataObject.getCanonicalName()); + IndexOptions indexOptions = new IndexOptions().unique(true); + collection.createIndex(Indexes.text(UNIQUEID), indexOptions); + } + + // Gets the GSON builder + private Gson getGSON() { + // excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose + // enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapter(Location.class, new LocationAdapter(plugin)) ; + builder.registerTypeAdapter(World.class, new WorldAdapter(plugin)); + builder.registerTypeAdapter(Flag.class, new FlagAdapter(bskyblock)); + builder.registerTypeAdapter(PotionEffectType.class, new PotionEffectTypeAdapter()); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + return builder.create(); + } + + @Override + public List loadObjects() { + List list = new ArrayList<>(); + Gson gson = getGSON(); + for (Document document : collection.find(new Document())) { + // The deprecated serialize option does not have a viable alternative without involving a huge amount of custom code + String json = JSON.serialize(document); + json = json.replaceFirst(MONGO_ID, UNIQUEID); + list.add(gson.fromJson(json, dataObject)); + } + return list; + } + + @Override + public T loadObject(String uniqueId) { + Document doc = collection.find(new Document(MONGO_ID, uniqueId)).limit(1).first(); + if (doc != null) { + Gson gson = getGSON(); + String json = JSON.serialize(doc).replaceFirst(MONGO_ID, UNIQUEID); + // load single object + return gson.fromJson(json, dataObject); + } + return null; + } + + @Override + public void saveObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + DataObject dataObj = (DataObject)instance; + try { + Gson gson = getGSON(); + String toStore = gson.toJson(instance); + // Change uniqueId to _id + toStore = toStore.replaceFirst(UNIQUEID, MONGO_ID); + // This parses JSON to a Mongo Document + Document document = Document.parse(toStore); + // Filter based on the id + Bson filter = new Document(MONGO_ID, dataObj.getUniqueId()); + // Set the options to upsert (update or insert if doc is not there) + FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().upsert(true); + // Do the deed + collection.findOneAndReplace(filter, document, options); + } catch (Exception e) { + plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + @Override + public void deleteObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + try { + collection.findOneAndDelete(new Document(MONGO_ID, ((DataObject)instance).getUniqueId())); + } catch (Exception e) { + plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) + */ + @Override + public boolean objectExists(String uniqueId) { + return collection.find(new Document(MONGO_ID, uniqueId)).first() != null; + } + + @Override + public void close() { + dbConnecter.closeConnection(); + + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java new file mode 100755 index 0000000..0a92c71 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java @@ -0,0 +1,22 @@ +package us.tastybento.bskyblock.database.mysql; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.BSBDbSetup; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; + +public class MySQLDatabase extends BSBDbSetup{ + + @Override + public AbstractDatabaseHandler getHandler(Class type) { + BSkyBlock plugin = BSkyBlock.getInstance(); + return new MySQLDatabaseHandler<>(plugin, type, new MySQLDatabaseConnecter(new DatabaseConnectionSettingsImpl( + plugin.getSettings().getDbHost(), + plugin.getSettings().getDbPort(), + plugin.getSettings().getDbName(), + plugin.getSettings().getDbUsername(), + plugin.getSettings().getDbPassword() + ))); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseConnecter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseConnecter.java new file mode 100644 index 0000000..a1588a0 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseConnecter.java @@ -0,0 +1,85 @@ +package us.tastybento.bskyblock.database.mysql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; + +public class MySQLDatabaseConnecter implements DatabaseConnecter { + + private String connectionUrl; + private DatabaseConnectionSettingsImpl dbSettings; + private Connection connection = null; + + /** + * Class for MySQL database connections using the settings provided + * @param dbSettings - database settings + */ + public MySQLDatabaseConnecter(DatabaseConnectionSettingsImpl dbSettings) { + this.dbSettings = dbSettings; + try { + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } catch (Exception e) { + Bukkit.getLogger().severe("Could not instantiate JDBC driver! " + e.getMessage()); + } + // jdbc:mysql://localhost:3306/Peoples?autoReconnect=true&useSSL=false + connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName() + "?autoReconnect=true&useSSL=false&allowMultiQueries=true"; + } + + @Override + public Connection createConnection() { + try { + connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword()); + } catch (SQLException e) { + Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage()); + } + return connection; + } + + @Override + public String getConnectionUrl() { + return connectionUrl; + } + + @Override + public String getUniqueId(String tableName) { + // Not used + return ""; + } + + @Override + public YamlConfiguration loadYamlFile(String string, String key) { + // Not used + return null; + } + + @Override + public boolean uniqueIdExists(String tableName, String key) { + // Not used + return false; + } + + @Override + public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, + Map commentMap) { + // Not used + + } + + @Override + public void closeConnection() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + Bukkit.getLogger().severe("Could not close MySQL database connection"); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java new file mode 100644 index 0000000..5552b68 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java @@ -0,0 +1,211 @@ +package us.tastybento.bskyblock.database.mysql; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.potion.PotionEffectType; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.mysql.adapters.FlagAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.LocationAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.PotionEffectTypeAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.WorldAdapter; +import us.tastybento.bskyblock.database.objects.DataObject; + +/** + * + * Class that inserts a into the corresponding database-table. + * + * @author tastybento + * + * @param + */ +public class MySQLDatabaseHandler extends AbstractDatabaseHandler { + + /** + * Connection to the database + */ + private Connection connection; + + private BSkyBlock bskyblock; + + /** + * Handles the connection to the database and creation of the initial database schema (tables) for + * the class that will be stored. + * @param plugin - BSkyBlock plugin object + * @param type - the type of class to be stored in the database. Must inherit DataObject + * @param dbConnecter - authentication details for the database + */ + public MySQLDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter dbConnecter) { + super(plugin, type, dbConnecter); + this.bskyblock = plugin; + connection = (Connection)dbConnecter.createConnection(); + // Check if the table exists in the database and if not, create it + createSchema(); + } + + /** + * Creates the table in the database if it doesn't exist already + */ + private void createSchema() { + String sql = "CREATE TABLE IF NOT EXISTS `" + + dataObject.getCanonicalName() + + "` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"; + // Prepare and execute the database statements + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.executeUpdate(); + } catch (SQLException e) { + plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage()); + } + } + + // Gets the GSON builder + private Gson getGSON() { + // excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose + // enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapter(Location.class, new LocationAdapter(plugin)) ; + builder.registerTypeAdapter(World.class, new WorldAdapter(plugin)); + builder.registerTypeAdapter(Flag.class, new FlagAdapter(bskyblock)); + builder.registerTypeAdapter(PotionEffectType.class, new PotionEffectTypeAdapter()); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + return builder.create(); + } + + @Override + public List loadObjects() { + List list = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT `json` FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("`"); + try (Statement preparedStatement = connection.createStatement()) { + try (ResultSet resultSet = preparedStatement.executeQuery(sb.toString())) { + // Load all the results + Gson gson = getGSON(); + while (resultSet.next()) { + list.add(gson.fromJson(resultSet.getString("json"), dataObject)); + } + } + } catch (SQLException e) { + plugin.logError("Could not load objects " + e.getMessage()); + } + return list; + } + + @Override + public T loadObject(String uniqueId) { + String sb = "SELECT `json` FROM `" + + dataObject.getCanonicalName() + + "` WHERE uniqueId = ? LIMIT 1"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { + // UniqueId needs to be placed in quotes + preparedStatement.setString(1, "\"" + uniqueId + "\""); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + // If there is a result, we only want/need the first one + Gson gson = getGSON(); + return gson.fromJson(resultSet.getString("json"), dataObject); + } + } + } catch (SQLException e) { + plugin.logError("Could not load object " + uniqueId + " " + e.getMessage()); + } + return null; + } + + @Override + public void saveObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + String sb = "INSERT INTO " + + "`" + + dataObject.getCanonicalName() + + "` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?"; + // Replace into is used so that any data in the table will be replaced with updated data + // The table name is the canonical name, so that add-ons can be sure of a unique table in the database + try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { + Gson gson = getGSON(); + String toStore = gson.toJson(instance); + preparedStatement.setString(1, toStore); + preparedStatement.setString(2, toStore); + preparedStatement.execute(); + } catch (SQLException e) { + plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + @Override + public void deleteObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + String sb = "DELETE FROM `" + + dataObject.getCanonicalName() + + "` WHERE uniqueId = ?"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { + Method getUniqueId = dataObject.getMethod("getUniqueId"); + String uniqueId = (String) getUniqueId.invoke(instance); + preparedStatement.setString(1, uniqueId); + preparedStatement.execute(); + } catch (Exception e) { + plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) + */ + @Override + public boolean objectExists(String uniqueId) { + // Create the query to see if this key exists + String query = "SELECT IF ( EXISTS( SELECT * FROM `" + + dataObject.getCanonicalName() + + "` WHERE `uniqueId` = ?), 1, 0)"; + + try (PreparedStatement preparedStatement = connection.prepareStatement(query)) { + preparedStatement.setString(1, uniqueId); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + return resultSet.getBoolean(1); + } + } + } catch (SQLException e) { + plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage()); + } + return false; + } + + @Override + public void close() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + plugin.logError("Could not close database for some reason"); + } + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java new file mode 100644 index 0000000..4e2f088 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; + +public class FlagAdapter extends TypeAdapter { + + private BSkyBlock plugin; + + public FlagAdapter(BSkyBlock plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, Flag value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getID()); + + } + + @Override + public Flag read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return plugin.getFlagsManager().getFlagByID(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java new file mode 100644 index 0000000..cab2dbd --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java @@ -0,0 +1,54 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class LocationAdapter extends TypeAdapter { + + private Plugin plugin; + + public LocationAdapter(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, Location location) throws IOException { + if (location == null) { + out.nullValue(); + return; + } + out.beginArray(); + out.value(location.getWorld().getName()); + out.value(location.getX()); + out.value(location.getY()); + out.value(location.getZ()); + out.value(location.getYaw()); + out.value(location.getPitch()); + out.endArray(); + } + + @Override + public Location read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + in.beginArray(); + World world = plugin.getServer().getWorld(in.nextString()); + double x = in.nextDouble(); + double y = in.nextDouble(); + double z = in.nextDouble(); + float yaw = (float)in.nextDouble(); + float pitch = (float)in.nextDouble(); + in.endArray(); + return new Location(world, x, y, z, yaw, pitch); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java new file mode 100644 index 0000000..df6b24e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java @@ -0,0 +1,32 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.potion.PotionEffectType; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class PotionEffectTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, PotionEffectType value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getName()); + + } + + @Override + public PotionEffectType read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return PotionEffectType.getByName(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java new file mode 100644 index 0000000..f9ee60c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class WorldAdapter extends TypeAdapter { + + private Plugin plugin; + + public WorldAdapter(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, World value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getName()); + + } + + @Override + public World read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return plugin.getServer().getWorld(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/DataObject.java b/src/main/java/us/tastybento/bskyblock/database/objects/DataObject.java new file mode 100644 index 0000000..a458713 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/DataObject.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.database.objects; + +import us.tastybento.bskyblock.BSkyBlock; + +/** + * Contains fields that must be in any data object + * @author tastybento + * + */ +public interface DataObject { + + default BSkyBlock getPlugin() { + return BSkyBlock.getInstance(); + } + + /** + * @return the uniqueId + */ + String getUniqueId(); + + /** + * @param uniqueId - unique ID the uniqueId to set + */ + void setUniqueId(String uniqueId); + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Island.java b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java new file mode 100755 index 0000000..d540b62 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java @@ -0,0 +1,692 @@ +package us.tastybento.bskyblock.database.objects; + +import java.util.Date; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.util.Vector; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.gson.annotations.Expose; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.adapters.Adapter; +import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.util.Pair; +import us.tastybento.bskyblock.util.Util; + +/** + * Stores all the info about an island + * Managed by IslandsManager + * Responsible for team information as well. + * + * @author Tastybento + * @author Poslovitch + */ +public class Island implements DataObject { + + @Expose + private String uniqueId = UUID.randomUUID().toString(); + + //// Island //// + // The center of the island itself + @Expose + private Location center; + + // Island range + @Expose + private int range; + + // Protection size + @Expose + private int protectionRange; + + // World the island started in. This may be different from the island location + @Expose + private World world; + + // Display name + @Expose + private String name = ""; + + // Time parameters + @Expose + private long createdDate; + @Expose + private long updatedDate; + + //// Team //// + @Expose + private UUID owner; + + @Expose + private Map members = new HashMap<>(); + + //// State //// + @Expose + private boolean spawn = false; + @Expose + private boolean purgeProtected = false; + + //// Protection flags //// + @Adapter(FlagSerializer.class) + @Expose + private Map flags = new HashMap<>(); + + @Expose + private int levelHandicap; + @Expose + private Map spawnPoint = new EnumMap<>(Environment.class); + + public Island() {} + + public Island(Location location, UUID owner, int protectionRange) { + setOwner(owner); + createdDate = System.currentTimeMillis(); + updatedDate = System.currentTimeMillis(); + world = location.getWorld(); + // Make a copy of the location + center = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); + range = BSkyBlock.getInstance().getIWM().getIslandDistance(world); + this.protectionRange = protectionRange; + } + + /** + * Adds a team member. If player is on banned list, they will be removed from it. + * @param playerUUID - the player's UUID + */ + public void addMember(UUID playerUUID) { + if (playerUUID != null) { + members.put(playerUUID, RanksManager.MEMBER_RANK); + } + } + /** + * Adds target to a list of banned players for this island. May be blocked by the event being cancelled. + * If the player is a member, coop or trustee, they will be removed from those lists. + * @param targetUUID - the target's UUID + * @return true if successfully added + */ + public boolean addToBanList(UUID targetUUID) { + if (targetUUID != null) { + members.put(targetUUID, RanksManager.BANNED_RANK); + return true; + } + return false; + } + + /** + * @return the banned + */ + public Set getBanned() { + Set result = new HashSet<>(); + for (Entry member: members.entrySet()) { + if (member.getValue() <= RanksManager.BANNED_RANK) { + result.add(member.getKey()); + } + } + return result; + } + + /** + * @return the center Location + */ + public Location getCenter(){ + return center; + } + + /** + * @return the date when the island was created + */ + public long getCreatedDate(){ + return createdDate; + } + /** + * Gets the Island Guard flag's setting. If this is a protection flag, the this will be the + * rank needed to bypass this flag. If it is a Settings flag, any non-zero value means the + * setting is allowed. + * @param flag - flag + * @return flag value + */ + public int getFlag(Flag flag) { + flags.putIfAbsent(flag, flag.getDefaultRank()); + return flags.get(flag); + } + + /** + * @return the flags + */ + public Map getFlags() { + return flags; + } + + /** + * @return the levelHandicap + */ + public int getLevelHandicap() { + return levelHandicap; + } + + /** + * Get the team members of the island. If this is empty or cleared, there is no team. + * @return the members - key is the UUID, value is the RanksManager enum, e.g. RanksManager.MEMBER_RANK + */ + public Map getMembers() { + return members; + } + + /** + * @return the members of the island (owner included) + */ + public ImmutableSet getMemberSet(){ + Builder result = new ImmutableSet.Builder<>(); + + for (Entry member: members.entrySet()) { + if (member.getValue() >= RanksManager.MEMBER_RANK) { + result.add(member.getKey()); + } + } + return result.build(); + } + + /** + * @return the minProtectedX + */ + public int getMinProtectedX() { + return center.getBlockX() - protectionRange; + } + + /** + * @return the minProtectedZ + */ + public int getMinProtectedZ() { + return center.getBlockZ() - protectionRange; + } + + /** + * @return the minX + */ + public int getMinX() { + return center.getBlockX() - range; + } + + /** + * @return the minZ + */ + public int getMinZ() { + return center.getBlockZ() - range; + } + + /** + * @return the island display name or the owner's name if none is set + */ + public String getName() { + return name; + } + + /** + * @return the owner (team leader) + */ + public UUID getOwner(){ + return owner; + } + + /** + * @return the protectionRange + */ + public int getProtectionRange() { + return protectionRange; + } + + /** + * @return true if the island is protected from the Purge, otherwise false + */ + public boolean getPurgeProtected(){ + return purgeProtected; + } + + /** + * Returns the island range. + * It is a convenience method that returns the exact same value than {@link Settings#getIslandDistance()}, although it has been saved into the Island object for easier access. + * @return the island range + * @see #getProtectionRange() + */ + public int getRange(){ + return range; + } + + /** + * Get the rank of user for this island + * @param user - the User + * @return rank integer + */ + public int getRank(User user) { + return members.getOrDefault(user.getUniqueId(), RanksManager.VISITOR_RANK); + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + /** + * @return the date when the island was updated (team member connection, etc...) + */ + public long getUpdatedDate(){ + return updatedDate; + } + + /** + * @return the world + */ + public World getWorld() { + return world; + } + + /** + * @return the x coordinate of the island center + */ + public int getX(){ + return center.getBlockX(); + } + + /** + * @return the y coordinate of the island center + */ + public int getY(){ + return center.getBlockY(); + } + + /** + * @return the z coordinate of the island center + */ + public int getZ(){ + return center.getBlockZ(); + } + + /** + * Checks if coords are in the island space + * @param x - x coordinate + * @param z - z coordinate + * @return true if in the island space + */ + public boolean inIslandSpace(int x, int z) { + return x >= getMinX() && x < getMinX() + range*2 && z >= getMinZ() && z < getMinZ() + range*2; + } + + /** + * Checks if location is in full island space, not just protected space + * @param location - location + * @return true if in island space + */ + public boolean inIslandSpace(Location location) { + return Util.sameWorld(world, location.getWorld()) && inIslandSpace(location.getBlockX(), location.getBlockZ()); + } + + /** + * Checks if the coords are in island space + * @param blockCoord - Pair(x,z) coords of block + * @return true or false + */ + public boolean inIslandSpace(Pair blockCoord) { + return inIslandSpace(blockCoord.x, blockCoord.z); + } + + /** + * Check if the flag is allowed or not + * For flags that are for the island in general and not related to rank. + * @param flag - flag + * @return true if allowed, false if not + */ + public boolean isAllowed(Flag flag) { + // A negative value means not allowed + return getFlag(flag) >= 0; + } + + /** + * Check if a user is allowed to bypass the flag or not + * @param user - the User - user + * @param flag - flag + * @return true if allowed, false if not + */ + public boolean isAllowed(User user, Flag flag) { + return getRank(user) >= getFlag(flag); + } + + /** + * Check if banned + * @param targetUUID - the target's UUID + * @return Returns true if target is banned on this island + */ + public boolean isBanned(UUID targetUUID) { + return members.containsKey(targetUUID) && members.get(targetUUID).equals(RanksManager.BANNED_RANK); + } + + /** + * @return spawn + */ + public boolean isSpawn() { + return spawn; + } + + /** + * Checks if a location is within this island's protected area + * + * @param target - target location + * @return true if it is, false if not + */ + public boolean onIsland(Location target) { + return Util.sameWorld(world, target.getWorld()) && target.getBlockX() >= getMinProtectedX() && target.getBlockX() < (getMinProtectedX() + protectionRange * 2) && target.getBlockZ() >= getMinProtectedZ() && target.getBlockZ() < (getMinProtectedZ() + protectionRange * 2); + } + + /** + * Removes target from the banned list. May be cancelled by unban event. + * @param targetUUID - the target's UUID + * @return true if successful, otherwise false. + */ + public boolean removeFromBanList(UUID targetUUID) { + return (members.remove(targetUUID) != null); + } + + /** + * Removes a player from the team member map. Do not call this directly. + * Use {@link us.tastybento.bskyblock.managers.IslandsManager#removePlayer(World, UUID)} + * @param playerUUID - uuid of player + */ + public void removeMember(UUID playerUUID) { + members.remove(playerUUID); + } + + /** + * @param center the center to set + */ + public void setCenter(Location center) { + if (center != null) { + this.world = center.getWorld(); + } + this.center = center; + } + + /** + * @param createdDate - the createdDate to sets + */ + public void setCreatedDate(long createdDate){ + this.createdDate = createdDate; + } + + /** + * Set the Island Guard flag rank + * @param flag - flag + * @param value - Use RanksManager settings, e.g. RanksManager.MEMBER + */ + public void setFlag(Flag flag, int value){ + flags.put(flag, value); + } + + /** + * @param flags the flags to set + */ + public void setFlags(Map flags) { + this.flags = flags; + } + + /** + * Resets the flags to their default as set in config.yml for this island. + * If flags are missing from the config, the default hard-coded value is used and set + */ + public void setFlagsDefaults() { + Map result = new HashMap<>(); + Flags.values().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION)) + .forEach(f -> result.put(f, BSkyBlock.getInstance().getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank()))); + Flags.values().stream().filter(f -> f.getType().equals(Flag.Type.SETTING)) + .forEach(f -> result.put(f, BSkyBlock.getInstance().getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank()))); + this.setFlags(result); + } + + /** + * @param levelHandicap the levelHandicap to set + */ + public void setLevelHandicap(int levelHandicap) { + this.levelHandicap = levelHandicap; + } + + /** + * @param members the members to set + */ + public void setMembers(Map members) { + this.members = members; + } + + /** + * @param name - the display name to set + * Set to null to remove the display name + */ + public void setName(String name){ + this.name = name; + } + + /** + * Sets the owner of the island. + * @param owner - the island owner - the owner/team leader to set + */ + public void setOwner(UUID owner){ + this.owner = owner; + if (owner == null) { + return; + } + // Defensive code: demote any previous owner + for (Entry en : members.entrySet()) { + if (en.getValue().equals(RanksManager.OWNER_RANK)) { + en.setValue(RanksManager.MEMBER_RANK); + } + } + members.put(owner, RanksManager.OWNER_RANK); + } + + /** + * @param protectionRange the protectionRange to set + */ + public void setProtectionRange(int protectionRange) { + this.protectionRange = protectionRange; + } + + /** + * @param purgeProtected - if the island is protected from the Purge + */ + public void setPurgeProtected(boolean purgeProtected){ + this.purgeProtected = purgeProtected; + } + + /** + * Sets the island range. + * This method should NEVER be used except for testing purposes. + *
+ * The range value is a copy of {@link Settings#getIslandDistance()} made when the Island got created in order to allow easier access to this value and must therefore remain AS IS. + * @param range the range to set + * @see #setProtectionRange(int) + */ + public void setRange(int range){ + this.range = range; + } + + /** + * Set user's rank to an arbitrary rank value + * @param user - the User + * @param rank - rank value + */ + public void setRank(User user, int rank) { + if (user.getUniqueId() != null) { + members.put(user.getUniqueId(), rank); + } + } + + /** + * @param ranks the ranks to set + */ + public void setRanks(Map ranks) { + members = ranks; + } + + /** + * @param isSpawn - if the island is the spawn + */ + public void setSpawn(boolean isSpawn){ + spawn = isSpawn; + } + + /** + * Get the default spawn location for this island. Note that this may only be valid + * after the initial pasting because the player can change the island after that point + * @return the spawnPoint + */ + public Map getSpawnPoint() { + return spawnPoint; + } + + /** + * Set when island is pasted + * @param spawnPoint the spawnPoint to set + */ + public void setSpawnPoint(Map spawnPoint) { + this.spawnPoint = spawnPoint; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + /** + * @param updatedDate - the updatedDate to sets + */ + public void setUpdatedDate(long updatedDate){ + this.updatedDate = updatedDate; + } + + /** + * @param world the world to set + */ + public void setWorld(World world) { + this.world = world; + } + + /** + * Show info on the island + * @param plugin - plugin + * @param user - the user who is receiving the info + * @param world - world to check + * @return true always + */ + public boolean showInfo(BSkyBlock plugin, User user, World world) { + user.sendMessage("commands.admin.info.title"); + if (owner == null) { + user.sendMessage("commands.admin.info.unowned"); + } else { + user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(owner), "[uuid]", owner.toString()); + Date d = new Date(plugin.getServer().getOfflinePlayer(owner).getLastPlayed()); + user.sendMessage("commands.admin.info.last-login","[date]", d.toString()); + + user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(owner))); + String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); + String total = plugin.getSettings().getResetLimit() < 0 ? "Unlimited" : String.valueOf(plugin.getSettings().getResetLimit()); + user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + // Show team members + showMembers(plugin, user, world); + } + Vector location = center.toVector(); + user.sendMessage("commands.admin.info.island-location", "[xyz]", Util.xyz(location)); + Vector from = center.toVector().subtract(new Vector(range, 0, range)).setY(0); + Vector to = center.toVector().add(new Vector(range-1, 0, range-1)).setY(center.getWorld().getMaxHeight()); + user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(from), "[xz2]", Util.xyz(to)); + user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(protectionRange)); + Vector pfrom = center.toVector().subtract(new Vector(protectionRange, 0, protectionRange)).setY(0); + Vector pto = center.toVector().add(new Vector(protectionRange-1, 0, protectionRange-1)).setY(center.getWorld().getMaxHeight()); + user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(pfrom), "[xz2]", Util.xyz(pto)); + if (spawn) { + user.sendMessage("commands.admin.info.is-spawn"); + } + Set banned = getBanned(); + if (!banned.isEmpty()) { + user.sendMessage("commands.admin.info.banned-players"); + banned.forEach(u -> user.sendMessage("commands.admin.info.banned-format", "[name]", plugin.getPlayers().getName(u))); + } + return true; + } + + /** + * Shows the members of this island + * @param plugin - plugin + * @param user - user who is requesting + * @param world - world to check + */ + public void showMembers(BSkyBlock plugin, User user, World world) { + if (plugin.getIslands().inTeam(world, user.getUniqueId())) { + user.sendMessage("commands.admin.info.team-members-title"); + members.forEach((u, i) -> { + if (owner.equals(u)) { + user.sendMessage("commands.admin.info.team-owner-format", "[name]", plugin.getPlayers().getName(u) + , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); + } else if (i > RanksManager.VISITOR_RANK){ + user.sendMessage("commands.admin.info.team-member-format", "[name]", plugin.getPlayers().getName(u) + , "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i))); + } + }); + } + + + } + + /** + * Toggles a settings flag + * @param flag - flag + */ + public void toggleFlag(Flag flag) { + if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) { + setSettingsFlag(flag, !isAllowed(flag)); + } + } + + /** + * Sets the state of a settings flag + * @param flag - flag + * @param state - true or false + */ + public void setSettingsFlag(Flag flag, boolean state) { + if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) { + flags.put(flag, state ? 1 : -1); + } + } + + /** + * Set the spawn location for this island type + * @param islandType - island type + * @param l - location + */ + public void setSpawnPoint(Environment islandType, Location l) { + spawnPoint.put(islandType, l); + } + + /** + * Get the spawn point for this island type + * @param islandType - island type + * @return - location or null if one does not exist + */ + public Location getSpawnPoint(Environment islandType) { + return spawnPoint.get(islandType); + } + + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Names.java b/src/main/java/us/tastybento/bskyblock/database/objects/Names.java new file mode 100644 index 0000000..30e4623 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Names.java @@ -0,0 +1,51 @@ +package us.tastybento.bskyblock.database.objects; + +import java.util.UUID; + +import com.google.gson.annotations.Expose; + +/** + * Stores player names and uuid's + * @author tastybento + * + */ +public class Names implements DataObject { + + @Expose + private String uniqueId = ""; // name + @Expose + private UUID uuid; + + public Names() {} + + public Names(String name, UUID uuid) { + this.uniqueId = name; + this.uuid = uuid; + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + /** + * @return the uuid + */ + public UUID getUuid() { + return uuid; + } + + /** + * @param uuid the uuid to set + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Players.java b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java new file mode 100755 index 0000000..44e8122 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java @@ -0,0 +1,308 @@ +package us.tastybento.bskyblock.database.objects; + +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import com.google.gson.annotations.Expose; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.util.Util; + +/** + * Tracks the following info on the player + * + * @author tastybento + */ +public class Players implements DataObject { + @Expose + private Map homeLocations = new HashMap<>(); + @Expose + private String uniqueId; + @Expose + private String playerName; + @Expose + private Map resets = new HashMap<>(); + @Expose + private String locale = ""; + @Expose + private int deaths; + @Expose + private Map kickedList = new HashMap<>(); + + /** + * This is required for database storage + */ + public Players() {} + + /** + * @param plugin - BSkyBlock plugin object + * @param uniqueId - unique ID + * Constructor - initializes the state variables + * + */ + public Players(BSkyBlock plugin, UUID uniqueId) { + this.uniqueId = uniqueId.toString(); + homeLocations = new HashMap<>(); + locale = ""; + kickedList = new HashMap<>(); + // Try to get player's name + this.playerName = Bukkit.getServer().getOfflinePlayer(uniqueId).getName(); + if (this.playerName == null) { + this.playerName = uniqueId.toString(); + } + } + + /** + * Gets the default home location. + * @param world - world to check + * @return Location - home location in world + */ + public Location getHomeLocation(World world) { + return getHomeLocation(world, 1); // Default + } + + /** + * Gets the home location by number for world + * @param world - includes world and any related nether or end worlds + * @param number - a number + * @return Location of this home or null if not available + */ + public Location getHomeLocation(World world, int number) { + return homeLocations.entrySet().stream() + .filter(en -> Util.sameWorld(en.getKey().getWorld(), world) && en.getValue() == number) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + } + + /** + * @param world - world + * @return List of home locations + */ + public Map getHomeLocations(World world) { + return homeLocations.entrySet().stream().filter(e -> Util.sameWorld(e.getKey().getWorld(),world)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + /** + * @return the homeLocations + */ + public Map getHomeLocations() { + return homeLocations; + } + + /** + * @return the kickedList + */ + public Map getKickedList() { + return kickedList; + } + + /** + * @param kickedList the kickedList to set + */ + public void setKickedList(Map kickedList) { + this.kickedList = kickedList; + } + + /** + * @param homeLocations the homeLocations to set + */ + public void setHomeLocations(Map homeLocations) { + this.homeLocations = homeLocations; + } + + /** + * @param playerName the playerName to set + */ + public void setPlayerName(String playerName) { + this.playerName = playerName; + } + + public Player getPlayer() { + return Bukkit.getPlayer(UUID.fromString(uniqueId)); + } + + public UUID getPlayerUUID() { + return UUID.fromString(uniqueId); + } + + public String getPlayerName() { + return playerName; + } + + /** + * Get number of resets done in this world + * @param world - world + * @return the resetsLeft + */ + public int getResets(World world) { + resets.putIfAbsent(world.getName(), 0); + return resets.get(world.getName()); + } + + /** + * @return the resets + */ + public Map getResets() { + return resets; + } + + /** + * @param resets the resets to set + */ + public void setResets(Map resets) { + this.resets = resets; + } + + /** + * @param resets + * the resets to set + */ + public void setResets(World world, int resets) { + this.resets.put(world.getName(), resets); + } + + /** + * Stores the home location of the player in a String format + * + * @param l + * a Bukkit location + */ + public void setHomeLocation(final Location l) { + setHomeLocation(l, 1); + } + + /** + * Stores the numbered home location of the player. Numbering starts at 1. + * @param location - the location + * @param number - a number + */ + public void setHomeLocation(Location location, int number) { + // Remove any home locations in the same world with the same number + homeLocations.entrySet().removeIf(e -> Util.sameWorld(location.getWorld(), e.getKey().getWorld()) && e.getValue().equals(number)); + homeLocations.put(location, number); + } + + /** + * Set the uuid for this player object + * @param uuid - UUID + */ + public void setPlayerUUID(UUID uuid) { + uniqueId = uuid.toString(); + } + + /** + * Clears all home Locations in world + * @param world - world + */ + public void clearHomeLocations(World world) { + homeLocations.keySet().removeIf(l -> Util.sameWorld(l.getWorld(), world)); + } + + /** + * @return the locale + */ + public String getLocale() { + return locale; + } + + /** + * @param locale the locale to set + */ + public void setLocale(String locale) { + this.locale = locale; + } + + /** + * @return the deaths + */ + public int getDeaths() { + return deaths; + } + + /** + * @param deaths the deaths to set + */ + public void setDeaths(int deaths) { + this.deaths = deaths > getPlugin().getSettings().getDeathsMax() ? getPlugin().getSettings().getDeathsMax() : deaths; + } + + /** + * Add death + */ + public void addDeath() { + if (deaths < getPlugin().getSettings().getDeathsMax()) { + deaths++; + } + } + + /** + * Can invite or still waiting for cool down to end + * + * @param location - the location + * to check + * @return number of mins/hours left until cool down ends + */ + public long getInviteCoolDownTime(Location location) { + // Check the hashmap + if (location != null && kickedList.containsKey(location)) { + // The location is in the list + // Check the date/time + Date kickedDate = new Date(kickedList.get(location)); + Calendar coolDownTime = Calendar.getInstance(); + coolDownTime.setTime(kickedDate); + coolDownTime.add(Calendar.MINUTE, getPlugin().getSettings().getInviteWait()); + // Add the invite cooldown period + Calendar timeNow = Calendar.getInstance(); + if (coolDownTime.before(timeNow)) { + // The time has expired + kickedList.remove(location); + return 0; + } else { + // Still not there yet + // Time in minutes + return (long) Math.ceil((coolDownTime.getTimeInMillis() - timeNow.getTimeInMillis()) / (1000 * 60D)); + } + } + return 0; + } + + /** + * Starts the invite cooldown timer for location. Location should be the center of an island. + * @param location - the location + */ + public void startInviteCoolDownTimer(Location location) { + if (location != null) { + kickedList.put(location, System.currentTimeMillis()); + } + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + /** + * Increments the reset counter for player in world + * @param world - world + */ + public void addReset(World world) { + resets.merge(world.getName(), 1, Integer::sum); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/adapters/Adapter.java b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/Adapter.java new file mode 100644 index 0000000..666ca5b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/Adapter.java @@ -0,0 +1,19 @@ +package us.tastybento.bskyblock.database.objects.adapters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * Denotes which adapter should be used to serialize or deserialize this field + * @author tastybento + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Adapter { + + Class value(); + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/adapters/AdapterInterface.java b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/AdapterInterface.java new file mode 100644 index 0000000..4fc75b8 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/AdapterInterface.java @@ -0,0 +1,26 @@ +package us.tastybento.bskyblock.database.objects.adapters; + + +/** + * Convert from to S or to V + * @author tastybento + * + * @param + * @param + */ +public interface AdapterInterface { + + /** + * Serialize object + * @param object - object to serialize + * @return serialized object + */ + S deserialize(Object object); + + /** + * Deserialize object + * @param object - object to deserialize + * @return deserialized object + */ + V serialize(Object object); +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer.java b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer.java new file mode 100644 index 0000000..9a4e50f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer.java @@ -0,0 +1,55 @@ +package us.tastybento.bskyblock.database.objects.adapters; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.configuration.MemorySection; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; + +/** + * Serializes the {@link us.tastybento.bskyblock.database.objects.Island#getFlags() getFlags()} and + * {@link us.tastybento.bskyblock.database.objects.Island#setFlags(Map)} () setFlags()} + * in {@link us.tastybento.bskyblock.database.objects.Island} + * @author tastybento + * + */ +public class FlagSerializer implements AdapterInterface, Map> { + + @SuppressWarnings("unchecked") + @Override + public Map deserialize(Object object) { + Map result = new HashMap<>(); + if (object == null) { + return result; + } + // For YAML + if (object instanceof MemorySection) { + MemorySection section = (MemorySection) object; + for (String key : section.getKeys(false)) { + result.put(BSkyBlock.getInstance().getFlagsManager().getFlagByID(key), section.getInt(key)); + } + } else { + for (Entry en : ((Map)object).entrySet()) { + result.put(BSkyBlock.getInstance().getFlagsManager().getFlagByID(en.getKey()), en.getValue()); + } + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public Map serialize(Object object) { + Map result = new HashMap<>(); + if (object == null) { + return result; + } + Map flags = (Map)object; + for (Entry en: flags.entrySet()) { + result.put(en.getKey().getID(), en.getValue()); + } + return result; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer2.java b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer2.java new file mode 100644 index 0000000..13be63f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/FlagSerializer2.java @@ -0,0 +1,52 @@ +package us.tastybento.bskyblock.database.objects.adapters; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.configuration.MemorySection; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; + +/** + * @author tastybento + * + */ +public class FlagSerializer2 implements AdapterInterface, Map> { + + @SuppressWarnings("unchecked") + @Override + public Map deserialize(Object object) { + Map result = new HashMap<>(); + if (object == null) { + return result; + } + // For YAML + if (object instanceof MemorySection) { + MemorySection section = (MemorySection) object; + for (String key : section.getKeys(false)) { + result.put(BSkyBlock.getInstance().getFlagsManager().getFlagByID(key), section.getBoolean(key) ? 0 : -1); + } + } else { + for (Entry en : ((Map)object).entrySet()) { + result.put(BSkyBlock.getInstance().getFlagsManager().getFlagByID(en.getKey()), en.getValue() ? 0 : -1); + } + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public Map serialize(Object object) { + Map result = new HashMap<>(); + if (object == null) { + return result; + } + Map flags = (Map)object; + for (Entry en: flags.entrySet()) { + result.put(en.getKey().getID(), en.getValue() >= 0); + } + return result; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/adapters/PotionEffectListAdapter.java b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/PotionEffectListAdapter.java new file mode 100644 index 0000000..4efdcc3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/objects/adapters/PotionEffectListAdapter.java @@ -0,0 +1,34 @@ +package us.tastybento.bskyblock.database.objects.adapters; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.potion.PotionEffectType; + +public class PotionEffectListAdapter implements AdapterInterface, List> { + + @SuppressWarnings("unchecked") + @Override + public List deserialize(Object from) { + List result = new ArrayList<>(); + if (from instanceof ArrayList) { + for (String type: (ArrayList)from) { + result.add(PotionEffectType.getByName(type)); + } + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public List serialize(Object to) { + List result = new ArrayList<>(); + if (to instanceof ArrayList) { + for (PotionEffectType type: (ArrayList)to) { + result.add(type.getName()); + } + } + return result; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/generators/ChunkGeneratorWorld.java b/src/main/java/us/tastybento/bskyblock/generators/ChunkGeneratorWorld.java new file mode 100644 index 0000000..d4661a3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/generators/ChunkGeneratorWorld.java @@ -0,0 +1,143 @@ +package us.tastybento.bskyblock.generators; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.util.noise.PerlinOctaveGenerator; + +import us.tastybento.bskyblock.BSkyBlock; + +/** + * @author tastybento + * Creates the world + */ +public class ChunkGeneratorWorld extends ChunkGenerator { + + BSkyBlock plugin; + Random rand = new Random(); + PerlinOctaveGenerator gen; + + /** + * @param plugin - BSkyBlock plugin object + */ + public ChunkGeneratorWorld(BSkyBlock plugin) { + super(); + this.plugin = plugin; + } + + @Override + public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, ChunkGenerator.BiomeGrid biomeGrid) { + if (world.getEnvironment().equals(World.Environment.NETHER)) { + return generateNetherChunks(world, random, chunkX, chunkZ, biomeGrid); + } + ChunkData result = createChunkData(world); + if (plugin.getSettings().getSeaHeight() != 0) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < plugin.getSettings().getSeaHeight(); y++) { + result.setBlock(x, y, z, Material.STATIONARY_WATER); + } + } + } + + } + return result; + } + + // This needs to be set to return true to override minecraft's default + // behavior + @Override + public boolean canSpawn(World world, int x, int z) { + return true; + } + + @Override + public List getDefaultPopulators(final World world) { + return Arrays.asList(new BlockPopulator[0]); + } + + /* + * Nether Section + */ + private ChunkData generateNetherChunks(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomeGrid) { + ChunkData result = createChunkData(world); + rand.setSeed(world.getSeed()); + gen = new PerlinOctaveGenerator((long) (random.nextLong() * random.nextGaussian()), 8); + // This is a nether generator + if (!world.getEnvironment().equals(Environment.NETHER)) { + return result; + } + if (plugin.getSettings().isNetherRoof()) { + // Make the roof - common across the world + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + // Do the ceiling + int maxHeight = world.getMaxHeight(); + result.setBlock(x, (maxHeight - 1), z, Material.BEDROCK); + // Next three layers are a mix of bedrock and netherrack + for (int y = 2; y < 5; y++) { + double r = gen.noise(x, (maxHeight - y), z, 0.5, 0.5); + if (r > 0D) { + result.setBlock(x, (maxHeight - y), z, Material.BEDROCK); + } + } + // Next three layers are a mix of netherrack and air + for (int y = 5; y < 8; y++) { + double r = gen.noise(x, (double)maxHeight - y, z, 0.5, 0.5); + if (r > 0D) { + result.setBlock(x, (maxHeight - y), z, Material.NETHERRACK); + } else { + result.setBlock(x, (maxHeight - y), z, Material.AIR); + } + } + // Layer 8 may be glowstone + double r = gen.noise(x, (double)maxHeight - 8, z, random.nextFloat(), random.nextFloat()); + if (r > 0.5D) { + // Have blobs of glowstone + switch (random.nextInt(4)) { + case 1: + // Single block + result.setBlock(x, (maxHeight - 8), z, Material.GLOWSTONE); + if (x < 14 && z < 14) { + result.setBlock(x + 1, (maxHeight - 8), z + 1, Material.GLOWSTONE); + result.setBlock(x + 2, (maxHeight - 8), z + 2, Material.GLOWSTONE); + result.setBlock(x + 1, (maxHeight - 8), z + 2, Material.GLOWSTONE); + result.setBlock(x + 1, (maxHeight - 8), z + 2, Material.GLOWSTONE); + } + break; + case 2: + // Stalatite + for (int i = 0; i < random.nextInt(10); i++) { + result.setBlock(x, (maxHeight - 8 - i), z, Material.GLOWSTONE); + } + break; + case 3: + result.setBlock(x, (maxHeight - 8), z, Material.GLOWSTONE); + if (x > 3 && z > 3) { + for (int xx = 0; xx < 3; xx++) { + for (int zz = 0; zz < 3; zz++) { + result.setBlock(x - xx, (maxHeight - 8 - random.nextInt(2)), z - xx, Material.GLOWSTONE); + } + } + } + break; + default: + result.setBlock(x, (maxHeight - 8), z, Material.GLOWSTONE); + } + result.setBlock(x, (maxHeight - 8), z, Material.GLOWSTONE); + } else { + result.setBlock(x, (maxHeight - 8), z, Material.AIR); + } + } + } + } + return result; + + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/generators/NetherPopulator.java b/src/main/java/us/tastybento/bskyblock/generators/NetherPopulator.java new file mode 100644 index 0000000..ded89fc --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/generators/NetherPopulator.java @@ -0,0 +1,122 @@ +package us.tastybento.bskyblock.generators; + +import java.util.Random; + +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.EntityType; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +/** + * @author tastybento + * Populates the Nether with appropriate blocks + * + */ +public class NetherPopulator extends BlockPopulator { + + @Override + public void populate(World world, Random random, Chunk source) { + // Rough check - convert spawners to Nether spawners + for (int x = 0; x < 16; x++) { + for (int y = 0; y < world.getMaxHeight(); y++) { + for (int z = 0; z < 16; z++) { + Block b = source.getBlock(x, y, z); + if (b.getType().equals(Material.MOB_SPAWNER)) { + CreatureSpawner cs = (CreatureSpawner) b.getState(); + switch (random.nextInt(3)) { + case 0: + cs.setSpawnedType(EntityType.BLAZE); + break; + case 1: + cs.setSpawnedType(EntityType.SKELETON); + break; + case 2: + cs.setSpawnedType(EntityType.MAGMA_CUBE); + break; + default: + cs.setSpawnedType(EntityType.BLAZE); + } + } else if (b.getType().equals(Material.OBSIDIAN)) { + b.setType(Material.CHEST); + Chest cs = (Chest) b.getState(); + Inventory chestInv = cs.getInventory(); + // Fill it with random goodies + /* + * 2 to 5 stacks of any of the following + * Diamonds 1 - 3 6.85% (5/73) + * Iron Ingots 1 - 5 6.85% (5/73) + * Gold Ingots 1 - 3 20.5% (15/73) + * Golden Sword 1 6.85% (5/73) + * Golden Chestplate 1 6.85% (5/73) + * Flint and Steel 1 6.85% (5/73) + * Nether Wart 3 - 7 6.85% (5/73) + * Saddle 1 13.7% (10/73) + * Golden Horse Armor 1 11.0% (8/73) + * Iron Horse Armor 1 6.85% (5/73) + * Diamond Horse Armor 1 4.11% (3/73) + * Obsidian 2 - 4 2.74% (2/73) + */ + // Pick how many stacks + int numOfStacks = 2 + random.nextInt(3); + // Pick the stacks + for (int i = 0; i < numOfStacks; i++) { + // Pick a random inventory slot + int slot = random.nextInt(chestInv.getSize()); + // Try a few times to find an empty slot (avoids an + // infinite loop potential) + for (int j = 0; j < chestInv.getSize(); j++) { + if (chestInv.getItem(slot) == null) { + break; + } + slot = random.nextInt(chestInv.getSize()); + } + int choice = random.nextInt(73); + if (choice < 5) { + chestInv.setItem(slot, new ItemStack(Material.DIAMOND, random.nextInt(2) + 1)); + } else if (choice < 10) { + chestInv.setItem(slot, new ItemStack(Material.IRON_INGOT, random.nextInt(4) + 1)); + } else if (choice < 25) { + chestInv.setItem(slot, new ItemStack(Material.GOLD_INGOT, random.nextInt(2) + 1)); + } else if (choice < 30) { + chestInv.setItem(slot, new ItemStack(Material.GOLD_SWORD, 1)); + } else if (choice < 35) { + chestInv.setItem(slot, new ItemStack(Material.GOLD_CHESTPLATE, 1)); + } else if (choice < 40) { + chestInv.setItem(slot, new ItemStack(Material.FLINT_AND_STEEL, 1)); + } else if (choice < 45) { + chestInv.setItem(slot, new ItemStack(Material.NETHER_STALK, random.nextInt(4) + 3)); + } else if (choice < 55) { + chestInv.setItem(slot, new ItemStack(Material.SADDLE, 1)); + } else if (choice < 63) { + chestInv.setItem(slot, new ItemStack(Material.GOLD_BARDING, 1)); + } else if (choice < 68) { + chestInv.setItem(slot, new ItemStack(Material.IRON_BARDING, 1)); + } else if (choice < 71) { + chestInv.setItem(slot, new ItemStack(Material.DIAMOND_BARDING, 1)); + } else { + chestInv.setItem(slot, new ItemStack(Material.OBSIDIAN, random.nextInt(3) + 1)); + } + } + + } else if (b.getType().equals(Material.STONE)) { + b.setType(Material.QUARTZ_ORE); + } else if (b.getType().equals(Material.DIRT)) { + world.generateTree(source.getBlock(x, y + 1, z).getLocation(), TreeType.BROWN_MUSHROOM); + b.setType(Material.SOUL_SAND); + } else if (b.getType().equals(Material.SOUL_SAND) && b.getRelative(BlockFace.UP).getType().equals(Material.AIR) + && random.nextInt(9) == 1) { + b.getRelative(BlockFace.UP).setType(Material.NETHER_WARTS); + } + } + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java b/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java new file mode 100644 index 0000000..ab54281 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java @@ -0,0 +1,688 @@ +package us.tastybento.bskyblock.island.builders; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.bukkit.Bukkit; +import org.bukkit.DyeColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Banner; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.block.Sign; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.ChestedHorse; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Tameable; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.Attachable; +import org.bukkit.material.Button; +import org.bukkit.material.Colorable; +import org.bukkit.material.Directional; +import org.bukkit.material.Lever; +import org.bukkit.material.MaterialData; +import org.bukkit.material.Openable; +import org.bukkit.material.Redstone; +import org.bukkit.material.Stairs; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class Clipboard { + + private enum TorchDir { + UNUSED, + EAST, + WEST, + SOUTH, + NORTH, + UP + } + + private static final String ATTACHED = "attached"; + + private static final String BLOCK = "blocks"; + + private static final String FACING = "facing"; + + private static final String POWERED = "powered"; + + private static final String LOAD_ERROR = "Could not load schems file - does not exist : "; + + private static final String BEDROCK = "bedrock"; + + private static final String INVENTORY = "inventory"; + + private static final String ENTITY = "entity"; + + private static final String COLOR = "color"; + + private YamlConfiguration blockConfig = new YamlConfiguration(); + private Location pos1; + private Location pos2; + private Location origin; + private BSkyBlock plugin; + private boolean copied; + + private File schemFolder; + + public Clipboard(BSkyBlock plugin, File schemFolder) { + super(); + this.plugin = plugin; + if (!schemFolder.exists()) { + schemFolder.mkdirs(); + } + this.schemFolder = schemFolder; + } + + /** + * @return the pos1 + */ + public Location getPos1() { + return pos1; + } + /** + * @param pos1 the pos1 to set + */ + public void setPos1(Location pos1) { + origin = null; + this.pos1 = pos1; + } + /** + * @return the pos2 + */ + public Location getPos2() { + return pos2; + } + /** + * @param pos2 the pos2 to set + */ + public void setPos2(Location pos2) { + origin = null; + this.pos2 = pos2; + } + + /** + * @return the origin + */ + public Location getOrigin() { + return origin; + } + /** + * @param origin the origin to set + */ + public void setOrigin(Location origin) { + this.origin = origin; + } + /** + * Copy the blocks between pos1 and pos2 to the clipboard + * @param user - user + * @return true if successful, false if pos1 or pos2 are undefined + */ + public boolean copy(User user, boolean copyAir) { + if (pos1 == null || pos2 == null) { + user.sendMessage("commands.admin.schem.need-pos1-pos2"); + return false; + } + // World + World world = pos1.getWorld(); + // Clear the clipboard + blockConfig = new YamlConfiguration(); + + int count = 0; + int minX = Math.max(pos1.getBlockX(),pos2.getBlockX()); + int maxX = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int minY = Math.max(pos1.getBlockY(),pos2.getBlockY()); + int maxY = Math.min(pos1.getBlockY(), pos2.getBlockY()); + int minZ = Math.max(pos1.getBlockZ(),pos2.getBlockZ()); + int maxZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + + for (int x = Math.min(pos1.getBlockX(), pos2.getBlockX()); x <= Math.max(pos1.getBlockX(),pos2.getBlockX()); x++) { + for (int y = Math.min(pos1.getBlockY(), pos2.getBlockY()); y <= Math.max(pos1.getBlockY(),pos2.getBlockY()); y++) { + for (int z = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); z <= Math.max(pos1.getBlockZ(),pos2.getBlockZ()); z++) { + Block block = world.getBlockAt(x, y, z); + if (copyBlock(block, origin == null ? user.getLocation() : origin, copyAir, world.getLivingEntities().stream() + .filter(Objects::nonNull) + .filter(e -> !(e instanceof Player) && e.getLocation().getBlock().equals(block)) + .collect(Collectors.toList()))) { + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y); + minZ = Math.min(minZ, z); + maxZ = Math.max(maxZ, z); + count ++; + } + } + } + } + blockConfig.set("size.xsize", maxX - minX + 1); + blockConfig.set("size.ysize", maxY - minY + 1); + blockConfig.set("size.zsize", maxZ - minZ + 1); + user.sendMessage("commands.admin.schem.copied-blocks", TextVariables.NUMBER, String.valueOf(count)); + copied = true; + return true; + } + + /** + * Pastes the clipboard to island location + * @param world - world in which to paste + * @param island - location to paste + * @param task - task to run after pasting + */ + public void paste(World world, Island island, Runnable task) { + // Offset due to bedrock + Vector off = new Vector(0,0,0); + if (blockConfig.contains(BEDROCK)) { + String[] offset = blockConfig.getString(BEDROCK).split(","); + off = new Vector(Integer.valueOf(offset[0]), Integer.valueOf(offset[1]), Integer.valueOf(offset[2])); + } + // Calculate location for pasting + Location loc = island.getCenter().toVector().subtract(off).toLocation(world); + // Paste + blockConfig.getConfigurationSection(BLOCK).getKeys(false).forEach(b -> pasteBlock(world, island, loc, blockConfig.getConfigurationSection(BLOCK + "." + b))); + // Run follow on task if it exists + if (task != null) { + Bukkit.getScheduler().runTaskLater(plugin, task, 2L); + } + } + + /** + * Paste clipboard at this location + * @param location + */ + public void paste(Location location) { + blockConfig.getConfigurationSection(BLOCK).getKeys(false).forEach(b -> pasteBlock(location.getWorld(), null, location, blockConfig.getConfigurationSection(BLOCK + "." + b))); + + } + + private void writeSign(Island island, Block block, List lines) { + Sign sign = (Sign) block.getState(); + org.bukkit.material.Sign s = (org.bukkit.material.Sign) sign.getData(); + // Handle spawn sign + if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) { + block.setType(Material.AIR); + // Orient to face same direction as sign + Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(), + block.getZ() + 0.5D, Util.blockFaceToFloat(s.getFacing().getOppositeFace()), 30F); + island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint); + return; + } + String name = TextVariables.NAME; + if (island != null) { + name = plugin.getPlayers().getName(island.getOwner()); + } + // Sub in player's name + for (int i = 0 ; i < lines.size(); i++) { + sign.setLine(i, lines.get(i).replace(TextVariables.NAME, name)); + } + sign.update(); + } + + + private void pasteBlock(World world, Island island, Location location, ConfigurationSection config) { + String[] pos = config.getName().split(","); + int x = location.getBlockX() + Integer.valueOf(pos[0]); + int y = location.getBlockY() + Integer.valueOf(pos[1]); + int z = location.getBlockZ() + Integer.valueOf(pos[2]); + // Default type is air + Material material = Material.getMaterial(config.getString("type", "AIR")); + Block block = world.getBlockAt(x, y, z); + if (config.getBoolean(ATTACHED)) { + plugin.getServer().getScheduler().runTask(plugin, () -> setBlock(island, block, config, material)); + } else { + setBlock(island, block, config, material); + } + } + + @SuppressWarnings("deprecation") + private void setBlock(Island island, Block block, ConfigurationSection config, Material material) { + // Block state + + if (config.getBoolean(ATTACHED) && material.toString().contains("TORCH")) { + TorchDir d = TorchDir.valueOf(config.getString(FACING)); + // The block below has to be set to something solid for this to work + Block rel = block.getRelative(BlockFace.DOWN); + Material rm = rel.getType(); + Byte data = rel.getData(); + if (rel.isEmpty() || rel.isLiquid()) { + rel.setType(Material.STONE); + block.setType(material); + block.setData((byte)d.ordinal()); + // Set the block back to what it was + rel.setType(rm); + rel.setData(data); + } else { + block.setType(material); + block.setData((byte)d.ordinal()); + } + return; + } + // Set the block type + block.setType(material, false); + // Set the block data + byte data = (byte)config.getInt("data"); + block.setData(data); + + // Get the block state + BlockState bs = block.getState(); + // Material Data + MaterialData md = bs.getData(); + if (md instanceof Openable) { + Openable open = (Openable)md; + open.setOpen(config.getBoolean("open")); + } + + if (md instanceof Directional) { + Directional facing = (Directional)md; + if (md instanceof Stairs) { + Stairs stairs = (Stairs)md; + stairs.setInverted(config.getBoolean("inverted")); + stairs.setFacingDirection(BlockFace.valueOf(config.getString(FACING, "NORTH"))); + } else { + facing.setFacingDirection(BlockFace.valueOf(config.getString(FACING, "NORTH"))); + } + } + + if (md instanceof Lever) { + Lever r = (Lever)md; + r.setPowered(config.getBoolean(POWERED)); + } + if (md instanceof Button) { + Button r = (Button)md; + r.setPowered(config.getBoolean(POWERED)); + } + + // Block data + // Signs + if (bs instanceof Sign) { + List lines = config.getStringList("lines"); + writeSign(island, block, lines); + } + // Banners + if (bs instanceof Banner) { + Banner banner = (Banner)bs; + DyeColor baseColor = DyeColor.valueOf(config.getString("baseColor", "RED")); + banner.setBaseColor(baseColor); + int i = 0; + ConfigurationSection pat = config.getConfigurationSection("pattern"); + if (pat != null) { + for (String pattern : pat.getKeys(false)) { + banner.setPattern(i, new Pattern(DyeColor.valueOf(pat.getString(pattern, "GREEN")) + , PatternType.valueOf(pattern))); + i++; + } + } + bs.update(true, false); + } + // Mob spawners + if (bs instanceof CreatureSpawner) { + CreatureSpawner spawner = ((CreatureSpawner) bs); + spawner.setSpawnedType(EntityType.valueOf(config.getString("spawnedType", "PIG"))); + spawner.setMaxNearbyEntities(config.getInt("maxNearbyEntities", 16)); + spawner.setMaxSpawnDelay(config.getInt("maxSpawnDelay", 2*60*20)); + spawner.setMinSpawnDelay(config.getInt("minSpawnDelay", 5*20)); + + spawner.setDelay(config.getInt("delay", -1)); + spawner.setRequiredPlayerRange(config.getInt("requiredPlayerRange", 16)); + spawner.setSpawnRange(config.getInt("spawnRange", 4)); + bs.update(true, false); + } + // Chests, in general + if (bs instanceof InventoryHolder) { + bs.update(true, false); + Inventory ih = ((InventoryHolder)bs).getInventory(); + if (config.isConfigurationSection(INVENTORY)) { + ConfigurationSection inv = config.getConfigurationSection(INVENTORY); + inv.getKeys(false).forEach(i -> ih.setItem(Integer.valueOf(i), (ItemStack)inv.get(i))); + } + } + + // Entities + if (config.isConfigurationSection(ENTITY)) { + ConfigurationSection en = config.getConfigurationSection(ENTITY); + en.getKeys(false).forEach(k -> { + ConfigurationSection ent = en.getConfigurationSection(k); + Location center = block.getLocation().add(new Vector(0.5, 0.0, 0.5)); + LivingEntity e = (LivingEntity)block.getWorld().spawnEntity(center, EntityType.valueOf(ent.getString("type", "PIG"))); + if (e != null) { + e.setCustomName(ent.getString("name")); + } + if (e instanceof Colorable && ent.contains(COLOR)) { + ((Colorable) e).setColor(DyeColor.valueOf(ent.getString(COLOR))); + } + if (e instanceof Tameable) { + ((Tameable)e).setTamed(ent.getBoolean("tamed")); + } + if (e instanceof ChestedHorse) { + ((ChestedHorse)e).setCarryingChest(ent.getBoolean("chest")); + } + if (e instanceof Ageable) { + if (ent.getBoolean("adult")) { + ((Ageable)e).setAdult(); + } else { + ((Ageable)e).setBaby(); + } + } + if (e instanceof AbstractHorse) { + AbstractHorse horse = (AbstractHorse)e; + horse.setDomestication(ent.getInt("domestication")); + ConfigurationSection inv = ent.getConfigurationSection(INVENTORY); + inv.getKeys(false).forEach(i -> horse.getInventory().setItem(Integer.valueOf(i), (ItemStack)inv.get(i))); + } + + if (e instanceof AbstractHorse) { + Horse horse = (Horse)e; + horse.setStyle(Horse.Style.valueOf(ent.getString("style", "NONE"))); + } + }); + } + + } + + @SuppressWarnings("deprecation") + private boolean copyBlock(Block block, Location copyOrigin, boolean copyAir, Collection entities) { + if (!copyAir && block.getType().equals(Material.AIR) && entities.isEmpty()) { + return false; + } + // Create position + int x = block.getLocation().getBlockX() - copyOrigin.getBlockX(); + int y = block.getLocation().getBlockY() - copyOrigin.getBlockY(); + int z = block.getLocation().getBlockZ() - copyOrigin.getBlockZ(); + String pos = x + "," + y + "," + z; + + // Position defines the section + ConfigurationSection s = blockConfig.createSection(BLOCK + "." + pos); + + // Set entities + for (LivingEntity e: entities) { + ConfigurationSection en = s.createSection("entity." + e.getUniqueId()); + en.set("type", e.getType().name()); + en.set("name", e.getCustomName()); + if (e instanceof Colorable) { + Colorable c = (Colorable)e; + en.set(COLOR, c.getColor().name()); + } + if (e instanceof Tameable && ((Tameable)e).isTamed()) { + en.set("tamed", true); + } + if (e instanceof ChestedHorse && ((ChestedHorse)e).isCarryingChest()) { + en.set("chest", true); + } + if (e instanceof Ageable) { + en.set("adult", ((Ageable)e).isAdult()); + } + if (e instanceof AbstractHorse) { + AbstractHorse horse = (AbstractHorse)e; + en.set("domestication", horse.getDomestication()); + for (int index = 0; index < horse.getInventory().getSize(); index++) { + ItemStack i = horse.getInventory().getItem(index); + if (i != null) { + en.set("inventory." + index, i); + } + } + } + + if (e instanceof Horse) { + Horse horse = (Horse)e; + en.set("style", horse.getStyle().name()); + } + } + + // Return if this is just air block + if (!copyAir && block.getType().equals(Material.AIR) && !entities.isEmpty()) { + return true; + } + + // Set the block type + s.set("type", block.getType().toString()); + if (block.getData() != 0) { + s.set("data", block.getData()); + } + if (block.getType().equals(Material.BEDROCK)) { + blockConfig.set(BEDROCK, x + "," + y + "," + z); + } + + // Block state + BlockState bs = block.getState(); + + // Material Data + MaterialData md = bs.getData(); + if (md instanceof Openable) { + Openable open = (Openable)md; + s.set("open", open.isOpen()); + } + if (md instanceof Directional) { + if (md instanceof Stairs) { + Stairs stairs = (Stairs)md; + s.set("inverted", stairs.isInverted()); + s.set(FACING, stairs.getAscendingDirection().name()); + } else { + Directional facing = (Directional)md; + s.set(FACING, facing.getFacing().name()); + } + } + if (md instanceof Attachable) { + Attachable facing = (Attachable)md; + s.set(FACING, facing.getFacing().name()); + s.set("attached-face", facing.getAttachedFace().name()); + s.set(ATTACHED, true); + } + if (md instanceof Colorable) { + Colorable c = (Colorable)md; + s.set(COLOR, c.getColor().name()); + } + if (block.getType().equals(Material.CARPET)) { + DyeColor c = DyeColor.getByWoolData(block.getData()); + s.set(COLOR, c.name()); + } + if (md instanceof Redstone) { + Redstone r = (Redstone)md; + blockConfig.set(POWERED, r.isPowered()); + } + + // Block data + if (bs instanceof Sign) { + Sign sign = (Sign)bs; + s.set("lines", Arrays.asList(sign.getLines())); + } + if (bs instanceof Banner) { + Banner banner = (Banner)bs; + s.set("baseColor", banner.getBaseColor().toString()); + banner.getPatterns().forEach(p -> s.set("pattern." + p.getPattern().toString(), p.getColor().toString())); + } + if (bs instanceof InventoryHolder) { + InventoryHolder ih = (InventoryHolder)bs; + for (int index = 0; index < ih.getInventory().getSize(); index++) { + ItemStack i = ih.getInventory().getItem(index); + if (i != null) { + s.set("inventory." + index, i); + } + } + } + if (bs instanceof CreatureSpawner) { + CreatureSpawner spawner = (CreatureSpawner)bs; + s.set("spawnedType",spawner.getSpawnedType().name()); + s.set("delay", spawner.getDelay()); + s.set("maxNearbyEntities", spawner.getMaxNearbyEntities()); + s.set("maxSpawnDelay", spawner.getMaxSpawnDelay()); + s.set("minSpawnDelay", spawner.getMinSpawnDelay()); + s.set("requiredPlayerRange", spawner.getRequiredPlayerRange()); + s.set("spawnRange", spawner.getSpawnRange()); + } + + return true; + } + + /** + * @return the blockConfig + */ + private YamlConfiguration getBlockConfig() { + return blockConfig; + } + + private void unzip(final String zipFilePath) throws IOException { + Path path = Paths.get(zipFilePath); + if (!(path.toFile().exists())) { + throw new IOException("No file exists!"); + } + try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath))) { + ZipEntry entry = zipInputStream.getNextEntry(); + while (entry != null) { + Path filePath = Paths.get(path.getParent().toString(), entry.getName()); + if (!entry.isDirectory()) { + unzipFiles(zipInputStream, filePath); + } else { + Files.createDirectories(filePath); + } + + zipInputStream.closeEntry(); + entry = zipInputStream.getNextEntry(); + } + } + } + + private static void unzipFiles(final ZipInputStream zipInputStream, final Path unzipFilePath) throws IOException { + + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFilePath.toAbsolutePath().toString()))) { + byte[] bytesIn = new byte[1024]; + int read; + while ((read = zipInputStream.read(bytesIn)) != -1) { + bos.write(bytesIn, 0, read); + } + } + + } + + private void zip(File targetFile) throws IOException { + try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getAbsolutePath() + ".schem"))) { + zipOutputStream.putNextEntry(new ZipEntry(targetFile.getName())); + try (FileInputStream inputStream = new FileInputStream(targetFile)) { + final byte[] buffer = new byte[1024]; + int length; + while((length = inputStream.read(buffer)) >= 0) { + zipOutputStream.write(buffer, 0, length); + } + try { + Files.delete(targetFile.toPath()); + } catch (Exception e) { + plugin.logError(e.getMessage()); + } + } + } + } + + public boolean isFull() { + return copied; + } + + + /** + * Load a file to clipboard + * @param fileName - filename in schems folder + * @throws IOException - if there's a load error with unziping or name + * @throws InvalidConfigurationException - the YAML of the schem is at fault + */ + public void load(String fileName) throws IOException, InvalidConfigurationException { + File zipFile = new File(schemFolder, fileName + ".schem"); + if (!zipFile.exists()) { + plugin.logError(LOAD_ERROR + zipFile.getName()); + throw new IOException(LOAD_ERROR + zipFile.getName()); + } + unzip(zipFile.getAbsolutePath()); + File file = new File(schemFolder, fileName); + if (!file.exists()) { + plugin.logError(LOAD_ERROR + file.getName()); + throw new IOException(LOAD_ERROR + file.getName()); + } + blockConfig = new YamlConfiguration(); + blockConfig.load(file); + copied = true; + Files.delete(file.toPath()); + } + + /* + Load a file to clipboard + */ + /** + * @param user - use trying to load + * @param fileName - filename + * @return - ture if load is successful, false if not + */ + public boolean load(User user, String fileName) { + try { + load(fileName); + } catch (IOException e1) { + user.sendMessage("commands.admin.schem.could-not-load"); + plugin.logError("Could not load schems file: " + fileName + " " + e1.getMessage()); + return false; + } catch (InvalidConfigurationException e1) { + user.sendMessage("commands.admin.schem.could-not-load"); + plugin.logError("Could not load schems file - YAML error : " + fileName + " " + e1.getMessage()); + return false; + } + user.sendMessage("general.success"); + return true; + } + + /** + * Save the clipboard to a file + * @param user - user who is copying + * @param newFile - filename + * @return - true if successful, false if error + */ + public boolean save(User user, String newFile) { + File file = new File(schemFolder, newFile); + try { + getBlockConfig().save(file); + } catch (IOException e) { + user.sendMessage("commands.admin.schem.could-not-save", "[message]", "Could not save temp schems file."); + plugin.logError("Could not save temporary schems file: " + file.getName()); + return false; + } + try { + zip(file); + } catch (IOException e) { + user.sendMessage("commands.admin.schem.could-not-save", "[message]", "Could not zip temp schems file."); + plugin.logError("Could not zip temporary schems file: " + file.getName()); + return false; + } + user.sendMessage("general.success"); + return true; + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/BannedVisitorCommands.java b/src/main/java/us/tastybento/bskyblock/listeners/BannedVisitorCommands.java new file mode 100644 index 0000000..4a77bc9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/BannedVisitorCommands.java @@ -0,0 +1,47 @@ +package us.tastybento.bskyblock.listeners; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; + +/** + * Blocks visitors from executing commands that they should not in the island world + * @author tastybento + * + */ +public class BannedVisitorCommands implements Listener { + + private BSkyBlock plugin; + + /** + * @param plugin - plugin + */ + public BannedVisitorCommands(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * Prevents visitors from using commands on islands, like /spawner + * @param e - event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onVisitorCommand(PlayerCommandPreprocessEvent e) { + if (!plugin.getIWM().inWorld(e.getPlayer().getLocation()) || e.getPlayer().isOp() + || e.getPlayer().hasPermission(plugin.getIWM().getPermissionPrefix(e.getPlayer().getWorld()) + "mod.bypassprotect") + || plugin.getIslands().locationIsOnIsland(e.getPlayer(), e.getPlayer().getLocation())) { + return; + } + // Check banned commands + String[] args = e.getMessage().substring(1).toLowerCase().split(" "); + if (plugin.getIWM().getVisitorBannedCommands(e.getPlayer().getWorld()).contains(args[0])) { + User user = User.getInstance(e.getPlayer()); + user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation("protection.command-is-banned")); + e.setCancelled(true); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/BlockEndDragon.java b/src/main/java/us/tastybento/bskyblock/listeners/BlockEndDragon.java new file mode 100644 index 0000000..17652b9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/BlockEndDragon.java @@ -0,0 +1,36 @@ +package us.tastybento.bskyblock.listeners; + +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; + +import us.tastybento.bskyblock.BSkyBlock; + +public class BlockEndDragon implements Listener { + private BSkyBlock plugin; + + public BlockEndDragon(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * This handles end dragon spawning prevention + * + * @param e - event + * @return true if dragon can spawn, false if not + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public boolean onDragonSpawn(CreatureSpawnEvent e) { + if (!e.getEntityType().equals(EntityType.ENDER_DRAGON) || plugin.getIWM().isDragonSpawn(e.getEntity().getWorld())) { + return true; + } + e.getEntity().setHealth(0); + e.getEntity().remove(); + e.setCancelled(true); + return false; + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java new file mode 100644 index 0000000..92ba35e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java @@ -0,0 +1,64 @@ +package us.tastybento.bskyblock.listeners; + +import java.util.UUID; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.PlayersManager; + +public class JoinLeaveListener implements Listener { + + private BSkyBlock plugin; + private PlayersManager players; + + /** + * @param plugin - BSkyBlock plugin object + */ + public JoinLeaveListener(BSkyBlock plugin) { + this.plugin = plugin; + players = plugin.getPlayers(); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerJoin(final PlayerJoinEvent event) { + if (event.getPlayer() == null) { + return; + } + User user = User.getInstance(event.getPlayer()); + if (user.getUniqueId() == null) { + return; + } + UUID playerUUID = user.getUniqueId(); + if (plugin.getPlayers().isKnown(playerUUID)) { + // Load player + players.addPlayer(playerUUID); + // Reset island resets if required + plugin.getIWM().getOverWorlds().stream() + .filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w)) + .forEach(w -> players.setResets(w, playerUUID, 0)); + // Set the player's name (it may have changed), but only if it isn't empty + if (!user.getName().isEmpty()) { + players.setPlayerName(user); + players.save(playerUUID); + } else { + plugin.logWarning("Player that just logged in has no name! " + playerUUID.toString()); + } + if (plugin.getIWM().inWorld(user.getLocation()) && Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) { + plugin.getIslands().clearArea(user.getLocation()); + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerQuit(final PlayerQuitEvent event) { + players.removeOnlinePlayer(event.getPlayer().getUniqueId()); + User.removePlayer(event.getPlayer()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/NetherPortals.java b/src/main/java/us/tastybento/bskyblock/listeners/NetherPortals.java new file mode 100644 index 0000000..2c35b77 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/NetherPortals.java @@ -0,0 +1,242 @@ +package us.tastybento.bskyblock.listeners; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.event.world.StructureGrowEvent; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; +import us.tastybento.bskyblock.util.teleport.SafeTeleportBuilder; + +public class NetherPortals implements Listener { + private static final String ERROR_NO_PERMISSION = "general.errors.no-permission"; + private final BSkyBlock plugin; + + public NetherPortals(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * Function to check proximity to nether or end spawn location. + * Used when playing with the standard nether or end. + * + * @param location - the location + * @return true if in the spawn area, false if not + */ + private boolean atSpawn(Location location) { + Vector p = location.toVector().multiply(new Vector(1, 0, 1)); + Vector spawn = location.getWorld().getSpawnLocation().toVector().multiply(new Vector(1, 0, 1)); + int radiusSquared = plugin.getIWM().getNetherSpawnRadius(location.getWorld()) ^ 2; + return (spawn.distanceSquared(p) < radiusSquared); + } + + /** + * If the player is not in the standard nether or standard end or op, do nothing. + * Used to protect the standard spawn for nether or end + * @param player - the player + * @return true if nothing needs to be done + */ + private boolean noAction(Player player) { + if (player.isOp() + || player.getWorld().getEnvironment().equals(Environment.NORMAL) + || !plugin.getIWM().inWorld(player.getLocation())) { + return true; + } + // Player is in an island world and in a nether or end + return (player.getWorld().getEnvironment().equals(Environment.NETHER) && plugin.getIWM().isNetherIslands(player.getWorld())) + || (player.getWorld().getEnvironment().equals(Environment.THE_END) && plugin.getIWM().isEndIslands(player.getWorld())); + } + + /** + * Prevents blocks from being broken + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlock().getLocation())) { + User.getInstance(e.getPlayer()).sendMessage(ERROR_NO_PERMISSION); + e.setCancelled(true); + } + } + + /** + * Protects standard nether or end spawn from bucket abuse + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBucketEmpty(PlayerBucketEmptyEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlockClicked().getLocation())) { + User.getInstance(e.getPlayer()).sendMessage(ERROR_NO_PERMISSION); + e.setCancelled(true); + } + } + + /** + * Handle end portals + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEndIslandPortal(PlayerPortalEvent e) { + if (!e.getCause().equals(TeleportCause.END_PORTAL) || !plugin.getIWM().inWorld(e.getFrom())) { + return; + } + World overWorld = Util.getWorld(e.getFrom().getWorld()); + // If entering a portal in the end, teleport home if you have one, else do nothing + if (e.getFrom().getWorld().getEnvironment().equals(Environment.THE_END)) { + if (plugin.getIslands().hasIsland(overWorld, e.getPlayer().getUniqueId())) { + e.setCancelled(true); + plugin.getIslands().homeTeleport(overWorld, e.getPlayer()); + } + return; + } + // Going to the end, then go to the same location in the end world + if (plugin.getIWM().isEndGenerate(overWorld) && plugin.getIWM().isEndIslands(overWorld)) { + World endWorld = plugin.getIWM().getEndWorld(overWorld); + // End exists and end islands are being used + Location to = plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.THE_END)).orElse(e.getFrom().toVector().toLocation(endWorld)); + e.setCancelled(true); + new SafeTeleportBuilder(plugin) + .entity(e.getPlayer()) + .location(to) + .build(); + } + } + + /** + * This handles non-player portal use + * Currently disables portal use by entities + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEntityPortal(EntityPortalEvent e) { + if (plugin.getIWM().inWorld(e.getFrom())) { + // Disable entity portal transfer due to dupe glitching + e.setCancelled(true); + } + } + + /** + * Prevent standard nether or end spawns from being blown up + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onExplosion(EntityExplodeEvent e) { + if (!plugin.getIWM().inWorld(e.getLocation()) + || plugin.getIWM().isIslandNether(e.getLocation().getWorld()) + || plugin.getIWM().isIslandEnd(e.getLocation().getWorld())) { + // Not used in island worlds + return false; + } + // Find out what is exploding + Entity expl = e.getEntity(); + if (expl == null) { + return false; + } + e.blockList().removeIf(b -> atSpawn(b.getLocation())); + return true; + } + + /** + * Handle nether portals + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onNetherPortal(PlayerPortalEvent e) { + if (!e.getCause().equals(TeleportCause.NETHER_PORTAL) || !plugin.getIWM().inWorld(e.getFrom())) { + return false; + } + // Get the overworld, which may be the same world + World overWorld = Util.getWorld(e.getFrom().getWorld()); + // If entering a portal in the nether, teleport to portal in overworld if there is one + if (e.getFrom().getWorld().getEnvironment().equals(Environment.NETHER)) { + // If this is from the island nether, then go to the same vector, otherwise try island home location + Location to = plugin.getIWM().isNetherIslands(overWorld) + ? plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NORMAL)).orElse(e.getFrom().toVector().toLocation(overWorld)) + : plugin.getIslands().getIslandLocation(overWorld, e.getPlayer().getUniqueId()); + + e.setCancelled(true); + // Else other worlds teleport to the nether + new SafeTeleportBuilder(plugin) + .entity(e.getPlayer()) + .location(to) + .portal() + .build(); + return true; + } + World nether = plugin.getIWM().getNetherWorld(overWorld); + // If this is to island nether, then go to the same vector, otherwise try spawn + Location to = (plugin.getIWM().isNetherIslands(overWorld) && plugin.getIWM().isNetherGenerate(overWorld)) + ? plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NETHER)).orElse(e.getFrom().toVector().toLocation(nether)) + : nether.getSpawnLocation(); + e.setCancelled(true); + // Else other worlds teleport to the nether + new SafeTeleportBuilder(plugin) + .entity(e.getPlayer()) + .location(to) + .portal() + .build(); + return true; + } + + /** + * Prevents placing of blocks at standard nether or end spawns + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerBlockPlace(BlockPlaceEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlock().getLocation())) { + User.getInstance(e.getPlayer()).sendMessage(ERROR_NO_PERMISSION); + e.setCancelled(true); + } + } + + /** + * Converts trees to gravel and glowstone + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onTreeGrow(StructureGrowEvent e) { + if (!plugin.getIWM().isNetherTrees(e.getWorld()) || !e.getWorld().getEnvironment().equals(Environment.NETHER)) { + return false; + } + for (BlockState b : e.getBlocks()) { + if (b.getType() == Material.LOG || b.getType() == Material.LOG_2) { + b.setType(Material.GRAVEL); + } else if (b.getType() == Material.LEAVES || b.getType() == Material.LEAVES_2) { + b.setType(Material.GLOWSTONE); + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/listeners/ObsidianToLava.java b/src/main/java/us/tastybento/bskyblock/listeners/ObsidianToLava.java new file mode 100644 index 0000000..5b9bb23 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/ObsidianToLava.java @@ -0,0 +1,89 @@ +package us.tastybento.bskyblock.listeners; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; + +/** + * Enables changing of obsidian back into lava + * @author tastybento + * + */ +public class ObsidianToLava implements Listener { + + private BSkyBlock plugin; + + /** + * @param plugin - plugin + */ + public ObsidianToLava(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * Enables changing of obsidian back into lava + * + * @param e - event + * @return false if obsidian not scooped, true if scooped + */ + @EventHandler(priority = EventPriority.NORMAL) + public boolean onPlayerInteract(final PlayerInteractEvent e) { + if (!plugin.getSettings().isAllowObsidianScooping() + || !plugin.getIWM().inWorld(e.getPlayer().getLocation()) + || !e.getPlayer().getGameMode().equals(GameMode.SURVIVAL) + || !e.getAction().equals(Action.RIGHT_CLICK_BLOCK) + || !(e.getItem() != null && e.getItem().getType().equals(Material.BUCKET)) + || !(e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.OBSIDIAN))) { + return false; + } + User user = User.getInstance(e.getPlayer()); + if (plugin.getIslands().userIsOnIsland(user.getWorld(), user)) { + // Look around to see if this is a lone obsidian block + Block b = e.getClickedBlock(); + + for (Block testBlock : getBlocksAround(b)) { + if (testBlock.getType().equals(Material.OBSIDIAN)) { + // Do nothing special + return false; + } + } + + user.sendMessage("general.tips.changing-obsidian-to-lava"); + e.getItem().setType(Material.LAVA_BUCKET); + e.getPlayer().getWorld().playSound(e.getPlayer().getLocation(), Sound.ITEM_BUCKET_FILL_LAVA, 1F, 1F); + e.getClickedBlock().setType(Material.AIR); + e.setCancelled(true); + return true; + } + + return false; + } + + private List getBlocksAround(Block b) { + List blocksAround = new ArrayList<>(); + for (int x = -2; x <= 2; x++) { + for (int y = -2; y <= 2; y++) { + for (int z = -2; z <= 2; z++) { + blocksAround.add(b.getWorld().getBlockAt(b.getX() + x, b.getY() + y, b.getZ() + z)); + } + } + } + + // Remove the block at x = 0, y = 0 and z = 0 (which is the base block) + blocksAround.remove(b); + + return blocksAround; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/PanelListenerManager.java b/src/main/java/us/tastybento/bskyblock/listeners/PanelListenerManager.java new file mode 100644 index 0000000..ffa7961 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/PanelListenerManager.java @@ -0,0 +1,82 @@ +package us.tastybento.bskyblock.listeners; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; + +public class PanelListenerManager implements Listener { + + private static HashMap openPanels = new HashMap<>(); + + @EventHandler(priority = EventPriority.LOWEST) + public void onInventoryClick(InventoryClickEvent event) { + // Close inventory if clicked outside and if setting is true + if (BSkyBlock.getInstance().getSettings().isClosePanelOnClickOutside() && event.getSlotType().equals(SlotType.OUTSIDE)) { + event.getWhoClicked().closeInventory(); + return; + } + User user = User.getInstance(event.getWhoClicked()); // The player that clicked the item + Inventory inventory = event.getInventory(); // The inventory that was + // Open the inventory panel that this player has open (they can only ever have one) + if (openPanels.containsKey(user.getUniqueId())) { + // Check the name of the panel + if (inventory.getName().equals(openPanels.get(user.getUniqueId()).getInventory().getName())) { + // Cancel the event. If they don't want it to be canceled then the click handler(s) should uncancel it + event.setCancelled(true); + // Get the panel itself + Panel panel = openPanels.get(user.getUniqueId()); + // Check that they clicked on a specific item + PanelItem pi = panel.getItems().get(event.getRawSlot()); + if (pi != null) { + pi.getClickHandler().ifPresent(handler -> + // Execute the handler's onClick method and optionally cancel the event if the handler returns true + event.setCancelled(handler.onClick(panel, user, event.getClick(), event.getSlot()))); + } + // If there is a listener, then run it. + panel.getListener().ifPresent(l -> l.onInventoryClick(user, event)); + + } else { + // Wrong name - delete this panel + openPanels.remove(user.getUniqueId()); + } + } + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onInventoryClose(InventoryCloseEvent event) { + if (openPanels.containsKey(event.getPlayer().getUniqueId())) { + // Run any close inventory methods + openPanels.get(event.getPlayer().getUniqueId()).getListener().ifPresent(l -> l.onInventoryClose(event)); + openPanels.remove(event.getPlayer().getUniqueId()); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onLogOut(PlayerQuitEvent event) { + if (openPanels.containsKey(event.getPlayer().getUniqueId())) { + openPanels.remove(event.getPlayer().getUniqueId()); + } + } + + /** + * @return the openPanels + */ + public static Map getOpenPanels() { + return openPanels; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/BlockInteractionListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/BlockInteractionListener.java new file mode 100644 index 0000000..6d87c7e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/BlockInteractionListener.java @@ -0,0 +1,162 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.block.FlowerPot; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + * + */ +public class BlockInteractionListener extends AbstractFlagListener { + + /** + * Handle interaction with blocks + * @param e - event + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerInteract(final PlayerInteractEvent e) { + if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + return; + } + switch (e.getClickedBlock().getType()) { + case ANVIL: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.ANVIL); + break; + case BEACON: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BEACON); + break; + case BED_BLOCK: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BED); + break; + case BREWING_STAND: + case CAULDRON: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREWING); + break; + case CHEST: + case STORAGE_MINECART: + case TRAPPED_CHEST: + case BLACK_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case RED_SHULKER_BOX: + case SILVER_SHULKER_BOX: + case WHITE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case DISPENSER: + case DROPPER: + case HOPPER: + case HOPPER_MINECART: + + checkIsland(e, e.getClickedBlock().getLocation(), Flags.CHEST); + break; + + case ACACIA_DOOR: + case BIRCH_DOOR: + case DARK_OAK_DOOR: + case IRON_DOOR: + case IRON_DOOR_BLOCK: + case JUNGLE_DOOR: + case SPRUCE_DOOR: + case WOODEN_DOOR: + case WOOD_DOOR: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.DOOR); + break; + case TRAP_DOOR: + case IRON_TRAPDOOR: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.TRAPDOOR); + break; + case ACACIA_FENCE_GATE: + case BIRCH_FENCE_GATE: + case DARK_OAK_FENCE_GATE: + case FENCE_GATE: + case JUNGLE_FENCE_GATE: + case SPRUCE_FENCE_GATE: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.GATE); + break; + + case BURNING_FURNACE: + case FURNACE: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.FURNACE); + break; + case ENCHANTMENT_TABLE: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.ENCHANTING); + break; + case ENDER_CHEST: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.ENDER_CHEST); + break; + case JUKEBOX: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.JUKEBOX); + break; + case NOTE_BLOCK: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.NOTE_BLOCK); + break; + case WORKBENCH: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.CRAFTING); + break; + case STONE_BUTTON: + case WOOD_BUTTON: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BUTTON); + break; + case LEVER: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.LEVER); + break; + case DIODE: + case DIODE_BLOCK_OFF: + case DIODE_BLOCK_ON: + case REDSTONE_COMPARATOR_ON: + case REDSTONE_COMPARATOR_OFF: + case DAYLIGHT_DETECTOR: + case DAYLIGHT_DETECTOR_INVERTED: + case REDSTONE_COMPARATOR: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.REDSTONE); + break; + case DRAGON_EGG: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREAK_BLOCKS); + break; + case ENDER_PORTAL_FRAME: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS); + break; + case FLOWER_POT: + FlowerPot pot = (FlowerPot) e.getClickedBlock().getState(); + if (pot.getContents() == null || pot.getContents().getItemType().equals(Material.AIR)) { + checkIsland(e, e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS); + } else { + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREAK_BLOCKS); + } + break; + default: + break; + + } + // Now check for in-hand items + if (e.getItem() != null) { + switch (e.getItem().getType()) { + case ENDER_PEARL: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.ENDER_PEARL); + break; + case MONSTER_EGG: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.SPAWN_EGGS); + break; + default: + break; + + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/BreakBlocksListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/BreakBlocksListener.java new file mode 100644 index 0000000..05083b5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/BreakBlocksListener.java @@ -0,0 +1,125 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.World.Environment; +import org.bukkit.block.Block; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.util.BlockIterator; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +public class BreakBlocksListener extends AbstractFlagListener { + + /** + * Prevents blocks from being broken + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockBreak(final BlockBreakEvent e) { + checkIsland(e, e.getBlock().getLocation(), Flags.BREAK_BLOCKS); + } + + /** + * Prevents the breakage of hanging items + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW) + public void onBreakHanging(final HangingBreakByEntityEvent e) { + if (e.getRemover() instanceof Player) { + setUser(User.getInstance(e.getRemover())).checkIsland(e, e.getEntity().getLocation(), Flags.BREAK_BLOCKS); + } + } + + /** + * Handles breaking objects + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerInteract(final PlayerInteractEvent e) { + // Only handle hitting things + if (!e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { + return; + } + + // Look along player's sight line to see if any blocks are skulls + try { + BlockIterator iter = new BlockIterator(e.getPlayer(), 10); + Block lastBlock = iter.next(); + while (iter.hasNext()) { + lastBlock = iter.next(); + if (lastBlock.getType().equals(Material.SKULL)) { + checkIsland(e, lastBlock.getLocation(), Flags.BREAK_BLOCKS); + return; + } + } + } catch (Exception ignored) {} + + switch (e.getClickedBlock().getType()) { + case CAKE_BLOCK: + case DRAGON_EGG: + case MOB_SPAWNER: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREAK_BLOCKS); + return; + case BED_BLOCK: + if (e.getPlayer().getWorld().getEnvironment().equals(Environment.NETHER)) { + // Prevent explosions checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREAK_BLOCKS); + return; + } + break; + default: + break; + } + } + + /** + * Handles vehicle breaking + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onVehicleDamageEvent(VehicleDamageEvent e) { + if (getIWM().inWorld(e.getVehicle().getLocation()) && e.getAttacker() instanceof Player) { + setUser(User.getInstance((Player) e.getAttacker())); + checkIsland(e, e.getVehicle().getLocation(), Flags.BREAK_BLOCKS); + } + } + + /** + * Protect item frames, armor stands, etc. Entities that are actually blocks... + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEntityDamage(EntityDamageByEntityEvent e) { + // Only handle item frames and armor stands + if (!(e.getEntity() instanceof ItemFrame) && !(e.getEntity() instanceof ArmorStand)) { + return; + } + + // Get the attacker + if (e.getDamager() instanceof Player) { + setUser(User.getInstance(e.getDamager())).checkIsland(e, e.getEntity().getLocation(), Flags.BREAK_BLOCKS); + } else if (e.getDamager() instanceof Projectile) { + // Find out who fired the arrow + Projectile p = (Projectile) e.getDamager(); + if (p.getShooter() instanceof Player && !setUser(User.getInstance((Player)p.getShooter())).checkIsland(e, e.getEntity().getLocation(), Flags.BREAK_BLOCKS)) { + e.getEntity().setFireTicks(0); + e.getDamager().remove(); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/BreedingListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/BreedingListener.java new file mode 100644 index 0000000..c7e22e4 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/BreedingListener.java @@ -0,0 +1,49 @@ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.entity.Animals; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles breeding protection + * Note - animal protection is done elsewhere. + * @author tastybento + * + */ +public class BreedingListener extends AbstractFlagListener { + + /** + * A list of items that cause breeding if a player has them in their hand and they click an animal + * This list may need to be extended with future versions of Minecraft. + */ + private static final List BREEDING_ITEMS = Arrays.asList( + Material.EGG, + Material.WHEAT, + Material.CARROT_ITEM, + Material.SEEDS); + + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onPlayerInteract(final PlayerInteractAtEntityEvent e) { + if (e.getRightClicked() instanceof Animals) { + ItemStack inHand = e.getPlayer().getInventory().getItemInMainHand(); + if (e.getHand().equals(EquipmentSlot.OFF_HAND)) { + inHand = e.getPlayer().getInventory().getItemInOffHand(); + } + if (inHand != null && BREEDING_ITEMS.contains(inHand.getType())) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.BREEDING); + } + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/BucketListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/BucketListener.java new file mode 100644 index 0000000..6eacf76 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/BucketListener.java @@ -0,0 +1,54 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles interaction with beds + * Note - bed protection from breaking or placing is done elsewhere. + * @author tastybento + * + */ +public class BucketListener extends AbstractFlagListener { + + /** + * Prevents emptying of buckets + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBucketEmpty(final PlayerBucketEmptyEvent e) { + if (e.getBlockClicked() != null) { + // This is where the water or lava actually will be dumped + Block dumpBlock = e.getBlockClicked().getRelative(e.getBlockFace()); + checkIsland(e, dumpBlock.getLocation(), Flags.BUCKET); + } + } + + /** + * Prevents collecting of lava, water, milk. If bucket use is denied in general, it is blocked. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW) + public void onBucketFill(final PlayerBucketFillEvent e) { + // Check filling of various liquids + if (e.getItemStack().getType().equals(Material.LAVA_BUCKET) && (!checkIsland(e, e.getBlockClicked().getLocation(), Flags.COLLECT_LAVA))) { + return; + } + if (e.getItemStack().getType().equals(Material.WATER_BUCKET) && (!checkIsland(e, e.getBlockClicked().getLocation(), Flags.COLLECT_WATER))) { + return; + } + if (e.getItemStack().getType().equals(Material.MILK_BUCKET) && (!checkIsland(e, e.getBlockClicked().getLocation(), Flags.MILKING))) { + return; + } + // Check general bucket use + checkIsland(e, e.getBlockClicked().getLocation(), Flags.BUCKET); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListener.java new file mode 100644 index 0000000..45a8181 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListener.java @@ -0,0 +1,29 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityExplodeEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + * + */ +public class ChestDamageListener extends AbstractFlagListener { + /** + * Prevent chest damage from explosion + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onExplosion(final EntityExplodeEvent e) { + if (getIWM().inWorld(e.getLocation()) && !Flags.CHEST_DAMAGE.isSetForWorld(e.getLocation().getWorld())) { + e.blockList().removeIf(b -> b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListener.java new file mode 100644 index 0000000..196152c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListener.java @@ -0,0 +1,39 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.world.ChunkLoadEvent; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Cleans super-flat world chunks or normal nether chunks if they generate accidentally + * due to lack of a generator being loaded + * @author tastybento + * + */ +public class CleanSuperFlatListener extends AbstractFlagListener { + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onChunkLoad(ChunkLoadEvent e) { + BSkyBlock plugin = BSkyBlock.getInstance(); + World world = e.getWorld(); + if (!e.getChunk().getBlock(0, 0, 0).getType().equals(Material.BEDROCK) + || !Flags.CLEAN_SUPER_FLAT.isSetForWorld(world) + || (world.getEnvironment().equals(Environment.NETHER) && (!plugin.getIWM().isNetherGenerate(world) || !plugin.getIWM().isNetherIslands(world))) + || (world.getEnvironment().equals(Environment.THE_END) && (!plugin.getIWM().isEndGenerate(world) || !plugin.getIWM().isEndIslands(world)))) { + return; + } + world.regenerateChunk(e.getChunk().getX(), e.getChunk().getZ()); + plugin.logWarning("Regenerating superflat chunk in " + world.getName() + " at blocks " + (e.getChunk().getX() * 16) + "," + (e.getChunk().getZ() * 16)); + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/CreeperListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/CreeperListener.java new file mode 100644 index 0000000..13590e7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/CreeperListener.java @@ -0,0 +1,55 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityExplodeEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Listens for creepers hsssssssh! + * For the {@link us.tastybento.bskyblock.lists.Flags#CREEPER_DAMAGE} + * and {@link us.tastybento.bskyblock.lists.Flags#CREEPER_GRIEFING} flags. + * @author tastybento + * + */ +public class CreeperListener extends AbstractFlagListener { + + /** + * Prevent damage from explosion + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onExplosion(final EntityExplodeEvent e) { + if (e.getEntity() == null || !e.getEntityType().equals(EntityType.CREEPER) || !getIWM().inWorld(e.getLocation())) { + return; + } + // If creeper damage is not allowed in world, remove it + if (!Flags.CREEPER_DAMAGE.isSetForWorld(e.getLocation().getWorld())) { + // If any were removed, then prevent damage too + e.blockList().clear(); + e.setCancelled(true); + return; + } + // Check for griefing + Creeper creeper = (Creeper)e.getEntity(); + if (!Flags.CREEPER_GRIEFING.isSetForWorld(e.getLocation().getWorld()) && creeper.getTarget() instanceof Player) { + Player target = (Player)creeper.getTarget(); + if (!getIslands().locationIsOnIsland(target, e.getLocation())) { + User user = User.getInstance(target); + user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(Flags.CREEPER_GRIEFING.getHintReference())); + e.setCancelled(true); + e.blockList().clear(); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/EggListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/EggListener.java new file mode 100644 index 0000000..1b97296 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/EggListener.java @@ -0,0 +1,28 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerEggThrowEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles throwing regular eggs (not spawn eggs) + * @author tastybento + * + */ +public class EggListener extends AbstractFlagListener { + + /** + * Handle visitor chicken egg throwing + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEggThrow(PlayerEggThrowEvent e) { + if (!checkIsland(e, e.getEgg().getLocation(), Flags.EGGS)) { + e.setHatching(false); + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/EnderChestListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/EnderChestListener.java new file mode 100644 index 0000000..6a437ce --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/EnderChestListener.java @@ -0,0 +1,60 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Prevents enderchest use and creation in world if it is not allowed + * @author tastybento + * + */ +public class EnderChestListener extends AbstractFlagListener { + + /** + * Prevents opening ender chest unless player has permission + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEnderChestOpen(PlayerInteractEvent e) { + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + e.setCancelled(checkEnderChest(e.getPlayer(), e.getClickedBlock().getType())); + } + } + + /** + * Prevents crafting of EnderChest unless the player has permission + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onCraft(CraftItemEvent e) { + e.setCancelled(checkEnderChest((Player)e.getWhoClicked(), e.getRecipe().getResult().getType())); + } + + private boolean checkEnderChest(Player player, Material type) { + if (type.equals(Material.ENDER_CHEST) + && getIWM().inWorld(player.getLocation()) + && !player.isOp() + && !player.hasPermission(getPlugin().getIWM().getPermissionPrefix(player.getWorld()) + ".craft.enderchest") + && !Flags.ENDER_CHEST.isSetForWorld(player.getWorld())) { + // Not allowed + User user = User.getInstance(player); + user.sendMessage("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(Flags.ENDER_CHEST.getHintReference())); + return true; + } + return false; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/EndermanListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/EndermanListener.java new file mode 100644 index 0000000..f7da1d7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/EndermanListener.java @@ -0,0 +1,59 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.entity.Enderman; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.material.MaterialData; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Listens for Endermen + * For the {@link us.tastybento.bskyblock.lists.Flags#ENDERMAN_GRIEFING} + * and {@link us.tastybento.bskyblock.lists.Flags#CREEPER_GRIEFING} flags. + * @author tastybento + * + */ +public class EndermanListener extends AbstractFlagListener { + /** + * Allows or prevents enderman griefing + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEndermanGrief(final EntityChangeBlockEvent e) { + if (!(e.getEntity() instanceof Enderman) || !getIWM().inWorld(e.getEntity().getLocation())) { + return; + } + if (!Flags.ENDERMAN_GRIEFING.isSetForWorld(e.getEntity().getWorld())) { + e.setCancelled(true); + } + } + + /** + * Drops the Enderman's block when he dies if he has one + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEndermanDeath(final EntityDeathEvent e) { + if (!(e.getEntity() instanceof Enderman) + || !getIWM().inWorld(e.getEntity().getLocation()) + || !Flags.ENDERMAN_DEATH_DROP.isSetForWorld(e.getEntity().getWorld())) { + return; + } + // Get the block the enderman is holding + Enderman ender = (Enderman) e.getEntity(); + MaterialData m = ender.getCarriedMaterial(); + if (m != null && !m.getItemType().equals(Material.AIR)) { + // Drop the item + e.getEntity().getWorld().dropItemNaturally(e.getEntity().getLocation(), m.toItemStack(1)); + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/EnterExitListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/EnterExitListener.java new file mode 100644 index 0000000..35e4bd9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/EnterExitListener.java @@ -0,0 +1,61 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Optional; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; + +/** + * The Enter/Exit messages flag is a global flag and applies everywhere + * @author tastybento + * + */ +public class EnterExitListener extends AbstractFlagListener { + + private static final Vector XZ = new Vector(1,0,1); + private static final String NAME = "[name]"; + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onMove(PlayerMoveEvent e) { + // Only process if Enter Exit flags are active, we are in the right world and there is a change in X or Z coords + if (!getIWM().inWorld(e.getFrom()) + || e.getFrom().toVector().multiply(XZ).equals(e.getTo().toVector().multiply(XZ)) + || !Flags.ENTER_EXIT_MESSAGES.isSetForWorld(e.getFrom().getWorld())) { + return; + } + Optional from = this.getIslands().getProtectedIslandAt(e.getFrom()); + Optional to = this.getIslands().getProtectedIslandAt(e.getTo()); + + /* + * Options: + * + * from = empty, to = island - entering + * from = island1, to = island2 - leaving 1, entering 2 + * from = island, to = empty - leaving + * from = empty, to = empty + * from = island, to = island + */ + if (from.equals(to)) { + return; + } + User user = User.getInstance(e.getPlayer()); + // Send message if island is owned by someone + from.filter(i -> i.getOwner() != null).ifPresent(i -> user.sendMessage("protection.flags.ENTER_EXIT_MESSAGES.now-leaving", NAME, !i.getName().isEmpty() ? i.getName() : + user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", NAME, getPlugin().getPlayers().getName(i.getOwner())))); + to.filter(i -> i.getOwner() != null).ifPresent(i -> user.sendMessage("protection.flags.ENTER_EXIT_MESSAGES.now-entering", NAME, !i.getName().isEmpty() ? i.getName() : + user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", NAME, getPlugin().getPlayers().getName(i.getOwner())))); + // Send message if island is unowned, but has a name + from.filter(i -> i.getOwner() == null && !i.getName().isEmpty()).ifPresent(i -> user.sendMessage("protection.flags.ENTER_EXIT_MESSAGES.now-leaving", NAME, i.getName())); + to.filter(i -> i.getOwner() == null && !i.getName().isEmpty()).ifPresent(i -> user.sendMessage("protection.flags.ENTER_EXIT_MESSAGES.now-entering", NAME, i.getName())); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/EntityInteractListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/EntityInteractListener.java new file mode 100644 index 0000000..f826b36 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/EntityInteractListener.java @@ -0,0 +1,41 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.Animals; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles interaction with entities like armor stands + * Note - armor stand protection from breaking or placing is done elsewhere. + * @author tastybento + * + */ +public class EntityInteractListener extends AbstractFlagListener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onPlayerInteract(final PlayerInteractAtEntityEvent e) { + if (e.getRightClicked() instanceof ArmorStand) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.ARMOR_STAND); + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerHitEntity(PlayerInteractEntityEvent e) { + // Animal riding + if (e.getRightClicked() instanceof Vehicle && e.getRightClicked() instanceof Animals) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.RIDING); + } + // Villager trading + if (e.getRightClicked().getType().equals(EntityType.VILLAGER)) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.TRADING); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/FireListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/FireListener.java new file mode 100644 index 0000000..eb6c43f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/FireListener.java @@ -0,0 +1,96 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.util.BlockIterator; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles fire + * @author tastybento + * + */ +public class FireListener extends AbstractFlagListener { + + /** + * Checks if fire is allowed. If not, cancels the action + * @param e - cancellable event + * @param l - location + * @param flag - flag to check + * @return - true if cancelled, false if not + */ + public boolean checkFire(Cancellable e, Location l, Flag flag) { + // Check world + if (!getIWM().inWorld(l)) { + return false; + } + // Check if the island exists and if fire is allowed + boolean cancel = getIslands().getIslandAt(l).map(i -> !i.isAllowed(flag)).orElse(!flag.isSetForWorld(l.getWorld())); + e.setCancelled(cancel); + return cancel; + } + + /** + * Prevents fire spread + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onBlockBurn(BlockBurnEvent e) { + return checkFire(e, e.getBlock().getLocation(), Flags.FIRE); + } + + /** + * Prevent fire spread + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onBlockSpread(BlockSpreadEvent e) { + return e.getSource().getType().equals(Material.FIRE) && checkFire(e, e.getBlock().getLocation(), Flags.FIRE_SPREAD); + } + + /** + * Igniting fires + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onBlockIgnite(BlockIgniteEvent e) { + // Check if this is a portal lighting - that is allowed any time + return !e.getBlock().getType().equals(Material.OBSIDIAN) && checkFire(e, e.getBlock().getLocation(), Flags.FIRE); + } + + /** + * Flint and Steel and Extinguishing fire + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerInteract(PlayerInteractEvent e) { + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getMaterial() != null && e.getMaterial().equals(Material.FLINT_AND_STEEL)) { + checkIsland(e, e.getClickedBlock().getLocation(), Flags.FIRE); + } + // Look along player's sight line to see if any blocks are fire. Players can hit fire out quite a long way away. + try { + BlockIterator iter = new BlockIterator(e.getPlayer(), 10); + while (iter.hasNext()) { + Block lastBlock = iter.next(); + if (lastBlock.getType().equals(Material.FIRE)) { + checkIsland(e, lastBlock.getLocation(), Flags.FIRE_EXTINGUISH); + } + } + } catch (Exception ex) { + // To catch at block iterator exceptions that can happen in the void or at the very top of blocks + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitClickListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitClickListener.java new file mode 100644 index 0000000..550d1b7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitClickListener.java @@ -0,0 +1,103 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.EntityType; +import org.bukkit.event.inventory.ClickType; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; +import us.tastybento.bskyblock.api.panels.builders.PanelBuilder; +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.util.Util; + +/** + * Provide geo limiting to mobs - removed them if they go outside island bounds + * @author tastybento + * + */ +public class GeoLimitClickListener implements ClickHandler { + + /** + * A list of all living entity types, minus some + */ + private final List livingEntityTypes = Arrays.stream(EntityType.values()) + .filter(EntityType::isAlive) + .filter(t -> !(t.equals(EntityType.PLAYER) || t.equals(EntityType.GIANT) || t.equals(EntityType.ARMOR_STAND))) + .sorted(Comparator.comparing(EntityType::name)) + .collect(Collectors.toList()); + + @Override + public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { + // Get the world + if (!user.inWorld()) { + user.sendMessage("general.errors.wrong-world"); + return true; + } + IslandWorldManager iwm = BSkyBlock.getInstance().getIWM(); + String reqPerm = iwm.getPermissionPrefix(Util.getWorld(user.getWorld())) + ".admin.settings.GEO_LIMIT_MOBS"; + if (!user.hasPermission(reqPerm)) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", "[permission]", reqPerm); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + return true; + } + + String panelName = user.getTranslation("protection.flags.GEO_LIMIT_MOBS.name"); + if (panel.getName().equals(panelName)) { + // This is a click on the geo limit panel + // Slot relates to the enum + EntityType c = livingEntityTypes.get(slot); + if (iwm.getGeoLimitSettings(user.getWorld()).contains(c.name())) { + iwm.getGeoLimitSettings(user.getWorld()).remove(c.name()); + } else { + iwm.getGeoLimitSettings(user.getWorld()).add(c.name()); + } + // Apply change to panel + panel.getInventory().setItem(slot, getPanelItem(c, user).getItem()); + } else { + // Open the Sub Settings panel + openPanel(user, panelName); + } + return true; + } + + private void openPanel(User user, String panelName) { + // Close the current panel + user.closeInventory(); + // Open a new panel + PanelBuilder pb = new PanelBuilder(); + pb.user(user).name(panelName); + // Make panel items + livingEntityTypes.forEach(c -> pb.item(getPanelItem(c, user))); + pb.build(); + + } + + private PanelItem getPanelItem(EntityType c, User user) { + PanelItemBuilder pib = new PanelItemBuilder(); + pib.name(Util.prettifyText(c.toString())); + pib.clickHandler(this); + if (BSkyBlock.getInstance().getIWM().getGeoLimitSettings(user.getWorld()).contains(c.name())) { + pib.icon(Material.GREEN_SHULKER_BOX); + pib.description(user.getTranslation("protection.panel.flag-item.setting-active")); + } else { + pib.icon(Material.RED_SHULKER_BOX); + pib.description(user.getTranslation("protection.panel.flag-item.setting-disabled")); + } + return pib.build(); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitMobsListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitMobsListener.java new file mode 100644 index 0000000..fa9ce21 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/GeoLimitMobsListener.java @@ -0,0 +1,85 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.projectiles.ProjectileSource; + +import us.tastybento.bskyblock.api.events.BSBReadyEvent; +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * Provide geo limiting to mobs - removed them if they go outside island bounds + * @author tastybento + * + */ +public class GeoLimitMobsListener extends AbstractFlagListener { + + private Map mobSpawnTracker = new WeakHashMap<>(); + + /** + * Start the tracker when the plugin is loaded + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void pluginReady(BSBReadyEvent event) { + // Kick off the task to remove entities that go outside island boundaries + Bukkit.getScheduler().runTaskTimer(getPlugin(), () -> { + mobSpawnTracker.entrySet().stream() + .filter(e -> !e.getValue().onIsland(e.getKey().getLocation())) + .map(Map.Entry::getKey) + .forEach(Entity::remove); + mobSpawnTracker.keySet().removeIf(e -> e == null || e.isDead()); + }, 20L, 20L); + } + + /** + * Track where the mob was created. This will determine its allowable movement zone. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void mobSpawn(CreatureSpawnEvent e) { + if (getIWM().inWorld(e.getLocation()) + && getIWM().getGeoLimitSettings(e.getLocation().getWorld()).contains(e.getEntityType().name())) { + getIslands().getIslandAt(e.getLocation()).ifPresent(i -> mobSpawnTracker.put(e.getEntity(), i)); + } + } + + /** + * Clean up the map when entity dies (does not handle entity removal) + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void MobDeath(final EntityDeathEvent e) { + mobSpawnTracker.remove(e.getEntity()); + } + + /** + * Deal with projectiles fired by entities + * @param e + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void ProjectileExplode(final ExplosionPrimeEvent e) { + if (e.getEntity() instanceof Projectile && getIWM().inWorld(e.getEntity().getLocation())) { + ProjectileSource source = ((Projectile)e.getEntity()).getShooter(); + if (source instanceof Entity) { + Entity shooter = (Entity)source; + if (mobSpawnTracker.containsKey(shooter) + && !mobSpawnTracker.get(shooter).onIsland(e.getEntity().getLocation())) { + e.getEntity().remove(); + e.setCancelled(true); + } + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/HurtingListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/HurtingListener.java new file mode 100644 index 0000000..05beec9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/HurtingListener.java @@ -0,0 +1,201 @@ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.entity.Animals; +import org.bukkit.entity.Entity; +import org.bukkit.entity.IronGolem; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Parrot; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Snowman; +import org.bukkit.entity.Squid; +import org.bukkit.entity.Villager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.potion.PotionEffect; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles hurting of monsters and animals directly and indirectly + * @author tastybento + * + */ +public class HurtingListener extends AbstractFlagListener { + + private HashMap thrownPotions = new HashMap<>(); + + /** + * Handles mob and monster protection + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEntityDamage(final EntityDamageByEntityEvent e) { + // Mobs being hurt + if (e.getEntity() instanceof Animals || e.getEntity() instanceof IronGolem || e.getEntity() instanceof Snowman) { + respond(e, e.getDamager(), Flags.HURT_ANIMALS); + } else if (e.getEntity() instanceof Villager) { + respond(e, e.getDamager(), Flags.HURT_VILLAGERS); + } else if (e.getEntity() instanceof Monster || e.getEntity() instanceof Squid || e.getEntity() instanceof Slime) { + respond(e, e.getDamager(), Flags.HURT_MONSTERS); + } + } + + /** + * Finds the true attacker, even if the attack was via a projectile + * @param e - event + * @param damager - damager + * @param flag - flag + */ + private void respond(EntityDamageByEntityEvent e, Entity damager, Flag flag) { + // Get the attacker + if (damager instanceof Player) { + setUser(User.getInstance(damager)).checkIsland(e, damager.getLocation(), flag); + } else if (damager instanceof Projectile) { + // Find out who fired the projectile + Projectile p = (Projectile) damager; + if (p.getShooter() instanceof Player) { + if (!setUser(User.getInstance((Player)p.getShooter())).checkIsland(e, damager.getLocation(), flag)) { + e.getEntity().setFireTicks(0); + damager.remove(); + } + } + } + } + + /** + * Handle attacks with a fishing rod + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onFishing(PlayerFishEvent e) { + if (e.getCaught() == null) { + return; + } + + if (((e.getCaught() instanceof Animals || e.getCaught() instanceof IronGolem || e.getCaught() instanceof Snowman) && checkIsland(e, e.getCaught().getLocation(), Flags.HURT_ANIMALS)) + || ((e.getCaught() instanceof Monster || e.getCaught() instanceof Squid || e.getCaught() instanceof Slime) && checkIsland(e, e.getCaught().getLocation(), Flags.HURT_MONSTERS)) + || (e.getCaught() instanceof Villager && checkIsland(e, e.getCaught().getLocation(), Flags.HURT_VILLAGERS))) { + e.getHook().remove(); + } + } + + /** + * Handles feeding cookies to parrots, which may hurt them + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerFeedParrots(PlayerInteractEntityEvent e) { + if (e.getRightClicked() instanceof Parrot) { + if ((e.getHand().equals(EquipmentSlot.HAND) && e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.COOKIE)) + || (e.getHand().equals(EquipmentSlot.OFF_HAND) && e.getPlayer().getInventory().getItemInOffHand().getType().equals(Material.COOKIE))) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.HURT_ANIMALS); + } + } + } + + /** + * Checks for splash damage. Remove damage if it should not affect. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onSplashPotionSplash(final PotionSplashEvent e) { + // Try to get the shooter + Projectile projectile = e.getEntity(); + if (projectile.getShooter() instanceof Player) { + Player attacker = (Player)projectile.getShooter(); + // Run through all the affected entities + for (LivingEntity entity: e.getAffectedEntities()) { + // Self damage + if (attacker.equals(entity)) { + continue; + } + // Monsters being hurt + if (entity instanceof Monster || entity instanceof Slime || entity instanceof Squid) { + if (!setUser(User.getInstance(attacker)).checkIsland(e, entity.getLocation(), Flags.HURT_MONSTERS)) { + for (PotionEffect effect : e.getPotion().getEffects()) { + entity.removePotionEffect(effect.getType()); + } + } + } + + // Mobs being hurt + if (entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman) { + if (!checkIsland(e, entity.getLocation(), Flags.HURT_ANIMALS)) { + for (PotionEffect effect : e.getPotion().getEffects()) { + entity.removePotionEffect(effect.getType()); + } + } + } + + // Villagers being hurt + if (entity instanceof Villager && !checkIsland(e, entity.getLocation(), Flags.HURT_VILLAGERS)) { + for (PotionEffect effect : e.getPotion().getEffects()) { + entity.removePotionEffect(effect.getType()); + } + } + } + } + } + + /** + * Handle lingering potions. This tracks when a potion has been initially splashed. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) { + // Try to get the shooter + Projectile projectile = e.getEntity(); + if (projectile.getShooter() instanceof Player) { + UUID uuid = ((Player)projectile.getShooter()).getUniqueId(); + // Store it and remove it when the effect is gone + thrownPotions.put(e.getAreaEffectCloud().getEntityId(), uuid); + getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), () -> thrownPotions.remove(e.getAreaEffectCloud().getEntityId()), e.getAreaEffectCloud().getDuration()); + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onLingeringPotionDamage(final EntityDamageByEntityEvent e) { //FIXME No need of #setCancelled() there? + if (e.getEntity() == null || e.getEntity().getUniqueId() == null) { + return; + } + + if (e.getCause().equals(DamageCause.ENTITY_ATTACK) && thrownPotions.containsKey(e.getDamager().getEntityId())) { + UUID attacker = thrownPotions.get(e.getDamager().getEntityId()); + // Self damage + if (attacker.equals(e.getEntity().getUniqueId())) { + return; + } + Entity entity = e.getEntity(); + // Monsters being hurt + if (entity instanceof Monster || entity instanceof Slime || entity instanceof Squid) { + checkIsland(e, entity.getLocation(), Flags.HURT_MONSTERS); + } + // Mobs being hurt + if (entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman) { + checkIsland(e, entity.getLocation(), Flags.HURT_ANIMALS); + } + // Villagers being hurt + if (entity instanceof Villager) { + checkIsland(e, entity.getLocation(), Flags.HURT_VILLAGERS); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/InventoryListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/InventoryListener.java new file mode 100644 index 0000000..755e7c1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/InventoryListener.java @@ -0,0 +1,58 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.block.Beacon; +import org.bukkit.block.BrewingStand; +import org.bukkit.block.Chest; +import org.bukkit.block.Dispenser; +import org.bukkit.block.Dropper; +import org.bukkit.block.Furnace; +import org.bukkit.block.Hopper; +import org.bukkit.block.ShulkerBox; +import org.bukkit.entity.Animals; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.InventoryClickEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles inventory protection + * @author tastybento + * + */ +public class InventoryListener extends AbstractFlagListener { + + /** + * Prevents visitors picking items from inventories + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onMountInventoryClick(InventoryClickEvent e) { + if (e.getInventory().getHolder() == null) { + return; + } + if (e.getInventory().getHolder() instanceof Animals) { + checkIsland(e, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY); + } + else if (e.getInventory().getHolder() instanceof Chest + || e.getInventory().getHolder() instanceof Dispenser + || e.getInventory().getHolder() instanceof Hopper + || e.getInventory().getHolder() instanceof Dropper + || e.getInventory().getHolder() instanceof ShulkerBox) { + setUser(User.getInstance(e.getWhoClicked())).checkIsland(e, e.getInventory().getLocation(), Flags.CHEST); + } + else if (e.getInventory().getHolder() instanceof Furnace) { + setUser(User.getInstance(e.getWhoClicked())).checkIsland(e, e.getInventory().getLocation(), Flags.FURNACE); + } + else if (e.getInventory().getHolder() instanceof BrewingStand) { + setUser(User.getInstance(e.getWhoClicked())).checkIsland(e, e.getInventory().getLocation(), Flags.BREWING); + } + else if (e.getInventory().getHolder() instanceof Beacon) { + setUser(User.getInstance(e.getWhoClicked())).checkIsland(e, e.getInventory().getLocation(), Flags.BEACON); + } + } + + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListener.java new file mode 100644 index 0000000..087056c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListener.java @@ -0,0 +1,123 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Arrays; + +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.inventory.ClickType; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; +import us.tastybento.bskyblock.api.panels.builders.PanelBuilder; +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; +import us.tastybento.bskyblock.util.teleport.SafeTeleportBuilder; + +/** + * Listener for invincible visitor settings. Handles click listening and damage events + * @author tastybento + * + */ +public class InvincibleVisitorsListener extends AbstractFlagListener implements ClickHandler { + + @Override + public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { + // Get the world + if (!user.inWorld()) { + user.sendMessage("general.errors.wrong-world"); + return true; + } + String reqPerm = getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + ".admin.settings.INVINCIBLE_VISITORS"; + if (!user.hasPermission(reqPerm)) { + user.sendMessage("general.errors.no-permission"); + user.sendMessage("general.errors.you-need", "[permission]", reqPerm); + user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); + return true; + } + + String ivPanelName = user.getTranslation("protection.flags.INVINCIBLE_VISITORS.name"); + if (panel.getName().equals(ivPanelName)) { + // This is a click on the IV panel + // Slot relates to the enum + DamageCause c = Arrays.asList(EntityDamageEvent.DamageCause.values()).get(slot); + if (getIWM().getIvSettings(user.getWorld()).contains(c.name())) { + getIWM().getIvSettings(user.getWorld()).remove(c.name()); + } else { + getIWM().getIvSettings(user.getWorld()).add(c.name()); + } + // Apply change to panel + panel.getInventory().setItem(slot, getPanelItem(c, user).getItem()); + } else { + // Open the IV Settings panel + openPanel(user, ivPanelName); + } + return true; + } + + private void openPanel(User user, String ivPanelName) { + // Close the current panel + user.closeInventory(); + // Open a new panel for visitor protection + PanelBuilder pb = new PanelBuilder(); + pb.user(user).name(ivPanelName); + // Make panel items + Arrays.stream(EntityDamageEvent.DamageCause.values()).forEach(c -> pb.item(getPanelItem(c, user))); + pb.build(); + + } + + private PanelItem getPanelItem(DamageCause c, User user) { + PanelItemBuilder pib = new PanelItemBuilder(); + pib.name(Util.prettifyText(c.toString())); + pib.clickHandler(this); + if (getIWM().getIvSettings(user.getWorld()).contains(c.name())) { + pib.icon(Material.GREEN_SHULKER_BOX); + pib.description(user.getTranslation("protection.panel.flag-item.setting-active")); + } else { + pib.icon(Material.RED_SHULKER_BOX); + pib.description(user.getTranslation("protection.panel.flag-item.setting-disabled")); + } + return pib.build(); + } + + /** + * Prevents visitors from getting damage if a particular damage type is listed in the config + * @param e - event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onVisitorGetDamage(EntityDamageEvent e) { + World world = e.getEntity().getWorld(); + if (!(e.getEntity() instanceof Player) + || !getIWM().inWorld(e.getEntity().getLocation()) + || !getIWM().getIvSettings(world).contains(e.getCause().name()) + || getIslands().userIsOnIsland(world, User.getInstance(e.getEntity()))) { + return; + } + // Player is a visitor and should be protected from damage + e.setCancelled(true); + Player p = (Player) e.getEntity(); + // Handle the void - teleport player back to island in a safe spot + if(e.getCause().equals(DamageCause.VOID)) { + // Will be set back after the teleport + p.setGameMode(GameMode.SPECTATOR); + getIslands().getIslandAt(p.getLocation()).ifPresent(i -> new SafeTeleportBuilder(getPlugin()).entity(p).island(i).build()); + } + } + + + +} + diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListener.java new file mode 100644 index 0000000..0309bb8 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListener.java @@ -0,0 +1,57 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerRespawnEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles respawning back on island + * @author tastybento + * + */ +public class IslandRespawnListener extends AbstractFlagListener { + + Map respawn = new HashMap<>(); + + /** + * Tag players who die in island space and have an island + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerDeath(PlayerDeathEvent e) { + if (getIWM().inWorld(e.getEntity().getLocation()) && Flags.ISLAND_RESPAWN.isSetForWorld(e.getEntity().getWorld()) + && getIslands().hasIsland(e.getEntity().getWorld(), e.getEntity().getUniqueId())) { + respawn.put(e.getEntity().getUniqueId(), e.getEntity().getWorld()); + } + } + + /** + * Place players back on their island if respawn on island is true and active + * @param e - event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerRespawn(PlayerRespawnEvent e) { + if (Flags.ISLAND_RESPAWN.isSetForWorld(e.getPlayer().getWorld()) && respawn.containsKey(e.getPlayer().getUniqueId())) { + World world = respawn.get(e.getPlayer().getUniqueId()); + respawn.remove(e.getPlayer().getUniqueId()); + Location respawnLocation = getIslands().getSafeHomeLocation(world, User.getInstance(e.getPlayer().getUniqueId()), 1); + if (respawnLocation != null) { + e.setRespawnLocation(respawnLocation); + } + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemDropPickUpListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemDropPickUpListener.java new file mode 100644 index 0000000..28078b1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemDropPickUpListener.java @@ -0,0 +1,36 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.player.PlayerDropItemEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + */ +public class ItemDropPickUpListener extends AbstractFlagListener { + + /* + * Handle item drop by visitors + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onVisitorDrop(PlayerDropItemEvent e) { + checkIsland(e, e.getItemDrop().getLocation(), Flags.ITEM_DROP); + } + + /* + * Handle item pickup by visitors + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onVisitorPickup(EntityPickupItemEvent e) { + if (e.getEntity() instanceof Player) { + // Disallow, but don't tell the player an error + setUser(User.getInstance(e.getEntity())).checkIsland(e, e.getItem().getLocation(), Flags.ITEM_PICKUP, false); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListener.java new file mode 100644 index 0000000..1722607 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListener.java @@ -0,0 +1,51 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Protects item frames from damage by mobs + * @author tastybento + * + */ +public class ItemFrameListener extends AbstractFlagListener { + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemFrameDamage(final EntityDamageByEntityEvent e) { + check(e, e.getEntity(), e.getDamager()); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemFrameDamage(final HangingBreakByEntityEvent e) { + check(e, e.getEntity(), e.getRemover()); + } + + private void check(Cancellable e, Entity entity, Entity damager) { + if (entity instanceof ItemFrame + && getIWM().inWorld(entity.getLocation()) + && !Flags.ITEM_FRAME_DAMAGE.isSetForWorld(entity.getWorld()) + && !(damager instanceof Player)) { + if (damager instanceof Projectile) { + if (!(((Projectile) damager).getShooter() instanceof Player)) { + e.setCancelled(true); + } + } else { + e.setCancelled(true); + } + return; + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/LeashListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/LeashListener.java new file mode 100644 index 0000000..6db8c77 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/LeashListener.java @@ -0,0 +1,51 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerLeashEntityEvent; +import org.bukkit.event.hanging.HangingPlaceEvent; +import org.bukkit.event.player.PlayerUnleashEntityEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + * + */ +public class LeashListener extends AbstractFlagListener { + + /** + * Prevents leashing + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onLeashUse(PlayerLeashEntityEvent e) { + checkIsland(e, e.getEntity().getLocation(), Flags.LEASH); + } + + + /** + * Prevents unleashing + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onLeashUse(PlayerUnleashEntityEvent e) { + checkIsland(e, e.getEntity().getLocation(), Flags.LEASH); + } + + /** + * Prevents hitching + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerLeashHitch(final HangingPlaceEvent e) { + if (e.getEntity() != null && e.getEntity().getType().equals(EntityType.LEASH_HITCH)) { + checkIsland(e, e.getEntity().getLocation(), Flags.LEASH); + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListener.java new file mode 100644 index 0000000..46f83f1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListener.java @@ -0,0 +1,147 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.vehicle.VehicleMoveEvent; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Listener for the lock flag + * Also handles ban protection + * + * @author tastybento + * + */ +public class LockAndBanListener implements Listener { + + private enum CheckResult { + BANNED, + LOCKED, + OPEN + } + + + // Teleport check + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerTeleport(PlayerTeleportEvent e) { + e.setCancelled(!checkAndNotify(e.getPlayer(), e.getTo()).equals(CheckResult.OPEN)); + } + + // Movement check + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerMove(PlayerMoveEvent e) { + // Ignore only vertical movement + if (e.getFrom().getBlockX() - e.getTo().getBlockX() == 0 && e.getFrom().getBlockZ() - e.getTo().getBlockZ() == 0) { + return; + } + if (!checkAndNotify(e.getPlayer(), e.getTo()).equals(CheckResult.OPEN)) { + e.setCancelled(true); + e.getFrom().getWorld().playSound(e.getFrom(), Sound.BLOCK_ANVIL_HIT, 1F, 1F); + e.getPlayer().setVelocity(new Vector(0,0,0)); + e.getPlayer().setGliding(false); + } + // Check from - just in case the player is inside the island + if (!check(e.getPlayer(), e.getFrom()).equals(CheckResult.OPEN)) { + // Has to be done 1 tick later otherwise it doesn't happen for some reason... + Bukkit.getScheduler().runTask(BSkyBlock.getInstance(), () -> eject(e.getPlayer())); + } + } + + // Vehicle move check + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onVehicleMove(VehicleMoveEvent e) { + // Ignore only vertical movement + if (e.getFrom().getBlockX() - e.getTo().getBlockX() == 0 && e.getFrom().getBlockZ() - e.getTo().getBlockZ() == 0) { + return; + } + // For each Player in the vehicle + e.getVehicle().getPassengers().stream().filter(en -> en instanceof Player).map(en -> (Player)en).forEach(p -> { + if (!checkAndNotify(p, e.getTo()).equals(CheckResult.OPEN)) { + p.leaveVehicle(); + p.teleport(e.getFrom()); + e.getFrom().getWorld().playSound(e.getFrom(), Sound.BLOCK_ANVIL_HIT, 1F, 1F); + eject(p); + } + }); + } + + // Login check + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerLogin(PlayerJoinEvent e) { + if (!checkAndNotify(e.getPlayer(), e.getPlayer().getLocation()).equals(CheckResult.OPEN)) { + eject(e.getPlayer()); + } + } + + /** + * Check if a player is banned or the island is locked + * @param player - player + * @param loc - location to check + * @return CheckResult LOCKED, BANNED or OPEN. If an island is locked, that will take priority over banned + */ + private CheckResult check(Player player, Location loc) { + BSkyBlock plugin = BSkyBlock.getInstance(); + // Ops are allowed everywhere + if (player.isOp()) { + return CheckResult.OPEN; + } + // See if the island is locked to non-members or player is banned + return plugin.getIslands().getProtectedIslandAt(loc) + .map(is -> { + if (is.isBanned(player.getUniqueId())) { + return player.hasPermission(plugin.getIWM().getPermissionPrefix(loc.getWorld()) + ".mod.bypassban") ? CheckResult.OPEN : CheckResult.BANNED; + } + if (!is.isAllowed(User.getInstance(player), Flags.LOCK)) { + return player.hasPermission(plugin.getIWM().getPermissionPrefix(loc.getWorld()) + ".mod.bypasslock") ? CheckResult.OPEN : CheckResult.LOCKED; + } + return CheckResult.OPEN; + }).orElse(CheckResult.OPEN); + } + + /** + * Checks if a player is banned from this location and notifies them if so + * @param player - player + * @param loc - location to check + * @return true if banned + */ + private CheckResult checkAndNotify(Player player, Location loc) { + CheckResult r = check(player,loc); + switch (r) { + case BANNED: + User.getInstance(player).notify("commands.island.ban.you-are-banned"); + break; + case LOCKED: + User.getInstance(player).notify("protection.locked"); + break; + default: + break; + } + return r; + } + + /** + * Sends player home + * @param player - player + */ + private void eject(Player player) { + player.setGameMode(GameMode.SPECTATOR); + // Teleport player to their home + if (BSkyBlock.getInstance().getIslands().hasIsland(player.getWorld(), player.getUniqueId())) { + BSkyBlock.getInstance().getIslands().homeTeleport(player.getWorld(), player); + } // else, TODO: teleport somewhere else? + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListener.java new file mode 100644 index 0000000..26b7b96 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListener.java @@ -0,0 +1,59 @@ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.Optional; + +import org.bukkit.entity.Animals; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Slime; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles natural mob spawning. + * @author tastybento + * + */ +public class MobSpawnListener extends AbstractFlagListener { + + /** + * Prevents mobs spawning naturally + * + * @param e - event + * @return true if cancelled + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onNaturalMobSpawn(CreatureSpawnEvent e) { + // If not in the right world, return + if (e.getEntity() == null || !getIWM().inWorld(e.getEntity().getLocation())) { + return false; + } + // Deal with natural spawning + if (e.getSpawnReason().equals(SpawnReason.NATURAL) + || e.getSpawnReason().equals(SpawnReason.JOCKEY) + || e.getSpawnReason().equals(SpawnReason.CHUNK_GEN) + || e.getSpawnReason().equals(SpawnReason.DEFAULT) + || e.getSpawnReason().equals(SpawnReason.MOUNT) + || e.getSpawnReason().equals(SpawnReason.NETHER_PORTAL)) { + + Optional island = getIslands().getIslandAt(e.getLocation()); + // Cancel the event if these are true + if ((e.getEntity() instanceof Monster || e.getEntity() instanceof Slime)) { + boolean cancel = island.map(i -> !i.isAllowed(Flags.MONSTER_SPAWN)).orElse(!Flags.MONSTER_SPAWN.isSetForWorld(e.getEntity().getWorld())); + e.setCancelled(cancel); + return cancel; + } else if (e.getEntity() instanceof Animals) { + boolean cancel = island.map(i -> !i.isAllowed(Flags.ANIMAL_SPAWN)).orElse(!Flags.ANIMAL_SPAWN.isSetForWorld(e.getEntity().getWorld())); + e.setCancelled(cancel); + return cancel; + } + } + return false; + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListener.java new file mode 100644 index 0000000..60f7b9c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListener.java @@ -0,0 +1,29 @@ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockRedstoneEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +public class OfflineRedstoneListener extends AbstractFlagListener { + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockRedstone(BlockRedstoneEvent e) { + // If offline redstone is allowed then return + if (!Flags.OFFLINE_REDSTONE.isSetForWorld(e.getBlock().getWorld())) { + // Check if island exists and members are online + getIslands().getProtectedIslandAt(e.getBlock().getLocation()).ifPresent(i -> { + for (UUID uuid : i.getMemberSet()) { + if (Bukkit.getPlayer(uuid) != null) { + return; + } + } + e.setNewCurrent(0); + }); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PVPListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PVPListener.java new file mode 100644 index 0000000..e4cf6a5 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PVPListener.java @@ -0,0 +1,193 @@ +package us.tastybento.bskyblock.listeners.flags; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.AreaEffectCloudApplyEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.event.player.PlayerFishEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles PVP + * @author tastybento + * + */ +public class PVPListener extends AbstractFlagListener { + + private HashMap thrownPotions = new HashMap<>(); + + /** + * This method protects players from PVP if it is not allowed and from + * arrows fired by other players + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEntityDamage(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getLocation())) { + // Allow self damage + if (e.getEntity().equals(e.getDamager())) { + return; + } + // Protect visitors + if (e.getCause().equals(DamageCause.ENTITY_ATTACK) && protectedVisitor((Player)e.getEntity())) { + if (e.getDamager() instanceof Player) { + User.getInstance(e.getDamager()).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } else if (e.getDamager() instanceof Projectile && ((Projectile)e.getDamager()).getShooter() instanceof Player) { + User.getInstance((Player)((Projectile)e.getDamager()).getShooter()).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } + e.setCancelled(true); + } else { + // PVP check + respond(e, e.getDamager(), e.getEntity(), getFlag(e.getEntity().getWorld())); + } + } + } + + /** + * Checks how to respond to an attack + * @param e - event + * @param damager - entity doing the damaging + * @param flag - flag + */ + private void respond(Cancellable e, Entity damager, Entity hurtEntity, Flag flag) { + // Get the attacker + if (damager instanceof Player) { + User user = User.getInstance(damager); + if (!setUser(user).checkIsland((Event)e, damager.getLocation(), flag)) { + user.sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + e.setCancelled(true); + } + } else if (damager instanceof Projectile) { + // Find out who fired the arrow + Projectile p = (Projectile) damager; + if (p.getShooter() instanceof Player) { + // Allow self damage + if (hurtEntity.equals(p.getShooter())) { + return; + } + User user = User.getInstance((Player)p.getShooter()); + if (!setUser(user).checkIsland((Event)e, damager.getLocation(), flag)) { + damager.setFireTicks(0); + damager.remove(); + user.sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + e.setCancelled(true); + } + } + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onFishing(PlayerFishEvent e) { + if (e.getCaught() instanceof Player && getPlugin().getIWM().inWorld(e.getCaught().getLocation())) { + // Allow self-inflicted damage + if (e.getCaught().equals(e.getPlayer())) { + return; + } + // Protect visitors + if (protectedVisitor((Player)e.getCaught())) { + User.getInstance(e.getPlayer()).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + e.setCancelled(true); + } else if (!checkIsland(e, e.getCaught().getLocation(), getFlag(e.getCaught().getWorld()))) { + e.getHook().remove(); + User.getInstance(e.getPlayer()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + e.setCancelled(true); + } + } + } + + /** + * Checks for splash damage. Remove damage if it should not affect. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onSplashPotionSplash(final PotionSplashEvent e) { + if (e.getEntity().getShooter() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getLocation())) { + User user = User.getInstance((Player)e.getEntity().getShooter()); + // Run through affected entities and cancel the splash if any are a protected player + e.setCancelled(e.getAffectedEntities().stream().anyMatch(le -> blockPVP(user, le, e, getFlag(e.getEntity().getWorld())))); + } + } + + /** + * Check if PVP should be blocked or not + * @param user - user who is initiating the action + * @param le - Living entity involed + * @param e - event driving + * @param flag - flag to check + * @return true if PVP should be blocked otherwise false + */ + private boolean blockPVP(User user, LivingEntity le, Event e, Flag flag) { + // Check for self-inflicted damage + if (user.getPlayer().equals(le)) { + return false; + } + if (le instanceof Player) { + // Protect visitors + if (protectedVisitor(le)) { + user.sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + return true; + } + // Check if PVP is allowed or not + if (!checkIsland(e, le.getLocation(), flag)) { + user.sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + return true; + } + } + return false; + } + + private boolean protectedVisitor(LivingEntity entity) { + return getPlugin().getIWM().getIvSettings(entity.getWorld()).contains(DamageCause.ENTITY_ATTACK.name()) + && !getIslands().userIsOnIsland(entity.getWorld(), User.getInstance(entity)); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) { + // Try to get the shooter + if (e.getEntity().getShooter() instanceof Player && getPlugin().getIWM().inWorld(e.getEntity().getLocation())) { + // Store it and remove it when the effect is gone (Entity ID, UUID of throwing player) + thrownPotions.put(e.getAreaEffectCloud().getEntityId(), ((Player)e.getEntity().getShooter()).getUniqueId()); + Bukkit.getScheduler().runTaskLater(getPlugin(), () -> thrownPotions.remove(e.getAreaEffectCloud().getEntityId()), e.getAreaEffectCloud().getDuration()); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onLingeringPotionDamage(AreaEffectCloudApplyEvent e) { + if (e.getEntity() != null && thrownPotions.containsKey(e.getEntity().getEntityId())) { + User user = User.getInstance(thrownPotions.get(e.getEntity().getEntityId())); + // Run through affected entities and delete them if they are safe + e.getAffectedEntities().removeIf(le -> !le.getUniqueId().equals(user.getUniqueId()) && blockPVP(user, le, e, getFlag(e.getEntity().getWorld()))); + } + } + + private Flag getFlag(World w) { + switch (w.getEnvironment()) { + case NETHER: + return Flags.PVP_NETHER; + case THE_END: + return Flags.PVP_END; + default: + return Flags.PVP_OVERWORLD; + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PhysicalInteractionListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PhysicalInteractionListener.java new file mode 100644 index 0000000..b3da102 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PhysicalInteractionListener.java @@ -0,0 +1,77 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + * + */ +public class PhysicalInteractionListener extends AbstractFlagListener { + + /** + * Handle physical interaction with blocks + * Crop trample, pressure plates, triggering redstone, tripwires + * @param e - event + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerInteract(PlayerInteractEvent e) { + if (!e.getAction().equals(Action.PHYSICAL)) { + return; + } + switch (e.getClickedBlock().getType()) { + case SOIL: + // Crop trample + checkIsland(e, e.getPlayer().getLocation(), Flags.CROP_TRAMPLE); + break; + case WOOD_PLATE: + case STONE_PLATE: + case GOLD_PLATE: + case IRON_PLATE: + // Pressure plates + checkIsland(e, e.getPlayer().getLocation(), Flags.PRESSURE_PLATE); + break; + default: + break; + + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onProjectileHit(EntityInteractEvent e) { + if (e.getEntity() == null || !(e.getEntity() instanceof Projectile)) { + return; + } + Projectile p = (Projectile)e.getEntity(); + if (p.getShooter() != null && p.getShooter() instanceof Player && e.getBlock() != null) { + // Set the user to the shooter + setUser(User.getInstance((Player)p.getShooter())); + + switch(e.getBlock().getType()) { + case WOOD_BUTTON: + case STONE_BUTTON: + checkIsland(e, e.getBlock().getLocation(), Flags.BUTTON); + break; + case WOOD_PLATE: + case STONE_PLATE: + case GOLD_PLATE: + case IRON_PLATE: + // Pressure plates + checkIsland(e, e.getBlock().getLocation(), Flags.PRESSURE_PLATE); + break; + default: + break; + } + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java new file mode 100644 index 0000000..7fa745f --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java @@ -0,0 +1,33 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPistonExtendEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Prevents pistons from pushing blocks outside island protection range + * @author tastybento + * + */ +public class PistonPushListener extends AbstractFlagListener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent e) { + // Only process if flag is active + if (Flags.PISTON_PUSH.isSetForWorld(e.getBlock().getWorld())) { + getIslands().getProtectedIslandAt(e.getBlock().getLocation()).ifPresent(i -> + e.setCancelled( + // Run through the location of all the relative blocks and see if they are outside the island + !e.getBlocks().stream() + .map(b -> b.getRelative(e.getDirection()).getLocation()) + // All blocks must be on the island, otherwise the event is cancelled + .allMatch(i::onIsland))); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PlaceBlocksListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PlaceBlocksListener.java new file mode 100644 index 0000000..1d05d23 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PlaceBlocksListener.java @@ -0,0 +1,96 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.EntityBlockFormEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author tastybento + */ +public class PlaceBlocksListener extends AbstractFlagListener { + + /** + * Check blocks being placed in general + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockPlace(final BlockPlaceEvent e) { + if (e.getBlock().getType().equals(Material.FIRE)) { + return; + } + checkIsland(e, e.getBlock().getLocation(), Flags.PLACE_BLOCKS); + } + + /** + * Handles placing items into ItemFrames + * @param e - event + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerHitEntity(PlayerInteractEntityEvent e) { + if (e.getRightClicked().getType().equals(EntityType.ITEM_FRAME)) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.PLACE_BLOCKS); + } + } + + /** + * Handle placing of fireworks, mine carts, end crystals, doors, chests and boats on land + * The doors and chests are related to an exploit. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerInteract(final PlayerInteractEvent e) { + if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + return; + } + switch (e.getClickedBlock().getType()) { + case FIREWORK: + checkIsland(e, e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS); + return; + case RAILS: + case POWERED_RAIL: + case DETECTOR_RAIL: + case ACTIVATOR_RAIL: + if (e.getMaterial() != null && (e.getMaterial() == Material.MINECART || e.getMaterial() == Material.STORAGE_MINECART || e.getMaterial() == Material.HOPPER_MINECART + || e.getMaterial() == Material.EXPLOSIVE_MINECART || e.getMaterial() == Material.POWERED_MINECART)) { + checkIsland(e, e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS); + } + return; + default: + // Check in-hand items + // This check protects against an exploit in 1.7.9 against cactus + // and sugar cane and placing boats on non-liquids + if (e.getMaterial() != null + && (e.getMaterial().equals(Material.END_CRYSTAL) || e.getMaterial().equals(Material.WOOD_DOOR) + || e.getMaterial().equals(Material.CHEST) || e.getMaterial().equals(Material.TRAPPED_CHEST) + || e.getMaterial().equals(Material.IRON_DOOR) || (e.getMaterial().name().contains("BOAT") + && !e.getClickedBlock().isLiquid()))) { + checkIsland(e, e.getPlayer().getLocation(), Flags.PLACE_BLOCKS); + } + } + } + + /** + * Handles Frost Walking on visitor's islands. This creates ice blocks, which is like placing blocks + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void onBlockForm(EntityBlockFormEvent e) { + if (e.getNewState().getType().equals(Material.FROSTED_ICE) && e.getEntity() instanceof Player) { + setUser(User.getInstance((Player)e.getEntity())); + checkIsland(e, e.getBlock().getLocation(), Flags.PLACE_BLOCKS); + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PortalListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PortalListener.java new file mode 100644 index 0000000..f9a30e7 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PortalListener.java @@ -0,0 +1,30 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles portal protection + * @author tastybento + * + */ +public class PortalListener extends AbstractFlagListener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPlayerPortal(PlayerPortalEvent e) { + if (e.getPlayer().isOp()) { + return; + } + if (e.getCause().equals(TeleportCause.NETHER_PORTAL)) { + checkIsland(e, e.getFrom(), Flags.PORTAL); + } else if (e.getCause().equals(TeleportCause.END_PORTAL)) { + // Silent check because it's spammy + checkIsland(e, e.getFrom(), Flags.PORTAL, true); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java new file mode 100644 index 0000000..7292312 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java @@ -0,0 +1,27 @@ +/* + + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerTeleportEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Removes mobs when teleporting to an island + * @author tastybento + * + */ +public class RemoveMobsListener extends AbstractFlagListener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onUserTeleport(PlayerTeleportEvent e) { + // Only process if flag is active + if (getIslands().locationIsOnIsland(e.getPlayer(), e.getTo()) && Flags.REMOVE_MOBS.isSetForWorld(e.getTo().getWorld())) { + getIslands().clearArea(e.getTo()); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/ShearingListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/ShearingListener.java new file mode 100644 index 0000000..7e0b3ac --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/ShearingListener.java @@ -0,0 +1,23 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerShearEntityEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles shearing + * @author tastybento + * + */ +public class ShearingListener extends AbstractFlagListener { + + // Protect sheep + @EventHandler(priority = EventPriority.LOW) + public void onShear(final PlayerShearEntityEvent e) { + checkIsland(e, e.getEntity().getLocation(), Flags.SHEARING); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/TNTListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/TNTListener.java new file mode 100644 index 0000000..6711654 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/TNTListener.java @@ -0,0 +1,84 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Protects islands from visitors blowing things up + * @author tastybento + * + */ +public class TNTListener extends AbstractFlagListener { + + /** + * Protect TNT from being set light by a fire arrow + * @param e - event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onTNTDamage(EntityChangeBlockEvent e) { + // Check world + if (!e.getBlock().getType().equals(Material.TNT) || !getIWM().inWorld(e.getBlock().getLocation())) { + return; + } + // Stop TNT from being damaged if it is being caused by a visitor with a flaming arrow + if (e.getEntity() instanceof Projectile) { + Projectile projectile = (Projectile) e.getEntity(); + // Find out who fired it + if (projectile.getShooter() instanceof Player && projectile.getFireTicks() > 0) { + Player shooter = (Player)projectile.getShooter(); + setUser(User.getInstance(shooter)); + if (!setUser(User.getInstance(shooter)).checkIsland(e, e.getBlock().getLocation(), Flags.BREAK_BLOCKS)) { + // Remove the arrow + projectile.remove(); + e.setCancelled(true); + } + } + } + } + + + /** + * Protect against priming of TNT unless break blocks is allowed + * @param e + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onTNTPriming(PlayerInteractEvent e) { + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) + && e.getClickedBlock().getType().equals(Material.TNT) + && e.getMaterial() != null + && e.getMaterial().equals(Material.FLINT_AND_STEEL)) { + checkIsland(e, e.getClickedBlock().getLocation(), Flags.BREAK_BLOCKS); + } + } + + /** + * Prevent TNT damage from explosion + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onExplosion(final EntityExplodeEvent e) { + if (e.getEntity() != null && e.getEntityType().equals(EntityType.PRIMED_TNT)) { + // Remove any blocks from the explosion list if they are inside a protected area + if (e.blockList().removeIf(b -> getIslands().getProtectedIslandAt(b.getLocation()).map(i -> !i.isAllowed(Flags.TNT)).orElse(false))) { + // If any were removed, then prevent damage too + e.setCancelled(true); + } + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/TeleportationListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/TeleportationListener.java new file mode 100644 index 0000000..a937504 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/TeleportationListener.java @@ -0,0 +1,34 @@ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.lists.Flags; + +/** + * Handles teleporting due to enderpearl or chorus fruit. + * @author tastybento + * + */ +public class TeleportationListener extends AbstractFlagListener { + + /** + * Ender pearl and chorus fruit teleport checks + * + * @param e - event + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerTeleport(final PlayerTeleportEvent e) { + + if (e.getCause() != null) { + if (e.getCause().equals(TeleportCause.ENDER_PEARL)) { + checkIsland(e, e.getTo(), Flags.ENDER_PEARL); + } else if (e.getCause().equals(TeleportCause.CHORUS_FRUIT)) { + checkIsland(e, e.getTo(), Flags.CHORUS_FRUIT); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEvents.java b/src/main/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEvents.java new file mode 100644 index 0000000..d731895 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEvents.java @@ -0,0 +1,159 @@ +package us.tastybento.bskyblock.listeners.protection; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.WeakHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Wither; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * This class manages flying mobs. If they exist the spawned island's limits they will be removed. + * + * @author tastybento + * + */ +public class FlyingMobEvents implements Listener { + private BSkyBlock plugin; + private WeakHashMap mobSpawnInfo; + + /** + * @param plugin - BSkyBlock plugin object + */ + public FlyingMobEvents(BSkyBlock plugin) { + this.plugin = plugin; + mobSpawnInfo = new WeakHashMap<>(); + + Bukkit.getScheduler().runTaskTimer(plugin, () -> { + Iterator> it = mobSpawnInfo.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + if (entry.getKey() == null) { + it.remove(); + } else { + if (entry.getKey() instanceof LivingEntity) { + if (!entry.getValue().inIslandSpace(entry.getKey().getLocation())) { + it.remove(); + // Kill mob + LivingEntity mob = (LivingEntity)entry.getKey(); + mob.setHealth(0); + entry.getKey().remove(); + } + } else { + // Not living entity + it.remove(); + } + } + } + }, 20L, 20L); + } + + /** + * Track where the mob was created. This will determine its allowable movement zone. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onMobSpawn(CreatureSpawnEvent e) { + // Only cover withers in the island world + if (!plugin.getIWM().inWorld(e.getEntity().getLocation()) || !(e.getEntityType().equals(EntityType.WITHER) + || e.getEntityType().equals(EntityType.BLAZE) + || e.getEntityType().equals(EntityType.GHAST))) { + return; + } + // Store where this mob originated + plugin.getIslands().getIslandAt(e.getLocation()).ifPresent(island->mobSpawnInfo.put(e.getEntity(),island)); + } + + /** + * Protects entities exploding. However, I am not sure if this will ever be called as pre-explosions should prevent it. + * @param e - event + * @return true if cancelled + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onMobExplosion(EntityExplodeEvent e) { + // Only cover in the island world + if (e.getEntity() == null || !plugin.getIWM().inWorld(e.getEntity().getLocation())) { + return false; + } + if (mobSpawnInfo.containsKey(e.getEntity()) && !mobSpawnInfo.get(e.getEntity()).inIslandSpace(e.getLocation())) { + // Cancel the explosion and block damage + e.blockList().clear(); + e.setCancelled(true); + return true; + } + return false; + } + + /** + * Deal with pre-explosions + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onWitherExplode(ExplosionPrimeEvent e) { + // Only cover withers in the island world + if (!plugin.getIWM().inWorld(e.getEntity().getLocation()) || e.getEntity() == null) { + return false; + } + // The wither or wither skulls can both blow up + if (e.getEntityType() == EntityType.WITHER + && mobSpawnInfo.containsKey(e.getEntity()) + && !mobSpawnInfo.get(e.getEntity()).inIslandSpace(e.getEntity().getLocation())) { + // Cancel the explosion + e.setCancelled(true); + return true; + } else if (e.getEntityType() == EntityType.WITHER_SKULL) { + // Get shooter + Projectile projectile = (Projectile)e.getEntity(); + if (projectile.getShooter() instanceof Wither) { + Wither wither = (Wither)projectile.getShooter(); + // Check the location + if (mobSpawnInfo.containsKey(wither) && !mobSpawnInfo.get(wither).inIslandSpace(e.getEntity().getLocation())) { + // Cancel the explosion + e.setCancelled(true); + return true; + } + } + } + return false; + } + + /** + * Withers change blocks to air after they are hit (don't know why) + * This prevents this when the wither has been spawned by a visitor + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onWitherChangeBlocks(EntityChangeBlockEvent e) { + // Only cover withers in the island world + if (e.getEntityType() != EntityType.WITHER || !plugin.getIWM().inWorld(e.getEntity().getLocation()) ) { + return; + } + if (mobSpawnInfo.containsKey(e.getEntity()) && !mobSpawnInfo.get(e.getEntity()).inIslandSpace(e.getEntity().getLocation())) { + // We know about this wither + // Cancel the block changes + e.setCancelled(true); + } + } + + /** + * Clean up the hashmap. It's probably not needed, but just in case. + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public Island onMobDeath(EntityDeathEvent e) { + return mobSpawnInfo.remove(e.getEntity()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/listeners/protection/InventorySave.java b/src/main/java/us/tastybento/bskyblock/listeners/protection/InventorySave.java new file mode 100644 index 0000000..46fb173 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/protection/InventorySave.java @@ -0,0 +1,68 @@ +package us.tastybento.bskyblock.listeners.protection; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.entity.Player; + +/** + * Stashes inventories when required for a player + * + * @author tastybento + * + */ +public class InventorySave { + private static InventorySave instance = new InventorySave(); + private HashMap inventories; + + /** + * Saves the inventory of a player + */ + public InventorySave() { + inventories = new HashMap<>(); + } + + /** Save player's inventory + * @param player - the player + */ + public void savePlayerInventory(Player player) { + // Save the player's armor and things + inventories.put(player.getUniqueId(),new InventoryStore(player.getInventory().getContents(), player.getInventory().getArmorContents())); + } + + /** + * Clears any saved inventory + * @param player - the player + */ + public void clearSavedInventory(Player player) { + inventories.remove(player.getUniqueId()); + } + /** + * Load the player's inventory + * + * @param player - the player + */ + public void loadPlayerInventory(Player player) { + // Get the info for this player + if (inventories.containsKey(player.getUniqueId())) { + InventoryStore inv = inventories.get(player.getUniqueId()); + player.getInventory().setContents(inv.getInventory()); + player.getInventory().setArmorContents(inv.getArmor()); + inventories.remove(player.getUniqueId()); + } + } + + public static InventorySave getInstance() { + return instance; + } + + /** + * Returns whether the player's inventory has been stored to give him back. + * + * @param uuid - UUID UUID of the player + * @return true if the inventory is stored, false otherwise + */ + public static boolean isStored(UUID uuid) { + return instance.inventories.containsKey(uuid); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/listeners/protection/InventoryStore.java b/src/main/java/us/tastybento/bskyblock/listeners/protection/InventoryStore.java new file mode 100644 index 0000000..af4fdbe --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/protection/InventoryStore.java @@ -0,0 +1,52 @@ +package us.tastybento.bskyblock.listeners.protection; + +import org.bukkit.inventory.ItemStack; + +/** + * Where the inventory data is stored + * + * @author tastybento + */ +public class InventoryStore { + private ItemStack[] inventory; + private ItemStack[] armor; + + /** + * @param inventory - item stack array + * @param armor - armor item stack array + */ + public InventoryStore(ItemStack[] inventory, ItemStack[] armor) { + this.inventory = inventory; + this.armor = armor; + } + + /** + * @return the inventory + */ + public ItemStack[] getInventory() { + return inventory; + } + + /** + * @param inventory + * the inventory to set + */ + public void setInventory(ItemStack[] inventory) { + this.inventory = inventory; + } + + /** + * @return the armor + */ + public ItemStack[] getArmor() { + return armor; + } + + /** + * @param armor + * the armor to set + */ + public void setArmor(ItemStack[] armor) { + this.armor = armor; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/lists/Flags.java b/src/main/java/us/tastybento/bskyblock/lists/Flags.java new file mode 100644 index 0000000..4960900 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/lists/Flags.java @@ -0,0 +1,236 @@ +package us.tastybento.bskyblock.lists; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Material; + +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.flags.Flag.Type; +import us.tastybento.bskyblock.api.flags.FlagBuilder; +import us.tastybento.bskyblock.listeners.flags.BlockInteractionListener; +import us.tastybento.bskyblock.listeners.flags.BreakBlocksListener; +import us.tastybento.bskyblock.listeners.flags.BreedingListener; +import us.tastybento.bskyblock.listeners.flags.BucketListener; +import us.tastybento.bskyblock.listeners.flags.ChestDamageListener; +import us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener; +import us.tastybento.bskyblock.listeners.flags.CreeperListener; +import us.tastybento.bskyblock.listeners.flags.EggListener; +import us.tastybento.bskyblock.listeners.flags.EnderChestListener; +import us.tastybento.bskyblock.listeners.flags.EndermanListener; +import us.tastybento.bskyblock.listeners.flags.EnterExitListener; +import us.tastybento.bskyblock.listeners.flags.EntityInteractListener; +import us.tastybento.bskyblock.listeners.flags.FireListener; +import us.tastybento.bskyblock.listeners.flags.GeoLimitClickListener; +import us.tastybento.bskyblock.listeners.flags.GeoLimitMobsListener; +import us.tastybento.bskyblock.listeners.flags.HurtingListener; +import us.tastybento.bskyblock.listeners.flags.InventoryListener; +import us.tastybento.bskyblock.listeners.flags.InvincibleVisitorsListener; +import us.tastybento.bskyblock.listeners.flags.IslandRespawnListener; +import us.tastybento.bskyblock.listeners.flags.ItemDropPickUpListener; +import us.tastybento.bskyblock.listeners.flags.ItemFrameListener; +import us.tastybento.bskyblock.listeners.flags.LeashListener; +import us.tastybento.bskyblock.listeners.flags.LockAndBanListener; +import us.tastybento.bskyblock.listeners.flags.MobSpawnListener; +import us.tastybento.bskyblock.listeners.flags.OfflineRedstoneListener; +import us.tastybento.bskyblock.listeners.flags.PVPListener; +import us.tastybento.bskyblock.listeners.flags.PhysicalInteractionListener; +import us.tastybento.bskyblock.listeners.flags.PistonPushListener; +import us.tastybento.bskyblock.listeners.flags.PlaceBlocksListener; +import us.tastybento.bskyblock.listeners.flags.PortalListener; +import us.tastybento.bskyblock.listeners.flags.RemoveMobsListener; +import us.tastybento.bskyblock.listeners.flags.ShearingListener; +import us.tastybento.bskyblock.listeners.flags.TNTListener; +import us.tastybento.bskyblock.listeners.flags.TeleportationListener; +import us.tastybento.bskyblock.managers.RanksManager; + +public class Flags { + + private Flags() {} + + // Disabled setting 'rank' + private static final int DISABLED = -1; + + // TODO: add ELYTRA + // TODO: add FISHING + // TODO: add KEEP_INVENTORY - is it needed? + + public static final Flag BREAK_BLOCKS = new FlagBuilder().id("BREAK_BLOCKS").icon(Material.STONE).listener(new BreakBlocksListener()).build(); + public static final Flag PLACE_BLOCKS = new FlagBuilder().id("PLACE_BLOCKS").icon(Material.GRASS).listener(new PlaceBlocksListener()).build(); + + // Block interactions - all use BlockInteractionListener() + public static final Flag ANVIL = new FlagBuilder().id("ANVIL").icon(Material.ANVIL).listener(new BlockInteractionListener()).build(); + public static final Flag BEACON = new FlagBuilder().id("BEACON").icon(Material.BEACON).build(); + public static final Flag BED = new FlagBuilder().id("BED").icon(Material.BED).build(); + public static final Flag BREWING = new FlagBuilder().id("BREWING").icon(Material.BREWING_STAND_ITEM).build(); + public static final Flag CHEST = new FlagBuilder().id("CHEST").icon(Material.CHEST).build(); + public static final Flag DOOR = new FlagBuilder().id("DOOR").allowedByDefault(true).icon(Material.WOOD_DOOR).build(); + public static final Flag TRAPDOOR = new FlagBuilder().id("TRAPDOOR").allowedByDefault(true).icon(Material.TRAP_DOOR).build(); + public static final Flag CRAFTING = new FlagBuilder().id("CRAFTING").allowedByDefault(true).icon(Material.WORKBENCH).build(); + public static final Flag ENCHANTING = new FlagBuilder().id("ENCHANTING").allowedByDefault(true).icon(Material.ENCHANTMENT_TABLE).build(); + public static final Flag FURNACE = new FlagBuilder().id("FURNACE").icon(Material.FURNACE).build(); + public static final Flag GATE = new FlagBuilder().id("GATE").allowedByDefault(true).icon(Material.FENCE_GATE).build(); + public static final Flag NOTE_BLOCK = new FlagBuilder().id("NOTE_BLOCK").icon(Material.NOTE_BLOCK).build(); + public static final Flag JUKEBOX = new FlagBuilder().id("JUKEBOX").icon(Material.JUKEBOX).build(); + public static final Flag LEVER = new FlagBuilder().id("LEVER").icon(Material.LEVER).build(); + public static final Flag BUTTON = new FlagBuilder().id("BUTTON").icon(Material.WOOD_BUTTON).build(); + public static final Flag REDSTONE = new FlagBuilder().id("REDSTONE").icon(Material.REDSTONE).build(); + public static final Flag SPAWN_EGGS = new FlagBuilder().id("SPAWN_EGGS").icon(Material.MONSTER_EGG).build(); + + // Entity interactions + public static final Flag ARMOR_STAND = new FlagBuilder().id("ARMOR_STAND").icon(Material.ARMOR_STAND).listener(new EntityInteractListener()).build(); + public static final Flag RIDING = new FlagBuilder().id("RIDING").icon(Material.GOLD_BARDING).build(); + public static final Flag TRADING = new FlagBuilder().id("TRADING").allowedByDefault(true).icon(Material.EMERALD).build(); + + // Breeding + public static final Flag BREEDING = new FlagBuilder().id("BREEDING").icon(Material.CARROT_ITEM).listener(new BreedingListener()).build(); + + // Buckets. All bucket use is covered by one listener + public static final Flag BUCKET = new FlagBuilder().id("BUCKET").icon(Material.BUCKET).listener(new BucketListener()).build(); + public static final Flag COLLECT_LAVA = new FlagBuilder().id("COLLECT_LAVA").icon(Material.LAVA_BUCKET).build(); + public static final Flag COLLECT_WATER = new FlagBuilder().id("COLLECT_WATER").icon(Material.WATER_BUCKET).build(); + public static final Flag MILKING = new FlagBuilder().id("MILKING").icon(Material.MILK_BUCKET).build(); + + // Chorus Fruit and Enderpearls + public static final Flag CHORUS_FRUIT = new FlagBuilder().id("CHORUS_FRUIT").icon(Material.CHORUS_FRUIT).listener(new TeleportationListener()).build(); + public static final Flag ENDER_PEARL = new FlagBuilder().id("ENDER_PEARL").icon(Material.ENDER_PEARL).build(); + + // Physical interactions + public static final Flag CROP_TRAMPLE = new FlagBuilder().id("CROP_TRAMPLE").icon(Material.WHEAT).listener(new PhysicalInteractionListener()).build(); + public static final Flag PRESSURE_PLATE = new FlagBuilder().id("PRESSURE_PLATE").icon(Material.GOLD_PLATE).build(); + + // Egg throwing + public static final Flag EGGS = new FlagBuilder().id("EGGS").icon(Material.EGG).listener(new EggListener()).build(); + + /* + * Fire + * I'll take you to burn. + * Fire + * I'll take you to learn. + * You gonna burn, burn, burn + * Fire + * I'll take you to burn + * - The Crazy World of Arthur Brown + */ + public static final Flag FIRE = new FlagBuilder().id("FIRE").icon(Material.FLINT_AND_STEEL).listener(new FireListener()).build(); + public static final Flag FIRE_EXTINGUISH = new FlagBuilder().id("FIRE_EXTINGUISH").icon(Material.POTION).build(); + + // Inventories + public static final Flag MOUNT_INVENTORY = new FlagBuilder().id("MOUNT_INVENTORY").icon(Material.IRON_BARDING).listener(new InventoryListener()).build(); + + // Hurting things + public static final Flag HURT_ANIMALS = new FlagBuilder().id("HURT_ANIMALS").icon(Material.STONE_SWORD).listener(new HurtingListener()).build(); + public static final Flag HURT_MONSTERS = new FlagBuilder().id("HURT_MONSTERS").icon(Material.WOOD_SWORD).build(); + public static final Flag HURT_VILLAGERS = new FlagBuilder().id("HURT_VILLAGERS").icon(Material.GOLD_SWORD).build(); + + // Leashes + public static final Flag LEASH = new FlagBuilder().id("LEASH").icon(Material.LEASH).listener(new LeashListener()).build(); + + // Portal use protection + public static final Flag PORTAL = new FlagBuilder().id("PORTAL").icon(Material.OBSIDIAN).listener(new PortalListener()).build(); + + // Shearing + public static final Flag SHEARING = new FlagBuilder().id("SHEARING").icon(Material.SHEARS).listener(new ShearingListener()).build(); + + // Item pickup or drop + public static final Flag ITEM_DROP = new FlagBuilder().id("ITEM_DROP").icon(Material.BEETROOT_SOUP).allowedByDefault(true).listener(new ItemDropPickUpListener()).build(); + public static final Flag ITEM_PICKUP = new FlagBuilder().id("ITEM_PICKUP").icon(Material.BEETROOT_SEEDS).build(); + + // Island lock + public static final Flag LOCK = new FlagBuilder().id("LOCK") + .icon(Material.TRIPWIRE_HOOK).type(Type.PROTECTION).allowedByDefault(true) + .defaultRank(RanksManager.VISITOR_RANK).listener(new LockAndBanListener()) + .build(); + + /* + * Settings flags (not protection flags) + */ + // PVP + public static final Flag PVP_OVERWORLD = new FlagBuilder().id("PVP_OVERWORLD").icon(Material.ARROW).type(Type.SETTING) + .defaultRank(DISABLED).listener(new PVPListener()).build(); + public static final Flag PVP_NETHER = new FlagBuilder().id("PVP_NETHER").icon(Material.IRON_AXE).type(Type.SETTING) + .defaultRank(DISABLED).build(); + public static final Flag PVP_END = new FlagBuilder().id("PVP_END").icon(Material.END_CRYSTAL).type(Type.SETTING) + .defaultRank(DISABLED).build(); + + // Others + public static final Flag ANIMAL_SPAWN = new FlagBuilder().id("ANIMAL_SPAWN").icon(Material.APPLE).allowedByDefault(true).type(Type.SETTING) + .listener(new MobSpawnListener()).build(); + public static final Flag MONSTER_SPAWN = new FlagBuilder().id("MONSTER_SPAWN").icon(Material.MOB_SPAWNER).allowedByDefault(true).type(Type.SETTING).build(); + public static final Flag FIRE_SPREAD = new FlagBuilder().id("FIRE_SPREAD").icon(Material.FIREWORK_CHARGE).allowedByDefault(true).type(Type.SETTING).build(); + public static final Flag TNT = new FlagBuilder().id("TNT").icon(Material.TNT).listener(new TNTListener()).allowedByDefault(false).type(Type.SETTING).build(); + /* + * World Settings - they apply to every island in the game worlds. + */ + + // World Settings - apply to every island in the game worlds + public static final Flag ENDER_CHEST = new FlagBuilder().id("ENDER_CHEST").icon(Material.ENDER_CHEST) + .allowedByDefault(false).type(Type.WORLD_SETTING) + .listener(new EnderChestListener()) + .build(); + + public static final Flag ENDERMAN_GRIEFING = new FlagBuilder().id("ENDERMAN_GRIEFING").icon(Material.END_BRICKS) + .allowedByDefault(true).type(Type.WORLD_SETTING) + .listener(new EndermanListener()) + .build(); + + public static final Flag ENDERMAN_DEATH_DROP = new FlagBuilder().id("ENDERMAN_DEATH_DROP").icon(Material.END_ROD) + .allowedByDefault(true).type(Type.WORLD_SETTING) + .build(); + + public static final Flag ENTER_EXIT_MESSAGES = new FlagBuilder().id("ENTER_EXIT_MESSAGES").icon(Material.DIRT).allowedByDefault(true).type(Type.WORLD_SETTING) + .listener(new EnterExitListener()) + .build(); + + public static final Flag PISTON_PUSH = new FlagBuilder().id("PISTON_PUSH").icon(Material.PISTON_BASE).allowedByDefault(true).type(Type.WORLD_SETTING) + .listener(new PistonPushListener()) + .build(); + + private static InvincibleVisitorsListener ilv = new InvincibleVisitorsListener(); + public static final Flag INVINCIBLE_VISITORS = new FlagBuilder().id("INVINCIBLE_VISITORS").icon(Material.DIAMOND_CHESTPLATE).type(Type.WORLD_SETTING) + .listener(ilv).onClick(ilv).subPanel(true).build(); + + public static final Flag GEO_LIMIT_MOBS = new FlagBuilder().id("GEO_LIMIT_MOBS").icon(Material.CHAINMAIL_CHESTPLATE).type(Type.WORLD_SETTING) + .listener(new GeoLimitMobsListener()).onClick(new GeoLimitClickListener()).subPanel(true).build(); + + public static final Flag REMOVE_MOBS = new FlagBuilder().id("REMOVE_MOBS").icon(Material.GLOWSTONE_DUST).type(Type.WORLD_SETTING) + .listener(new RemoveMobsListener()).allowedByDefault(true).build(); + + public static final Flag ITEM_FRAME_DAMAGE = new FlagBuilder().id("ITEM_FRAME_DAMAGE").icon(Material.ITEM_FRAME).type(Type.WORLD_SETTING) + .listener(new ItemFrameListener()).allowedByDefault(false).build(); + + public static final Flag ISLAND_RESPAWN = new FlagBuilder().id("ISLAND_RESPAWN").icon(Material.TORCH).type(Type.WORLD_SETTING) + .listener(new IslandRespawnListener()).allowedByDefault(true).build(); + + public static final Flag OFFLINE_REDSTONE = new FlagBuilder().id("OFFLINE_REDSTONE").icon(Material.REDSTONE_COMPARATOR).type(Type.WORLD_SETTING) + .listener(new OfflineRedstoneListener()).allowedByDefault(true).build(); + + public static final Flag CLEAN_SUPER_FLAT = new FlagBuilder().id("CLEAN_SUPER_FLAT").icon(Material.BEDROCK).type(Type.WORLD_SETTING) + .listener(new CleanSuperFlatListener()).allowedByDefault(false).build(); + + public static final Flag CHEST_DAMAGE = new FlagBuilder().id("CHEST_DAMAGE").icon(Material.TRAPPED_CHEST).type(Type.WORLD_SETTING) + .listener(new ChestDamageListener()).allowedByDefault(false).build(); + public static final Flag CREEPER_DAMAGE = new FlagBuilder().id("CREEPER_DAMAGE").listener(new CreeperListener()).icon(Material.GREEN_SHULKER_BOX).type(Type.WORLD_SETTING) + .allowedByDefault(true).build(); + /** + * Prevents creeper griefing. This is where a visitor will trigger a creeper to blow up an island. + */ + public static final Flag CREEPER_GRIEFING = new FlagBuilder().id("CREEPER_GRIEFING").icon(Material.FIREWORK).type(Type.WORLD_SETTING) + .allowedByDefault(false).build(); + + /** + * @return List of all the flags in this class + */ + public static List values() { + return Arrays.stream(Flags.class.getFields()).map(field -> { + try { + return (Flag)field.get(null); + } catch (IllegalArgumentException | IllegalAccessException e) { + Bukkit.getLogger().severe("Could not get Flag values " + e.getMessage()); + } + return null; + }).collect(Collectors.toList()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/lists/Placeholders.java b/src/main/java/us/tastybento/bskyblock/lists/Placeholders.java new file mode 100644 index 0000000..8cad6bc --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/lists/Placeholders.java @@ -0,0 +1,33 @@ +package us.tastybento.bskyblock.lists; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.placeholders.Placeholder; +import us.tastybento.bskyblock.api.placeholders.PlaceholderBuilder; + +public class Placeholders { + + // Utility classes, which are collections of static members, are not meant to be instantiated. + private Placeholders() {} + + public static final Placeholder PLUGIN_NAME = new PlaceholderBuilder().identifier("bsb_plugin_name").value(user -> BSkyBlock.getInstance().getDescription().getName()).build(); + + /** + * @return List of all the flags in this class + */ + public static List values() { + return Arrays.stream(Placeholders.class.getFields()).map(field -> { + try { + return (Placeholder)field.get(null); + } catch (IllegalArgumentException | IllegalAccessException e) { + Bukkit.getLogger().severe("Could not get Placeholders values " + e.getMessage()); + } + return null; + }).collect(Collectors.toList()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java b/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java new file mode 100644 index 0000000..3509bb3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java @@ -0,0 +1,217 @@ +package us.tastybento.bskyblock.managers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.addons.AddonClassLoader; +import us.tastybento.bskyblock.api.addons.exception.InvalidAddonFormatException; +import us.tastybento.bskyblock.api.events.addon.AddonEvent; + +/** + * @author Tastybento, ComminQ + */ +public class AddonsManager { + + private static final boolean DEBUG = false; + private static final String LOCALE_FOLDER = "locales"; + private List addons; + private List loader; + private final Map> classes = new HashMap<>(); + private BSkyBlock plugin; + + public AddonsManager(BSkyBlock plugin) { + this.plugin = plugin; + addons = new ArrayList<>(); + loader = new ArrayList<>(); + } + + /** + * Loads all the addons from the addons folder + */ + public void loadAddons() { + plugin.log("Loading addons..."); + File f = new File(plugin.getDataFolder(), "addons"); + if (!f.exists() && !f.mkdirs()) { + plugin.logError("Cannot make addons folder!"); + return; + } + + Arrays.stream(Objects.requireNonNull(f.listFiles())).filter(x -> !x.isDirectory() && x.getName().endsWith(".jar")).forEach(this::loadAddon); + addons.forEach(Addon::onLoad); + plugin.log("Loaded " + addons.size() + " addons."); + } + + /** + * Enables all the addons + */ + public void enableAddons() { + plugin.log("Enabling addons..."); + addons.forEach(addon -> { + addon.onEnable(); + Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.ENABLE).build()); + addon.setEnabled(true); + plugin.log("Enabling " + addon.getDescription().getName() + "..."); + }); + plugin.log("Addons successfully enabled."); + } + + /** + * Gets the addon by name + * @param name - addon name + * @return Optional addon object + */ + public Optional getAddonByName(String name){ + return addons.stream().filter(a -> a.getDescription().getName().contains(name)).findFirst(); + } + + private void loadAddon(File f) { + try { + Addon addon; + // Check that this is a jar + if (!f.getName().endsWith(".jar")) { + throw new IOException("Filename must end in .jar. Name is '" + f.getName() + "'"); + } + try (JarFile jar = new JarFile(f)) { + // Obtain the addon.yml file + JarEntry entry = jar.getJarEntry("addon.yml"); + if (entry == null) { + throw new InvalidAddonFormatException("Addon doesn't contains description file"); + + } + // Open a reader to the jar + BufferedReader reader = new BufferedReader(new InputStreamReader(jar.getInputStream(entry))); + // Grab the description in the addon.yml file + YamlConfiguration data = new YamlConfiguration(); + data.load(reader); + // Load the addon + AddonClassLoader addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader()); + // Add to the list of loaders + loader.add(addonClassLoader); + + // Get the addon itself + addon = addonClassLoader.getAddon(); + // Initialize some settings + addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName())); + addon.setAddonFile(f); + + File localeDir = new File(plugin.getDataFolder(), LOCALE_FOLDER + File.separator + addon.getDescription().getName()); + // Obtain any locale files and save them + for (String localeFile : listJarYamlFiles(jar, LOCALE_FOLDER)) { + addon.saveResource(localeFile, localeDir, false, true); + } + plugin.getLocalesManager().loadLocales(addon.getDescription().getName()); + // Fire the load event + Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.LOAD).build()); + // Add it to the list of addons + addons.add(addon); + + // Inform the console + plugin.log("Loaded BSkyBlock addon " + addon.getDescription().getName() + "..."); + } catch (Exception e) { + plugin.log(e.getMessage()); + } + + } catch (Exception e) { + if (DEBUG) { + plugin.log(f.getName() + " is not a jarfile, ignoring..."); + } + } + } + + /** + * Disable all the enabled addons + */ + public void disableAddons() { + plugin.log("Disabling addons..."); + // Unload addons + addons.forEach(addon -> { + addon.onDisable(); + Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.DISABLE).build()); + plugin.log("Disabling " + addon.getDescription().getName() + "..."); + }); + + loader.forEach(l -> { + try { + l.close(); + } catch (IOException ignore) { + // Ignore + } + }); + plugin.log("Addons successfully disabled."); + } + + public List getAddons() { + return addons; + } + + public List getLoader() { + return loader; + } + + public void setLoader(List loader) { + this.loader = loader; + } + + /** + * Finds a class by name that has been loaded by this loader + * @param name - name of the class + * @return Class - the class + */ + public Class getClassByName(final String name) { + return classes.getOrDefault(name, loader.stream().map(l -> l.findClass(name, false)).filter(Objects::nonNull).findFirst().orElse(null)); + } + + /** + * Sets a class that this loader should know about + * + * @param name - name of the class + * @param clazz - the class + */ + public void setClass(final String name, final Class clazz) { + classes.putIfAbsent(name, clazz); + } + + /** + * Lists all the yml files found in the jar in the folder + * @param jar - the jar file + * @param folderPath - the path within the jar + * @return a list of files + - if the file cannot be read + */ + public List listJarYamlFiles(JarFile jar, String folderPath) { + List result = new ArrayList<>(); + + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String path = entry.getName(); + + if (!path.startsWith(folderPath)) { + continue; + } + + if (entry.getName().endsWith(".yml")) { + result.add(entry.getName()); + } + + } + return result; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/CommandsManager.java b/src/main/java/us/tastybento/bskyblock/managers/CommandsManager.java new file mode 100644 index 0000000..20452f3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/CommandsManager.java @@ -0,0 +1,37 @@ +package us.tastybento.bskyblock.managers; + +import java.lang.reflect.Field; +import java.util.HashMap; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; + +import us.tastybento.bskyblock.api.commands.CompositeCommand; + +public class CommandsManager { + + private HashMap commands = new HashMap<>(); + + public void registerCommand(CompositeCommand command) { + commands.put(command.getLabel(), command); + // Use reflection to obtain the commandMap method in Bukkit's server. It used to be visible, but isn't anymore. + try{ + Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + commandMapField.setAccessible(true); + CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer()); + commandMap.register(command.getLabel(), command); + } + catch(Exception exception){ + Bukkit.getLogger().severe("Bukkit server commandMap method is not there! This means no commands can be registered!"); + } + } + + public CompositeCommand getCommand(String command) { + return commands.get(command); + } + + public String listCommands() { + return commands.keySet().toString(); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java b/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java new file mode 100644 index 0000000..f7ad11d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java @@ -0,0 +1,89 @@ +package us.tastybento.bskyblock.managers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.event.Listener; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.lists.Flags; + +/** + * @author Poslovitch + * @author tastybento + */ +public class FlagsManager { + + private BSkyBlock plugin; + private List flags = new ArrayList<>(); + + /** + * Stores the flag listeners that have already been registered into Bukkit's API to avoid duplicates. + * Value is true if the listener has been registered already + */ + private Map registeredListeners = new HashMap<>(); + + public FlagsManager(BSkyBlock plugin) { + this.plugin = plugin; + + // Register default flags + Flags.values().forEach(this::registerFlag); + } + + /** + * Register a new flag with BSkyBlock + * @param flag flag to be registered + * @return true if successfully registered, false if not, e.g., because one with the same ID already exists + */ + public boolean registerFlag(Flag flag) { + // Check in case the flag id or icon already exists + for (Flag fl : flags) { + if (fl.getID().equals(flag.getID()) || fl.getIcon().equals(flag.getIcon())) { + return false; + } + } + flags.add(flag); + // If there is a listener which is not already registered, register it into Bukkit if the plugin is fully loaded + flag.getListener().ifPresent(this::registerListener); + return true; + } + + /** + * Register any unregistered listeners - called after the plugin is fully loaded + */ + public void registerListeners() { + registeredListeners.entrySet().stream().filter(e -> !e.getValue()).map(Map.Entry::getKey).forEach(this::registerListener); + } + + /** + * Tries to register a listener if the plugin is loaded + * @param l + */ + private void registerListener(Listener l) { + registeredListeners.putIfAbsent(l, false); + if (BSkyBlock.getInstance().isLoaded() && !registeredListeners.get(l)) { + Bukkit.getServer().getPluginManager().registerEvents(l, plugin); + registeredListeners.put(l, true); + } + } + + /** + * @return list of all flags + */ + public List getFlags() { + return flags; + } + + /** + * Get flag by ID + * @param id unique id for this flag + * @return Flag or null if not known + */ + public Flag getFlagByID(String id) { + return flags.stream().filter(flag -> flag.getID().equals(id)).findFirst().orElse(null); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java b/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java new file mode 100644 index 0000000..551fae3 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java @@ -0,0 +1,763 @@ +package us.tastybento.bskyblock.managers; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.entity.EntityType; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.generators.ChunkGeneratorWorld; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.util.Util; + +/** + * Handles registration and management of worlds + * + * @author tastybento + * + */ +public class IslandWorldManager { + + private static final String MULTIVERSE_SET_GENERATOR = "mv modify set generator "; + private static final String MULTIVERSE_IMPORT = "mv import "; + private static final String NETHER = "_nether"; + private static final String THE_END = "_the_end"; + private static final String CREATING = "Creating "; + + private BSkyBlock plugin; + private World islandWorld; + private World netherWorld; + private World endWorld; + private Map worlds; + private Map worldSettings; + + /** + * Generates the Skyblock worlds. + */ + public IslandWorldManager(BSkyBlock plugin) { + this.plugin = plugin; + worlds = new HashMap<>(); + worldSettings = new HashMap<>(); + if (plugin.getSettings().isUseOwnGenerator()) { + plugin.log("Default world generator disabled."); + islandWorld = Bukkit.getWorld(plugin.getSettings().getWorldName()); + if (plugin.getSettings().isNetherGenerate()) { + netherWorld = plugin.getServer().getWorld(plugin.getSettings().getWorldName() + NETHER); + if (netherWorld == null) { + plugin.logError("Island nether world does not exist - you must create it manually"); + } + } + if (plugin.getSettings().isEndGenerate()) { + endWorld = plugin.getServer().getWorld(plugin.getSettings().getWorldName() + THE_END); + if (endWorld == null) { + plugin.logError("Island end world does not exist - you must create it manually"); + } + } + if (islandWorld != null) { + addWorld(islandWorld, plugin.getSettings()); + } else { + plugin.logError("Island world does not exist - you must create it manually"); + } + return; + } + if (plugin.getServer().getWorld(plugin.getSettings().getWorldName()) == null) { + plugin.log(CREATING + plugin.getName() + "'s Island World..."); + } + // Create the world if it does not exist + islandWorld = WorldCreator.name(plugin.getSettings().getWorldName()).type(WorldType.FLAT) + .environment(World.Environment.NORMAL).generator(new ChunkGeneratorWorld(plugin)).createWorld(); + addWorld(islandWorld, plugin.getSettings()); + // Make the nether if it does not exist + if (plugin.getSettings().isNetherGenerate()) { + if (plugin.getServer().getWorld(plugin.getSettings().getWorldName() + NETHER) == null) { + plugin.log(CREATING + plugin.getName() + "'s Nether..."); + } + if (!plugin.getSettings().isNetherIslands()) { + netherWorld = WorldCreator.name(plugin.getSettings().getWorldName() + NETHER).type(WorldType.NORMAL) + .environment(World.Environment.NETHER).createWorld(); + } else { + netherWorld = WorldCreator.name(plugin.getSettings().getWorldName() + NETHER).type(WorldType.FLAT) + .generator(new ChunkGeneratorWorld(plugin)).environment(World.Environment.NETHER).createWorld(); + } + } + // Make the end if it does not exist + if (plugin.getSettings().isEndGenerate()) { + if (plugin.getServer().getWorld(plugin.getSettings().getWorldName() + THE_END) == null) { + plugin.log(CREATING + plugin.getName() + "'s End World..."); + } + if (!plugin.getSettings().isEndIslands()) { + endWorld = WorldCreator.name(plugin.getSettings().getWorldName() + THE_END).type(WorldType.NORMAL) + .environment(World.Environment.THE_END).createWorld(); + } else { + endWorld = WorldCreator.name(plugin.getSettings().getWorldName() + THE_END).type(WorldType.FLAT) + .generator(new ChunkGeneratorWorld(plugin)).environment(World.Environment.THE_END) + .createWorld(); + } + } + } + + /** + * Registers a world with Multiverse if the plugin exists + * + * @param world + * - world + */ + private void multiverseReg(World world) { + if (!isUseOwnGenerator(world) && Bukkit.getServer().getPluginManager().isPluginEnabled("Multiverse-Core")) { + Bukkit.getScheduler().runTask(plugin, + () -> Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), + MULTIVERSE_IMPORT + world.getName() + " normal -g " + plugin.getName())); + Bukkit.getScheduler().runTask(plugin, () -> { + if (!Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), + MULTIVERSE_SET_GENERATOR + plugin.getName() + " " + world.getName())) { + plugin.logError("Multiverse is out of date! - Upgrade to latest version!"); + } + }); + } + } + + /** + * Should only be used by BSB-specific functions + * + * @return the islandWorld + */ + public World getBSBIslandWorld() { + if (plugin.getSettings().isUseOwnGenerator()) { + return Bukkit.getServer().getWorld(plugin.getSettings().getWorldName()); + } + return islandWorld; + } + + /** + * Should only be used by BSB-specific functions + * + * @return the netherWorld + */ + public World getBSBNetherWorld() { + if (plugin.getSettings().isUseOwnGenerator()) { + return Bukkit.getServer().getWorld(plugin.getSettings().getWorldName() + NETHER); + } + return netherWorld; + } + + /** + * Should only be used by BSB-specific functions + * + * @return the endWorld + */ + public World getBSBEndWorld() { + if (plugin.getSettings().isUseOwnGenerator()) { + return Bukkit.getServer().getWorld(plugin.getSettings().getWorldName() + THE_END); + } + return endWorld; + } + + /** + * Checks if a location is in any of the island worlds + * + * @param loc + * - location + * @return true if in a world or false if not + */ + public boolean inWorld(Location loc) { + return worlds.containsKey(Util.getWorld(loc.getWorld())); + } + + /** + * @return List of over worlds + */ + public List getOverWorlds() { + return worlds.keySet().stream().filter(w -> w.getEnvironment().equals(Environment.NORMAL)) + .collect(Collectors.toList()); + } + + /** + * Get friendly names of all the over worlds + * + * @return Set of world names + */ + public Set getOverWorldNames() { + return worlds.entrySet().stream().filter(e -> e.getKey().getEnvironment().equals(Environment.NORMAL)) + .map(Map.Entry::getValue).collect(Collectors.toSet()); + } + + /** + * Get a set of world names that user does not already have an island in + * + * @param user + * - user + * @return set of world names, or empty set + */ + public Set getFreeOverWorldNames(User user) { + return worlds.entrySet().stream().filter(e -> e.getKey().getEnvironment().equals(Environment.NORMAL)) + .filter(w -> !plugin.getIslands().hasIsland(w.getKey(), user)).map(Map.Entry::getValue) + .collect(Collectors.toSet()); + } + + /** + * Check if a name is a known friendly world name, ignores case + * + * @param name + * - world name + * @return true if name is a known world name + */ + public boolean isOverWorld(String name) { + return worlds.entrySet().stream().filter(e -> e.getKey().getEnvironment().equals(Environment.NORMAL)) + .anyMatch(w -> w.getValue().equalsIgnoreCase(name)); + } + + /** + * Add world to the list of known worlds along with a friendly name that will be + * used in commands + * + * @param world - world + */ + public void addWorld(World world, WorldSettings settings) { + String friendlyName = settings.getFriendlyName().isEmpty() ? world.getName() : settings.getFriendlyName(); + worlds.put(world, friendlyName); + worldSettings.put(world, settings); + // Call Multiverse + multiverseReg(world); + // Set default island settings + Flags.values().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION)) + .forEach(f -> settings.getDefaultIslandFlags().putIfAbsent(f, f.getDefaultRank())); + Flags.values().stream().filter(f -> f.getType().equals(Flag.Type.SETTING)) + .forEach(f -> settings.getDefaultIslandSettings().putIfAbsent(f, f.getDefaultRank())); + Bukkit.getScheduler().runTask(plugin, () -> { + // Set world difficulty + Difficulty diff = settings.getDifficulty(); + if (diff == null) { + diff = Difficulty.NORMAL; + settings.setDifficulty(diff); + } + world.setDifficulty(diff); + + // Handle nether and end difficulty levels + if (settings.isNetherGenerate()) { + this.getNetherWorld(world).setDifficulty(diff); + } + if (settings.isEndGenerate()) { + this.getEndWorld(world).setDifficulty(diff); + } + plugin.log("Added world " + friendlyName + " (" + world.getDifficulty() + ")"); + }); + + } + + /** + * Get the settings for this world or sub-worlds (nether, end) + * + * @param world + * - world + * @return world settings, or null if world is unknown + */ + public WorldSettings getWorldSettings(World world) { + return worldSettings.get(Util.getWorld(world)); + } + + /** + * Get the world based on friendly name. + * + * @param friendlyName + * - friendly name of world + * @return world, or null if it does not exist + */ + public World getWorld(String friendlyName) { + return worlds.entrySet().stream().filter(n -> n.getValue().equalsIgnoreCase(friendlyName)) + .map(Map.Entry::getKey).findFirst().orElse(null); + } + + /** + * @return the entityLimits + */ + public Map getEntityLimits(World world) { + return worldSettings.get(Util.getWorld(world)).getEntityLimits(); + } + + /** + * @return the islandDistance + */ + public int getIslandDistance(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandDistance(); + } + + /** + * @return the islandHeight + */ + public int getIslandHeight(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandHeight(); + } + + /** + * @return the islandProtectionRange + */ + public int getIslandProtectionRange(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandProtectionRange(); + } + + /** + * @return the islandStartX + */ + public int getIslandStartX(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandStartX(); + } + + /** + * @return the islandStartZ + */ + public int getIslandStartZ(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandStartZ(); + } + + /** + * @return the islandXOffset + */ + public int getIslandXOffset(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandXOffset(); + } + + /** + * @return the islandZOffset + */ + public int getIslandZOffset(World world) { + return worldSettings.get(Util.getWorld(world)).getIslandZOffset(); + } + + /** + * @return the maxIslands + */ + public int getMaxIslands(World world) { + return worldSettings.get(Util.getWorld(world)).getMaxIslands(); + } + + /** + * @return the netherSpawnRadius + */ + public int getNetherSpawnRadius(World world) { + return worldSettings.get(Util.getWorld(world)).getNetherSpawnRadius(); + } + + /** + * @return the seaHeight + */ + public int getSeaHeight(World world) { + return worldSettings.get(Util.getWorld(world)).getSeaHeight(); + } + + /** + * @return the tileEntityLimits + */ + public Map getTileEntityLimits(World world) { + return worldSettings.get(Util.getWorld(world)).getTileEntityLimits(); + } + + /** + * @return the worldName + */ + public String getWorldName(World world) { + return worldSettings.get(Util.getWorld(world)).getWorldName(); + } + + /** + * @return the endGenerate + */ + public boolean isEndGenerate(World world) { + return worldSettings.get(Util.getWorld(world)).isEndGenerate(); + } + + /** + * @return the endIslands + */ + public boolean isEndIslands(World world) { + return worldSettings.get(Util.getWorld(world)).isEndIslands(); + } + + /** + * @return the netherGenerate + */ + public boolean isNetherGenerate(World world) { + return worldSettings.get(Util.getWorld(world)).isNetherGenerate(); + } + + /** + * @return the netherIslands + */ + public boolean isNetherIslands(World world) { + return worldSettings.get(Util.getWorld(world)).isNetherIslands(); + } + + /** + * Checks if a world is a known nether world + * + * @param world + * - world + * @return true if world is a known and valid nether world + */ + public boolean isNether(World world) { + World w = Util.getWorld(world); + return worldSettings.containsKey(w) && worldSettings.get(w).isNetherGenerate(); + } + + /** + * Checks if a world is a known island nether world + * + * @param world + * - world + * @return true if world is a known and valid nether world + */ + public boolean isIslandNether(World world) { + World w = Util.getWorld(world); + return worldSettings.containsKey(w) && worldSettings.get(w).isNetherGenerate() + && worldSettings.get(w).isNetherIslands(); + } + + /** + * Checks if a world is a known end world + * + * @param world + * - world + * @return true if world is a known and valid nether world + */ + public boolean isEnd(World world) { + World w = Util.getWorld(world); + return worldSettings.containsKey(w) && worldSettings.get(w).isEndGenerate(); + } + + /** + * Checks if a world is a known island end world + * + * @param world + * - world + * @return true if world is a known and valid nether world + */ + public boolean isIslandEnd(World world) { + World w = Util.getWorld(world); + return worldSettings.containsKey(w) && worldSettings.get(w).isEndGenerate() + && worldSettings.get(w).isEndIslands(); + } + + /** + * Get the nether world of this overWorld + * + * @param overWorld + * - overworld + * @return nether world, or null if it does not exist + */ + public World getNetherWorld(World overWorld) { + return Bukkit.getWorld(overWorld.getName() + NETHER); + } + + /** + * Get the end world of this overWorld + * + * @param overWorld + * - overworld + * @return end world, or null if it does not exist + */ + public World getEndWorld(World overWorld) { + return Bukkit.getWorld(overWorld.getName() + THE_END); + } + + /** + * Check if nether trees should be created in the nether or not + * + * @param world + * - world + * @return true or false + */ + public boolean isNetherTrees(World world) { + World w = Util.getWorld(world); + return worldSettings.containsKey(w) && worldSettings.get(w).isNetherTrees(); + } + + /** + * Whether the End Dragon can spawn or not in this world + * + * @param world + * - world + * @return true (default) if it can spawn or not + */ + public boolean isDragonSpawn(World world) { + World w = Util.getWorld(world); + return !worldSettings.containsKey(w) || worldSettings.get(w).isDragonSpawn(); + } + + /** + * @return a comma separated string of friendly world names + */ + public String getFriendlyNames() { + StringBuilder r = new StringBuilder(); + worlds.values().forEach(n -> r.append(n).append(", ")); + if (r.length() > 0) { + r.setLength(r.length() - 2); + } + return r.toString(); + } + + /** + * Gets world from friendly name + * + * @param friendlyWorldName + * - friendly world name. Used for commands. + * @return world, or null if not known + */ + public World getIslandWorld(String friendlyWorldName) { + return worlds.entrySet().stream().filter(e -> e.getValue().equalsIgnoreCase(friendlyWorldName)).findFirst() + .map(Map.Entry::getKey).orElse(null); + } + + /** + * Get max team size for this world + * + * @param world + * - world + * @return max team size + */ + public int getMaxTeamSize(World world) { + return worldSettings.get(Util.getWorld(world)).getMaxTeamSize(); + } + + /** + * Get max homes for world + * + * @param world + * - world + * @return max homes + */ + public int getMaxHomes(World world) { + return worldSettings.get(Util.getWorld(world)).getMaxHomes(); + } + + /** + * Get the friendly name for world or related nether or end + * + * @param world + * - world + * @return Friendly name + */ + public String getFriendlyName(World world) { + return worldSettings.get(Util.getWorld(world)).getFriendlyName(); + } + + /** + * Get the permission prefix for this world. No trailing dot included. + * + * @param world + * - world + * @return permission prefix for this world + */ + public String getPermissionPrefix(World world) { + return worldSettings.get(Util.getWorld(world)).getPermissionPrefix(); + + } + + /** + * Get the invincible visitor settings for this world + * + * @param world + * - world + * @return invincible visitor settings + */ + public List getIvSettings(World world) { + return worldSettings.get(Util.getWorld(world)).getIvSettings(); + } + + /** + * Returns whether a world flag is set or not Same result as calling + * {@link Flag#isSetForWorld(World)} + * + * @param world + * - world + * @param flag + * - world setting flag + * @return true or false + */ + public boolean isWorldFlag(World world, Flag flag) { + return flag.isSetForWorld(world); + } + + /** + * Get the default game mode for this world. + * + * @param world + * - world + * @return GameMode: SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR + */ + public GameMode getDefaultGameMode(World world) { + return worldSettings.get(Util.getWorld(world)).getDefaultGameMode(); + } + + /** + * Get the set of entity types not to remove when player teleports to island + * + * @param world + * - world + * @return - set of entity types + */ + public Set getRemoveMobsWhitelist(World world) { + return worldSettings.get(Util.getWorld(world)).getRemoveMobsWhitelist(); + } + + /** + * @return the onJoinResetMoney + */ + public boolean isOnJoinResetMoney(World world) { + return worldSettings.get(Util.getWorld(world)).isOnJoinResetMoney(); + } + + /** + * @return the onJoinResetInventory + */ + public boolean isOnJoinResetInventory(World world) { + return worldSettings.get(Util.getWorld(world)).isOnJoinResetInventory(); + } + + /** + * @return the onJoinResetEnderChest + */ + public boolean isOnJoinResetEnderChest(World world) { + return worldSettings.get(Util.getWorld(world)).isOnJoinResetEnderChest(); + } + + /** + * @return the onLeaveResetMoney + */ + public boolean isOnLeaveResetMoney(World world) { + return worldSettings.get(Util.getWorld(world)).isOnLeaveResetMoney(); + } + + /** + * @return the onLeaveResetInventory + */ + public boolean isOnLeaveResetInventory(World world) { + return worldSettings.get(Util.getWorld(world)).isOnLeaveResetInventory(); + } + + /** + * @return the onLeaveResetEnderChest + */ + public boolean isOnLeaveResetEnderChest(World world) { + return worldSettings.get(Util.getWorld(world)).isOnLeaveResetEnderChest(); + } + + /** + * The data folder for the addon that registered this world, or the plugin's + * data folder if none found + * + * @return + */ + public File getDataFolder(World world) { + return worldSettings.get(Util.getWorld(world)).getAddon().map(Addon::getDataFolder) + .orElse(plugin.getDataFolder()); + } + + /** + * Get the addon associated with this world set + * + * @param world + * - world + * @return Addon, or empty + */ + public Optional getAddon(World world) { + return worldSettings.get(Util.getWorld(world)).getAddon(); + } + + /** + * Get default island flag settings for this world. + * + * @param world + * - world + * @return default rank settings for new islands. + */ + public Map getDefaultIslandFlags(World world) { + return worldSettings.get(Util.getWorld(world)).getDefaultIslandFlags(); + } + + public List getVisibleSettings(World world) { + return worldSettings.get(Util.getWorld(world)).getVisibleSettings(); + } + + /** + * Return island setting defaults for world + * + * @param world + * - world + * @return default settings for new islands + */ + public Map getDefaultIslandSettings(World world) { + return worldSettings.get(Util.getWorld(world)).getDefaultIslandSettings(); + } + + public boolean isUseOwnGenerator(World world) { + return worldSettings.get(Util.getWorld(world)).isUseOwnGenerator(); + } + + /** + * Return banned commands for visitors + * @return the visitorbannedcommands + */ + public List getVisitorBannedCommands(World world) { + return worldSettings.get(Util.getWorld(world)).getVisitorBannedCommands(); + } + + /** + * Check if water is not safe, e.g., it is acid, in the world + * @param world - world + * @return true if water is not safe, e.g.for home locations + */ + public boolean isWaterNotSafe(World world) { + return worldSettings.get(Util.getWorld(world)).isWaterUnsafe(); + } + + /** + * Get a list of entity types that should not exit the island limits + * @param world - world + * @return list + */ + public List getGeoLimitSettings(World world) { + return worldSettings.get(Util.getWorld(world)).getGeoLimitSettings(); + } + + /** + * Get the reset limit for this world + * @param world - world + * @return number of resets allowed. -1 = unlimited + */ + public int getResetLimit(World world) { + return worlds.containsKey(Util.getWorld(world)) ? worldSettings.get(Util.getWorld(world)).getResetLimit() : -1; + } + + + /** + * Gets the time stamp for when all player resets were zeroed + * @param world + */ + public long getResetEpoch(World world) { + return worldSettings.get(Util.getWorld(world)).getResetEpoch(); + } + + /** + * Sets the time stamp for when all player resets were zeroed + * @param world + */ + public void setResetEpoch(World world) { + worldSettings.get(Util.getWorld(world)).setResetEpoch(System.currentTimeMillis()); + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java new file mode 100644 index 0000000..d3f9aab --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java @@ -0,0 +1,821 @@ +package us.tastybento.bskyblock.managers; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.material.SimpleAttachableMaterialData; +import org.bukkit.material.TrapDoor; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Constants; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.BSBDatabase; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.island.IslandCache; +import us.tastybento.bskyblock.util.DeleteIslandChunks; +import us.tastybento.bskyblock.util.Util; +import us.tastybento.bskyblock.util.teleport.SafeTeleportBuilder; + +/** + * The job of this class is manage all island related data. + * It also handles island ownership, including team, trustees, coops, etc. + * The data object that it uses is Island + * @author tastybento + * + */ +public class IslandsManager { + + private BSkyBlock plugin; + + /** + * One island can be spawn, this is the one - otherwise, this value is null + */ + private Map spawn; + + private BSBDatabase handler; + + /** + * The last locations where an island were put. + * This is not stored persistently and resets when the server starts + */ + private Map last; + // Metrics data + private int metrics_createdcount = 0; + + // Island Cache + private IslandCache islandCache; + + // Async database saving semaphore + private boolean midSave; + + /** + * Islands Manager + * @param plugin - plugin + */ + public IslandsManager(BSkyBlock plugin){ + this.plugin = plugin; + // Set up the database handler to store and retrieve Island classes + handler = new BSBDatabase<>(plugin, Island.class); + islandCache = new IslandCache(); + spawn = new HashMap<>(); + last = new HashMap<>(); + } + + /** + * This is a generic scan that can work in the overworld or the nether + * @param l - location around which to scan + * @param i - the range to scan for a location less than 0 means the full island. + * @return - safe location, or null if none can be found + */ + public Location bigScan(Location l, int i) { + if (l == null) { + return null; + } + final int height; + final int depth; + if (i > 0) { + height = i; + depth = i; + } else { + + Optional island = getIslandAt(l); + if (!island.isPresent()) { + return null; + } + i = island.get().getProtectionRange(); + height = l.getWorld().getMaxHeight() - l.getBlockY(); + depth = l.getBlockY(); + } + + // Work outwards from l until the closest safe location is found. + int minXradius = 0; + int maxXradius = 0; + int minZradius = 0; + int maxZradius = 0; + int minYradius = 0; + int maxYradius = 0; + + do { + int minX = l.getBlockX()-minXradius; + int minZ = l.getBlockZ()-minZradius; + int minY = l.getBlockY()-minYradius; + int maxX = l.getBlockX()+maxXradius; + int maxZ = l.getBlockZ()+maxZradius; + int maxY = l.getBlockY()+maxYradius; + for (int x = minX; x<= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + for (int y = minY; y <= maxY; y++) { + if (!((x > minX && x < maxX) && (z > minZ && z < maxZ) && (y > minY && y < maxY))) { + Location ultimate = new Location(l.getWorld(), x + 0.5D, y, z + 0.5D); + if (isSafeLocation(ultimate)) { + return ultimate; + } + } + } + } + } + if (minXradius < i) { + minXradius++; + } + if (maxXradius < i) { + maxXradius++; + } + if (minZradius < i) { + minZradius++; + } + if (maxZradius < i) { + maxZradius++; + } + if (minYradius < depth) { + minYradius++; + } + if (maxYradius < height) { + maxYradius++; + } + } while (minXradius < i || maxXradius < i || minZradius < i || maxZradius < i || minYradius < depth + || maxYradius < height); + // Nothing worked + return null; + } + + /** + * Checks if this location is safe for a player to teleport to. Used by + * warps and boat exits Unsafe is any liquid or air and also if there's no + * space + * + * @param l + * - Location to be checked + * @return true if safe, otherwise false + */ + public boolean isSafeLocation(Location l) { + if (l == null) { + return false; + } + Block ground = l.getBlock().getRelative(BlockFace.DOWN); + Block space1 = l.getBlock(); + Block space2 = l.getBlock().getRelative(BlockFace.UP); + + // Ground must be solid + if (!ground.getType().isSolid()) { + return false; + } + // Cannot be submerged + if (space1.isLiquid() && space2.isLiquid()) { + return false; + } + // Check if water is safe in this world + if (space1.isLiquid() && plugin.getIWM().isWaterNotSafe(l.getWorld())) { + return false; + } + + // Portals are not "safe" + if (space1.getType() == Material.PORTAL || ground.getType() == Material.PORTAL || space2.getType() == Material.PORTAL + || space1.getType() == Material.ENDER_PORTAL || ground.getType() == Material.ENDER_PORTAL || space2.getType() == Material.ENDER_PORTAL) { + return false; + } + if (ground.getType().equals(Material.STATIONARY_LAVA) || ground.getType().equals(Material.LAVA) + || space1.getType().equals(Material.STATIONARY_LAVA) || space1.getType().equals(Material.LAVA) + || space2.getType().equals(Material.STATIONARY_LAVA) || space2.getType().equals(Material.LAVA)) { + return false; + } + + MaterialData md = ground.getState().getData(); + if (md instanceof SimpleAttachableMaterialData) { + if (md instanceof TrapDoor) { + TrapDoor trapDoor = (TrapDoor) md; + if (trapDoor.isOpen()) { + return false; + } + } else { + return false; + } + } + if (ground.getType().equals(Material.CACTUS) || ground.getType().equals(Material.BOAT) || ground.getType().toString().contains("FENCE") + || ground.getType().equals(Material.SIGN_POST) || ground.getType().equals(Material.WALL_SIGN)) { + return false; + } + // Check that the space is not solid + // The isSolid function is not fully accurate (yet) so we have to + // check + // a few other items + // isSolid thinks that PLATEs and SIGNS are solid, but they are not + return (!space1.getType().isSolid() || space1.getType().equals(Material.SIGN_POST) || space1.getType().equals(Material.WALL_SIGN)) && (!space2.getType().isSolid() || space2.getType().equals(Material.SIGN_POST) || space2.getType().equals(Material.WALL_SIGN)); + } + + /** + * Create an island with no owner at location + * @param location - the location - location + * @return Island + */ + public Island createIsland(Location location){ + return createIsland(location, null); + } + + /** + * Create an island with owner. Note this does not create the schematic. It just creates the island data object. + * @param location - the location - location + * @param owner - the island owner UUID + * @return Island or null if the island could not be created for some reason + */ + public Island createIsland(Location location, UUID owner){ + Island island = new Island(location, owner, plugin.getIWM().getIslandProtectionRange(location.getWorld())); + if (islandCache.addIsland(island)) { + return island; + } + return null; + } + + /** + * Deletes island. If island is null, it does nothing + * @param island - island + * @param removeBlocks - if the island blocks should be removed or not + */ + public void deleteIsland(Island island, boolean removeBlocks) { + if (island == null) { + return; + } + // Set the owner of the island to no one. + island.setOwner(null); + island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK); + if (removeBlocks) { + // Remove players from island + removePlayersFromIsland(island); + // Remove island from the cache + islandCache.deleteIslandFromCache(island); + // Remove the island from the database + handler.deleteObject(island); + // Remove blocks from world + new DeleteIslandChunks(plugin, island); + } + } + + public int getCount(){ + return islandCache.size(); + } + + /** + * Gets the island for this player. If they are in a team, the team island is returned + * @param world - world to check + * @param user - user + * @return Island or null + */ + public Island getIsland(World world, User user){ + return islandCache.get(world, user.getUniqueId()); + } + + /** + * Gets the island for this player. If they are in a team, the team island is returned + * @param world - world to check + * @param uuid - user's uuid + * @return Island or null + */ + public Island getIsland(World world, UUID uuid){ + return islandCache.get(world, uuid); + } + + /** + * Returns the island at the location or Optional empty if there is none. + * This includes the full island space, not just the protected area + * + * @param location - the location + * @return Optional Island object + */ + public Optional getIslandAt(Location location) { + return Optional.ofNullable(islandCache.getIslandAt(location)); + } + + /** + * Returns the player's island location in World + * Returns an island location OR a team island location + * + * @param world - world to check + * @param uuid - the player's UUID + * @return Location of player's island or null if one does not exist + */ + public Location getIslandLocation(World world, UUID uuid) { + if (hasIsland(world, uuid)) { + return getIsland(world, uuid).getCenter(); + } + return null; + } + + public Location getLast(World world) { + return last.get(world); + } + + /** + * Returns a set of island member UUID's for the island of playerUUID + * This includes the owner of the island. If there is no island, this set will be empty. + * + * @param world - world to check + * @param playerUUID - the player's UUID + * @return Set of team UUIDs + */ + public Set getMembers(World world, UUID playerUUID) { + return islandCache.getMembers(world, playerUUID); + } + + /** + * Returns the island being public at the location or Optional Empty if there is none + * + * @param location - the location + * @return Optional Island object + */ + + public Optional getProtectedIslandAt(Location location) { + return getIslandAt(location).filter(i -> i.onIsland(location)); + } + + /** + * Determines a safe teleport spot on player's island or the team island + * they belong to. + * + * @param world - world to check + * @param user - the player + * @param number - a number - starting home location e.g., 1 + * @return Location of a safe teleport spot or null if one cannot be found + */ + public Location getSafeHomeLocation(World world, User user, int number) { + // Try the numbered home location first + Location l = plugin.getPlayers().getHomeLocation(world, user, number); + + if (l == null) { + // Get the default home, which may be null too, but that's okay + number = 1; + l = plugin.getPlayers().getHomeLocation(world, user, number); + } + // Check if it is safe + if (l != null) { + if (isSafeLocation(l)) { + return l; + } + // To cover slabs, stairs and other half blocks, try one block above + Location lPlusOne = l.clone(); + lPlusOne.add(new Vector(0, 1, 0)); + if (isSafeLocation(lPlusOne)) { + // Adjust the home location accordingly + plugin.getPlayers().setHomeLocation(user, lPlusOne, number); + return lPlusOne; + } + } + // Home location either isn't safe, or does not exist so try the island + // location + if (plugin.getIslands().inTeam(world, user.getUniqueId())) { + l = plugin.getIslands().getIslandLocation(world, user.getUniqueId()); + if (isSafeLocation(l)) { + plugin.getPlayers().setHomeLocation(user, l, number); + return l; + } else { + // try team leader's home + Location tlh = plugin.getPlayers().getHomeLocation(world, plugin.getIslands().getTeamLeader(world, user.getUniqueId())); + if (tlh != null && isSafeLocation(tlh)) { + plugin.getPlayers().setHomeLocation(user, tlh, number); + return tlh; + } + } + } else { + l = plugin.getIslands().getIslandLocation(world, user.getUniqueId()); + if (isSafeLocation(l)) { + plugin.getPlayers().setHomeLocation(user, l, number); + return l.clone().add(new Vector(0.5D,0,0.5D)); + } + } + if (l == null) { + plugin.logWarning(user.getName() + " player has no island in world " + world.getName() + "!"); + return null; + } + // If these island locations are not safe, then we need to get creative + // Try the default location + Location dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 2.5D, 0F, 30F); + if (isSafeLocation(dl)) { + plugin.getPlayers().setHomeLocation(user, dl, number); + return dl; + } + // Try just above the bedrock + dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 0.5D, 0F, 30F); + if (isSafeLocation(dl)) { + plugin.getPlayers().setHomeLocation(user, dl, number); + return dl; + } + // Try all the way up to the sky + for (int y = l.getBlockY(); y < 255; y++) { + final Location n = new Location(l.getWorld(), l.getX() + 0.5D, y, l.getZ() + 0.5D); + if (isSafeLocation(n)) { + plugin.getPlayers().setHomeLocation(user, n, number); + return n; + } + } + // Unsuccessful + return null; + } + + /** + * Get the island that is defined as spawn in this world + * @param world - world + * @return island or null + */ + public Island getSpawn(World world){ + return spawn.get(world); + } + + /** + * Get the spawn point on the spawn island if it exists + * @param world - world + * @return the spawnPoint or null if spawn does not exist + */ + public Location getSpawnPoint(World world) { + return spawn.containsKey(world) ? spawn.get(world).getSpawnPoint(world.getEnvironment()) : null; + } + + /** + * Provides UUID of this player's team leader or null if it does not exist + * @param world - world to check + * @param leaderUUID - the leader's UUID + * @return UUID of leader or null if player has no island + */ + public UUID getTeamLeader(World world, UUID leaderUUID) { + return islandCache.getTeamLeader(world, leaderUUID); + } + + /** + * Checks if a player has an island in the world + * @param world - world to check + * @param user - the user + * @return true if player has island and owns it + */ + public boolean hasIsland(World world, User user) { + return islandCache.hasIsland(world, user.getUniqueId()); + } + + /** + * Checks if a player has an island in the world + * @param world - world to check + * @param uuid - the user's uuid + * @return true if player has island and owns it + */ + public boolean hasIsland(World world, UUID uuid) { + return islandCache.hasIsland(world, uuid); + } + + /** + * This teleports player to their island. If not safe place can be found + * then the player is sent to spawn via /spawn command + * + * @param world - world to check + * @param player - the player + */ + public void homeTeleport(World world, Player player) { + homeTeleport(world, player, 1, false); + } + + /** + * Teleport player to a home location. If one cannot be found a search is done to + * find a safe place. + * + * @param world - world to check + * @param player - the player + * @param number - a number - home location to do to + */ + public void homeTeleport(World world, Player player, int number) { + homeTeleport(world, player, number, false); + } + + /** + * This teleports player to their island. If not safe place can be found + * then the player is sent to spawn via /spawn command + * + * @param world - world to check + * @param player - the player + * @param newIsland - true if this is a new island teleport + */ + public void homeTeleport(World world, Player player, boolean newIsland) { + homeTeleport(world, player, 1, newIsland); + } + + /** + * Teleport player to a home location. If one cannot be found a search is done to + * find a safe place. + * + * @param world - world to check + * @param player - the player + * @param number - a number - home location to do to + * @param newIsland - true if this is a new island teleport + */ + public void homeTeleport(World world, Player player, int number, boolean newIsland) { + User user = User.getInstance(player); + Location home = getSafeHomeLocation(world, user, number); + // Stop any gliding + player.setGliding(false); + // Check if the player is a passenger in a boat + if (player.isInsideVehicle()) { + Entity boat = player.getVehicle(); + if (boat instanceof Boat) { + player.leaveVehicle(); + // Remove the boat so they don't lie around everywhere + boat.remove(); + player.getInventory().addItem(new ItemStack(Material.BOAT, 1)); + player.updateInventory(); + } + } + if (home == null) { + // Try to fix this teleport location and teleport the player if possible + new SafeTeleportBuilder(plugin).entity(player) + .island(plugin.getIslands().getIsland(world, user)) + .homeNumber(number) + .build(); + return; + } + player.teleport(home); + if (number == 1) { + user.sendMessage("commands.island.go.teleport"); + } else { + user.sendMessage("commands.island.go.teleported", "[number]", String.valueOf(number)); + } + // Exit spectator mode if in it + if (player.getGameMode().equals(GameMode.SPECTATOR)) { + player.setGameMode(plugin.getIWM().getDefaultGameMode(world)); + } + // If this is a new island, then run commands and do resets + if (newIsland) { + // TODO add command running + + // Remove money inventory etc. + if (plugin.getIWM().isOnJoinResetEnderChest(world)) { + user.getPlayer().getEnderChest().clear(); + } + if (plugin.getIWM().isOnJoinResetInventory(world)) { + user.getPlayer().getInventory().clear(); + } + if (plugin.getSettings().isUseEconomy() && plugin.getIWM().isOnJoinResetMoney(world)) { + // TODO: needs Vault + } + } + } + + /** + * Indicates whether a player is at an island spawn or not + * + * @param playerLoc - player's location + * @return true if they are, false if they are not, or spawn does not exist + */ + public boolean isAtSpawn(Location playerLoc) { + return spawn.containsKey(playerLoc.getWorld()) && spawn.get(playerLoc.getWorld()).onIsland(playerLoc); + } + + /** + * @param spawn the spawn to set + */ + public void setSpawn(Island spawn) { + this.spawn.put(spawn.getWorld(), spawn); + } + + /** + * @param uniqueId - unique ID + * @return true if the player is the owner of their island, i.e., owner or team leader + */ + public boolean isOwner(World world, UUID uniqueId) { + return hasIsland(world, uniqueId) && getIsland(world, uniqueId).getOwner().equals(uniqueId); + } + + /** + * Clear and reload all islands from database + */ + public void load(){ + islandCache.clear(); + handler.loadObjects().forEach(islandCache::addIsland); + } + + /** + * Checks if a specific location is within the protected range of an island + * that the player is a member of (owner or member) + * + * @param player - the player + * @param loc - location + * @return true if location is on island of player + */ + public boolean locationIsOnIsland(Player player, Location loc) { + if (player == null) { + return false; + } + // Get the player's island + return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.getMemberSet().contains(player.getUniqueId())).orElse(false); + } + + public int metrics_getCreatedCount(){ + return metrics_createdcount; + } + + public void metrics_setCreatedCount(int count){ + metrics_createdcount = count; + } + + /** + * Checks if an online player is in the protected area of their island, a team island or a + * coop island in the specific world in the arguments. Note that the user + * + * @param world - the world to check + * @param user - the user + * @return true if on their island in world, false if not + */ + public boolean userIsOnIsland(World world, User user) { + if (user == null) { + return false; + } + return Optional.ofNullable(getIsland(world, user)).map(i -> i.onIsland(user.getLocation())).orElse(false); + } + + /** + * Removes this player from any and all islands in world + * @param world - world + * @param user - user + */ + public void removePlayer(World world, User user) { + islandCache.removePlayer(world, user.getUniqueId()); + save(true); + } + + /** + * Removes this player from any and all islands in world + * @param world - world + * @param uuid - user's uuid + */ + public void removePlayer(World world, UUID uuid) { + islandCache.removePlayer(world, uuid); + save(true); + } + + /** + * This removes players from an island overworld and nether - used when reseting or deleting an island + * Mobs are killed when the chunks are refreshed. + * @param island to remove players from + */ + public void removePlayersFromIsland(Island island) { + // Teleport players away + for (Player player : Bukkit.getOnlinePlayers()) { + if (island.inIslandSpace(player.getLocation().getBlockX(), player.getLocation().getBlockZ())) { + // Teleport island players to their island home + if (hasIsland(island.getWorld(), player.getUniqueId()) || plugin.getIslands().inTeam(island.getWorld(), player.getUniqueId())) { + homeTeleport(island.getWorld(), player); + } else { + // Move player to spawn + if (spawn.containsKey(island.getWorld())) { + // go to island spawn + player.teleport(spawn.get(island.getWorld()).getSpawnPoint(island.getWorld().getEnvironment())); + } else { + if (!player.performCommand(Constants.SPAWNCOMMAND)) { + plugin.logWarning("During island deletion player " + player.getName() + " could not be sent to spawn so was dropped, sorry."); + } + } + } + } + } + } + + /** + * Save the islands to the database + * @param async - if true, saving will be done async + */ + public void save(boolean async){ + if (midSave) { + // If it's already saving, then do nothing + return; + } + Collection collection = islandCache.getIslands(); + if(async) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + midSave = true; + for(Island island : collection){ + try { + handler.saveObject(island); + } catch (Exception e) { + plugin.logError("Could not save island to database when running async! " + e.getMessage()); + } + } + midSave = false; + }); + } else { + for(Island island : collection){ + try { + handler.saveObject(island); + } catch (Exception e) { + plugin.logError("Could not save island to database when running sync! " + e.getMessage()); + } + } + } + } + + /** + * Puts a player in a team. Removes them from their old island if required. + * @param teamIsland - team island + * @param playerUUID - the player's UUID + */ + public void setJoinTeam(Island teamIsland, UUID playerUUID) { + // Add player to new island + teamIsland.addMember(playerUUID); + islandCache.addPlayer(playerUUID, teamIsland); + // Save the database + save(false); + + } + + public void setLast(Location last) { + this.last.put(last.getWorld(), last); + } + + /** + * Called when a player leaves a team + * @param world - world + * @param uuid - the player's UUID + */ + public void setLeaveTeam(World world, UUID uuid) { + plugin.getPlayers().clearHomeLocations(world, uuid); + removePlayer(world, uuid); + } + + public void shutdown(){ + save(false); + islandCache.clear(); + handler.close(); + } + + /** + * Checks if a player is in a team + * @param world - world + * @param playerUUID - player's UUID + * @return true if in team, false if not + */ + public boolean inTeam(World world, UUID playerUUID) { + return getMembers(world, playerUUID).size() > 1; + } + + + /** + * Makes a new leader for an island + * @param world - world + * @param user - the user who is issuing the command + * @param targetUUID - the current island member who is going to become the leader + */ + public void makeLeader(World world, User user, UUID targetUUID, String permPrefix) { + makeLeader(user, targetUUID, getIsland(world, targetUUID), permPrefix); + } + + /** + * Makes a new leader for an island + * @param user - requester + * @param targetUUID - new leader + * @param island - island to register + */ + public void makeLeader(User user, UUID targetUUID, Island island, String permPrefix) { + islandCache.setOwner(island, targetUUID); + + user.sendMessage("commands.island.team.setowner.name-is-the-owner", "[name]", plugin.getPlayers().getName(targetUUID)); + + // Check if online + User target = User.getInstance(targetUUID); + target.sendMessage("commands.island.team.setowner.you-are-the-owner"); + if (target.isOnline()) { + // Check if new leader has a different range permission than the island size + int range = Util.getPermValue(target.getPlayer(), permPrefix + "island.range.", plugin.getIWM().getIslandProtectionRange(Util.getWorld(island.getWorld()))); + // Range can go up or down + if (range != island.getProtectionRange()) { + user.sendMessage("commands.admin.setrange.range-updated", "[number]", String.valueOf(range)); + target.sendMessage("commands.admin.setrange.range-updated", "[number]", String.valueOf(range)); + plugin.log("Makeleader: Island protection range changed from " + island.getProtectionRange() + " to " + + range + " for " + user.getName() + " due to permission."); + } + island.setProtectionRange(range); + + } + } + + /** + * Clear an area of mobs as per world rules. Radius is 5 blocks in every direction. + * @param loc - location to clear + */ + public void clearArea(Location loc) { + loc.getWorld().getNearbyEntities(loc, 5D, 5D, 5D).stream() + .filter(en -> (en instanceof Monster)) + .filter(en -> !plugin.getIWM().getRemoveMobsWhitelist(loc.getWorld()).contains(en.getType())) + .forEach(Entity::remove); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/LocalesManager.java b/src/main/java/us/tastybento/bskyblock/managers/LocalesManager.java new file mode 100644 index 0000000..b395161 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/LocalesManager.java @@ -0,0 +1,139 @@ +package us.tastybento.bskyblock.managers; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import org.bukkit.configuration.file.YamlConfiguration; +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.localization.BSBLocale; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.FileLister; + +/** + * @author tastybento, Poslovitch + */ +public class LocalesManager { + + private BSkyBlock plugin; + private Map languages = new HashMap<>(); + private static final String LOCALE_FOLDER = "locales"; + + public LocalesManager(BSkyBlock plugin) { + this.plugin = plugin; + loadLocales("BSkyBlock"); // Default + } + + /** + * Gets the reference from the locale file for this user + * @param user - the User + * @param reference - a reference that can be found in a locale file + * @return the translated string, or if the translation does not exist, the default language version, or if that does not exist null + */ + public String get(User user, String reference) { + BSBLocale locale = languages.get(user.getLocale()); + if (locale != null && locale.contains(reference)) { + return locale.get(reference); + } + // Return the default + if (languages.get(Locale.forLanguageTag(plugin.getSettings().getDefaultLanguage())).contains(reference)) { + return languages.get(Locale.forLanguageTag(plugin.getSettings().getDefaultLanguage())).get(reference); + } + // Or try in the en-US locale + if (languages.get(Locale.forLanguageTag("en-US")).contains(reference)) { + return languages.get(Locale.forLanguageTag("en-US")).get(reference); + } + return null; + } + + /** + * Loads all the locales available. If the locale folder does not exist, one will be created and + * filled with locale files from the jar. + */ + public void loadLocales(String parent) { + // Describe the filter - we only want files that are correctly named + // Files must be 9 chars long + FilenameFilter ymlFilter = (dir, name) -> name.toLowerCase().endsWith(".yml") && name.length() == 9; + + // Run through the files and store the locales + File localeDir = new File(plugin.getDataFolder(), LOCALE_FOLDER + File.separator + parent); + // If the folder does not exist, then make it and fill with the locale files from the jar + // If it does exist, then new files will NOT be written! + if (!localeDir.exists()) { + localeDir.mkdirs(); + FileLister lister = new FileLister(plugin); + try { + for (String name : lister.listJar(LOCALE_FOLDER)) { + // We cannot use Bukkit's saveResource, because we want it to go into a specific folder, so... + // Get the last part of the name + int lastIndex = name.lastIndexOf('/'); + File targetFile = new File(localeDir, name.substring(lastIndex >= 0 ? lastIndex : 0, name.length())); + copyFile(name, targetFile); + } + } catch (IOException e) { + plugin.logError("Could not copy locale files from jar " + e.getMessage()); + } + } + + // Store all the locales available + for (File language : Objects.requireNonNull(localeDir.listFiles(ymlFilter))) { + Locale localeObject = Locale.forLanguageTag(language.getName().substring(0, language.getName().length() - 4)); + + try { + YamlConfiguration languageYaml = YamlConfiguration.loadConfiguration(language); + + if (languages.containsKey(localeObject)) { + // Merge into current language + languages.get(localeObject).merge(languageYaml); + } else { + // New language + languages.put(localeObject, new BSBLocale(localeObject, languageYaml)); + } + } catch (Exception e) { + BSkyBlock.getInstance().logError("Could not load '" + language.getName() + "' : " + e.getMessage() + + " with the following cause '" + e.getCause() + "'." + + " The file has likely an invalid YML format or has been made unreadable during the process." + ); + } + } + } + + private void copyFile(String name, File targetFile) { + try (InputStream initialStream = plugin.getResource(name)) { + if (!targetFile.exists()) { + java.nio.file.Files.copy(initialStream, targetFile.toPath()); + } + } catch (IOException e) { + plugin.logError("Could not copy locale files from jar " + e.getMessage()); + } + } + + public List getAvailableLocales(boolean sort) { + if (sort) { + List locales = new LinkedList<>(languages.keySet()); + + locales.sort((locale1, locale2) -> { + if (locale1.toLanguageTag().equals(plugin.getSettings().getDefaultLanguage())) return -2; + else if (locale1.toLanguageTag().startsWith("en")) return -1; + else if (locale1.toLanguageTag().equals(locale2.toLanguageTag())) return 0; + else return 1; + }); + + return locales; + } else { + return new ArrayList<>(languages.keySet()); + } + } + + public Map getLanguages() { + return this.languages; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java b/src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java new file mode 100644 index 0000000..8789976 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java @@ -0,0 +1,449 @@ +package us.tastybento.bskyblock.managers; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.BSBDatabase; +import us.tastybento.bskyblock.database.objects.Names; +import us.tastybento.bskyblock.database.objects.Players; + +public class PlayersManager { + + private BSkyBlock plugin; + private BSBDatabase handler; + private BSBDatabase names; + + private Map playerCache; + private Set inTeleport; + + /** + * Provides a memory cache of online player information + * This is the one-stop-shop of player info + * If the player is not cached, then a request is made to Players to obtain it + * + * @param plugin - BSkyBlock plugin object + */ + public PlayersManager(BSkyBlock plugin){ + this.plugin = plugin; + // Set up the database handler to store and retrieve Players classes + handler = new BSBDatabase<>(plugin, Players.class); + // Set up the names database + names = new BSBDatabase<>(plugin, Names.class); + playerCache = new HashMap<>(); + inTeleport = new HashSet<>(); + } + + + /** + * Load all players - not normally used as to load all players into memory will be wasteful + */ + public void load(){ + playerCache.clear(); + inTeleport.clear(); + handler.loadObjects().forEach(p -> playerCache.put(p.getPlayerUUID(), p)); + } + + /** + * Save all players + * @param async - if true, save async + */ + public void save(boolean async){ + Collection set = Collections.unmodifiableCollection(playerCache.values()); + if(async) { + Runnable save = () -> set.forEach(handler::saveObject); + Bukkit.getScheduler().runTaskAsynchronously(plugin, save); + } else { + set.forEach(handler::saveObject); + } + } + + public void shutdown(){ + save(false); + playerCache.clear(); + handler.close(); + } + + /** + * Get player by UUID. Adds player to cache if not in there already + * @param uuid of player + * @return player object or null if it does not exist + */ + public Players getPlayer(UUID uuid){ + if (!playerCache.containsKey(uuid)) { + addPlayer(uuid); + } + return playerCache.get(uuid); + } + + /* + * Cache control methods + */ + + /** + * Adds a player to the cache. If the UUID does not exist, a new player is made + * @param playerUUID - the player's UUID + */ + public void addPlayer(UUID playerUUID) { + if (playerUUID == null) { + return; + } + if (!playerCache.containsKey(playerUUID)) { + Players player; + // If the player is in the database, load it, otherwise create a new player + if (handler.objectExists(playerUUID.toString())) { + player = handler.loadObject(playerUUID.toString()); + } else { + player = new Players(plugin, playerUUID); + } + playerCache.put(playerUUID, player); + } + } + + /** + * Stores the player's info and removes the player from the cache + * + * @param playerUUID - the player - UUID of player + * + */ + public void removeOnlinePlayer(UUID playerUUID) { + save(playerUUID); + playerCache.remove(playerUUID); + } + + /** + * Saves all players on the server and clears the cache + */ + public void removeAllPlayers() { + playerCache.keySet().forEach(this::save); + playerCache.clear(); + } + + /* + * Player info query methods + */ + + /** + * Checks if the player is known or not + * + * @param uniqueID - unique ID + * @return true if player is know, otherwise false + */ + public boolean isKnown(UUID uniqueID) { + if (uniqueID == null) { + return false; + } + // Try cache + return playerCache.containsKey(uniqueID) || handler.objectExists(uniqueID.toString()); + // Get from the database - do not add to cache yet + } + + /** + * Sets the home location for the player + * @param user - the player + * @param location - the location + * @param number - a number - 1 is default. Can be any number. + */ + public void setHomeLocation(User user, Location location, int number) { + addPlayer(user.getUniqueId()); + playerCache.get(user.getUniqueId()).setHomeLocation(location,number); + } + + /** + * Sets the home location for the player + * @param playerUUID - the player's UUID + * @param location - the location + * @param number - a number - 1 is default. Can be any number. + */ + public void setHomeLocation(UUID playerUUID, Location location, int number) { + addPlayer(playerUUID); + playerCache.get(playerUUID).setHomeLocation(location,number); + } + + /** + * Set the default home location for player + * @param playerUUID - the player's UUID + * @param location - the location + */ + public void setHomeLocation(UUID playerUUID, Location location) { + addPlayer(playerUUID); + playerCache.get(playerUUID).setHomeLocation(location,1); + } + + /** + * Clears any home locations for player + * @param world - world + * @param playerUUID - the player's UUID + */ + public void clearHomeLocations(World world, UUID playerUUID) { + addPlayer(playerUUID); + playerCache.get(playerUUID).clearHomeLocations(world); + } + + /** + * Returns the home location, or null if none + * @param world - world + * + * @param user - the player + * @param number - a number + * @return Home location or null if none + */ + public Location getHomeLocation(World world, User user, int number) { + addPlayer(user.getUniqueId()); + return playerCache.get(user.getUniqueId()).getHomeLocation(world, number); + } + + /** + * Returns the home location, or null if none + * @param world - world + * + * @param playerUUID - the player's UUID + * @param number - a number + * @return Home location or null if none + */ + public Location getHomeLocation(World world, UUID playerUUID, int number) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getHomeLocation(world, number); + } + + /** + * Gets the default home location for player + * @param playerUUID - the player's UUID + * @return Home location or null if none + */ + public Location getHomeLocation(World world, UUID playerUUID) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getHomeLocation(world, 1); + } + + /** + * Provides all home locations for player + * @param playerUUID - the player's UUID + * @return List of home locations + */ + public Map getHomeLocations(World world, UUID playerUUID) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getHomeLocations(world); + } + + /** + * Attempts to return a UUID for a given player's name. + * @param name - name of player + * @return UUID of player or null if unknown + */ + @SuppressWarnings("deprecation") + public UUID getUUID(String name) { + // See if this is a UUID + // example: 5988eecd-1dcd-4080-a843-785b62419abb + if (name.length() == 36 && name.contains("-")) { + try { + return UUID.fromString(name); + } catch (Exception ignored) {} + } + // Look in the name cache, then the data base and finally Bukkit blocking request + return playerCache.values().stream() + .filter(p -> p.getPlayerName().equalsIgnoreCase(name)).findFirst() + .map(p -> UUID.fromString(p.getUniqueId())) + .orElse(names.objectExists(name) + ? names.loadObject(name).getUuid() + : Bukkit.getOfflinePlayer(name).getUniqueId()); + } + + /** + * Sets the player's name and updates the name>UUID database + * @param user - the User + */ + public void setPlayerName(User user) { + addPlayer(user.getUniqueId()); + playerCache.get(user.getUniqueId()).setPlayerName(user.getName()); + // Add to names database + names.saveObject(new Names(user.getName(), user.getUniqueId())); + } + + /** + * Obtains the name of the player from their UUID + * Player must have logged into the game before + * + * @param playerUUID - the player's UUID + * @return String - playerName, empty string if UUID is null + */ + public String getName(UUID playerUUID) { + if (playerUUID == null) { + return ""; + } + addPlayer(playerUUID); + return playerCache.get(playerUUID).getPlayerName(); + } + + /** + * Gets how many island resets the player has done + * @param world + * + * @param playerUUID - the player's UUID + * @return number of resets + */ + public int getResets(World world, UUID playerUUID) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getResets(world); + } + + /** + * Sets how many resets the player has performed + * + * @param world - world + * @param playerUUID - the player's UUID + * @param resets - number of resets + */ + public void setResets(World world, UUID playerUUID, int resets) { + addPlayer(playerUUID); + playerCache.get(playerUUID).setResets(world, resets); + } + + /** + * Returns how long the player must wait before they can be invited to an + * island with the location + * + * @param playerUUID - the player's UUID + * @param location - the location + * @return time to wait in minutes/hours + */ + public long getInviteCoolDownTime(UUID playerUUID, Location location) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getInviteCoolDownTime(location); + } + + /** + * Starts the timer for the player for this location before which they can + * be invited + * Called when they are kicked from an island or leave. + * + * @param playerUUID - the player's UUID + * @param location - the location + */ + public void startInviteCoolDownTimer(UUID playerUUID, Location location) { + addPlayer(playerUUID); + playerCache.get(playerUUID).startInviteCoolDownTimer(location); + } + + /** + * Returns the locale for this player. If missing, will return nothing + * @param playerUUID - the player's UUID + * @return name of the locale this player uses + */ + public String getLocale(UUID playerUUID) { + addPlayer(playerUUID); + if (playerUUID == null) { + return ""; + } + return playerCache.get(playerUUID).getLocale(); + } + + /** + * Sets the locale this player wants to use + * @param playerUUID - the player's UUID + * @param localeName - locale name, e.g., en-US + */ + public void setLocale(UUID playerUUID, String localeName) { + addPlayer(playerUUID); + playerCache.get(playerUUID).setLocale(localeName); + } + + /** + * Add death to player + * @param playerUUID - the player's UUID + */ + public void addDeath(UUID playerUUID) { + addPlayer(playerUUID); + playerCache.get(playerUUID).addDeath(); + } + + /** + * Set death number for player + * @param playerUUID - the player's UUID + * @param deaths - number of deaths + */ + public void setDeaths(UUID playerUUID, int deaths) { + addPlayer(playerUUID); + playerCache.get(playerUUID).setDeaths(deaths); + } + + /** + * Get number of times player has died in BSkyBlock worlds since counting began + * @param playerUUID - the player's UUID + * @return number of deaths + */ + public int getDeaths(UUID playerUUID) { + addPlayer(playerUUID); + return playerCache.get(playerUUID).getDeaths(); + } + + /** + * Sets if a player is mid-teleport or not + * @param uniqueId - unique ID + */ + public void setInTeleport(UUID uniqueId) { + inTeleport.add(uniqueId); + } + + /** + * Removes player from in-teleport + * @param uniqueId - unique ID + */ + public void removeInTeleport(UUID uniqueId) { + inTeleport.remove(uniqueId); + } + + /** + * @param uniqueId - unique ID + * @return true if a player is mid-teleport + */ + public boolean isInTeleport(UUID uniqueId) { + return inTeleport.contains(uniqueId); + } + + /** + * Saves the player to the database + * @param playerUUID - the player's UUID + */ + public void save(UUID playerUUID) { + if (playerCache.containsKey(playerUUID)) { + handler.saveObject(playerCache.get(playerUUID)); + } + } + + /** + * Tries to get the user from his name + * @param name - name + * @return user - user + */ + public User getUser(String name) { + return getUser(getUUID(name)); + } + + /** + * Tries to get the user from his UUID + * @param uuid - UUID + * @return user - user + */ + public User getUser(UUID uuid) { + return User.getInstance(uuid); + } + + + public void addReset(World world, UUID playerUUID) { + addPlayer(playerUUID); + playerCache.get(playerUUID).addReset(world); + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/RanksManager.java b/src/main/java/us/tastybento/bskyblock/managers/RanksManager.java new file mode 100644 index 0000000..747d3f6 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/RanksManager.java @@ -0,0 +1,161 @@ +package us.tastybento.bskyblock.managers; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import us.tastybento.bskyblock.BSkyBlock; + +public class RanksManager { + + // Constants that define the hard coded rank values + public static final String ADMIN_RANK_REF = "ranks.admin"; + public static final String MOD_RANK_REF = "ranks.mod"; + public static final String OWNER_RANK_REF = "ranks.owner"; + public static final String MEMBER_RANK_REF = "ranks.member"; + public static final String VISITOR_RANK_REF = "ranks.visitor"; + public static final String BANNED_RANK_REF = "ranks.banned"; + public static final int ADMIN_RANK = 10000; + public static final int MOD_RANK = 5000; + public static final int OWNER_RANK = 1000; + public static final int MEMBER_RANK = 900; + public static final int VISITOR_RANK = 0; + public static final int BANNED_RANK = -1; + + private BSkyBlock plugin; + + // The store of ranks + private LinkedHashMap ranks = new LinkedHashMap<>(); + + /** + * @param plugin - BSkyBlock plugin object + */ + public RanksManager(BSkyBlock plugin) { + super(); + this.plugin = plugin; + // Hard coded ranks + ranksPut(ADMIN_RANK_REF, ADMIN_RANK); + ranksPut(MOD_RANK_REF, MOD_RANK); + ranksPut(OWNER_RANK_REF, OWNER_RANK); + ranksPut(MEMBER_RANK_REF, MEMBER_RANK); + ranksPut(VISITOR_RANK_REF, VISITOR_RANK); + ranksPut(BANNED_RANK_REF, BANNED_RANK); + loadCustomRanks(); + } + + /** + * Loads the custom ranks from the settings + */ + public void loadCustomRanks() { + for (Entry en : plugin.getSettings().getCustomRanks().entrySet()) { + if (!addRank(en.getKey(),en.getValue())) { + plugin.logError("Error loading custom rank: " + en.getKey() + " " + en.getValue() + " skipping..."); + } + } + } + + /** + * Try to add a new rank. Owner, member, visitor and banned ranks cannot be changed. + * @param reference - a reference that can be found in a locale file + * @param value - the rank value + * @return true if the rank was successfully added + */ + public boolean addRank(String reference, int value) { + if (reference.equalsIgnoreCase(OWNER_RANK_REF) + || reference.equalsIgnoreCase(MEMBER_RANK_REF) + || reference.equalsIgnoreCase(VISITOR_RANK_REF) + || reference.equalsIgnoreCase(BANNED_RANK_REF) + || reference.equalsIgnoreCase(ADMIN_RANK_REF) + || reference.equalsIgnoreCase(MOD_RANK_REF)) { + return false; + } + ranksPut(reference, value); + + return true; + } + + private void ranksPut(String reference, int value) { + ranks.put(reference, value); + // Sort + ranks = ranks.entrySet().stream() + .sorted(Map.Entry.comparingByValue()) + .collect(Collectors.toMap( + Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + } + + /** + * Try to remove a rank. Owner, member, visitor and banned ranks cannot be removed. + * @param reference - a reference that can be found in a locale file + * @return true if removed + */ + public boolean removeRank(String reference) { + return !reference.equalsIgnoreCase(OWNER_RANK_REF) + && !reference.equalsIgnoreCase(MEMBER_RANK_REF) + && !reference.equalsIgnoreCase(VISITOR_RANK_REF) + && !reference.equalsIgnoreCase(BANNED_RANK_REF) + && !reference.equalsIgnoreCase(ADMIN_RANK_REF) + && !reference.equalsIgnoreCase(MOD_RANK_REF) && (ranks.remove(reference) != null); + + } + + /** + * Get the rank value for this reference + * @param reference - locale reference to the name of this rank + * @return rank value or zero if this is an unknown rank + */ + public int getRankValue(String reference) { + return ranks.getOrDefault(reference, VISITOR_RANK); + } + + /** + * Get the ranks. Ranks are listed in ascending order + * @return immutable map of ranks + */ + public Map getRanks() { + return new LinkedHashMap<>(ranks); + } + + + /** + * Gets the next rank value above the current rank. Highest is {@link RanksManager#OWNER_RANK} + * @param currentRank - current rank value + * @return Optional rank value + */ + public int getRankUpValue(int currentRank) { + return getRanks().values().stream().mapToInt(x -> { + if (x > currentRank) { + return x; + } + return OWNER_RANK; + }).min().orElse(currentRank); + } + + /** + * Gets the previous rank value below the current rank. Lowest is {@link RanksManager#VISITOR_RANK} + * @param currentRank - current rank value + * @return Optional rank value + */ + public int getRankDownValue(int currentRank) { + return getRanks().values().stream().mapToInt(x -> { + if (x < currentRank) { + return x; + } + return VISITOR_RANK; + }).max().orElse(currentRank); + } + + /** + * Gets the reference to the rank name for value + * @param rank - value + * @return Reference + */ + public String getRank(int rank) { + for (Entry en : ranks.entrySet()) { + if (rank == en.getValue()) { + return en.getKey(); + } + } + return ""; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/SchemsManager.java b/src/main/java/us/tastybento/bskyblock/managers/SchemsManager.java new file mode 100644 index 0000000..d176d22 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/SchemsManager.java @@ -0,0 +1,114 @@ +package us.tastybento.bskyblock.managers; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.bukkit.World; +import org.bukkit.configuration.InvalidConfigurationException; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.island.builders.Clipboard; + +public class SchemsManager { + + private BSkyBlock plugin; + private Map islandSchems; + + /** + * @param plugin + */ + public SchemsManager(BSkyBlock plugin) { + this.plugin = plugin; + islandSchems = new HashMap<>(); + } + + /** + * @param schems - schems folder for either the addon or the plugin + * @param world - world + * @param name - name of the schem to save (excluding .schem) + */ + private void copySchems(File schems, World world, String name) { + if (!schems.exists() && !schems.mkdirs()) { + plugin.logError("Could not make schems folder!"); + return; + } + File schem = new File(schems, name + ".schem"); + if (schem.exists()) { + // No overwriting + return; + } + Optional addon = plugin.getIWM().getAddon(world); + if (addon.isPresent()) { + addon.get().saveResource("schems/" + name + ".schem", false); + } else { + plugin.saveResource("schems/" + name + ".schem", false); + } + } + + public Clipboard get(World world) { + return islandSchems.get(world); + } + + /** + * Load schems for world. Will try and load nether and end schems too if settings are set. + * @param world - world + */ + public void loadIslands(World world) { + if (!plugin.getSchemsManager().loadSchem(world, "island")) { + plugin.logError("Could not load island.schem for " + plugin.getIWM().getFriendlyName(world)); + } + if (plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world) + && !plugin.getSchemsManager().loadSchem(plugin.getIWM().getNetherWorld(world), "nether-island")) { + plugin.logError("Could not load nether_island.schem for " + plugin.getIWM().getFriendlyName(world)); + } + if (plugin.getIWM().isEndGenerate(world) && plugin.getIWM().isEndIslands(world) + && !plugin.getSchemsManager().loadSchem(plugin.getIWM().getEndWorld(world), "end-island")) { + plugin.logError("Could not load end_island.schem for " + plugin.getIWM().getFriendlyName(world)); + } + } + + private boolean loadSchem(World world, String name) { + plugin.log("Loading " + name + ".schem for " + world.getName()); + File schems = new File(plugin.getIWM().getDataFolder(world), "schems"); + copySchems(schems, world, name); + try { + Clipboard cb = new Clipboard(plugin, schems); + cb.load(name); + islandSchems.put(world, cb); + } catch (IOException | InvalidConfigurationException e) { + plugin.logError("Could not load " + name + " schem"); + return false; + } + return true; + } + + /** + * Paste the schem for world to the island center location and run task afterwards + * @param world - world to paste to + * @param island - the island who owns this schem + * @param task - task to run after pasting is completed + */ + public void paste(World world, Island island, Runnable task) { + if (islandSchems.containsKey(world)) { + islandSchems.get(world).paste(world, island, task); + } else { + plugin.logError("Tried to paste schem for " + world.getName() + " but it is not loaded!"); + } + } + + /** + * Paste the schem to world for island + * @param world + * @param island + */ + public void paste(World world, Island island) { + paste(world, island, null); + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java b/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java new file mode 100644 index 0000000..4282acb --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java @@ -0,0 +1,210 @@ +package us.tastybento.bskyblock.managers.island; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; + +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class IslandCache { + private Map islandsByLocation; + /** + * Every player who is associated with an island is in this map. + */ + private Map> islandsByUUID; + private Map grids; + + public IslandCache() { + islandsByLocation = new HashMap<>(); + islandsByUUID = new HashMap<>(); + grids = new HashMap<>(); + } + + /** + * Adds an island to the grid + * @param island - island to add + * @return true if successfully added, false if not + */ + public boolean addIsland(Island island) { + if (island.getCenter() == null || island.getWorld() == null) { + return false; + } + islandsByLocation.put(island.getCenter(), island); + // Make world + islandsByUUID.putIfAbsent(island.getWorld(), new HashMap<>()); + // Only add islands to this map if they are owned + if (island.getOwner() != null) { + islandsByUUID.get(island.getWorld()).put(island.getOwner(), island); + island.getMemberSet().forEach(member -> islandsByUUID.get(island.getWorld()).put(member, island)); + } + return addToGrid(island); + } + + /** + * Adds a player's UUID to the look up for islands. Does no checking + * @param uuid - player's uuid + * @param island - island to associate with this uuid. Only one island can be associated per world. + */ + public void addPlayer(UUID uuid, Island island) { + islandsByUUID.putIfAbsent(island.getWorld(), new HashMap<>()); + islandsByUUID.get(island.getWorld()).put(uuid, island); + } + + /** + * Adds an island to the grid register + * @param newIsland - new island + * @return true if successfully added, false if not + */ + private boolean addToGrid(Island newIsland) { + grids.putIfAbsent(newIsland.getWorld(), new IslandGrid()); + return grids.get(newIsland.getWorld()).addToGrid(newIsland); + } + + public void clear() { + islandsByLocation.clear(); + islandsByUUID.clear(); + } + + /** + * Deletes an island from the database. Does not remove blocks + * @param island - island to delete + * @return true if successful, false if not + */ + public boolean deleteIslandFromCache(Island island) { + if (!islandsByLocation.remove(island.getCenter(), island) || !islandsByUUID.containsKey(island.getWorld())) { + return false; + } + islandsByUUID.get(island.getWorld()).entrySet().removeIf(en -> en.getValue().equals(island)); + // Remove from grid + grids.putIfAbsent(island.getWorld(), new IslandGrid()); + return grids.get(island.getWorld()).removeFromGrid(island); + } + + /** + * Get island based on the exact center location of the island + * @param location - location to search for + * @return island or null if it does not exist + */ + public Island get(Location location) { + return islandsByLocation.get(location); + } + + /** + * Returns island referenced by UUID + * @param world - world to check + * @param uuid - player + * @return island or null if none + */ + public Island get(World world, UUID uuid) { + return islandsByUUID.containsKey(Util.getWorld(world)) ? islandsByUUID.get(Util.getWorld(world)).get(uuid) : null; + } + + /** + * Returns the island at the location or null if there is none. + * This includes the full island space, not just the protected area + * + * @param location - the location + * @return Island object + */ + public Island getIslandAt(Location location) { + if (location == null || !grids.containsKey(Util.getWorld(location.getWorld()))) { + return null; + } + return grids.get(Util.getWorld(location.getWorld())).getIslandAt(location.getBlockX(), location.getBlockZ()); + } + + public Collection getIslands() { + return Collections.unmodifiableCollection(islandsByLocation.values()); + } + + /** + * @param world - world to check + * @param uuid - uuid of player to check + * @return set of UUID's of island members. If there is no island, this set will be empty + */ + public Set getMembers(World world, UUID uuid) { + islandsByUUID.putIfAbsent(Util.getWorld(world), new HashMap<>()); + Island island = islandsByUUID.get(Util.getWorld(world)).get(uuid); + if (island != null) { + return island.getMemberSet(); + } + return new HashSet<>(0); + } + + /** + * @param world - the world to check + * @param uuid - player's uuid + * @return team leader's UUID, the player UUID if they are not in a team, or null if there is no island + */ + public UUID getTeamLeader(World world, UUID uuid) { + islandsByUUID.putIfAbsent(Util.getWorld(world), new HashMap<>()); + Island island = islandsByUUID.get(Util.getWorld(world)).get(uuid); + if (island != null) { + return island.getOwner(); + } + return null; + } + + /** + * @param world - the world to check + * @param uuid - the player + * @return true if player has island and owns it + */ + public boolean hasIsland(World world, UUID uuid) { + islandsByUUID.putIfAbsent(Util.getWorld(world), new HashMap<>()); + Island island = islandsByUUID.get(Util.getWorld(world)).get(uuid); + return island != null && island.getOwner().equals(uuid); + } + + /** + * Removes a player from the cache. If the player has an island, the island owner is removed and membership cleared. + * The island is removed from the islandsByUUID map, but kept in the location map. + * @param world - world + * @param uuid - player's UUID + */ + public void removePlayer(World world, UUID uuid) { + world = Util.getWorld(world); + islandsByUUID.putIfAbsent(world, new HashMap<>()); + Island island = islandsByUUID.get(world).get(uuid); + if (island != null) { + if (island.getOwner() != null && island.getOwner().equals(uuid)) { + // Clear ownership and members + island.getMembers().clear(); + island.setOwner(null); + } else { + // Remove player from the island membership + island.removeMember(uuid); + } + } + islandsByUUID.get(world).remove(uuid); + } + + /** + * Get the number of islands in the cache + * @return the number of islands + */ + public int size() { + return islandsByLocation.size(); + } + + /** + * Sets an island owner. Clears out any other owner + * @param island - island + * @param newOwnerUUID - new owner + */ + public void setOwner(Island island, UUID newOwnerUUID) { + island.setOwner(newOwnerUUID); + islandsByUUID.putIfAbsent(Util.getWorld(island.getWorld()), new HashMap<>()); + islandsByUUID.get(Util.getWorld(island.getWorld())).put(newOwnerUUID, island); + islandsByLocation.put(island.getCenter(), island); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/island/IslandGrid.java b/src/main/java/us/tastybento/bskyblock/managers/island/IslandGrid.java new file mode 100644 index 0000000..f2124ff --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/island/IslandGrid.java @@ -0,0 +1,85 @@ +package us.tastybento.bskyblock.managers.island; + +import java.util.Map.Entry; +import java.util.TreeMap; + +import us.tastybento.bskyblock.database.objects.Island; + +/** + * Handles the island location grid for each world + * @author tastybento + * + */ +public class IslandGrid { + private TreeMap> grid = new TreeMap<>(); + + /** + * Adds island to grid + * @param island - island to add + * @return true if successfully added, false if island already exists, or there is an overlap + */ + public boolean addToGrid(Island island) { + if (grid.containsKey(island.getMinX())) { + TreeMap zEntry = grid.get(island.getMinX()); + if (zEntry.containsKey(island.getMinZ())) { + return false; + } else { + // Add island + zEntry.put(island.getMinZ(), island); + grid.put(island.getMinX(), zEntry); + } + } else { + // Add island + TreeMap zEntry = new TreeMap<>(); + zEntry.put(island.getMinZ(), island); + grid.put(island.getMinX(), zEntry); + } + return true; + } + + /** + * Remove island from grid + * @param island - the island to remove + * @return true if island existed and was deleted, false if there was nothing to delete + */ + public boolean removeFromGrid(Island island) { + // Remove from grid + if (island != null) { + int x = island.getMinX(); + int z = island.getMinZ(); + if (grid.containsKey(x)) { + TreeMap zEntry = grid.get(x); + if (zEntry.containsKey(z)) { + // Island exists - delete it + zEntry.remove(z); + grid.put(x, zEntry); + return true; + } + } + } + return false; + } + + /** + * Returns the island at the x,z location or null if there is none. + * This includes the full island space, not just the protected area. + * + * @param x - x coordinate + * @param z - z coordinate + * @return Island or null + */ + public Island getIslandAt(int x, int z) { + Entry> en = grid.floorEntry(x); + if (en != null) { + Entry ent = en.getValue().floorEntry(z); + if (ent != null) { + // Check if in the island range + Island island = ent.getValue(); + if (island.inIslandSpace(x, z)) { + return island; + } + } + } + return null; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java b/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java new file mode 100644 index 0000000..72bc3bb --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java @@ -0,0 +1,279 @@ +package us.tastybento.bskyblock.managers.island; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +/** + * Create and paste a new island + * @author tastybento + * + */ +public class NewIsland { + private static final Integer MAX_UNOWNED_ISLANDS = 10; + private BSkyBlock plugin; + private Island island; + private final User user; + private final Reason reason; + private final World world; + private enum Result { + ISLAND_FOUND, + BLOCK_AT_CENTER, + BLOCKS_IN_AREA, + FREE + } + + private NewIsland(Island oldIsland, User user, Reason reason, World world) { + super(); + plugin = BSkyBlock.getInstance(); + this.user = user; + this.reason = reason; + this.world = world; + newIsland(); + if (oldIsland != null) { + // Delete the old island + plugin.getIslands().deleteIsland(oldIsland, true); + } + } + + /** + * @return the island that was created + */ + public Island getIsland() { + return island; + } + + /** + * Start building a new island + * @return New island builder object + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Build a new island for a player + * @author tastybento + * + */ + public static class Builder { + private Island oldIsland2; + private User user2; + private Reason reason2; + private World world2; + + public Builder oldIsland(Island oldIsland) { + this.oldIsland2 = oldIsland; + this.world2 = oldIsland.getWorld(); + return this; + } + + + public Builder player(User player) { + this.user2 = player; + return this; + } + + public Builder reason(Reason reason) { + this.reason2 = reason; + return this; + } + + public Builder world(World world) { + this.world2 = world; + return this; + } + + public Island build() throws IOException { + if (user2 != null) { + NewIsland newIsland = new NewIsland(oldIsland2, user2, reason2, world2); + return newIsland.getIsland(); + } + throw new IOException("Insufficient parameters. Must have a schematic and a player"); + } + } + + /** + * Makes an island. + */ + public void newIsland() { + Location next = getNextIsland(); + if (next == null) { + plugin.logError("Failed to make island - no unoccupied spot found"); + return; + } + // Add to the grid + island = plugin.getIslands().createIsland(next, user.getUniqueId()); + // Save the player so that if the server is reset weird things won't happen + + // Clear any old home locations (they should be clear, but just in case) + plugin.getPlayers().clearHomeLocations(world, user.getUniqueId()); + + // Set home location + plugin.getPlayers().setHomeLocation(user, next, 1); + + // Fire event + IslandBaseEvent event = IslandEvent.builder() + .involvedPlayer(user.getUniqueId()) + .reason(reason) + .island(island) + .location(island.getCenter()) + .build(); + if (event.isCancelled()) { + return; + } + // Create island + plugin.getSchemsManager().paste(world, island, () -> { + // Set initial spawn point if one exists + if (island.getSpawnPoint(Environment.NORMAL) != null) { + plugin.getPlayers().setHomeLocation(user, island.getSpawnPoint(Environment.NORMAL), 1); + } + // Teleport player after this island is built + plugin.getIslands().homeTeleport(world, user.getPlayer(), true); + }); + // Make nether island + if (plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world) && plugin.getIWM().getNetherWorld(world) != null) { + plugin.getSchemsManager().paste(plugin.getIWM().getNetherWorld(world), island); + } + + // Make end island + if (plugin.getIWM().isEndGenerate(world) && plugin.getIWM().isEndIslands(world) && plugin.getIWM().getEndWorld(world) != null) { + plugin.getSchemsManager().paste(plugin.getIWM().getEndWorld(world), island); + } + + // Set default settings + island.setFlagsDefaults(); + + // Fire exit event + Reason reasonDone = Reason.CREATED; + switch (reason) { + case CREATE: + reasonDone = Reason.CREATED; + break; + case RESET: + reasonDone = Reason.RESETTED; + break; + default: + break; + } + IslandEvent.builder() + .involvedPlayer(user.getUniqueId()) + .reason(reasonDone) + .island(island) + .location(island.getCenter()) + .build(); + + } + + /** + * Get the location of next free island spot + * @return Location of island spot or null if one cannot be found + */ + private Location getNextIsland() { + Location last = plugin.getIslands().getLast(world); + if (last == null) { + last = new Location(world, plugin.getIWM().getIslandXOffset(world) + plugin.getIWM().getIslandStartX(world), + plugin.getIWM().getIslandHeight(world), plugin.getIWM().getIslandZOffset(world) + plugin.getIWM().getIslandStartZ(world)); + } + // Find a free spot + Map result = new EnumMap<>(Result.class); + Result r = isIsland(last); + while (!r.equals(Result.FREE) && result.getOrDefault(Result.BLOCK_AT_CENTER, 0) < MAX_UNOWNED_ISLANDS) { + last = nextGridLocation(last); + result.merge(r, 1, (k,v) -> v++); + r = isIsland(last); + } + if (!r.equals(Result.FREE)) { + // We could not find a free spot within the limit required. It's likely this world is not empty + plugin.logError("Could not find a free spot for islands! Is this world empty?"); + plugin.logError("Blocks at center locations: " + result.getOrDefault(Result.BLOCK_AT_CENTER, 0) + " max " + MAX_UNOWNED_ISLANDS); + plugin.logError("Blocks around center locations: " + result.getOrDefault(Result.BLOCKS_IN_AREA, 0) + " max " + MAX_UNOWNED_ISLANDS); + plugin.logError("Known islands: " + result.getOrDefault(Result.ISLAND_FOUND, 0) + " max unlimited."); + return null; + } + plugin.getIslands().setLast(last); + return last; + } + + /** + * Checks if there is an island or blocks at this location + * @param location - the location + * @return true if island found, null if blocks found, false if nothing found + */ + private Result isIsland(Location location){ + location = Util.getClosestIsland(location); + if (plugin.getIslands().getIslandAt(location).isPresent()) { + return Result.ISLAND_FOUND; + } + + if (!plugin.getIWM().isUseOwnGenerator(location.getWorld())) { + // Block check + if (!location.getBlock().isEmpty() && !location.getBlock().getType().equals(Material.WATER)) { + plugin.getIslands().createIsland(location); + return Result.BLOCK_AT_CENTER; + } + // Look around + for (int x = -5; x <= 5; x++) { + for (int y = 10; y < location.getWorld().getMaxHeight(); y++) { + for (int z = -5; z <= 5; z++) { + if (!location.getWorld().getBlockAt(x + location.getBlockX(), y, z + location.getBlockZ()).isEmpty() + && !location.getWorld().getBlockAt(x + location.getBlockX(), y, z + location.getBlockZ()).getType().equals(Material.WATER)) { + plugin.getIslands().createIsland(location); + return Result.BLOCKS_IN_AREA; + } + } + } + } + } + return Result.FREE; + } + + + /** + * Finds the next free island spot based off the last known island Uses + * island_distance setting from the config file Builds up in a grid fashion + * + * @param lastIsland - last island location + * @return Location of next free island + */ + private Location nextGridLocation(final Location lastIsland) { + int x = lastIsland.getBlockX(); + int z = lastIsland.getBlockZ(); + int d = plugin.getIWM().getIslandDistance(lastIsland.getWorld()) * 2; + if (x < z) { + if (-1 * x < z) { + lastIsland.setX(lastIsland.getX() + d); + return lastIsland; + } + lastIsland.setZ(lastIsland.getZ() + d); + return lastIsland; + } + if (x > z) { + if (-1 * x >= z) { + lastIsland.setX(lastIsland.getX() - d); + return lastIsland; + } + lastIsland.setZ(lastIsland.getZ() - d); + return lastIsland; + } + if (x <= 0) { + lastIsland.setZ(lastIsland.getZ() + d); + return lastIsland; + } + lastIsland.setZ(lastIsland.getZ() - d); + return lastIsland; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/panels/LanguagePanel.java b/src/main/java/us/tastybento/bskyblock/panels/LanguagePanel.java new file mode 100644 index 0000000..e5b55e6 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/panels/LanguagePanel.java @@ -0,0 +1,71 @@ +package us.tastybento.bskyblock.panels; + +import java.util.Locale; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.panels.builders.PanelBuilder; +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; + +/** + * @author Poslovitch + */ +public class LanguagePanel { + + private LanguagePanel() {} + + /** + * Dynamically creates the panel. + * @param user the User to show the panel to + */ + public static void openPanel(User user) { + PanelBuilder panelBuilder = new PanelBuilder() + .name(user.getTranslation("language.panel-title")); + + for (Locale locale : BSkyBlock.getInstance().getLocalesManager().getAvailableLocales(true)) { + PanelItemBuilder localeIcon = new PanelItemBuilder(); + + ItemStack localeBanner = BSkyBlock.getInstance().getLocalesManager().getLanguages().get(locale).getBanner(); + if (localeBanner != null) { + localeIcon.icon(localeBanner); + } else { + localeIcon.icon(new ItemStack(Material.BANNER, 1)); // Set to a blank banner. + } + + localeIcon.name(fancyLocaleDisplayName(user, locale)) + .clickHandler((panel, u, click, slot) -> { + BSkyBlock.getInstance().getPlayers().setLocale(u.getUniqueId(), locale.toLanguageTag()); + u.sendMessage("language.edited", "[lang]", fancyLocaleDisplayName(u, locale)); + openPanel(u); + return true; + }); + + if (user.getLocale().toLanguageTag().equals(locale.toLanguageTag())) { + localeIcon.description(user.getTranslation("language.selected")); + } + + panelBuilder.item(localeIcon.build()); + } + + panelBuilder.build().open(user); + } + + /** + * Returns a properly capitalized String based on locale's display name from user's current locale. + * @param user - the User + * @param locale - the Locale to get the display name from + * @return properly capitalized String of the locale's display name in user's current locale + */ + private static String fancyLocaleDisplayName(User user, Locale locale) { + // Get the display name of the locale based on current user's locale + String localeDisplayName = locale.getDisplayName(user.getLocale()); + + // Set the first letter to an uppercase, to make it nice and fancy :D + localeDisplayName = localeDisplayName.substring(0,1).toUpperCase() + localeDisplayName.substring(1); + + return localeDisplayName; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/panels/SettingsPanel.java b/src/main/java/us/tastybento/bskyblock/panels/SettingsPanel.java new file mode 100644 index 0000000..0b4bd2c --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/panels/SettingsPanel.java @@ -0,0 +1,70 @@ +package us.tastybento.bskyblock.panels; + +import java.util.Comparator; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.builders.PanelBuilder; +import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; +import us.tastybento.bskyblock.api.user.User; + +/** + * Creates settings panels + * @author Poslovitch, tastybento + */ +public class SettingsPanel { + + private static final String PROTECTION_PANEL = "protection.panel."; + + private SettingsPanel() {} + + /** + * Dynamically creates the panel. + * @param plugin - plugin + * @param user the User to show the panel to + */ + public static void openPanel(BSkyBlock plugin, User user, Flag.Type flagType, World world) { + String friendlyWorldName = plugin.getIWM().getFriendlyName(world); + // Create the panel + PanelBuilder panelBuilder = new PanelBuilder() + .name(user.getTranslation(PROTECTION_PANEL + flagType.toString() + ".title", "[world_name]", friendlyWorldName)) + .size(54); + + setupHeader(user, panelBuilder, flagType, world, friendlyWorldName); + + plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(flagType)) + .sorted(Comparator.comparing(Flag::getID)).forEach((f -> panelBuilder.item(f.toPanelItem(plugin, user)))); + + // Show it to the player + panelBuilder.build().open(user); + } + + private static void setupHeader(User user, PanelBuilder panelBuilder, Flag.Type currentFlagType, World world, String friendlyWorldName) { + int slot = 2; + for (Flag.Type flagType : Flag.Type.values()) { + PanelItem panelItem = new PanelItemBuilder() + .icon(flagType.getIcon()) + .name(user.getTranslation(PROTECTION_PANEL + flagType.toString() + ".title", "[world_name]", friendlyWorldName)) + .description(user.getTranslation(PROTECTION_PANEL + flagType.toString() + ".description")) + .glow(flagType.equals(currentFlagType)) + .clickHandler((panel, user1, clickType, slot1) -> { + if (!flagType.equals(currentFlagType)) { + openPanel(BSkyBlock.getInstance(), user, flagType, world); + } + return true; + }) + .build(); + panelBuilder.item(slot, panelItem); + slot += 2; + } + + while(panelBuilder.nextSlot() < 9) { + panelBuilder.item(new PanelItemBuilder().icon(new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 15)).build()); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/util/DeleteIslandChunks.java b/src/main/java/us/tastybento/bskyblock/util/DeleteIslandChunks.java new file mode 100644 index 0000000..ea46d5a --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/DeleteIslandChunks.java @@ -0,0 +1,55 @@ +package us.tastybento.bskyblock.util; + +import org.bukkit.World; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; +import us.tastybento.bskyblock.database.objects.Island; + +/** + * Deletes islands fast using chunk regeneration + * + * @author tastybento + * + */ +public class DeleteIslandChunks { + + /** + * Deletes the island + * @param plugin - BSkyBlock plugin object + * @param island - island to delete + */ + public DeleteIslandChunks(final BSkyBlock plugin, final Island island) { + // Fire event + IslandBaseEvent event = IslandEvent.builder().island(island).reason(Reason.DELETE).build(); + if (event.isCancelled()) { + return; + } + final World world = island.getCenter().getWorld(); + if (world == null) { + return; + } + int minXChunk = island.getMinX() / 16; + int maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) /16; + int minZChunk = island.getMinZ() / 16; + int maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) /16; + for (int x = minXChunk; x <= maxXChunk; x++) { + for (int z = minZChunk; z<=maxZChunk; z++) { + world.regenerateChunk(x, z); + if (plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world)) { + plugin.getIWM().getNetherWorld(world).regenerateChunk(x, z); + + } + if (plugin.getIWM().isEndGenerate(world) && plugin.getIWM().isEndIslands(world)) { + plugin.getIWM().getEndWorld(world).regenerateChunk(x, z); + } + } + } + // Fire event + IslandEvent.builder().island(island).reason(Reason.DELETED).build(); + + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/util/FileLister.java b/src/main/java/us/tastybento/bskyblock/util/FileLister.java new file mode 100755 index 0000000..e2dfb17 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/FileLister.java @@ -0,0 +1,85 @@ +package us.tastybento.bskyblock.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +/** + * @author Tastybento + * @author Poslovitch + */ +public class FileLister{ + private Plugin plugin; + + public FileLister(Plugin level){ + plugin = level; + } + + /** + * Returns a list of yml files in the folder given. If the folder does not exist in the file system + * it can check the plugin jar instead. + * @param folderPath - folder path + * @param checkJar - if true, the jar will be checked + * @return List of file names + + */ + public List list(String folderPath, boolean checkJar) throws IOException { + List result = new ArrayList<>(); + // Check if the folder exists + File localeDir = new File(plugin.getDataFolder(), folderPath); + if (localeDir.exists()) { + FilenameFilter ymlFilter = (File dir, String name) -> name.toLowerCase().endsWith(".yml"); + return Arrays.asList(Objects.requireNonNull(localeDir.list(ymlFilter))); + } else if (checkJar) { + // Else look in the JAR + return listJar(folderPath); + } + return result; + } + + public List listJar(String folderPath) throws IOException { + List result = new ArrayList<>(); + // Look in the JAR + File jarfile; + + try { + Method method = JavaPlugin.class.getDeclaredMethod("getFile"); + method.setAccessible(true); + + jarfile = (File) method.invoke(plugin); + } catch (Exception e) { + throw new IOException(e); + } + + JarFile jar = new JarFile(jarfile); + + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String path = entry.getName(); + + if (!path.startsWith(folderPath)) { + continue; + } + + if (entry.getName().endsWith(".yml")) { + result.add(entry.getName()); + } + + } + jar.close(); + + return result; + } +} diff --git a/src/main/java/us/tastybento/bskyblock/util/HeadGetter.java b/src/main/java/us/tastybento/bskyblock/util/HeadGetter.java new file mode 100644 index 0000000..d78eb5b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/HeadGetter.java @@ -0,0 +1,77 @@ +package us.tastybento.bskyblock.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.panels.PanelItem; + +public class HeadGetter { + private static Map cachedHeads = new HashMap<>(); + private static final Map names = new HashMap<>(); + private BSkyBlock plugin; + private static Map> headRequesters = new HashMap<>(); + + /** + * @param plugin - plugin + */ + public HeadGetter(BSkyBlock plugin) { + super(); + this.plugin = plugin; + runPlayerHeadGetter(); + } + + @SuppressWarnings("deprecation") + private void runPlayerHeadGetter() { + plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, () -> { + synchronized(names) { + Iterator> it = names.entrySet().iterator(); + if (it.hasNext()) { + Entry en = it.next(); + ItemStack playerSkull = new ItemStack(Material.SKULL_ITEM, 1, (short) 3); + SkullMeta meta = (SkullMeta) playerSkull.getItemMeta(); + meta.setOwner(en.getKey()); + playerSkull.setItemMeta(meta); + // Save in cache + cachedHeads.put(en.getKey(), playerSkull); + // Tell requesters the head came in + if (headRequesters.containsKey(en.getKey())) { + for (HeadRequester req : headRequesters.get(en.getKey())) { + en.getValue().setHead(playerSkull.clone()); + plugin.getServer().getScheduler().runTask(plugin, () -> req.setHead(en.getValue())); + } + } + it.remove(); + } + } + }, 0L, 20L); + } + + /** + * @param panelItem - head to update + * @param requester - callback class + */ + public static void getHead(PanelItem panelItem, HeadRequester requester) { + // Check if in cache + if (cachedHeads.containsKey(panelItem.getName())) { + panelItem.setHead(cachedHeads.get(panelItem.getName()).clone()); + requester.setHead(panelItem); + } else { + // Get the name + headRequesters.putIfAbsent(panelItem.getName(), new HashSet<>()); + Set requesters = headRequesters.get(panelItem.getName()); + requesters.add(requester); + headRequesters.put(panelItem.getName(), requesters); + names.put(panelItem.getName(), panelItem); + } + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/util/HeadRequester.java b/src/main/java/us/tastybento/bskyblock/util/HeadRequester.java new file mode 100644 index 0000000..1091291 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/HeadRequester.java @@ -0,0 +1,11 @@ +package us.tastybento.bskyblock.util; + +import us.tastybento.bskyblock.api.panels.PanelItem; + +public interface HeadRequester { + + /** + * @param item - panel item + */ + void setHead(PanelItem item); +} diff --git a/src/main/java/us/tastybento/bskyblock/util/ItemParser.java b/src/main/java/us/tastybento/bskyblock/util/ItemParser.java new file mode 100644 index 0000000..443715e --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/ItemParser.java @@ -0,0 +1,156 @@ +package us.tastybento.bskyblock.util; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BannerMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; + +/** + * Utility class that parses a String into an ItemStack. + * It is used for converting config file entries to objects. + * + * @author tastybento, Poslovitch + */ +public class ItemParser { + + private ItemParser() {} + + public static ItemStack parse(String s){ + if (s == null) { + return null; + } + String[] part = s.split(":"); + + // Material-specific handling + if (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW")) { + return potion(part); + } else if (part[0].contains("BANNER")) { + return banner(part); + } + + // Generic handling + if (part.length == 2) { + // Material:Qty + return two(part); + } else if (part.length == 3) { + // Material:Durability:Qty + return three(part); + } + return null; + } + + private static ItemStack two(String[] part) { + int reqAmount; + try { + reqAmount = Integer.parseInt(part[1]); + } catch (Exception e) { + return null; + } + + Material reqItem = Material.getMaterial(part[0].toUpperCase() + "_ITEM"); + if (reqItem == null) { + // Try the item + reqItem = Material.getMaterial(part[0].toUpperCase()); + } + + if (reqItem == null) { + return null; + } + return new ItemStack(reqItem, reqAmount); + } + + private static ItemStack three(String[] part) { + // Rearrange + String[] twoer = {part[0], part[2]}; + ItemStack result = two(twoer); + if (result == null) { + return null; + } + if (StringUtils.isNumeric(part[1])) { + result.setDurability((short) Integer.parseInt(part[1])); + } else if (result.getType().equals(Material.MONSTER_EGG)) { + // Check if this is a string + EntityType entityType = EntityType.valueOf(part[1]); + SpawnEggMeta meta = ((SpawnEggMeta)result.getItemMeta()); + meta.setSpawnedType(entityType); + result.setItemMeta(meta); + } + return result; + } + + private static ItemStack potion(String[] part) { + if (part.length != 6) { + return null; + } + int reqAmount; + try { + reqAmount = Integer.parseInt(part[5]); + } catch (Exception e) { + return null; + } + /* + * # Format POTION:NAME::::QTY + # LEVEL, EXTENDED, SPLASH, LINGER are optional. + # LEVEL is a number, 1 or 2 + # LINGER is for V1.9 servers and later + # Examples: + # POTION:STRENGTH:1:EXTENDED:SPLASH:1 + # POTION:INSTANT_DAMAGE:2::LINGER:2 + # POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1 + # POTION:WEAKNESS::::1 - any weakness potion + */ + ItemStack result = new ItemStack(Material.POTION); + if (part[4].equalsIgnoreCase("SPLASH")) { + result = new ItemStack(Material.SPLASH_POTION); + } else if (part[4].equalsIgnoreCase("LINGER")) { + result = new ItemStack(Material.LINGERING_POTION); + } + if (part[0].equalsIgnoreCase("TIPPED_ARROW")) { + result = new ItemStack(Material.TIPPED_ARROW); + } + PotionMeta potionMeta = (PotionMeta)(result.getItemMeta()); + PotionType type = PotionType.valueOf(part[1].toUpperCase()); + boolean isUpgraded = !part[2].isEmpty() && !part[2].equalsIgnoreCase("1"); + boolean isExtended = part[3].equalsIgnoreCase("EXTENDED"); + PotionData data = new PotionData(type, isExtended, isUpgraded); + potionMeta.setBasePotionData(data); + + result.setAmount(reqAmount); + return result; + } + + private static ItemStack banner(String[] part) { + try { + if (part.length == 2) { + return new ItemStack(Material.BANNER, Integer.parseInt(part[1])); + } + if (part.length >= 3) { + int reqAmount = Integer.parseInt(part[1]); + + @SuppressWarnings("deprecation") + ItemStack result = new ItemStack(Material.BANNER, reqAmount, DyeColor.valueOf(part[2]).getDyeData()); + + BannerMeta meta = (BannerMeta) result.getItemMeta(); + for (int i = 3; i < part.length; i += 2) { + meta.addPattern(new Pattern(DyeColor.valueOf(part[i + 1]), PatternType.valueOf(part[i]))); + } + + result.setItemMeta(meta); + + return result; + } else { + return null; + } + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/util/Pair.java b/src/main/java/us/tastybento/bskyblock/util/Pair.java new file mode 100644 index 0000000..577a8a1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/Pair.java @@ -0,0 +1,60 @@ +package us.tastybento.bskyblock.util; + + +public class Pair { + public final X x; + public final Z z; + + public Pair(X x, Z z) { + this.x = x; + this.z = z; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Pair [x=" + x + ", z=" + z + "]"; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((x == null) ? 0 : x.hashCode()); + result = prime * result + ((z == null) ? 0 : z.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Pair)) { + return false; + } + Pair other = (Pair) obj; + if (x == null) { + if (other.x != null) { + return false; + } + } else if (!x.equals(other.x)) { + return false; + } + if (z == null) { + return other.z == null; + } else return z.equals(other.z); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/util/Util.java b/src/main/java/us/tastybento/bskyblock/util/Util.java new file mode 100755 index 0000000..f99a02b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/Util.java @@ -0,0 +1,279 @@ +package us.tastybento.bskyblock.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; + +/** + * A set of utility methods + * + * @author tastybento + * @author Poslovitch + */ +public class Util { + + private static final String NETHER = "_nether"; + private static final String THE_END = "_the_end"; + private static String serverVersion = null; + private static BSkyBlock plugin = BSkyBlock.getInstance(); + + private Util() {} + + public static void setPlugin(BSkyBlock p) { + plugin = p; + } + + /** + * Returns the server version + * @return server version + */ + public static String getServerVersion() { + if (serverVersion == null) { + String serverPackageName = Bukkit.getServer().getClass().getPackage().getName(); + serverVersion = serverPackageName.substring(serverPackageName.lastIndexOf('.') + 1); + } + return serverVersion; + } + + /** + * This returns the coordinate of where an island should be on the grid. + * + * @param location - the location location to query + * @return Location of closest island + */ + public static Location getClosestIsland(Location location) { + int dist = plugin.getIWM().getIslandDistance(location.getWorld()); + long x = Math.round((double) location.getBlockX() / dist) * dist + plugin.getIWM().getIslandXOffset(location.getWorld()); + long z = Math.round((double) location.getBlockZ() / dist) * dist + plugin.getIWM().getIslandZOffset(location.getWorld()); + if (location.getBlockX() == x && location.getBlockZ() == z) { + return location; + } + int y = plugin.getIWM().getIslandHeight(location.getWorld()); + return new Location(location.getWorld(), x, y, z); + } + + /** + * Converts a serialized location to a Location. Returns null if string is + * empty + * + * @param s - serialized location in format "world:x:y:z:y:p" + * @return Location + */ + public static Location getLocationString(final String s) { + if (s == null || s.trim().equals("")) { + return null; + } + final String[] parts = s.split(":"); + if (parts.length == 6) { + final World w = Bukkit.getServer().getWorld(parts[0]); + if (w == null) { + return null; + } + // Parse string as double just in case + int x = (int) Double.parseDouble(parts[1]); + int y = (int) Double.parseDouble(parts[2]); + int z = (int) Double.parseDouble(parts[3]); + final float yaw = Float.intBitsToFloat(Integer.parseInt(parts[4])); + final float pitch = Float.intBitsToFloat(Integer.parseInt(parts[5])); + return new Location(w, x + 0.5D, y, z + 0.5D, yaw, pitch); + } + return null; + } + + /** + * Converts a location to a simple string representation + * If location is null, returns empty string + * Only stores block ints. Inverse function returns block centers + * + * @param l - the location + * @return String of location in format "world:x:y:z:y:p" + */ + public static String getStringLocation(final Location l) { + if (l == null || l.getWorld() == null) { + return ""; + } + return l.getWorld().getName() + ":" + l.getBlockX() + ":" + l.getBlockY() + ":" + l.getBlockZ() + ":" + Float.floatToIntBits(l.getYaw()) + ":" + Float.floatToIntBits(l.getPitch()); + } + + /** + * Converts a name like IRON_INGOT into Iron Ingot to improve readability + * + * @param ugly + * The string such as IRON_INGOT + * @return A nicer version, such as Iron Ingot + * + * Credits to mikenon on GitHub! + */ + public static String prettifyText(String ugly) { + StringBuilder fin = new StringBuilder(); + ugly = ugly.toLowerCase(); + if (ugly.contains("_")) { + String[] splt = ugly.split("_"); + int i = 0; + for (String s : splt) { + i += 1; + fin.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)); + if (i < splt.length) { + fin.append(" "); + } + } + } else { + fin.append(Character.toUpperCase(ugly.charAt(0))).append(ugly.substring(1)); + } + return fin.toString(); + } + + /** + * Return a list of online players this player can see, i.e. are not invisible + * @param user - the User - if null, all player names on the server are shown + * @return a list of online players this player can see + */ + public static List getOnlinePlayerList(User user) { + if (user == null || !user.isPlayer()) { + // Console and null get to see every player + return Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()); + } + // Otherwise prevent invisible players from seeing + return Bukkit.getOnlinePlayers().stream().filter(p -> user.getPlayer().canSee(p)).map(Player::getName).collect(Collectors.toList()); + } + + /** + * Returns all of the items that begin with the given start, + * ignoring case. Intended for tabcompletion. + * + * @param list - string list + * @param start - first few chars of a string + * @return List of items that start with the letters + */ + public static List tabLimit(final List list, final String start) { + final List returned = new ArrayList<>(); + for (String s : list) { + if (s == null) { + continue; + } + if (s.toLowerCase().startsWith(start.toLowerCase())) { + returned.add(s); + } + } + + return returned; + } + + /** + * Get the maximum value of a numerical perm setting + * @param player - the player - the player to check + * @param perm - the start of the perm, e.g., bskyblock.maxhomes + * @param permValue - the default value - the result may be higher or lower than this + * @return max value + */ + public static int getPermValue(Player player, String perm, int permValue) { + for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) { + if (perms.getPermission().startsWith(perm + ".")) { + // Get the max value should there be more than one + if (perms.getPermission().contains(perm + ".*")) { + return permValue; + } else { + String[] spl = perms.getPermission().split(perm + "."); + if (spl.length > 1) { + if (!NumberUtils.isDigits(spl[1])) { + plugin.logError("Player " + player.getName() + " has permission: " + perms.getPermission() + " <-- the last part MUST be a number! Ignoring..."); + + } else { + permValue = Math.max(permValue, Integer.valueOf(spl[1])); + } + } + } + } + // Do some sanity checking + if (permValue < 1) { + permValue = 1; + } + } + return permValue; + } + + public static String xyz(Vector location) { + return location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); + } + + + /** + * Checks is world = world2 irrespective of the world type + * @param world - world + * @param world2 - world + * @return true if the same + */ + public static boolean sameWorld(World world, World world2) { + String worldName = world.getName().replaceAll(NETHER, "").replaceAll(THE_END, ""); + String world2Name = world2.getName().replaceAll(NETHER, "").replaceAll(THE_END, ""); + return worldName.equalsIgnoreCase(world2Name); + } + + /** + * Convert world to an overworld + * @param world - world + * @return over world + */ + public static World getWorld(World world) { + return world.getEnvironment().equals(Environment.NORMAL) ? world : Bukkit.getWorld(world.getName().replaceAll(NETHER, "").replaceAll(THE_END, "")); + } + + /** + * Converts block face direction to radial degrees. Returns 0 if block face + * is not radial. + * + * @param face + * @return degrees + */ + public static float blockFaceToFloat(BlockFace face) { + switch (face) { + case EAST: + return 90F; + case EAST_NORTH_EAST: + return 67.5F; + case EAST_SOUTH_EAST: + return 0F; + case NORTH: + return 0F; + case NORTH_EAST: + return 45F; + case NORTH_NORTH_EAST: + return 22.5F; + case NORTH_NORTH_WEST: + return 337.5F; + case NORTH_WEST: + return 315F; + case SOUTH: + return 180F; + case SOUTH_EAST: + return 135F; + case SOUTH_SOUTH_EAST: + return 157.5F; + case SOUTH_SOUTH_WEST: + return 202.5F; + case SOUTH_WEST: + return 225F; + case WEST: + return 270F; + case WEST_NORTH_WEST: + return 292.5F; + case WEST_SOUTH_WEST: + return 247.5F; + default: + return 0F; + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/util/YmlCommentParser.java b/src/main/java/us/tastybento/bskyblock/util/YmlCommentParser.java new file mode 100644 index 0000000..5fc19c1 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/YmlCommentParser.java @@ -0,0 +1,217 @@ +package us.tastybento.bskyblock.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; + +/** + * A very simplistic yml parser, that only do the following: + *
    + *
  1. Keep track of indentation-levels and sections.
  2. + *
  3. Handle comments.
  4. + *
+ */ +public class YmlCommentParser { + private static final Logger log = Bukkit.getLogger(); + private static final Pattern SECTION_PATTERN = Pattern.compile("^(?\\s*)(?[^ \\-][^:]*):(?[^#]*)?(?#.*)?"); + private static final Pattern COMMENT_PATTERN = Pattern.compile("^(?\\s*)(?#.*)"); + private Map commentMap = new HashMap<>(); + + public Map getCommentMap() { + return Collections.unmodifiableMap(commentMap); + } + + + public void addComment(String path, String comment) { + commentMap.put(path, comment); + } + + public void addComments(Map comments) { + commentMap.putAll(comments); + } + + private void readLines(BufferedReader rdr) throws IOException { + int indentLevel = 0; + Deque
sections = new ArrayDeque<>(); + StringBuilder comments = new StringBuilder(); + String baseKey = null; + sections.add(new Section(null, 0)); + String line; + int lineNum = 1; + boolean isFirstAfterSection = true; + while ((line = rdr.readLine()) != null) { + Matcher commentM = COMMENT_PATTERN.matcher(line); + Matcher sectionM = SECTION_PATTERN.matcher(line); + if (commentM.matches()) { + comments.append(commentM.group("comment")).append("\n"); + } else if (sectionM.matches()) { + String comment = sectionM.group("comment"); + if (comment != null && !comment.trim().isEmpty()) { + comments.append(comment).append("\n"); + } + String name = sectionM.group("name").trim(); + String value = sectionM.group("value"); + String indent = sectionM.group("indent"); + if (isFirstAfterSection && indent.length() > indentLevel) { + indentLevel = indent.length(); + sections.peek().setIndentation(indentLevel); + } else if (indent.length() < indentLevel) { + while (indent.length() < indentLevel && !sections.isEmpty()) { + sections.pop(); + baseKey = sections.peek().getPath(); + indentLevel = sections.peek().getIndentation(); + isFirstAfterSection = false; + } + } + String path = getPath(baseKey, name); + if (value != null && !value.trim().isEmpty()) { + // Scalar with value + addComments(path, comments); + if (!isFirstAfterSection && indent.length() > indentLevel) { + log.warning("line " + lineNum + ": mixed indentation, expected " + indentLevel + " but got " + indent.length()); + } + isFirstAfterSection = false; + } else if (indent.length() >= indentLevel) { + indentLevel = indent.length(); + sections.push(createSection(path, indentLevel, comments)); + baseKey = path; + isFirstAfterSection = true; + } + } else if (line.trim().isEmpty()) { + // Currently gathered comments are reset - they are "floating", decoupled from sections. + comments.setLength(0); + comments.trimToSize(); + } + lineNum++; + } + } + + private String getPath(String baseKey, String name) { + return baseKey != null ? baseKey + "." + name : name; + } + + private Section createSection(String path, int indentLevel, StringBuilder comments) { + Section section = new Section(path, indentLevel); + addComments(path, comments); + return section; + } + + private void addComments(String path, StringBuilder comments) { + if (comments.length() > 0) { + commentMap.put(path, comments.toString()); + comments.setLength(0); + comments.trimToSize(); + } + } + + public String getComment(String path) { + return commentMap.get(path); + } + + /** + * Merges the comments into the "pure" yml. + * @param ymlPure A YML data-tree, without comments. + * @return A YML data-tree including comments. + */ + public String mergeComments(String ymlPure) { + StringBuilder sb = new StringBuilder(); + boolean isFirstAfterSection = true; + Deque
sections = new ArrayDeque<>(); + sections.push(new Section(null, 0)); + int indentLevel = 0; + String baseKey = null; + int lineNum = 1; + // First section shares comments with the header - so ignore that one + boolean isHeader = true; + for (String line : ymlPure.split("\n")) { + // Skip header + Matcher commentM = COMMENT_PATTERN.matcher(line); + if (isHeader && (commentM.matches() || line.trim().isEmpty())) { + continue; // Skip header + } + isHeader = false; + Matcher sectionM = SECTION_PATTERN.matcher(line); + if (sectionM.matches()) { + String name = sectionM.group("name").trim(); + String value = sectionM.group("value"); + String indent = sectionM.group("indent"); + if (isFirstAfterSection && indent.length() > indentLevel) { + indentLevel = indent.length(); + sections.peek().setIndentation(indentLevel); + } else if (indent.length() < indentLevel) { + while (indent.length() < indentLevel && !sections.isEmpty()) { + sections.pop(); + baseKey = sections.peek().getPath(); + indentLevel = sections.peek().getIndentation(); + isFirstAfterSection = false; + } + } + String path = getPath(baseKey, name); + String comment = getComment(path); + if (comment != null) { + sb.append(lineNum > 1 ? "\n" : "").append(comment + .replaceAll("^#", Matcher.quoteReplacement(indent + "#")) + .replaceAll("\n#", Matcher.quoteReplacement("\n" + indent + "#"))); + } + if (value != null && !value.trim().isEmpty()) { + // Scalar with value + isFirstAfterSection = false; + } else if (indent.length() >= indentLevel) { + indentLevel = indent.length(); + sections.push(new Section(path, indentLevel)); + baseKey = path; + isFirstAfterSection = true; + } + } + lineNum++; + sb.append(line).append("\n"); + } + return sb.toString().replaceAll("\r\n", "\n").replaceAll("\n\r", "\n").replaceAll("\n", "\r\n"); + } + + public void load(Reader reader) throws IOException { + readLines(new BufferedReader(reader)); + } + + public void loadFromString(String contents) { + try { + readLines(new BufferedReader(new StringReader(contents))); + } catch (IOException e) { + throw new IllegalStateException("Unable to read from string", e); + } + } + + + private static class Section { + private int indentation; + private final String path; + + private Section(String name, int indentation) { + this.indentation = indentation; + path = name; + } + + public int getIndentation() { + return indentation; + } + + public String getPath() { + return path; + } + + public void setIndentation(int indentLevel) { + indentation = indentLevel; + } + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/util/YmlConfiguration.java b/src/main/java/us/tastybento/bskyblock/util/YmlConfiguration.java new file mode 100644 index 0000000..8ca1b67 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/YmlConfiguration.java @@ -0,0 +1,44 @@ +package us.tastybento.bskyblock.util; + +import java.util.Map; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * A YamlConfiguration that supports comments + * + * Note: This includes a VERY SIMPLISTIC Yaml-parser, which sole purpose is to detect and store comments. + */ +public class YmlConfiguration extends YamlConfiguration { + private YmlCommentParser commentParser = new YmlCommentParser(); + + public String getComment(String key) { + String comment = commentParser.getComment(key); + return comment != null ? comment.replaceAll("^# ?", "").replaceAll("\n# ?", "") : null; + } + + public Map getComments() { + return commentParser.getCommentMap(); + } + + public void addComment(String path, String comment) { + commentParser.addComment(path, comment); + } + public void addComments(Map comments) { + commentParser.addComments(comments); + } + + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + super.loadFromString(contents); + commentParser.loadFromString(contents); + } + + @Override + public String saveToString() { + String ymlPure = super.saveToString(); + return commentParser.mergeComments(ymlPure); + } + +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleport.java b/src/main/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleport.java new file mode 100644 index 0000000..34e226b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleport.java @@ -0,0 +1,319 @@ +package us.tastybento.bskyblock.util.teleport; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.ChunkSnapshot; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.Vector; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Pair; + +/** + * A class that calculates finds a safe spot asynchronously and then teleports the player there. + * @author tastybento + * + */ +public class SafeSpotTeleport { + + private static final int MAX_CHUNKS = 200; + private static final long SPEED = 1; + private static final int MAX_RADIUS = 200; + private boolean checking; + private BukkitTask task; + + // Parameters + private final Entity entity; + private final Location location; + private boolean portal; + private final int homeNumber; + + // Locations + private Location bestSpot; + + private BSkyBlock plugin; + private List> chunksToScan; + + /** + * Teleports and entity to a safe spot on island + * @param plugin - BSkyBlock plugin object + * @param entity - entity to teleport + * @param location - the location + * @param failureMessage - already translated failure message + * @param portal - true if this is a portal teleport + * @param homeNumber - home number to go to + */ + protected SafeSpotTeleport(BSkyBlock plugin, final Entity entity, final Location location, final String failureMessage, boolean portal, + int homeNumber) { + this.plugin = plugin; + this.entity = entity; + this.location = location; + this.portal = portal; + this.homeNumber = homeNumber; + + // Put player into spectator mode + if (entity instanceof Player && ((Player)entity).getGameMode().equals(GameMode.SURVIVAL)) { + ((Player)entity).setGameMode(GameMode.SPECTATOR); + } + + // If there is no portal scan required, try the desired location immediately + if (plugin.getIslands().isSafeLocation(location)) { + if (portal) { + // If the desired location is safe, then that's where you'll go if there's no portal + bestSpot = location; + } else { + // If this is not a portal teleport, then go to the safe location immediately + entity.teleport(location); + return; + } + } + + // Get chunks to scan + chunksToScan = getChunksToScan(); + + // Start checking + checking = true; + + // Start a recurring task until done or cancelled + task = Bukkit.getScheduler().runTaskTimer(plugin, () -> { + List chunkSnapshot = new ArrayList<>(); + if (checking) { + Iterator> it = chunksToScan.iterator(); + if (!it.hasNext()) { + // Nothing left + tidyUp(entity, failureMessage); + return; + } + // Add chunk snapshots to the list + while (it.hasNext() && chunkSnapshot.size() < MAX_CHUNKS) { + Pair pair = it.next(); + chunkSnapshot.add(location.getWorld().getChunkAt(pair.x, pair.z).getChunkSnapshot()); + it.remove(); + } + // Move to next step + checking = false; + checkChunks(chunkSnapshot); + } + }, 0L, SPEED); + } + + private void tidyUp(Entity entity, String failureMessage) { + // Nothing left to check and still not canceled + task.cancel(); + // Check portal + if (portal && bestSpot != null) { + // No portals found, teleport to the best spot we found + teleportEntity(bestSpot); + if (entity instanceof Player && ((Player)entity).getGameMode().equals(GameMode.SPECTATOR)) { + ((Player)entity).setGameMode(plugin.getIWM().getDefaultGameMode(bestSpot.getWorld())); + } + } else if (entity instanceof Player && !failureMessage.isEmpty()) { + // Failed, no safe spot + entity.sendMessage(failureMessage); + if (((Player)entity).getGameMode().equals(GameMode.SPECTATOR)) { + if (plugin.getIWM().inWorld(entity.getLocation())) { + ((Player)entity).setGameMode(plugin.getIWM().getDefaultGameMode(entity.getWorld())); + } else { + // Last resort + ((Player)entity).setGameMode(GameMode.SURVIVAL); + } + } + } + + } + + /** + * Gets a set of chunk coords that will be scanned. + * @return - list of chunk coords to be scanned + */ + private List> getChunksToScan() { + List> result = new ArrayList<>(); + // Get island if available + Optional island = plugin.getIslands().getIslandAt(location); + int maxRadius = island.map(Island::getProtectionRange).orElse(plugin.getIWM().getIslandProtectionRange(location.getWorld())); + maxRadius = maxRadius > MAX_RADIUS ? MAX_RADIUS : maxRadius; + + int x = location.getBlockX(); + int z = location.getBlockZ(); + // Create ever increasing squares around the target location + int radius = 0; + do { + for (int i = x - radius; i <= x + radius; i+=16) { + for (int j = z - radius; j <= z + radius; j+=16) { + addChunk(result, island, new Pair<>(i,j), new Pair<>(i/16, j/16)); + } + } + radius++; + } while (radius < maxRadius); + return result; + } + + private void addChunk(List> result, Optional island, Pair blockCoord, Pair chunkCoord) { + if (!result.contains(chunkCoord)) { + // Add the chunk coord + if (!island.isPresent()) { + // If there is no island, just add it + result.add(chunkCoord); + } else { + // If there is an island, only add it if the coord is in island space + island.ifPresent(is -> { + if (is.inIslandSpace(blockCoord)) { + result.add(chunkCoord); + } + }); + } + } + } + + /** + * Loops through the chunks and if a safe spot is found, fires off the teleportation + * @param chunkSnapshot - list of chunk snapshots to check + */ + private void checkChunks(final List chunkSnapshot) { + // Run async task to scan chunks + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + for (ChunkSnapshot chunk: chunkSnapshot) { + if (scanChunk(chunk)) { + task.cancel(); + return; + } + } + // Nothing happened, change state + checking = true; + }); + } + + + /** + * @param chunk - chunk snapshot + * @return true if a safe spot was found + */ + private boolean scanChunk(ChunkSnapshot chunk) { + // Max height + int maxHeight = location.getWorld().getMaxHeight() - 20; + // Run through the chunk + for (int x = 0; x< 16; x++) { + for (int z = 0; z < 16; z++) { + // Work down from the entry point up + for (int y = Math.min(chunk.getHighestBlockYAt(x, z), maxHeight); y >= 0; y--) { + if (checkBlock(chunk, x,y,z, maxHeight)) { + return true; + } + } // end y + } //end z + } // end x + return false; + } + + /** + * Teleports entity to the safe spot + */ + private void teleportEntity(final Location loc) { + task.cancel(); + // Return to main thread and teleport the player + Bukkit.getScheduler().runTask(plugin, () -> { + if (!portal && entity instanceof Player && homeNumber > 0) { + // Set home if so marked + plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber); + } + Vector velocity = entity.getVelocity(); + entity.teleport(loc); + // Exit spectator mode if in it + if (entity instanceof Player) { + Player player = (Player)entity; + if (player.getGameMode().equals(GameMode.SPECTATOR)) { + player.setGameMode(plugin.getIWM().getDefaultGameMode(loc.getWorld())); + } + } else { + entity.setVelocity(velocity); + } + }); + + } + + /** + * Returns true if the location is a safe one. + * @param chunk - chunk snapshot + * @param x - x coordinate + * @param y - y coordinate + * @param z - z coordinate + * @param worldHeight - height of world + * @return true if this is a safe spot, false if this is a portal scan + */ + private boolean checkBlock(ChunkSnapshot chunk, int x, int y, int z, int worldHeight) { + World world = location.getWorld(); + Material type = chunk.getBlockType(x, y, z); + if (!type.equals(Material.AIR)) { // AIR + Material space1 = chunk.getBlockType(x, Math.min(y + 1, worldHeight), z); + Material space2 = chunk.getBlockType(x, Math.min(y + 2, worldHeight), z); + if ((space1.equals(Material.AIR) && space2.equals(Material.AIR)) || (space1.equals(Material.PORTAL) && space2.equals(Material.PORTAL)) + && (!type.toString().contains("FENCE") && !type.toString().contains("DOOR") && !type.toString().contains("GATE") && !type.toString().contains("PLATE"))) { + switch (type) { + // Unsafe + case ANVIL: + case BARRIER: + case BOAT: + case CACTUS: + case DOUBLE_PLANT: + case ENDER_PORTAL: + case FIRE: + case FLOWER_POT: + case LADDER: + case LAVA: + case LEVER: + case LONG_GRASS: + case PISTON_EXTENSION: + case PISTON_MOVING_PIECE: + case SIGN_POST: + case SKULL: + case STANDING_BANNER: + case STATIONARY_LAVA: + case STATIONARY_WATER: + case STONE_BUTTON: + case TORCH: + case TRIPWIRE: + case WATER: + case WEB: + case WOOD_BUTTON: + //Block is dangerous + break; + case PORTAL: + if (portal) { + // A portal has been found, switch to non-portal mode now + portal = false; + } + break; + default: + return safe(chunk, x, y, z, world); + } + } + } + return false; + } + + private boolean safe(ChunkSnapshot chunk, int x, int y, int z, World world) { + Vector newSpot = new Vector(chunk.getX() * 16 + x + 0.5D, y + 1, chunk.getZ() * 16 + z + 0.5D); + if (portal) { + if (bestSpot == null) { + // Stash the best spot + bestSpot = newSpot.toLocation(world); + } + return false; + } else { + teleportEntity(newSpot.toLocation(world)); + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilder.java b/src/main/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilder.java new file mode 100644 index 0000000..8209d7d --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilder.java @@ -0,0 +1,104 @@ +package us.tastybento.bskyblock.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; + +public class SafeTeleportBuilder { + + private BSkyBlock plugin; + private Entity entity; + private int homeNumber = 0; + private boolean portal = false; + private String failureMessage = ""; + private Location location; + + + public SafeTeleportBuilder(BSkyBlock plugin) { + this.plugin = plugin; + } + + /** + * Set who or what is going to teleport + * @param entity - entity to teleport + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder entity(Entity entity) { + this.entity = entity; + return this; + } + + /** + * Set the island to teleport to + * @param island - island destination + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder island(Island island) { + this.location = island.getCenter(); + return this; + } + + /** + * Set the home number to this number + * @param homeNumber - home number + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder homeNumber(int homeNumber) { + this.homeNumber = homeNumber; + return this; + } + + /** + * This is a portal teleportation + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder portal() { + this.portal = true; + return this; + } + + /** + * Set the failure message if this teleport cannot happen + * @param failureMessage - failure message to report to user + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder failureMessage(String failureMessage) { + this.failureMessage = failureMessage; + return this; + } + + /** + * Set the desired location + * @param location - the location + * @return SafeTeleportBuilder + */ + public SafeTeleportBuilder location(Location location) { + this.location = location; + return this; + } + + /** + * Try to teleport the player + * @return SafeSpotTeleport + */ + public SafeSpotTeleport build() { + // Error checking + if (entity == null) { + plugin.logError("Attempt to safe teleport a null entity!"); + return null; + } + if (location == null) { + plugin.logError("Attempt to safe teleport to a null location!"); + return null; + } + if (failureMessage.isEmpty() && entity instanceof Player) { + failureMessage = User.getInstance(entity).getTranslation("general.errors.warp-not-safe"); + } + return new SafeSpotTeleport(plugin, entity, location, failureMessage, portal, homeNumber); + } + +} diff --git a/src/test/java/bskyblock/TestBSkyBlock.java b/src/test/java/bskyblock/TestBSkyBlock.java new file mode 100644 index 0000000..1b21916 --- /dev/null +++ b/src/test/java/bskyblock/TestBSkyBlock.java @@ -0,0 +1,528 @@ +package bskyblock; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.commands.CompositeCommand; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.team.TeamEvent; +import us.tastybento.bskyblock.api.flags.AbstractFlagListener; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.flags.FlagBuilder; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ BSkyBlock.class, Flags.class, Util.class, Bukkit.class}) +public class TestBSkyBlock { + private static final UUID MEMBER_UUID = UUID.randomUUID(); + private static final UUID OWNER_UUID = UUID.randomUUID(); + private static final UUID VISITOR_UUID = UUID.randomUUID(); + private final UUID playerUUID = UUID.randomUUID(); + private static CommandSender sender; + private static Player player; + private static Location location; + private static BSkyBlock plugin; + private static FlagsManager flagsManager; + private static Block block; + private static Player ownerOfIsland; + private static Player visitorToIsland; + + @Before + public void setUp() { + // Set up plugin + plugin = mock(BSkyBlock.class); + when(plugin.getCommandsManager()).thenReturn(mock(CommandsManager.class)); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + World world = mock(World.class); + Mockito.when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + Mockito.when(server.getWorld("world")).thenReturn(world); + Mockito.when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + + OfflinePlayer offlinePlayer = mock(OfflinePlayer.class); + when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer); + when(offlinePlayer.getName()).thenReturn("tastybento"); + + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + sender = mock(CommandSender.class); + player = mock(Player.class); + ownerOfIsland = mock(Player.class); + visitorToIsland = mock(Player.class); + Mockito.when(player.hasPermission(Mockito.anyString())).thenReturn(true); + User.getInstance(player); + when(Bukkit.getPlayer(Mockito.any(UUID.class))).thenReturn(player); + + location = mock(Location.class); + Mockito.when(location.getWorld()).thenReturn(world); + Mockito.when(location.getBlockX()).thenReturn(0); + Mockito.when(location.getBlockY()).thenReturn(0); + Mockito.when(location.getBlockZ()).thenReturn(0); + + Mockito.when(player.getLocation()).thenReturn(location); + Mockito.when(ownerOfIsland.getLocation()).thenReturn(location); + Mockito.when(visitorToIsland.getLocation()).thenReturn(location); + + Mockito.when(player.getUniqueId()).thenReturn(MEMBER_UUID); + Mockito.when(ownerOfIsland.getUniqueId()).thenReturn(OWNER_UUID); + Mockito.when(visitorToIsland.getUniqueId()).thenReturn(VISITOR_UUID); + + PowerMockito.mockStatic(Flags.class); + + flagsManager = new FlagsManager(plugin); + Mockito.when(plugin.getFlagsManager()).thenReturn(flagsManager); + + block = Mockito.mock(Block.class); + + // Worlds + IslandWorldManager iwm = mock(IslandWorldManager.class); + Mockito.when(plugin.getIWM()).thenReturn(iwm); + Mockito.when(iwm.getBSBIslandWorld()).thenReturn(world); + Mockito.when(iwm.getBSBNetherWorld()).thenReturn(world); + Mockito.when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + // We want the read tabLimit call here + when(Util.tabLimit(Mockito.any(), Mockito.anyString())).thenCallRealMethod(); + + // Islands + IslandsManager im = mock(IslandsManager.class); + Mockito.when(plugin.getIslands()).thenReturn(im); + + Island island = new Island(); + island.setOwner(OWNER_UUID); + island.setCenter(location); + island.setProtectionRange(100); + HashMap members = new HashMap<>(); + members.put(OWNER_UUID, RanksManager.OWNER_RANK); + members.put(MEMBER_UUID, RanksManager.MEMBER_RANK); + island.setMembers(members); + Mockito.when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(Optional.of(island)); + + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet<>()); + } + + @Test + public void testIslandEvent() { + + // Test island events + IslandBaseEvent event = TeamEvent.builder() + //.island(getIslands().getIsland(playerUUID)) + .reason(TeamEvent.Reason.INFO) + .involvedPlayer(playerUUID) + .build(); + assertEquals(playerUUID, event.getPlayerUUID()); + } + + @Test + public void testCommandAPI() { + // Test command + User user = User.getInstance(playerUUID); + CompositeCommand testCommand = new TestCommand(); + testCommand.setOnlyPlayer(true); + testCommand.setPermission("default.permission"); + // Test basic execution + assertTrue(testCommand.execute(user, testCommand.getLabel(), new ArrayList<>())); + assertEquals("test",testCommand.getLabel()); + assertEquals(2, testCommand.getAliases().size()); + assertEquals("t", testCommand.getAliases().get(0)); + assertTrue(testCommand.isOnlyPlayer()); + assertNull(testCommand.getParent()); + assertEquals("default.permission", testCommand.getPermission()); + // Check commands and aliases match to correct class + for (Entry command : testCommand.getSubCommands().entrySet()) { + assertEquals(testCommand.getSubCommand(command.getKey()), Optional.of(command.getValue())); + // Check aliases + for (String alias : command.getValue().getAliases()) { + assertEquals(testCommand.getSubCommand(alias), Optional.of(command.getValue())); + } + } + String[] args = {""}; + // Results are alphabetically sorted + assertEquals(Arrays.asList("help", "sub1","sub2"), testCommand.tabComplete(player, "test", args)); + assertNotSame(Arrays.asList("help", "sub1","sub2"), testCommand.tabComplete(sender, "test", args)); + args[0] = "su"; + assertEquals(Arrays.asList("sub1","sub2"), testCommand.tabComplete(player, "test", args)); + args[0] = "d"; + assertNotSame(Arrays.asList("help", "sub1","sub2"), testCommand.tabComplete(player, "test", args)); + args[0] = "sub1"; + assertEquals(Collections.emptyList(), testCommand.tabComplete(player, "test", args)); + String[] args2 = {"sub2",""}; + assertEquals(Arrays.asList("help", "subsub"), testCommand.tabComplete(player, "test", args2)); + args2[1] = "s"; + assertEquals(Collections.singletonList("subsub"), testCommand.tabComplete(player, "test", args2)); + String[] args3 = {"sub2","subsub", ""}; + assertEquals(Arrays.asList("help", "subsubsub"), testCommand.tabComplete(player, "test", args3)); + // Test for overridden tabcomplete + assertEquals(Arrays.asList("Ben", "Bill", "Florian", "Ted", "help"), + testCommand.tabComplete(player, "test", new String[] {"sub2", "subsub", "subsubsub", ""})); + // Test for partial word + assertEquals(Arrays.asList("Ben", "Bill"), + testCommand.tabComplete(player, "test", new String[] {"sub2", "subsub", "subsubsub", "b"})); + + // Test command arguments + CompositeCommand argCmd = new Test3ArgsCommand(); + argCmd.setOnlyPlayer(true); + argCmd.setPermission("default.permission"); + assertTrue(argCmd.execute(player, "args", new String[]{"give", "100", "ben"})); + assertFalse(testCommand.execute(player, "test", new String[] {"sub2", "subsub", "subsubsub"})); + assertFalse(testCommand.execute(player, "test", new String[] {"sub2", "subsub", "subsubsub", "ben"})); + assertFalse(testCommand.execute(player, "test", new String[] {"sub2", "subsub", "subsubsub", "ben", "100"})); + assertTrue(testCommand.execute(player, "test", new String[] {"sub2", "subsub", "subsubsub", "ben", "100", "today"})); + + // Usage tests + assertEquals("/test", testCommand.getUsage()); + assertEquals("test.params", testCommand.getParameters()); + + // Test help + //assertTrue(testCommand.execute(player, "test", new String[] {"help"})); + } + + private class TestCommand extends CompositeCommand { + + public TestCommand() { + super("test", "t", "tt"); + setParameters("test.params"); + } + + @Override + public boolean execute(User user, String label, List args) { + return true; + } + + @Override + public void setup() { + // Set up sub commands + new TestSubCommand(this); // Level 1 + new TestSubCommand2(this); // Has sub command + } + + } + + private class TestSubCommand extends CompositeCommand { + + public TestSubCommand(CompositeCommand parent) { + super(parent, "sub1", "subone"); + } + + @Override + public void setup() { + setParameters("sub.params"); + } + + @Override + public boolean execute(User user, String label, List args) { + return true; + } + + } + + private class TestSubCommand2 extends CompositeCommand { + + public TestSubCommand2(CompositeCommand parent) { + super(parent, "sub2", "level1"); + + } + + @Override + public boolean execute(User user, String label, List args) { + return true; + } + + @Override + public void setup() { + // Set up sub commands + new TestSubSubCommand(this); // Level 2 + } + } + + private class TestSubSubCommand extends CompositeCommand { + + public TestSubSubCommand(CompositeCommand parent) { + super(parent, "subsub", "level2", "subsubby"); + + } + + @Override + public boolean execute(User user, String label, List args) { + return true; + } + + @Override + public void setup() { + // Set up sub commands + new TestSubSubSubCommand(this); // Level 3 + } + + } + + private class TestSubSubSubCommand extends CompositeCommand { + + public TestSubSubSubCommand(CompositeCommand parent) { + super(parent, "subsubsub", "level3", "subsubsubby"); + } + + @Override + public void setup() {} + + @Override + public boolean execute(User user, String label, List args) { + + return args.size() == 3; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + List options = new ArrayList<>(Arrays.asList("Florian", "Ben", "Bill", "Ted")); + return Optional.of(Util.tabLimit(options, lastArg)); + } + } + + private class Test3ArgsCommand extends CompositeCommand { + + public Test3ArgsCommand() { + super("args", ""); + } + + @Override + public void setup() {} + + @Override + public boolean execute(User user, String label, List args) { + return args.size() == 3; + } + + } + + // Protection tests + @Test + public void TestProtection() { + User owner = User.getInstance(playerUUID); + Island island = new Island(); + island.setOwner(playerUUID); + island.setCenter(location); + island.setProtectionRange(100); + + assertNotNull(island); + + User visitor = User.getInstance(UUID.randomUUID()); + assertEquals(RanksManager.OWNER_RANK, island.getRank(owner)); + assertEquals(RanksManager.VISITOR_RANK, island.getRank(visitor)); + + // Make members + UUID member1 = UUID.randomUUID(); + UUID member2 = UUID.randomUUID(); + UUID member3 = UUID.randomUUID(); + + // Add members + island.addMember(member1); + island.addMember(member2); + island.addMember(member3); + + Set members = island.getMemberSet(); + assertTrue(members.contains(playerUUID)); + assertTrue(members.contains(member1)); + assertTrue(members.contains(member2)); + assertTrue(members.contains(member3)); + + // Remove members + island.removeMember(member3); + members = island.getMemberSet(); + assertTrue(members.contains(playerUUID)); + assertTrue(members.contains(member1)); + assertTrue(members.contains(member2)); + assertFalse(members.contains(member3)); + + // Ban member + island.addToBanList(member1); + members = island.getMemberSet(); + assertTrue(members.contains(playerUUID)); + assertFalse(members.contains(member1)); + assertTrue(members.contains(member2)); + assertFalse(members.contains(member3)); + + Set banned = island.getBanned(); + assertTrue(banned.contains(member1)); + + // Unban + island.removeFromBanList(member1); + assertFalse(island.getBanned().contains(member1)); + + // Protection + + // Check default settings + // Owner should be able to do anything + assertTrue(island.isAllowed(owner, Flags.PLACE_BLOCKS)); + assertTrue(island.isAllowed(owner, Flags.BREAK_BLOCKS)); + + // Visitor can do nothing + assertFalse(island.isAllowed(visitor, Flags.PLACE_BLOCKS)); + assertFalse(island.isAllowed(visitor, Flags.BREAK_BLOCKS)); + + // Set up protection settings - members can break blocks, visitors and place blocks + island.setFlag(Flags.BREAK_BLOCKS, RanksManager.MEMBER_RANK); + assertFalse(island.isAllowed(visitor, Flags.BREAK_BLOCKS)); + + island.setFlag(Flags.PLACE_BLOCKS, RanksManager.VISITOR_RANK); + assertFalse(island.isAllowed(visitor, Flags.BREAK_BLOCKS)); + + // Owner should be able to do anything + assertTrue(island.isAllowed(owner, Flags.PLACE_BLOCKS)); + assertTrue(island.isAllowed(owner, Flags.BREAK_BLOCKS)); + + // Visitor can only place blocks + assertTrue(island.isAllowed(visitor, Flags.PLACE_BLOCKS)); + assertFalse(island.isAllowed(visitor, Flags.BREAK_BLOCKS)); + + // Check if the members have capability + User mem1 = User.getInstance(member1); // Visitor + User mem2 = User.getInstance(member2); // Member + island.addToBanList(member3); + User mem3 = User.getInstance(member3); // Banned + + // Member 1 is a visitor + assertTrue(island.isAllowed(mem1, Flags.PLACE_BLOCKS)); + assertFalse(island.isAllowed(mem1, Flags.BREAK_BLOCKS)); + + // Member 2 is a team member + assertTrue(island.isAllowed(mem2, Flags.PLACE_BLOCKS)); + assertTrue(island.isAllowed(mem2, Flags.BREAK_BLOCKS)); + + // Member 3 is no longer a member and is banned + assertFalse(island.isAllowed(mem3, Flags.PLACE_BLOCKS)); + assertFalse(island.isAllowed(mem3, Flags.BREAK_BLOCKS)); + } + + @Test + public void TestEventProtection() { + // Test events + + FlagListener fl = new FlagListener(plugin); + + // checking events - vistor + Event e3 = new BlockBreakEvent(block, visitorToIsland); + Assert.assertFalse(fl.checkIsland(e3, location, Flags.BREAK_BLOCKS, true)); + + // checking events - owner + Event e = new BlockBreakEvent(block, ownerOfIsland); + Assert.assertTrue(fl.checkIsland(e, location, Flags.BREAK_BLOCKS, true)); + + // checking events - member + Event e2 = new BlockBreakEvent(block, player); + Assert.assertTrue(fl.checkIsland(e2, location, Flags.BREAK_BLOCKS, true)); + + } + + @Test + public void TestDefaultFlags() { + // Check all the default flags + FlagsManager fm = new FlagsManager(plugin); + Collection defaultFlags = Flags.values(); + Collection f = fm.getFlags(); + for (Flag flag : defaultFlags) { + assertTrue(flag.getID(), f.contains(flag)); + } + for (Flag flag : f) { + assertTrue(flag.getID(), defaultFlags.contains(flag)); + } + } + + @Test + public void TestCustomFlags() { + // Custom + FlagListener fl = new FlagListener(plugin); + Flag customFlag = new FlagBuilder().id("CUSTOM_FLAG").icon(Material.DIAMOND).listener(fl).build(); + assertEquals("CUSTOM_FLAG", customFlag.getID()); + assertEquals(Material.DIAMOND, customFlag.getIcon()); + assertEquals(fl, customFlag.getListener().get()); + // Add it to the Flag Manager + flagsManager.registerFlag(customFlag); + assertEquals(customFlag, flagsManager.getFlagByID("CUSTOM_FLAG")); + } + + /** + * Dummy flag listener + * + */ + private class FlagListener extends AbstractFlagListener { + FlagListener(BSkyBlock plugin) { + // Set the plugin explicitly + setPlugin(plugin); + } + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/addons/AddonTest.java b/src/test/java/us/tastybento/bskyblock/api/addons/AddonTest.java new file mode 100644 index 0000000..5887bea --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/addons/AddonTest.java @@ -0,0 +1,280 @@ +package us.tastybento.bskyblock.api.addons; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.managers.AddonsManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { BSkyBlock.class }) +public class AddonTest { + + @Mock + static BSkyBlock plugin; + static JavaPlugin javaPlugin; + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Mock item factory (for itemstacks) + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + ItemMeta itemMeta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + + } + + class TestClass extends Addon { + + @Override + public void onEnable() { + + } + + @Override + public void onDisable() { + + } + + } + + @Test + public void testAddon() { + TestClass test = new TestClass(); + assertNotNull(test); + assertFalse(test.isEnabled()); + } + + @Test + public void testGetBSkyBlock() { + TestClass test = new TestClass(); + assertEquals(plugin, test.getBSkyBlock()); + } + + @Test + public void testGetConfig() { + TestClass test = new TestClass(); + assertNotNull(test.getConfig()); + } + + @Test + public void testGetDataFolder() { + TestClass test = new TestClass(); + File file = mock(File.class); + assertNull(test.getDataFolder()); + test.setDataFolder(file); + assertEquals(file, test.getDataFolder()); + } + + @Test + public void testGetDescription() { + TestClass test = new TestClass(); + AddonDescription d = new AddonDescription(); + assertNull(test.getDescription()); + test.setDescription(d); + assertEquals(d, test.getDescription()); + } + + @Test + public void testGetFile() { + TestClass test = new TestClass(); + File file = mock(File.class); + assertNull(test.getFile()); + test.setAddonFile(file); + assertEquals(file, test.getFile()); + } + + @Test + public void testGetLogger() { + TestClass test = new TestClass(); + assertEquals(plugin.getLogger(), test.getLogger()); + } + + @Test + public void testGetServer() { + TestClass test = new TestClass(); + assertEquals(plugin.getServer(), test.getServer()); + } + + @Test + public void testIsEnabled() { + TestClass test = new TestClass(); + assertFalse(test.isEnabled()); + } + + @Test + public void testRegisterListener() { + class TestListener implements Listener {} + TestListener listener = new TestListener(); + TestClass test = new TestClass(); + test.registerListener(listener); + } + + + @Test + public void testSaveConfig() { + //TestClass test = new TestClass(); + // This will wipe out the config.yml of BSB so I am commenting it out + //test.saveConfig(); + } + + @Test + public void testSaveDefaultConfig() { + TestClass test = new TestClass(); + File jarFile = new File("addon.jar"); + File dataFolder = new File("dataFolder"); + test.setDataFolder(dataFolder); + test.setAddonFile(jarFile); + test.saveDefaultConfig(); + } + + @Test(expected = IllegalArgumentException.class) + public void testSaveResourceStringBoolean() { + TestClass test = new TestClass(); + test.saveResource("", true); + } + + @Test(expected = IllegalArgumentException.class) + public void testSaveResourceStringBooleanNull() { + TestClass test = new TestClass(); + test.saveResource(null, true); + } + + @Test + public void testSaveResourceStringBooleanNoFile() throws IOException { + TestClass test = new TestClass(); + File jarFile = new File("addon.jar"); + File dataFolder = new File("dataFolder"); + test.setDataFolder(dataFolder); + test.setAddonFile(jarFile); + test.saveResource("no_such_file", true); + } + + @Test + public void testSaveResourceStringFileBooleanBoolean() { + TestClass test = new TestClass(); + File jarFile = new File("addon.jar"); + File dataFolder = new File("dataFolder"); + test.setDataFolder(dataFolder); + test.setAddonFile(jarFile); + test.saveResource("no_such_file", jarFile, false, false); + test.saveResource("no_such_file", jarFile, false, true); + test.saveResource("no_such_file", jarFile, true, false); + test.saveResource("no_such_file", jarFile, true, true); + + } + + @Test + public void testGetResource() { + TestClass test = new TestClass(); + File jarFile = new File("addon.jar"); + File dataFolder = new File("dataFolder"); + test.setDataFolder(dataFolder); + test.setAddonFile(jarFile); + assertNull(test.getResource("nothing")); + } + + @Test + public void testSetAddonFile() { + TestClass test = new TestClass(); + File jarFile = new File("addon.jar"); + test.setAddonFile(jarFile); + assertEquals(jarFile, test.getFile()); + } + + @Test + public void testSetDataFolder() { + TestClass test = new TestClass(); + File dataFolder = new File("dataFolder"); + test.setDataFolder(dataFolder); + assertEquals(dataFolder, test.getDataFolder()); + } + + @Test + public void testSetDescription() { + TestClass test = new TestClass(); + AddonDescription desc = new AddonDescription(); + test.setDescription(desc); + assertEquals(desc, test.getDescription()); + } + + @Test + public void testSetEnabled() { + TestClass test = new TestClass(); + test.setEnabled(false); + assertFalse(test.isEnabled()); + test.setEnabled(true); + assertTrue(test.isEnabled()); + } + + @Test + public void testGetPlayers() { + TestClass test = new TestClass(); + PlayersManager pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + assertEquals(pm, test.getPlayers()); + } + + @Test + public void testGetIslands() { + TestClass test = new TestClass(); + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + assertEquals(im, test.getIslands()); + } + + @Test + public void testGetAddonByName() { + AddonsManager am = new AddonsManager(plugin); + when(plugin.getAddonsManager()).thenReturn(am); + TestClass test = new TestClass(); + assertEquals(Optional.empty(),test.getAddonByName("addon")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommandTest.java b/src/test/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommandTest.java new file mode 100644 index 0000000..775e1da --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/commands/DefaultHelpCommandTest.java @@ -0,0 +1,228 @@ +package us.tastybento.bskyblock.api.commands; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.events.command.CommandEvent; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, CommandEvent.class}) +public class DefaultHelpCommandTest { + + private User user; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player player = mock(Player.class); + // Sometimes use: Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + User.setPlugin(plugin); + // Set up user already + User.getInstance(player); + + // Parent command has no aliases + IslandCommand ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // No island for player to begin with (set it later in the tests) + IslandsManager im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Has team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getIslands()).thenReturn(im); + + + PlayersManager pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + } + + class FakeParent extends CompositeCommand { + + public FakeParent() { + super("island", "is"); + } + + @Override + public void setup() { + } + + @Override + public boolean execute(User user, String label, List args) { + return false; + } + + } + + @Test + public void testDefaultHelpCommand() throws Exception { + CompositeCommand cc = mock(CompositeCommand.class); + + DefaultHelpCommand dhc = new DefaultHelpCommand(cc); + assertNotNull(dhc); + Mockito.verify(cc).getSubCommands(); + } + + @Test + public void testSetup() { + CompositeCommand cc = mock(CompositeCommand.class); + DefaultHelpCommand dhc = new DefaultHelpCommand(cc); + assertNotNull(dhc); + // Verify that parent's parameters and permission is used + Mockito.verify(cc).getParameters(); + Mockito.verify(cc).getDescription(); + Mockito.verify(cc).getPermission(); + } + + @Test + public void testExecuteUserListOfString() { + CompositeCommand parent = mock(CompositeCommand.class); + when(parent.getLabel()).thenReturn("island"); + when(parent.getUsage()).thenReturn("island"); + when(parent.getParameters()).thenReturn("parameters"); + when(parent.getDescription()).thenReturn("description"); + when(parent.getPermission()).thenReturn("permission"); + when(user.getTranslation("island")).thenReturn("island"); + when(user.getTranslation("parameters")).thenReturn(""); + when(user.getTranslation("description")).thenReturn("the main island command"); + DefaultHelpCommand dhc = new DefaultHelpCommand(parent); + dhc.execute(user, dhc.getLabel(), new ArrayList<>()); + Mockito.verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); + Mockito.verify(user).getTranslation("island"); + Mockito.verify(user).getTranslation("parameters"); + Mockito.verify(user).getTranslation("description"); + Mockito.verify(user).sendMessage( + "commands.help.syntax", + "[usage]", + "island", + "[parameters]", + "", + "[description]", + "the main island command" + ); + Mockito.verify(user).sendMessage("commands.help.end"); + } + + @Test + public void testExecuteSecondLevelHelp() { + CompositeCommand parent = mock(CompositeCommand.class); + when(parent.getLabel()).thenReturn("island"); + when(parent.getUsage()).thenReturn("island"); + when(parent.getParameters()).thenReturn("parameters"); + when(parent.getDescription()).thenReturn("description"); + when(parent.getPermission()).thenReturn("permission"); + when(user.getTranslation("island")).thenReturn("island"); + when(user.getTranslation("parameters")).thenReturn(""); + when(user.getTranslation("description")).thenReturn("the main island command"); + DefaultHelpCommand dhc = new DefaultHelpCommand(parent); + List args = new ArrayList<>(); + args.add("1"); + dhc.execute(user, dhc.getLabel(), args); + // There are no header or footer shown + Mockito.verify(user).getTranslation("island"); + Mockito.verify(user).getTranslation("parameters"); + Mockito.verify(user).getTranslation("description"); + Mockito.verify(user).sendMessage( + "commands.help.syntax", + "[usage]", + "island", + "[parameters]", + "", + "[description]", + "the main island command" + ); + } + + @Test + public void testExecuteDirectHelpHelp() { + CompositeCommand parent = mock(CompositeCommand.class); + when(parent.getLabel()).thenReturn("island"); + when(parent.getUsage()).thenReturn("island"); + when(parent.getParameters()).thenReturn("parameters"); + when(parent.getDescription()).thenReturn("description"); + when(parent.getPermission()).thenReturn("permission"); + when(user.getTranslation("island")).thenReturn("island"); + when(user.getTranslation("parameters")).thenReturn(""); + when(user.getTranslation("description")).thenReturn("the main island command"); + when(user.getTranslation("commands.help.parameters")).thenReturn("help-parameters"); + when(user.getTranslation("commands.help.description")).thenReturn("the help command"); + DefaultHelpCommand dhc = new DefaultHelpCommand(parent); + List args = new ArrayList<>(); + // Test /island help team + args.add("team"); + dhc.execute(user, dhc.getLabel(), args); + // There are no header or footer shown + Mockito.verify(user).getTranslation("island"); + Mockito.verify(user).getTranslation("commands.help.parameters"); + Mockito.verify(user).getTranslation("commands.help.description"); + Mockito.verify(user).sendMessage( + "commands.help.syntax", + "[usage]", + "island", + "[parameters]", + "help-parameters", + "[description]", + "the help command" + ); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/flags/FlagTest.java b/src/test/java/us/tastybento/bskyblock/api/flags/FlagTest.java new file mode 100644 index 0000000..1f0cc80 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/flags/FlagTest.java @@ -0,0 +1,244 @@ +package us.tastybento.bskyblock.api.flags; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.flags.Flag.Type; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ BSkyBlock.class, Util.class, Bukkit.class }) +public class FlagTest { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + // World Settings + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemF = mock(ItemFactory.class); + ItemMeta im = mock(ItemMeta.class); + when(itemF.getItemMeta(Mockito.any())).thenReturn(im); + when(Bukkit.getItemFactory()).thenReturn(itemF); + + } + + @Test + public void testHashCode() { + Flag flag1 = new Flag(null, null, null, null, 0, null, false); + Flag flag2 = new Flag(null, null, null, null, 0, null, false); + assertTrue(flag1.hashCode() == flag2.hashCode()); + } + + @Test + public void testFlag() { + assertNotNull(new Flag(null, null, null, null, 0, null, false)); + } + + @Test + public void testGetID() { + Flag id = new Flag("id", null, null, null, 0, null, false); + assertEquals("id", id.getID()); + } + + @Test + public void testGetIcon() { + Flag id = new Flag("id", Material.ACACIA_DOOR, null, null, 0, null, false); + assertEquals(Material.ACACIA_DOOR, id.getIcon()); + } + + @Test + public void testGetListener() { + Listener l = mock(Listener.class); + Flag id = new Flag("id", Material.ACACIA_DOOR, l, null, 0, null, false); + Optional ol = Optional.ofNullable(l); + assertEquals(ol, id.getListener()); + id = new Flag("id", Material.ACACIA_DOOR, null, null, 0, null, false); + assertEquals(Optional.empty(), id.getListener()); + } + + @Test + public void testIsDefaultSetting() { + Type type = Type.SETTING; + Flag id = new Flag("id", Material.ACACIA_DOOR, null, type , 0, null, false); + assertFalse(id.isSetForWorld(mock(World.class))); + id = new Flag("id", Material.ACACIA_DOOR, null, type, 0, null, false); + id.setDefaultSetting(true); + assertTrue(id.isSetForWorld(mock(World.class))); + } + + @Test + public void testSetDefaultSetting() { + Type type = Type.SETTING; + Flag id = new Flag("id", Material.ACACIA_DOOR, null, type, 0, null, false); + assertFalse(id.isSetForWorld(mock(World.class))); + id.setDefaultSetting(true); + assertTrue(id.isSetForWorld(mock(World.class))); + id.setDefaultSetting(false); + assertFalse(id.isSetForWorld(mock(World.class))); + + } + + @Test + public void testIsDefaultSetting_World_Setting() { + Type type = Type.WORLD_SETTING; + Flag id = new Flag("id", Material.ACACIA_DOOR, null, type , 0, null, false); + assertFalse(id.isSetForWorld(mock(World.class))); + // Default can only be set once with world settings, so use a new id for flag + id = new Flag("id2", Material.ACACIA_DOOR, null, type, 0, null, false); + id.setDefaultSetting(true); + assertTrue(id.isSetForWorld(mock(World.class))); + } + + @Test + public void testGetType() { + Flag id = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + assertEquals(Flag.Type.PROTECTION,id.getType()); + id = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.SETTING, 0, null, false); + assertEquals(Flag.Type.SETTING,id.getType()); + } + + @Test + public void testGetDefaultRank() { + Flag id = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 100, null, false); + assertEquals(100, id.getDefaultRank()); + } + + @SuppressWarnings("unlikely-arg-type") + @Test + public void testEqualsObject() { + Flag flag1 = null; + Flag flag2 = new Flag(null, null, null, null, 0, null, false); + + assertFalse(flag2.equals(null)); + int i = 45; + assertFalse(flag2.equals(i)); + + flag1 = new Flag(null, null, null, null, 0, null, false); + flag2 = flag1; + assertTrue(flag1.equals(flag2)); + assertTrue(flag2.equals(flag1)); + + flag2 = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + assertFalse(flag1.equals(flag2)); + assertFalse(flag2.equals(flag1)); + + } + + @Test + public void testToPanelItem() throws Exception { + BSkyBlock plugin = mock(BSkyBlock.class); + + IslandsManager im = mock(IslandsManager.class); + + Island island = mock(Island.class); + when(island.getFlag(Mockito.any())).thenReturn(RanksManager.VISITOR_RANK); + + User user = mock(User.class); + when(user.getUniqueId()).thenReturn(UUID.randomUUID()); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + StringBuilder sb = new StringBuilder(); + Arrays.stream(invocation.getArguments()).forEach(sb::append); + sb.append("mock"); + return sb.toString(); + } + + }; + + when(user.getTranslation(Mockito.anyVararg())).thenAnswer(answer); + when(user.getTranslation(Mockito.any(),Mockito.any(),Mockito.any())).thenAnswer(answer); + + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(im.getIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(island); + Optional oL = Optional.ofNullable(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oL); + when(plugin.getIslands()).thenReturn(im); + + RanksManager rm = mock(RanksManager.class); + when(plugin.getRanksManager()).thenReturn(rm); + when(rm.getRank(Mockito.eq(RanksManager.VISITOR_RANK))).thenReturn("Visitor"); + when(rm.getRank(Mockito.eq(RanksManager.OWNER_RANK))).thenReturn("Owner"); + + Flag id = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + + PanelItem pi = id.toPanelItem(plugin, user); + + verify(user).getTranslation(Mockito.eq("protection.flags.id.name")); + verify(user).getTranslation(Mockito.eq("protection.panel.flag-item.name-layout"), Mockito.anyVararg()); + + assertEquals(Material.ACACIA_DOOR, pi.getItem().getType()); + + } + + @Test + public void testToString() { + Flag id = new Flag("id", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + assertEquals("Flag [id=id, icon=ACACIA_DOOR, listener=null, type=PROTECTION, defaultSetting=false, defaultRank=0, clickHandler=null, subPanel=false]", id.toString()); + } + + @Test + public void testCompareTo() { + Flag aaa = new Flag("AAA", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + Flag bbb = new Flag("BBB", Material.ACACIA_DOOR, null, Flag.Type.PROTECTION, 0, null, false); + assertTrue(aaa.compareTo(bbb) < bbb.compareTo(aaa)); + assertTrue(aaa.compareTo(aaa) == 0); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClickTest.java b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClickTest.java new file mode 100644 index 0000000..cf9d38e --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/CycleClickTest.java @@ -0,0 +1,284 @@ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.managers.RanksManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class, Util.class }) +public class CycleClickTest { + + private static final Integer PROTECTION_RANGE = 200; + private static final Integer X = 600; + private static final Integer Y = 120; + private static final Integer Z = 10000; + private BSkyBlock plugin; + private UUID uuid; + private User user; + private IslandsManager im; + private Island island; + private Flag flag; + private Panel panel; + private Inventory inv; + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + World world = mock(World.class); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + User.setPlugin(plugin); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + when(user.getWorld()).thenReturn(world); + when(user.hasPermission(Mockito.anyString())).thenReturn(true); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + PlayersManager pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Notifier + Notifier notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + Location loc = mock(Location.class); + when(loc.getWorld()).thenReturn(world); + when(loc.getBlockX()).thenReturn(X); + when(loc.getBlockY()).thenReturn(Y); + when(loc.getBlockZ()).thenReturn(Z); + when(island.getCenter()).thenReturn(loc); + when(island.getProtectionRange()).thenReturn(PROTECTION_RANGE); + // Island is not locked by default + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + // Island owner is user by default + when(island.getOwner()).thenReturn(uuid); + + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // Common from to's + Location outside = mock(Location.class); + when(outside.getWorld()).thenReturn(world); + when(outside.getBlockX()).thenReturn(X + PROTECTION_RANGE + 1); + when(outside.getBlockY()).thenReturn(Y); + when(outside.getBlockZ()).thenReturn(Z); + + Location inside = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 1); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + + Location inside2 = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 2); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(inside2))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(outside))).thenReturn(Optional.empty()); + + PanelItem panelItem = mock(PanelItem.class); + flag = mock(Flag.class); + when(flag.toPanelItem(Mockito.any(), Mockito.any())).thenReturn(panelItem); + when(panelItem.getItem()).thenReturn(mock(ItemStack.class)); + FlagsManager fm = mock(FlagsManager.class); + when(fm.getFlagByID(Mockito.anyString())).thenReturn(flag); + when(plugin.getFlagsManager()).thenReturn(fm); + + RanksManager rm = mock(RanksManager.class); + + when(plugin.getRanksManager()).thenReturn(rm); + + // Provide a current rank value - member + when(island.getFlag(Mockito.any())).thenReturn(RanksManager.MEMBER_RANK); + // Set up up and down ranks + when(rm.getRankUpValue(Mockito.eq(RanksManager.MEMBER_RANK))).thenReturn(RanksManager.OWNER_RANK); + when(rm.getRankDownValue(Mockito.eq(RanksManager.MEMBER_RANK))).thenReturn(RanksManager.VISITOR_RANK); + + panel = mock(Panel.class); + inv = mock(Inventory.class); + when(panel.getInventory()).thenReturn(inv); + + // IslandWorldManager + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + } + + @Test + public void testNotInWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + CycleClick udc = new CycleClick("LOCK"); + assertTrue(udc.onClick(panel, user, ClickType.LEFT, 5)); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.wrong-world")); + } + + @Test + public void testNoPremission() { + when(user.hasPermission(Mockito.anyString())).thenReturn(false); + CycleClick udc = new CycleClick("LOCK"); + assertTrue(udc.onClick(panel, user, ClickType.LEFT, 5)); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.no-permission")); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.you-need"), Mockito.eq("[permission]"), Mockito.eq("bskyblock.settings.LOCK")); + } + + @Test + public void testUpDownClick() { + CycleClick udc = new CycleClick("LOCK"); + assertNotNull(udc); + } + + @Test + public void testOnLeftClick() { + final int SLOT = 5; + CycleClick udc = new CycleClick("LOCK"); + // Rank starts at member + // Click left + assertTrue(udc.onClick(panel, user, ClickType.LEFT, SLOT)); + Mockito.verify(island).setFlag(Mockito.eq(flag), Mockito.eq(RanksManager.OWNER_RANK)); + Mockito.verify(flag).toPanelItem(Mockito.any(), Mockito.any()); + Mockito.verify(inv).setItem(Mockito.eq(SLOT), Mockito.any()); + // Check rollover + // Clicking when Owner should go to Visitor + when(island.getFlag(Mockito.any())).thenReturn(RanksManager.OWNER_RANK); + assertTrue(udc.onClick(panel, user, ClickType.LEFT, SLOT)); + Mockito.verify(island).setFlag(Mockito.eq(flag), Mockito.eq(RanksManager.VISITOR_RANK)); + Mockito.verify(flag, Mockito.times(2)).toPanelItem(Mockito.any(), Mockito.any()); + Mockito.verify(inv, Mockito.times(2)).setItem(Mockito.eq(SLOT), Mockito.any()); + } + + @Test + public void testOnRightClick() { + final int SLOT = 5; + CycleClick udc = new CycleClick("LOCK"); + // Rank starts at member + // Right click + assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); + Mockito.verify(island).setFlag(Mockito.eq(flag), Mockito.eq(RanksManager.VISITOR_RANK)); + Mockito.verify(flag).toPanelItem(Mockito.any(), Mockito.any()); + Mockito.verify(inv).setItem(Mockito.eq(SLOT), Mockito.any()); + // Check rollover + // Clicking when Visitor should go to Owner + when(island.getFlag(Mockito.any())).thenReturn(RanksManager.VISITOR_RANK); + assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); + Mockito.verify(island).setFlag(Mockito.eq(flag), Mockito.eq(RanksManager.OWNER_RANK)); + Mockito.verify(flag, Mockito.times(2)).toPanelItem(Mockito.any(), Mockito.any()); + Mockito.verify(inv, Mockito.times(2)).setItem(Mockito.eq(SLOT), Mockito.any()); + } + + @Test + public void testAllClicks() { + // Test all possible click types + CycleClick udc = new CycleClick("LOCK"); + Arrays.asList(ClickType.values()).forEach(c -> assertTrue(udc.onClick(panel, user, c, 0))); + } + + @Test + public void testNotOwner() { + UUID u; + do { + u = UUID.randomUUID(); + } while(u.equals(uuid)); + + when(island.getOwner()).thenReturn(u); + Mockito.verify(plugin, Mockito.never()).getRanksManager(); + + } + + @Test + public void testNullIsland() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(null); + Mockito.verify(plugin, Mockito.never()).getRanksManager(); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClickTest.java b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClickTest.java new file mode 100644 index 0000000..240abbf --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/IslandToggleClickTest.java @@ -0,0 +1,134 @@ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class }) +public class IslandToggleClickTest { + + private IslandWorldManager iwm; + private IslandToggleClick listener; + private Panel panel; + private User user; + private Flag flag; + private IslandsManager im; + private Island island; + private UUID uuid; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + when(plugin.getIWM()).thenReturn(iwm); + + listener = new IslandToggleClick("test"); + + panel = mock(Panel.class); + when(panel.getInventory()).thenReturn(mock(Inventory.class)); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.getWorld()).thenReturn(mock(World.class)); + when(user.getLocation()).thenReturn(mock(Location.class)); + when(user.getPlayer()).thenReturn(mock(Player.class)); + when(user.hasPermission(Mockito.anyString())).thenReturn(true); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + FlagsManager fm = mock(FlagsManager.class); + flag = mock(Flag.class); + when(flag.isSetForWorld(Mockito.any())).thenReturn(false); + PanelItem item = mock(PanelItem.class); + when(item.getItem()).thenReturn(mock(ItemStack.class)); + when(flag.toPanelItem(Mockito.any(), Mockito.eq(user))).thenReturn(item); + when(fm.getFlagByID(Mockito.anyString())).thenReturn(flag); + when(plugin.getFlagsManager()).thenReturn(fm); + + // Island Manager + im = mock(IslandsManager.class); + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(im.getIsland(Mockito.any(World.class), Mockito.any(User.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + } + + @Test + public void testOnClickWrongWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.wrong-world"); + } + + @Test + public void testOnClickNoPermission() { + when(user.hasPermission(Mockito.anyString())).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.no-permission"); + Mockito.verify(user).sendMessage("general.errors.you-need", "[permission]", "bskyblock.settings.test"); + } + + @Test + public void testOnClick() { + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(island).toggleFlag(flag); + } + + @Test + public void testOnClickNoIsland() { + when(im.getIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(null); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(island, Mockito.never()).toggleFlag(flag); + } + + @Test + public void testOnClickNotOwner() { + // Pick a different UUID from owner + UUID u = UUID.randomUUID(); + while(u.equals(uuid)) { + u = UUID.randomUUID(); + } + when(island.getOwner()).thenReturn(u); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(island, Mockito.never()).toggleFlag(flag); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClickTest.java b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClickTest.java new file mode 100644 index 0000000..e8b9ff2 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/flags/clicklisteners/WorldToggleClickTest.java @@ -0,0 +1,101 @@ +package us.tastybento.bskyblock.api.flags.clicklisteners; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class }) +public class WorldToggleClickTest { + + private IslandWorldManager iwm; + private WorldToggleClick listener; + private Panel panel; + private User user; + private Flag flag; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + when(plugin.getIWM()).thenReturn(iwm); + + listener = new WorldToggleClick("test"); + + panel = mock(Panel.class); + when(panel.getInventory()).thenReturn(mock(Inventory.class)); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.getWorld()).thenReturn(mock(World.class)); + when(user.getLocation()).thenReturn(mock(Location.class)); + when(user.getPlayer()).thenReturn(mock(Player.class)); + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + FlagsManager fm = mock(FlagsManager.class); + flag = mock(Flag.class); + when(flag.isSetForWorld(Mockito.any())).thenReturn(false); + PanelItem item = mock(PanelItem.class); + when(item.getItem()).thenReturn(mock(ItemStack.class)); + when(flag.toPanelItem(Mockito.any(), Mockito.eq(user))).thenReturn(item); + when(fm.getFlagByID(Mockito.anyString())).thenReturn(flag); + when(plugin.getFlagsManager()).thenReturn(fm); + + } + + @Test + public void testOnClickWrongWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.wrong-world"); + } + + @Test + public void testOnClickNoPermission() { + when(user.hasPermission(Mockito.anyString())).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.no-permission"); + Mockito.verify(user).sendMessage("general.errors.you-need", "[permission]", "bskyblock.admin.world.settings.test"); + } + + @Test + public void testOnClick() { + when(user.hasPermission(Mockito.anyString())).thenReturn(true); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(flag).setSetting(Mockito.any(), Mockito.eq(true)); + Mockito.verify(panel).getInventory(); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilderTest.java b/src/test/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilderTest.java new file mode 100644 index 0000000..5cb2322 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilderTest.java @@ -0,0 +1,168 @@ +package us.tastybento.bskyblock.api.panels.builders; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; + +@RunWith(PowerMockRunner.class) +public class PanelItemBuilderTest { + + + @SuppressWarnings("deprecation") + @BeforeClass + public static void setUp() throws Exception { + Server server = mock(Server.class); + World world = mock(World.class); + world = mock(World.class); + Mockito.when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + Mockito.when(server.getWorld("world")).thenReturn(world); + Mockito.when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(skullMeta.getOwner()).thenReturn("tastybento"); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + + OfflinePlayer offlinePlayer = mock(OfflinePlayer.class); + when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer); + when(offlinePlayer.getName()).thenReturn("tastybento"); + + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + //when(Bukkit.getServer()).thenReturn(server); + } + + @Test + public void testIconMaterial() { + PanelItemBuilder builder = new PanelItemBuilder(); + builder.icon(Material.STONE); + PanelItem item = builder.build(); + assertNotNull(item.getItem().getType()); + assertEquals(Material.STONE, item.getItem().getType()); + } + + @Test + public void testIconItemStack() { + PanelItemBuilder builder = new PanelItemBuilder(); + builder.icon(new ItemStack(Material.IRON_ORE)); + PanelItem item = builder.build(); + assertNotNull(item.getItem().getType()); + assertEquals(Material.IRON_ORE, item.getItem().getType()); + } + + @SuppressWarnings("deprecation") + @Test + public void testIconString() { + PanelItemBuilder builder = new PanelItemBuilder(); + builder.icon("tastybento"); + PanelItem item = builder.build(); + assertNotNull(item.getItem().getType()); + SkullMeta skullMeta = (SkullMeta)item.getItem().getItemMeta(); + assertEquals("tastybento",skullMeta.getOwner()); + assertEquals(Material.SKULL_ITEM, item.getItem().getType()); + } + + @Test + public void testName() { + PanelItemBuilder builder = new PanelItemBuilder(); + builder.name("test"); + PanelItem item = builder.build(); + assertEquals("test",item.getName()); + } + + @Test + public void testDescriptionListOfString() { + PanelItemBuilder builder = new PanelItemBuilder(); + List test = Arrays.asList("test line 1", "test line 2"); + builder.description(test); + PanelItem item = builder.build(); + assertEquals(test, item.getDescription()); + } + + @Test + public void testDescriptionStringArray() { + PanelItemBuilder builder = new PanelItemBuilder(); + List test = Arrays.asList("test line 3", "test line 4"); + builder.description("test line 3", "test line 4"); + PanelItem item = builder.build(); + assertEquals(test, item.getDescription()); + } + + @Test + public void testDescriptionString() { + PanelItemBuilder builder = new PanelItemBuilder(); + List test = Arrays.asList("test line 5"); + builder.description("test line 5"); + PanelItem item = builder.build(); + assertEquals(test, item.getDescription()); + } + + @Test + public void testClickHandler() { + PanelItemBuilder builder = new PanelItemBuilder(); + // Test without click handler + PanelItem item = builder.clickHandler(null).build(); + assertFalse(item.getClickHandler().isPresent()); + + item = builder.clickHandler(new Clicker()).build(); + assertTrue(item.getClickHandler().isPresent()); + assertTrue(item.getClickHandler().map(x -> x.onClick(null, null, ClickType.LEFT, 0)).orElse(false)); + } + + @Test + public void testGlow() { + PanelItemBuilder builder = new PanelItemBuilder(); + // Test without glowing + PanelItem item = builder.glow(false).build(); + assertFalse(item.isGlow()); + + // Test with glowing + item = builder.glow(true).build(); + assertTrue(item.isGlow()); + } + + public class Clicker implements PanelItem.ClickHandler { + + @Override + public boolean onClick(Panel panel, User user, ClickType click, int slot) { + return true; + } + + } +} diff --git a/src/test/java/us/tastybento/bskyblock/api/user/UserTest.java b/src/test/java/us/tastybento/bskyblock/api/user/UserTest.java new file mode 100644 index 0000000..cab7b2b --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/api/user/UserTest.java @@ -0,0 +1,487 @@ +package us.tastybento.bskyblock.api.user; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + + +@RunWith(PowerMockRunner.class) +public class UserTest { + + private static Player player; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + player = mock(Player.class); + when(server.getPlayer(Mockito.any(UUID.class))).thenReturn(player); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + + } + + private CommandSender sender; + + @Before + public void setUp() throws Exception { + sender = mock(CommandSender.class); + User.clearUsers(); + User.setPlugin(null); + } + + @Test + public void testGetInstanceCommandSender() { + User user = User.getInstance(sender); + assertNotNull(user); + assertEquals(sender,user.getSender()); + } + + @Test + public void testGetInstancePlayer() { + Player player = mock(Player.class); + User user = User.getInstance(player); + assertNotNull(user); + assertEquals(player,user.getPlayer()); + } + + @Test + public void testGetInstanceUUID() { + UUID uuid = UUID.randomUUID(); + User user = User.getInstance(uuid); + assertNotNull(user); + assertEquals(uuid,user.getUniqueId()); + } + + @Test + public void testRemovePlayer() { + User.removePlayer(player); + } + + @Test + public void testSetPlugin() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + } + + @Test + public void testGetEffectivePermissions() { + Set value = new HashSet<>(); + PermissionAttachmentInfo perm = new PermissionAttachmentInfo(sender, "perm", null, false); + value.add(perm); + when(sender.getEffectivePermissions()).thenReturn(value ); + User user = User.getInstance(sender); + assertEquals(value, user.getEffectivePermissions()); + } + + @Test + public void testGetInventory() { + PlayerInventory value = mock(PlayerInventory.class); + when(player.getInventory()).thenReturn(value); + assertEquals(value, player.getInventory()); + User user = User.getInstance(player); + assertNotNull(user.getInventory()); + assertEquals(value, user.getInventory()); + } + + @Test + public void testGetLocation() { + Location loc = mock(Location.class); + when(player.getLocation()).thenReturn(loc); + User user = User.getInstance(player); + assertNotNull(user.getLocation()); + assertEquals(loc, user.getLocation()); + } + + @Test + public void testGetName() { + String name = "tastybento"; + when(player.getName()).thenReturn(name); + User user = User.getInstance(player); + assertNotNull(user.getName()); + assertEquals(name, user.getName()); + + } + + @Test + public void testGetPlayer() { + User user = User.getInstance(player); + assertEquals(player, user.getPlayer()); + } + + @Test + public void testIsPlayer() { + User user = User.getInstance(sender); + assertFalse(user.isPlayer()); + user = User.getInstance(player); + assertTrue(user.isPlayer()); + } + + @Test + public void testGetSender() { + User user = User.getInstance(sender); + assertEquals(sender, user.getSender()); + } + + @Test + public void testGetUniqueId() { + UUID uuid = UUID.randomUUID(); + when(player.getUniqueId()).thenReturn(uuid); + User user = User.getInstance(player); + assertEquals(uuid, user.getUniqueId()); + } + + @Test + public void testHasPermission() { + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + User user = User.getInstance(player); + assertTrue(user.hasPermission("")); + assertTrue(user.hasPermission("perm")); + } + + @Test + public void testIsOnline() { + when(player.isOnline()).thenReturn(true); + User user = User.getInstance(player); + assertTrue(user.isOnline()); + } + + @Test + public void testIsOp() { + when(player.isOp()).thenReturn(true); + User user = User.getInstance(player); + assertTrue(user.isOp()); + } + + @Test + public void testGetTranslation() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation [test]"); + + User user = User.getInstance(player); + assertEquals("mock translation [test]", user.getTranslation("a.reference")); + assertEquals("mock translation variable", user.getTranslation("a.reference", "[test]", "variable")); + + // Test no translation found + when(lm.get(any(), any())).thenReturn(null); + assertEquals("a.reference", user.getTranslation("a.reference")); + + } + + @Test + public void testGetTranslationOrNothing() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + // Return the original string to pretend that a translation could not be found + when(lm.get(any(), any())).thenReturn("fake.reference"); + + User user = User.getInstance(player); + assertEquals("", user.getTranslationOrNothing("fake.reference")); + assertEquals("", user.getTranslationOrNothing("fake.reference", "[test]", "variable")); + } + + @Test + public void testSendMessage() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + String translation = ChatColor.RED + "" + ChatColor.BOLD + "test translation"; + when(lm.get(any(), any())).thenReturn(translation); + + Player pl = mock(Player.class); + + User user = User.getInstance(pl); + user.sendMessage("a.reference"); + Mockito.verify(pl).sendMessage(translation); + } + + @Test + public void testSendMessageNullUser() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + String translation = ChatColor.RED + "" + ChatColor.BOLD + "test translation"; + when(lm.get(any(), any())).thenReturn(translation); + + Player pl = mock(Player.class); + + User user = User.getInstance(UUID.randomUUID()); + user.sendMessage("a.reference"); + Mockito.verify(pl, Mockito.never()).sendMessage(Mockito.anyString()); + + } + + @Test + public void testSendMessageBlankTranslation() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + // Nothing - blank translation + when(lm.get(any(), any())).thenReturn(""); + when(plugin.getLocalesManager()).thenReturn(lm); + + Player pl = mock(Player.class); + User user = User.getInstance(pl); + user.sendMessage("a.reference"); + Mockito.verify(pl, Mockito.never()).sendMessage(Mockito.anyString()); + + } + + @Test + public void testSendMessageOnlyColors() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + + Player pl = mock(Player.class); + User user = User.getInstance(pl); + + // Nothing - just color codes + StringBuilder allColors = new StringBuilder(); + for (ChatColor cc : ChatColor.values()) { + allColors.append(cc); + } + when(lm.get(any(), any())).thenReturn(allColors.toString()); + user.sendMessage("a.reference"); + Mockito.verify(pl, Mockito.never()).sendMessage(Mockito.anyString()); + + } + + @Test + public void testSendRawMessage() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + String raw = ChatColor.RED + "" + ChatColor.BOLD + "test message"; + + Player pl = mock(Player.class); + + User user = User.getInstance(pl); + user.sendRawMessage(raw); + Mockito.verify(pl).sendMessage(raw); + + + } + + @Test + public void testSendRawMessageNullUser() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + String raw = ChatColor.RED + "" + ChatColor.BOLD + "test message"; + + Player pl = mock(Player.class); + + User user = User.getInstance(UUID.randomUUID()); + user.sendRawMessage(raw); + Mockito.verify(pl, Mockito.never()).sendMessage(Mockito.anyString()); + + } + + @Test + public void testNotifyStringStringArrayNotifyOK() { + BSkyBlock plugin = mock(BSkyBlock.class); + Notifier notifier = mock(Notifier.class); + + when(plugin.getNotifier()).thenReturn(notifier); + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + String translation = ChatColor.RED + "" + ChatColor.BOLD + "test translation"; + when(lm.get(any(), any())).thenReturn(translation); + + Player pl = mock(Player.class); + User user = User.getInstance(pl); + + // Set notify + when(notifier.notify(Mockito.any(), Mockito.eq(translation))).thenReturn(true); + + user.notify("a.reference"); + Mockito.verify(notifier).notify(user, translation); + + } + + + @Test + public void testSetGameMode() { + Player pl = mock(Player.class); + User user = User.getInstance(pl); + for (GameMode gm: GameMode.values()) { + user.setGameMode(gm); + } + Mockito.verify(pl, Mockito.times(GameMode.values().length)).setGameMode(Mockito.any()); + + } + + @Test + public void testTeleport() { + Player pl = mock(Player.class); + User user = User.getInstance(pl); + when(pl.teleport(Mockito.any(Location.class))).thenReturn(true); + Location loc = mock(Location.class); + user.teleport(loc); + Mockito.verify(pl).teleport(loc); + + } + + @Test + public void testGetWorld() { + Player pl = mock(Player.class); + World world = mock(World.class); + when(pl.getWorld()).thenReturn(world); + User user = User.getInstance(pl); + assertEquals(world, user.getWorld()); + + } + + @Test + public void testCloseInventory() { + Player pl = mock(Player.class); + User user = User.getInstance(pl); + user.closeInventory(); + Mockito.verify(pl).closeInventory(); + } + + @Test + public void testGetLocalePlayer() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + PlayersManager pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + when(pm.getLocale(Mockito.any())).thenReturn("en-US"); + + // Confirm that Locale object is correctly obtained + Locale locale = Locale.US; + Player pl = mock(Player.class); + User user = User.getInstance(pl); + assertEquals(locale, user.getLocale()); + } + + @Test + public void testGetLocaleConsole() { + BSkyBlock plugin = mock(BSkyBlock.class); + User.setPlugin(plugin); + PlayersManager pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + when(pm.getLocale(Mockito.any())).thenReturn("en-US"); + + // Confirm that Locale object is correctly obtained + Locale locale = Locale.US; + // Try for console + User console = User.getInstance(mock(CommandSender.class)); + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDefaultLanguage()).thenReturn("en-US"); + assertEquals(locale, console.getLocale()); + } + + @Test + public void testUpdateInventory() { + Player pl = mock(Player.class); + User user = User.getInstance(pl); + user.updateInventory(); + Mockito.verify(pl).updateInventory(); + } + + @Test + public void testPerformCommand() { + Player pl = mock(Player.class); + User user = User.getInstance(pl); + user.performCommand("test"); + Mockito.verify(pl).performCommand("test"); + } + + @SuppressWarnings("unlikely-arg-type") + @Test + public void testEqualsObject() { + User user1 = User.getInstance(UUID.randomUUID()); + User user2 = User.getInstance(UUID.randomUUID()); + assertTrue(user1.equals(user1)); + assertFalse(user1.equals(user2)); + assertFalse(user1.equals(null)); + assertFalse(user2.equals(user1)); + assertFalse(user2.equals(null)); + assertFalse(user2.equals("a string")); + + user1 = User.getInstance((UUID)null); + assertFalse(user2.equals(user1)); + + } + + @Test + public void testHashCode() { + UUID uuid = UUID.randomUUID(); + User user1 = User.getInstance(uuid); + User user2 = User.getInstance(uuid); + assertEquals(user1, user2); + assertTrue(user1.hashCode() == user2.hashCode()); + } + + @Test + public void testNullPlayer() { + User user = User.getInstance((Player)null); + assertNull(user); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/AdminCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/AdminCommandTest.java new file mode 100644 index 0000000..4b94165 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/AdminCommandTest.java @@ -0,0 +1,112 @@ +/** + * + */ +package us.tastybento.bskyblock.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.plugin.PluginManager; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { BSkyBlock.class }) +public class AdminCommandTest { + + @Mock + static BSkyBlock plugin; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.AdminCommand#AdminCommand()}. + */ + @Test + public void testAdminCommand() { + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + assertNotNull(new AdminCommand()); + // Verify the command has been registered + Mockito.verify(cm).registerCommand(Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.AdminCommand#setup()}. + */ + @Test + public void testSetup() { + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + AdminCommand ac = new AdminCommand(); + ac.setup(); + assertEquals("bskyblock.admin.*", ac.getPermission()); + assertFalse(ac.isOnlyPlayer()); + assertEquals("commands.admin.help.parameters", ac.getParameters()); + assertEquals("commands.admin.help.description", ac.getDescription()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.AdminCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUserListOfString() { + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + AdminCommand ac = new AdminCommand(); + assertTrue(ac.execute(mock(User.class), ac.getLabel(), new ArrayList<>())); + + // No such command + String[] args2 = {"random", "junk"}; + assertFalse(ac.execute(mock(User.class), ac.getLabel(), Arrays.asList(args2))); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/IslandCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/IslandCommandTest.java new file mode 100644 index 0000000..3c8dd8c --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/IslandCommandTest.java @@ -0,0 +1,163 @@ +package us.tastybento.bskyblock.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent.IslandEventBuilder; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.managers.island.NewIsland; +import us.tastybento.bskyblock.managers.island.NewIsland.Builder; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, NewIsland.class, IslandEvent.class }) +public class IslandCommandTest { + + @Mock + static BSkyBlock plugin; + private static World world; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + world = mock(World.class); + when(world.getName()).thenReturn("BSkyBlock_test_world"); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + } + + @Before + public void setup() { + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getWorld(Mockito.anyString())).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + } + + @Test + public void testIslandCommand() { + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + assertNotNull(new IslandCommand()); + // Verify the command has been registered + Mockito.verify(cm).registerCommand(Mockito.any()); + } + + @Test + public void testSetup() { + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + IslandCommand ic = new IslandCommand(); + assertEquals("commands.island.help.description", ic.getDescription()); + assertTrue(ic.isOnlyPlayer()); + // Permission + assertEquals("bskyblock.island", ic.getPermission()); + + } + + @Test + public void testExecuteUserListOfString() throws IOException { + PowerMockito.mockStatic(NewIsland.class); + Builder builder = mock(Builder.class); + Island island = mock(Island.class); + when(builder.build()).thenReturn(island); + when(builder.oldIsland(Mockito.any())).thenReturn(builder); + when(builder.player(Mockito.any())).thenReturn(builder); + when(builder.reason(Mockito.any())).thenReturn(builder); + when(builder.world(Mockito.any())).thenReturn(builder); + when(NewIsland.builder()).thenReturn(builder); + + PowerMockito.mockStatic(IslandEvent.class); + IslandEventBuilder ieb = mock(IslandEventBuilder.class); + when(ieb.admin(Mockito.anyBoolean())).thenReturn(ieb); + IslandBaseEvent event = mock(IslandBaseEvent.class); + when(ieb.build()).thenReturn(event); + when(ieb.involvedPlayer(Mockito.any())).thenReturn(ieb); + when(ieb.island(Mockito.any())).thenReturn(ieb); + when(ieb.location(Mockito.any())).thenReturn(ieb); + when(ieb.reason(Mockito.any())).thenReturn(ieb); + when(IslandEvent.builder()).thenReturn(ieb); + + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // Setup + IslandCommand ic = new IslandCommand(); + assertFalse(ic.execute(null, ic.getLabel(), new ArrayList<>())); + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + User user = mock(User.class); + UUID uuid = UUID.randomUUID(); + Player player = mock(Player.class); + when(user.getPlayer()).thenReturn(player); + when(user.getUniqueId()).thenReturn(uuid); + PlayersManager pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + + // User has an island - so go there! + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(island); + assertTrue(ic.execute(user, ic.getLabel(), new ArrayList<>())); + when(user.getWorld()).thenReturn(world); + + + Location location = mock(Location.class); + when(island.getCenter()).thenReturn(location); + // No island yet, one will be created + when(im.createIsland(Mockito.any(), Mockito.any())).thenReturn(island); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + assertTrue(ic.execute(user, ic.getLabel(), new ArrayList<>())); + + // No such command + String[] args2 = {"random", "junk"}; + assertFalse(ic.execute(user, ic.getLabel(), Arrays.asList(args2))); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommandTest.java new file mode 100644 index 0000000..ff218ea --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsAllCommandTest.java @@ -0,0 +1,124 @@ +package us.tastybento.bskyblock.commands.admin; + +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminClearResetsAllCommandTest { + + private AdminCommand ac; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminClearResetsAllCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteCheckConfirm() { + AdminClearResetsAllCommand itl = new AdminClearResetsAllCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.confirm"), Mockito.eq("[seconds]"), Mockito.any()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommandTest.java new file mode 100644 index 0000000..ae915ad --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminClearResetsCommandTest.java @@ -0,0 +1,174 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.admin; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author ben + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminClearResetsCommandTest { + + private AdminCommand ac; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminClearResetsCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminClearResetsCommand itl = new AdminClearResetsCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + Mockito.verify(user).sendMessage(Mockito.eq("commands.help.header"), Mockito.eq("[label]"), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminClearResetsCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminClearResetsCommand itl = new AdminClearResetsCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminClearResetsCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecutePlayerNoIsland() { + AdminClearResetsCommand itl = new AdminClearResetsCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-no-island")); + } + + /** + * Test method for {@link us.AdminClearResetsCommand.tastybento.bskyblock.commands.admin.AdminClearResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteSuccess() { + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + AdminClearResetsCommand itl = new AdminClearResetsCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + // Add other verifications + Mockito.verify(user).sendMessage("commands.admin.clearresets.cleared"); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommandTest.java new file mode 100644 index 0000000..6bd3f74 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminInfoCommandTest.java @@ -0,0 +1,213 @@ +package us.tastybento.bskyblock.commands.admin; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.*; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminInfoCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link AdminInfoCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteNoTargetConsole() { + AdminInfoCommand itl = new AdminInfoCommand(ac); + CommandSender sender = mock(CommandSender.class); + User console = User.getInstance(sender); + assertFalse(itl.execute(console, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminInfoCommand#execute(User, String, List)} . + */ + @Test + public void testExecuteUnknownPlayer() { + AdminInfoCommand itl = new AdminInfoCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminInfoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecutePlayerHasNoIsland() { + AdminInfoCommand itl = new AdminInfoCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-no-island")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminInfoCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteSuccess() { + AdminInfoCommand itl = new AdminInfoCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(is); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(is).showInfo(Mockito.eq(plugin), Mockito.eq(user), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminInfoCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteUserNotOnIsland() { + when(user.isPlayer()).thenReturn(true); + AdminInfoCommand itl = new AdminInfoCommand(ac); + // No island here + when(im.getIslandAt(Mockito.any())).thenReturn(Optional.empty()); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Confirm other verifications + Mockito.verify(user).sendMessage("commands.admin.info.no-island"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.AdminInfoCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteSuccessUserOnIsland() { + when(user.isPlayer()).thenReturn(true); + AdminInfoCommand itl = new AdminInfoCommand(ac); + Location loc = mock(Location.class); + + // Island has owner + Island is = mock(Island.class); + when(is.getOwner()).thenReturn(uuid); + when(is.showInfo(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true); + Optional opi = Optional.of(is); + when(im.getIslandAt(Mockito.any())).thenReturn(opi); + when(user.getLocation()).thenReturn(loc); + + + assertTrue(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Confirm other verifications + Mockito.verify(is).showInfo(Mockito.eq(plugin), Mockito.eq(user), Mockito.any()); + } +} \ No newline at end of file diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommandTest.java new file mode 100644 index 0000000..8f3a22e --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminRegisterCommandTest.java @@ -0,0 +1,224 @@ +package us.tastybento.bskyblock.commands.admin; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminRegisterCommandTest { + + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecutePlayerHasIsland() { + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-island")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteInTeam() { + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.register.cannot-register-team-player"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteAlreadyOwnedIsland() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + Location loc = mock(Location.class); + + // Island has owner + Island is = mock(Island.class); + when(is.getOwner()).thenReturn(uuid); + Optional opi = Optional.of(is); + when(im.getIslandAt(Mockito.any())).thenReturn(opi); + when(user.getLocation()).thenReturn(loc); + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.register.already-owned"); + } + + /** + * Test method for {@link us.us.tastybento.bskyblock.commands.admin.teams.AdminRegisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteSuccess() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + Island is = mock(Island.class); + Location loc = mock(Location.class); + when(loc.toVector()).thenReturn(new Vector(123,123,432)); + when(is.getCenter()).thenReturn(loc); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + Optional opi = Optional.of(is); + when(im.getIslandAt(Mockito.any())).thenReturn(opi); + when(user.getLocation()).thenReturn(loc); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + // Add other verifications + Mockito.verify(user).sendMessage("commands.admin.register.registered-island", "[xyz]", "123,123,432"); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommandTest.java new file mode 100644 index 0000000..ecdd540 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/AdminUnregisterCommandTest.java @@ -0,0 +1,194 @@ +package us.tastybento.bskyblock.commands.admin; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminUnregisterCommandTest { + + private AdminCommand ac; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminUnregisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminUnregisterCommand itl = new AdminUnregisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminUnregisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminUnregisterCommand itl = new AdminUnregisterCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminUnregisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecutePlayerNoIsland() { + AdminUnregisterCommand itl = new AdminUnregisterCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-no-island")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminUnregisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteInTeam() { + when(im.inTeam(Mockito.any(),Mockito.any())).thenReturn(true); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + AdminUnregisterCommand itl = new AdminUnregisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.unregister.cannot-unregister-team-player"); + } + + /** + * Test method for {@link us.us.tastybento.bskyblock.commands.admin.teams.AdminUnregisterCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteSuccess() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + Island is = mock(Island.class); + Location loc = mock(Location.class); + when(loc.toVector()).thenReturn(new Vector(123,123,432)); + when(is.getCenter()).thenReturn(loc); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + AdminUnregisterCommand itl = new AdminUnregisterCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + // Add other verifications + Mockito.verify(user).sendMessage("commands.admin.unregister.unregistered-island", "[xyz]", "123,123,432"); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommandTest.java new file mode 100644 index 0000000..2fb58a1 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeCommandTest.java @@ -0,0 +1,145 @@ +package us.tastybento.bskyblock.commands.admin.range; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminRangeCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + }}; + when(lm.get(Mockito.any(), Mockito.any())).thenAnswer(answer ); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + @Test + public void testExecuteConsoleNoArgs() { + AdminRangeCommand arc = new AdminRangeCommand(ac); + CommandSender sender = mock(CommandSender.class); + User console = User.getInstance(sender); + arc.execute(console, "", new ArrayList<>()); + // Show help + Mockito.verify(sender).sendMessage("commands.help.header"); + } + + @Test + public void testExecutePlayerNoArgs() { + AdminRangeCommand arc = new AdminRangeCommand(ac); + arc.execute(user, "", new ArrayList<>()); + // Show help" + Mockito.verify(user).sendMessage("commands.help.header","[label]","BSkyBlock"); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommandTest.java new file mode 100644 index 0000000..c42d2c5 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeDisplayCommandTest.java @@ -0,0 +1,175 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.admin.range; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminRangeDisplayCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + }}; + when(lm.get(Mockito.any(), Mockito.any())).thenAnswer(answer ); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeDisplayCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecutePlayerDisplayArgs() { + AdminRangeDisplayCommand ardc = new AdminRangeDisplayCommand(ac); + ardc.execute(user, "display", new ArrayList<>()); + // Show display + Mockito.verify(user).sendMessage("commands.admin.range.display.showing"); + Mockito.verify(user).sendMessage("commands.admin.range.display.hint"); + // Run command again + ardc.execute(user, "display", new ArrayList<>()); + // Remove + Mockito.verify(user).sendMessage("commands.admin.range.display.hiding"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeDisplayCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecutePlayeShowArgs() { + AdminRangeDisplayCommand ardc = new AdminRangeDisplayCommand(ac); + ardc.execute(user, "show", new ArrayList<>()); + // Show display + Mockito.verify(user).sendMessage("commands.admin.range.display.showing"); + Mockito.verify(user).sendMessage("commands.admin.range.display.hint"); + // Run command again + ardc.execute(user, "show", new ArrayList<>()); + Mockito.verify(user).sendMessage("commands.admin.range.display.already-on"); + ardc.execute(user, "hide", new ArrayList<>()); + Mockito.verify(user).sendMessage("commands.admin.range.display.hiding"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeDisplayCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecutePlayeHideArgs() { + AdminRangeDisplayCommand ardc = new AdminRangeDisplayCommand(ac); + ardc.execute(user, "hide", new ArrayList<>()); + Mockito.verify(user).sendMessage("commands.admin.range.display.already-off"); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommandTest.java new file mode 100644 index 0000000..5c477e7 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeResetCommandTest.java @@ -0,0 +1,206 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.admin.range; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminRangeResetCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(iwm.getIslandProtectionRange(Mockito.any())).thenReturn(200); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + Island island = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + } + }; + + when(lm.get(Mockito.any(), Mockito.any())).thenAnswer(answer ); + when(plugin.getLocalesManager()).thenReturn(lm); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteConsoleNoArgs() { + AdminRangeResetCommand arc = new AdminRangeResetCommand(ac); + CommandSender sender = mock(CommandSender.class); + User console = User.getInstance(sender); + arc.execute(console, "", new ArrayList<>()); + // Show help + Mockito.verify(sender).sendMessage("commands.help.header"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecutePlayerNoArgs() { + AdminRangeResetCommand arc = new AdminRangeResetCommand(ac); + arc.execute(user, "", new ArrayList<>()); + // Show help + Mockito.verify(user).sendMessage("commands.help.header","[label]","BSkyBlock"); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminRangeResetCommand arc = new AdminRangeResetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("general.errors.unknown-player"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteKnownPlayerNoIsland() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + AdminRangeResetCommand arc = new AdminRangeResetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("general.errors.player-has-no-island"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteKnownPlayer() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeResetCommand arc = new AdminRangeResetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.reset.success", TextVariables.NUMBER, "200"); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommandTest.java new file mode 100644 index 0000000..1fa40c1 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/range/AdminRangeSetCommandTest.java @@ -0,0 +1,295 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.admin.range; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminRangeSetCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(iwm.getIslandProtectionRange(Mockito.any())).thenReturn(200); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + Island island = mock(Island.class); + when(island.getRange()).thenReturn(50); + when(island.getProtectionRange()).thenReturn(50); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + } + }; + + when(lm.get(Mockito.any(), Mockito.any())).thenAnswer(answer ); + when(plugin.getLocalesManager()).thenReturn(lm); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteConsoleNoArgs() { + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + CommandSender sender = mock(CommandSender.class); + User console = User.getInstance(sender); + arc.execute(console, "", new ArrayList<>()); + // Show help + Mockito.verify(sender).sendMessage("commands.help.header"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecutePlayerNoArgs() { + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + arc.execute(user, "", new ArrayList<>()); + // Show help + Mockito.verify(user).sendMessage("commands.help.header","[label]","BSkyBlock"); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("100"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("general.errors.unknown-player"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteKnownPlayerNoIsland() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("100"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("general.errors.player-has-no-island"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteTooHigh() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("100"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.too-high", TextVariables.NUMBER, "50"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteNotANumber() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("NAN"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.not-numeric", TextVariables.NUMBER, "NAN"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteDoubleNumber() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("3.141592654"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.not-numeric", TextVariables.NUMBER, "3.141592654"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteZero() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("0"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.too-low", TextVariables.NUMBER, "0"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteNegNumber() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("-437645"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.not-numeric", TextVariables.NUMBER, "-437645"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteSame() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("50"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.invalid-value.same-as-before", TextVariables.NUMBER, "50"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.range.AdminRangeSetCommand#execute(us.tastybento.bskyblock.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecute() { + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); + List args = new ArrayList<>(); + args.add("tastybento"); + args.add("48"); + arc.execute(user, "", args); + Mockito.verify(user).sendMessage("commands.admin.range.set.success", TextVariables.NUMBER, "48"); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommandTest.java new file mode 100644 index 0000000..a3a6c77 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamAddCommandTest.java @@ -0,0 +1,307 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminTeamAddCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ac.getTopLabel()).thenReturn("bsb"); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + } + + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteWrongArgs() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + List args = new ArrayList<>(); + assertFalse(itl.execute(user, itl.getLabel(), args)); + // Show help + args.add("arg1"); + assertFalse(itl.execute(user, itl.getLabel(), args)); + // Show help + args.add("args2"); + args.add("args3"); + assertFalse(itl.execute(user, itl.getLabel(), args)); + // Show help + } + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + // Unknown leader + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(null); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + assertFalse(itl.execute(user, ac.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("general.errors.unknown-player-name", "[name]", "tastybento"); + + // Unknown target + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(null); + assertFalse(itl.execute(user, ac.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("general.errors.unknown-player-name", "[name]", "poslovich"); + } + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteTargetTargetInTeam() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + when(im.inTeam(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.invite.errors.already-on-team")); + } + + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteAddNoIsland() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + // No island, + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("general.errors.player-has-no-island"); + + } + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteAddNotLeader() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + // Has island, has team, but not a leader + when(im.hasIsland(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.eq(uuid))).thenReturn(notUUID); + + // Island + Island island = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(island); + + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.team.add.name-not-leader", "[name]", "tastybento"); + Mockito.verify(island).showMembers(Mockito.eq(plugin), Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteAddTargetHasIsland() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + // Has island, has team, is leader + when(im.hasIsland(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(Mockito.any(),Mockito.eq(uuid))).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.eq(uuid))).thenReturn(uuid); + + // Target has island + when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); + + } + + /** + * Test method for {@link AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteAddTargetHasIslandNoTeam() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + // Has island, no team + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + + // Target has island + when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(true); + + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.team.add.name-has-island", "[name]", "poslovich"); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminTeamAddCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteSuccess() { + AdminTeamAddCommand itl = new AdminTeamAddCommand(ac); + String[] name = {"tastybento", "poslovich"}; + + when(pm.getUUID(Mockito.eq("tastybento"))).thenReturn(uuid); + when(pm.getUUID(Mockito.eq("poslovich"))).thenReturn(notUUID); + + // Has island, no team + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + + // Target has no island + when(im.hasIsland(Mockito.any(), Mockito.eq(notUUID))).thenReturn(false); + + // Island + Island island = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(island); + + // Player name + when(pm.getName(Mockito.eq(uuid))).thenReturn("tastybento"); + when(pm.getName(Mockito.eq(notUUID))).thenReturn("poslovich"); + when(plugin.getPlayers()).thenReturn(pm); + + // Success + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(island).addMember(notUUID); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommandTest.java new file mode 100644 index 0000000..1637006 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamDisbandCommandTest.java @@ -0,0 +1,198 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.*; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminTeamDisbandCommandTest { + + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link AdminTeamDisbandCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminTeamDisbandCommand itl = new AdminTeamDisbandCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + } + + /** + * Test method for {@link AdminTeamDisbandCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminTeamDisbandCommand itl = new AdminTeamDisbandCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link AdminTeamDisbandCommand#execute(User, String, List)}. + */ + @Test + public void testExecutePlayerNotInTeam() { + AdminTeamDisbandCommand itl = new AdminTeamDisbandCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(new HashSet<>()); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-in-team")); + } + + /** + * Test method for {@link AdminTeamDisbandCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteDisbandNotLeader() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(uuid); + when(pm.getName(Mockito.any())).thenReturn("leader"); + + AdminTeamDisbandCommand itl = new AdminTeamDisbandCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.team.disband.use-disband-leader", "[leader]", "leader"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminTeamDisbandCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteSuccess() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + // Leader + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(notUUID); + // Members + Set members = new HashSet<>(); + members.add(uuid); + members.add(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + + AdminTeamDisbandCommand itl = new AdminTeamDisbandCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(im, Mockito.never()).setLeaveTeam(Mockito.any(), Mockito.eq(notUUID)); + Mockito.verify(im).setLeaveTeam(Mockito.any(), Mockito.eq(uuid)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommandTest.java new file mode 100644 index 0000000..bea3bfa --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamKickCommandTest.java @@ -0,0 +1,197 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.admin.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.*; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminTeamKickCommandTest { + + private BSkyBlock plugin; + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link AdminTeamKickCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminTeamKickCommand itl = new AdminTeamKickCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link AdminTeamKickCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminTeamKickCommand itl = new AdminTeamKickCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link AdminTeamKickCommand#execute(User, String, List)}. + */ + @Test + public void testExecutePlayerNotInTeam() { + AdminTeamKickCommand itl = new AdminTeamKickCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(new HashSet<>()); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-in-team")); + } + + /** + * Test method for {@link AdminTeamKickCommand#execute(User, String, List)} . + */ + @Test + public void testExecuteKickLeader() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(notUUID); + + AdminTeamKickCommand itl = new AdminTeamKickCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("commands.admin.team.kick.cannot-kick-leader")); + Mockito.verify(is).showMembers(Mockito.any(), Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminTeamKickCommand#execute(User, String, List)}. + */ + @Test + public void testExecute() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(uuid); + + AdminTeamKickCommand itl = new AdminTeamKickCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(im).removePlayer(Mockito.any(), Mockito.eq(notUUID)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommandTest.java new file mode 100644 index 0000000..3eb1744 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/admin/team/AdminTeamMakeLeaderCommandTest.java @@ -0,0 +1,199 @@ +package us.tastybento.bskyblock.commands.admin.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.*; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.AdminCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class AdminTeamMakeLeaderCommandTest { + + private AdminCommand ac; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ac = mock(AdminCommand.class); + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + World world = mock(World.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.isOwner(Mockito.any(),Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(),Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + } + + + /** + * Test method for {@link AdminTeamMakeLeaderCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteNoTarget() { + AdminTeamMakeLeaderCommand itl = new AdminTeamMakeLeaderCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link AdminTeamMakeLeaderCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + AdminTeamMakeLeaderCommand itl = new AdminTeamMakeLeaderCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link AdminTeamMakeLeaderCommand#execute(User, String, List)}. + */ + @Test + public void testExecutePlayerNotInTeam() { + AdminTeamMakeLeaderCommand itl = new AdminTeamMakeLeaderCommand(ac); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(new HashSet<>()); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-in-team")); + } + + /** + * Test method for {@link AdminTeamMakeLeaderCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteMakeLeaderAlreadyLeader() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(notUUID); + + AdminTeamMakeLeaderCommand itl = new AdminTeamMakeLeaderCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.team.makeleader.already-leader"); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.admin.team.AdminTeamMakeLeaderCommand#execute(User, String, List)}. + */ + @Test + public void testExecuteSuccess() { + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + Island is = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + // Leader + when(im.getTeamLeader(Mockito.any(), Mockito.eq(notUUID))).thenReturn(uuid); + when(pm.getName(Mockito.eq(uuid))).thenReturn("leader"); + // Members + Set members = new HashSet<>(); + members.add(uuid); + members.add(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + + AdminTeamMakeLeaderCommand itl = new AdminTeamMakeLeaderCommand(ac); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + // Add other verifications + Mockito.verify(im).makeLeader(Mockito.any(), Mockito.eq(user), Mockito.eq(notUUID), Mockito.any()); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanCommandTest.java new file mode 100644 index 0000000..dd53d41 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanCommandTest.java @@ -0,0 +1,392 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandBanCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private Island island; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandBanCommand#execute(User, String, List)}. + */ + // Island ban command by itself + + // *** Error conditions *** + // Ban without an island + // Ban as not a team leader + // Ban unknown user + // Ban self + // Ban team mate + // Ban someone you have already banned + // Ban an Op + + // *** Working conditions *** + // Ban offline user + // Ban online user + + @Test + public void testNoArgs() { + IslandBanCommand ibc = new IslandBanCommand(ic); + assertFalse(ibc.execute(user, ibc.getLabel(), new ArrayList<>())); + } + + @Test + public void testNoIsland() { + IslandBanCommand ibc = new IslandBanCommand(ic); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.no-island"); + } + + @Test + public void testNotOwner() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.not-leader"); + } + + @Test + public void testUnknownUser() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(pm.getUUID(Mockito.anyString())).thenReturn(null); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.unknown-player"); + } + + @Test + public void testBanSelf() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.ban.cannot-ban-yourself"); + } + + @Test + public void testBanTeamMate() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID teamMate = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(teamMate); + Set members = new HashSet<>(); + members.add(uuid); + members.add(teamMate); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.ban.cannot-ban-member"); + } + + @Test + public void testBanAlreadyBanned() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID bannedUser = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(bannedUser); + when(island.isBanned(Mockito.eq(bannedUser))).thenReturn(true); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.ban.player-already-banned"); + } + + @Test + public void testBanOp() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID op = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(op); + PowerMockito.mockStatic(User.class); + User opUser = mock(User.class); + when(opUser.isOp()).thenReturn(true); + when(opUser.isPlayer()).thenReturn(true); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(opUser); + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.ban.cannot-ban"); + } + + @Test + public void testBanOfflineUser() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID targetUuid = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(targetUuid); + PowerMockito.mockStatic(User.class); + User targetUser = mock(User.class); + when(targetUser.isOp()).thenReturn(false); + when(targetUser.isPlayer()).thenReturn(true); + when(targetUser.isOnline()).thenReturn(false); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(targetUser); + + // Allow adding to ban list + when(island.addToBanList(Mockito.any())).thenReturn(true); + + assertTrue(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.success"); + Mockito.verify(targetUser).sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, user.getName()); + } + + @Test + public void testBanOnlineUser() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID op = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(op); + PowerMockito.mockStatic(User.class); + User targetUser = mock(User.class); + when(targetUser.isOp()).thenReturn(false); + when(targetUser.isPlayer()).thenReturn(true); + when(targetUser.isOnline()).thenReturn(true); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(targetUser); + // Allow adding to ban list + when(island.addToBanList(Mockito.any())).thenReturn(true); + + assertTrue(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.success"); + Mockito.verify(targetUser).sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, user.getName()); + } + + @Test + public void testCancelledBan() { + IslandBanCommand ibc = new IslandBanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID op = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(op); + PowerMockito.mockStatic(User.class); + User targetUser = mock(User.class); + when(targetUser.isOp()).thenReturn(false); + when(targetUser.isPlayer()).thenReturn(true); + when(targetUser.isOnline()).thenReturn(true); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(targetUser); + // Disallow adding to ban list - even cancelled + when(island.addToBanList(Mockito.any())).thenReturn(false); + + assertFalse(ibc.execute(user, ibc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user, Mockito.never()).sendMessage("general.success"); + Mockito.verify(targetUser, Mockito.never()).sendMessage("commands.island.ban.owner-banned-you", "[owner]", user.getName()); + } + + @Test + public void testTabComplete() { + + String[] names = {"adam", "ben", "cara", "dave", "ed", "frank", "freddy", "george", "harry", "ian", "joe"}; + Map online = new HashMap<>(); + + Set banned = new HashSet<>(); + Set onlinePlayers = new HashSet<>(); + for (int j = 0; j < names.length; j++) { + Player p = mock(Player.class); + UUID uuid = UUID.randomUUID(); + when(p.getUniqueId()).thenReturn(uuid); + when(p.getName()).thenReturn(names[j]); + online.put(uuid, names[j]); + // Ban the first 3 players + if (j < 3) { + banned.add(uuid); + } + onlinePlayers.add(p); + } + + when(island.isBanned(Mockito.any(UUID.class))).thenAnswer(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return banned.contains(invocation.getArgumentAt(0, UUID.class)); + } + + }); + // Create the names + when(pm.getName(Mockito.any(UUID.class))).then(new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return online.getOrDefault(invocation.getArgumentAt(0, UUID.class), "tastybento"); + } + + }); + + // Return a set of online players + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getOnlinePlayers()).then(new Answer>() { + + @Override + public Set answer(InvocationOnMock invocation) throws Throwable { + return onlinePlayers; + } + + }); + + IslandBanCommand ibc = new IslandBanCommand(ic); + // Set up the user + User user = mock(User.class); + when(user.getUniqueId()).thenReturn(UUID.randomUUID()); + Player player = mock(Player.class); + // Player can see every other player except Ian + when(player.canSee(Mockito.any(Player.class))).thenAnswer(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + Player p = invocation.getArgumentAt(0, Player.class); + return !p.getName().equals("ian"); + } + + }); + when(user.getPlayer()).thenReturn(player); + + // Get the tab-complete list with no argument + Optional> result = ibc.tabComplete(user, "", new LinkedList<>()); + assertFalse(result.isPresent()); + + // Get the tab-complete list with one argument + LinkedList args = new LinkedList<>(); + args.add(""); + result = ibc.tabComplete(user, "", args); + assertTrue(result.isPresent()); + List r = result.get().stream().sorted().collect(Collectors.toList()); + // Compare the expected with the actual + String[] expectedNames = {"dave", "ed", "frank", "freddy", "george", "harry", "joe"}; + assertTrue(Arrays.equals(expectedNames, r.toArray())); + + // Get the tab-complete list with one letter argument + args = new LinkedList<>(); + args.add("d"); + result = ibc.tabComplete(user, "", args); + assertTrue(result.isPresent()); + r = result.get().stream().sorted().collect(Collectors.toList()); + // Compare the expected with the actual + String[] expectedName = {"dave"}; + assertTrue(Arrays.equals(expectedName, r.toArray())); + + // Get the tab-complete list with one letter argument + args = new LinkedList<>(); + args.add("fr"); + result = ibc.tabComplete(user, "", args); + assertTrue(result.isPresent()); + r = result.get().stream().sorted().collect(Collectors.toList()); + // Compare the expected with the actual + String[] expected = {"frank", "freddy"}; + assertTrue(Arrays.equals(expected, r.toArray())); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommandTest.java new file mode 100644 index 0000000..5ffa26a --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/IslandBanlistCommandTest.java @@ -0,0 +1,173 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandBanlistCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private Island island; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getTopLabel()).thenReturn("island"); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandBanlistCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testWithArgs() { + IslandBanlistCommand iubc = new IslandBanlistCommand(ic); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + // Verify show help + } + + @Test + public void testNoIsland() { + IslandBanlistCommand iubc = new IslandBanlistCommand(ic); + assertFalse(iubc.execute(user, iubc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("general.errors.no-island"); + } + + @Test + public void testBanlistNooneBanned() { + IslandBanlistCommand iubc = new IslandBanlistCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + assertTrue(iubc.execute(user, iubc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("commands.island.banlist.noone"); + } + + @Test + public void testBanlistBanned() { + IslandBanlistCommand iubc = new IslandBanlistCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Make a ban list + String[] names = {"adam", "ben", "cara", "dave", "ed", "frank", "freddy", "george", "harry", "ian", "joe"}; + Set banned = new HashSet<>(); + Map uuidToName = new HashMap<>(); + for (int j = 0; j < names.length; j++) { + UUID uuid = UUID.randomUUID(); + banned.add(uuid); + uuidToName.put(uuid, names[j]); + } + when(island.getBanned()).thenReturn(banned); + // Respond to name queries + when(pm.getName(Mockito.any(UUID.class))).then(new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return uuidToName.getOrDefault(invocation.getArgumentAt(0, UUID.class), "tastybento"); + } + + }); + assertTrue(iubc.execute(user, iubc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("commands.island.banlist.the-following"); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/IslandGoCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/IslandGoCommandTest.java new file mode 100644 index 0000000..b8867ed --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/IslandGoCommandTest.java @@ -0,0 +1,198 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * Test for island go command + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, Util.class}) +public class IslandGoCommandTest { + private IslandCommand ic; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private Island island; + private Player player; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getTopLabel()).thenReturn("island"); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + // Number of homes + PowerMockito.mockStatic(Util.class); + // 1 home for now + when(Util.getPermValue(Mockito.any(Player.class), Mockito.anyString(), Mockito.anyInt())).thenReturn(1); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoArgsNoIsland() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(null); + IslandGoCommand igc = new IslandGoCommand(ic); + assertFalse(igc.execute(user, igc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("general.errors.no-island"); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoArgs() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + IslandGoCommand igc = new IslandGoCommand(ic); + assertTrue(igc.execute(user, igc.getLabel(), new ArrayList<>())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoArgsMultipleHomes() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(Util.getPermValue(Mockito.any(Player.class), Mockito.anyString(), Mockito.anyInt())).thenReturn(3); + IslandGoCommand igc = new IslandGoCommand(ic); + assertTrue(igc.execute(user, igc.getLabel(), new ArrayList<>())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteArgs1MultipleHomes() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(Util.getPermValue(Mockito.any(Player.class), Mockito.anyString(), Mockito.anyInt())).thenReturn(3); + IslandGoCommand igc = new IslandGoCommand(ic); + List args = new ArrayList<>(); + args.add("1"); + assertTrue(igc.execute(user, igc.getLabel(), args)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteArgs2MultipleHomes() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(Util.getPermValue(Mockito.any(Player.class), Mockito.anyString(), Mockito.anyInt())).thenReturn(3); + IslandGoCommand igc = new IslandGoCommand(ic); + List args = new ArrayList<>(); + args.add("2"); + assertTrue(igc.execute(user, igc.getLabel(), args)); + Mockito.verify(user).sendMessage("commands.island.go.tip", TextVariables.LABEL, "island"); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandGoCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteArgsJunkMultipleHomes() { + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(Util.getPermValue(Mockito.any(Player.class), Mockito.anyString(), Mockito.anyInt())).thenReturn(3); + IslandGoCommand igc = new IslandGoCommand(ic); + List args = new ArrayList<>(); + args.add("sdfsdf"); + assertTrue(igc.execute(user, igc.getLabel(), args)); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/IslandResetCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/IslandResetCommandTest.java new file mode 100644 index 0000000..276d2eb --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/IslandResetCommandTest.java @@ -0,0 +1,322 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.managers.island.NewIsland; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, NewIsland.class }) +public class IslandResetCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private Settings s; + private IslandsManager im; + private PlayersManager pm; + private World world; + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // User, sometime use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getTopLabel()).thenReturn("island"); + // World + world = mock(World.class); + when(ic.getWorld()).thenReturn(world); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + BukkitTask task = mock(BukkitTask.class); + when(sch.runTaskLater(Mockito.any(), Mockito.any(Runnable.class), Mockito.any(Long.class))).thenReturn(task); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + + // IWM friendly name + iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getResetLimit(Mockito.any())).thenReturn(3); + + + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandResetCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + * @throws IOException + */ + @Test + public void testNoIsland() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Test the reset command + // Does not have island + assertFalse(irc.execute(user, irc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("general.errors.no-island"); + } + + @Test + public void testNotLeader() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + assertFalse(irc.execute(user, irc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("general.errors.not-leader"); + } + + @Test + public void testHasTeam() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + assertFalse(irc.execute(user, irc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("commands.island.reset.must-remove-members"); + } + + @Test + public void testNoResetsLeft() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now has no team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + + // Block based on no resets left + when(pm.getResets(Mockito.eq(world),Mockito.eq(uuid))).thenReturn(3); + + assertFalse(irc.execute(user, irc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("commands.island.reset.none-left"); + } + + @Test + public void testNoConfirmationRequired() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now has no team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Give the user some resets + when(pm.getResets(Mockito.eq(world), Mockito.eq(uuid))).thenReturn(1); + // Set so no confirmation required + when(s.isResetConfirmation()).thenReturn(false); + + // Old island mock + Island oldIsland = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(oldIsland); + + // Mock up NewIsland builder + NewIsland.Builder builder = mock(NewIsland.Builder.class); + when(builder.player(Mockito.any())).thenReturn(builder); + when(builder.oldIsland(Mockito.any())).thenReturn(builder); + when(builder.reason(Mockito.any())).thenReturn(builder); + when(builder.build()).thenReturn(mock(Island.class)); + PowerMockito.mockStatic(NewIsland.class); + when(NewIsland.builder()).thenReturn(builder); + + // Reset command, no confirmation required + assertTrue(irc.execute(user, irc.getLabel(), new ArrayList<>())); + // Verify that build new island was called and the number of resets left shown + Mockito.verify(builder).build(); + Mockito.verify(user).sendMessage("commands.island.reset.resets-left", "[number]", "2"); + } + + @Test + public void testUnlimitedResets() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now has no team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Give the user some resets + when(pm.getResets(Mockito.eq(world), Mockito.eq(uuid))).thenReturn(1); + // Set so no confirmation required + when(s.isResetConfirmation()).thenReturn(false); + + // Old island mock + Island oldIsland = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(oldIsland); + + // Mock up NewIsland builder + NewIsland.Builder builder = mock(NewIsland.Builder.class); + when(builder.player(Mockito.any())).thenReturn(builder); + when(builder.oldIsland(Mockito.any())).thenReturn(builder); + when(builder.reason(Mockito.any())).thenReturn(builder); + when(builder.build()).thenReturn(mock(Island.class)); + PowerMockito.mockStatic(NewIsland.class); + when(NewIsland.builder()).thenReturn(builder); + // Test with unlimited resets + when(iwm.getResetLimit(Mockito.any())).thenReturn(-1); + + // Reset + assertTrue(irc.execute(user, irc.getLabel(), new ArrayList<>())); + // Verify that build new island was called and the number of resets left shown + Mockito.verify(builder).build(); + // This should not be shown + Mockito.verify(user, Mockito.never()).sendMessage("commands.island.reset.resets-left", "[number]", "1"); + } + + @Test + public void testConfirmationRequired() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now has no team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Give the user some resets + when(pm.getResets(Mockito.eq(world), Mockito.eq(uuid))).thenReturn(1); + // Set so no confirmation required + when(s.isResetConfirmation()).thenReturn(false); + + // Old island mock + Island oldIsland = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(oldIsland); + + // Mock up NewIsland builder + NewIsland.Builder builder = mock(NewIsland.Builder.class); + when(builder.player(Mockito.any())).thenReturn(builder); + when(builder.oldIsland(Mockito.any())).thenReturn(builder); + when(builder.reason(Mockito.any())).thenReturn(builder); + when(builder.build()).thenReturn(mock(Island.class)); + PowerMockito.mockStatic(NewIsland.class); + when(NewIsland.builder()).thenReturn(builder); + + // Require confirmation + when(s.isResetConfirmation()).thenReturn(true); + when(s.getConfirmationTime()).thenReturn(20); + + // Reset + assertTrue(irc.execute(user, irc.getLabel(), new ArrayList<>())); + // Check for message + Mockito.verify(user).sendMessage("general.confirm", "[seconds]", String.valueOf(s.getConfirmationTime())); + + // Send command again to confirm + assertTrue(irc.execute(user, irc.getLabel(), new ArrayList<>())); + + } + + @Test + public void testNewIslandError() throws IOException { + IslandResetCommand irc = new IslandResetCommand(ic); + // Now has island, but is not the leader + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now is owner, but still has team + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Now has no team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Give the user some resets + when(pm.getResets(Mockito.eq(world), Mockito.eq(uuid))).thenReturn(1); + // Set so no confirmation required + when(s.isResetConfirmation()).thenReturn(false); + + // Old island mock + Island oldIsland = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(oldIsland); + + // Mock up NewIsland builder + NewIsland.Builder builder = mock(NewIsland.Builder.class); + when(builder.player(Mockito.any())).thenReturn(builder); + when(builder.oldIsland(Mockito.any())).thenReturn(builder); + when(builder.reason(Mockito.any())).thenReturn(builder); + when(builder.build()).thenThrow(new IOException()); + PowerMockito.mockStatic(NewIsland.class); + when(NewIsland.builder()).thenReturn(builder); + + // Require no confirmation + when(s.isResetConfirmation()).thenReturn(false); + + // Reset + assertFalse(irc.execute(user, irc.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage("commands.island.create.unable-create-island"); + + + } +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommandTest.java new file mode 100644 index 0000000..d4064d5 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/IslandUnbanCommandTest.java @@ -0,0 +1,253 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.localization.TextVariables; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandUnbanCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private Island island; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + } + + /** + * Test method for {@link us.tastybento.bskyblock.commands.island.IslandUnbanCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + // Island ban command by itself + + // *** Error conditions *** + // Unban without an island + // Unban as not a team leader + // Unban unknown user + // Unban self + // Unban someone not banned + + // *** Working conditions *** + // Unban user + + @Test + public void testNoArgs() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + assertFalse(iubc.execute(user, iubc.getLabel(), new ArrayList<>())); + } + + @Test + public void testNoIsland() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.no-island"); + } + + @Test + public void testNotOwner() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.not-leader"); + } + + @Test + public void testUnknownUser() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(pm.getUUID(Mockito.anyString())).thenReturn(null); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.errors.unknown-player"); + } + + @Test + public void testBanSelf() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.unban.cannot-unban-yourself"); + } + + @Test + public void testBanNotBanned() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID bannedUser = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(bannedUser); + when(island.isBanned(Mockito.eq(bannedUser))).thenReturn(false); + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("commands.island.unban.player-not-banned"); + } + + @Test + public void testUnbanUser() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID targetUUID = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(targetUUID); + PowerMockito.mockStatic(User.class); + User targetUser = mock(User.class); + when(targetUser.isOp()).thenReturn(false); + when(targetUser.isPlayer()).thenReturn(true); + when(targetUser.isOnline()).thenReturn(false); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(targetUser); + // Mark as banned + when(island.isBanned(Mockito.eq(targetUUID))).thenReturn(true); + + // Allow removing from ban list + when(island.removeFromBanList(Mockito.any())).thenReturn(true); + + assertTrue(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user).sendMessage("general.success"); + Mockito.verify(targetUser).sendMessage("commands.island.unban.you-are-unbanned", TextVariables.NAME, user.getName()); + } + + @Test + public void testCancelledUnban() { + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + UUID targetUUID = UUID.randomUUID(); + when(pm.getUUID(Mockito.anyString())).thenReturn(targetUUID); + PowerMockito.mockStatic(User.class); + User targetUser = mock(User.class); + when(targetUser.isOp()).thenReturn(false); + when(targetUser.isPlayer()).thenReturn(true); + when(targetUser.isOnline()).thenReturn(false); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(targetUser); + // Mark as banned + when(island.isBanned(Mockito.eq(targetUUID))).thenReturn(true); + + // Allow removing from ban list + when(island.removeFromBanList(Mockito.any())).thenReturn(false); + + assertFalse(iubc.execute(user, iubc.getLabel(), Arrays.asList("bill"))); + Mockito.verify(user, Mockito.never()).sendMessage("general.success"); + Mockito.verify(targetUser, Mockito.never()).sendMessage("commands.island.unban.you-are-unbanned", "[owner]", user.getName()); + } + + @Test + public void testTabComplete() { + Set banned = new HashSet<>(); + // Add ten people to the banned list + for (int i = 0; i < 10; i++) { + banned.add(UUID.randomUUID()); + } + when(island.getBanned()).thenReturn(banned); + when(pm.getName(Mockito.any())).thenReturn("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); + IslandUnbanCommand iubc = new IslandUnbanCommand(ic); + User user = mock(User.class); + when(user.getUniqueId()).thenReturn(UUID.randomUUID()); + Optional> result = iubc.tabComplete(user, "", new LinkedList<>()); + assertTrue(result.isPresent()); + String[] names = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}; + assertTrue(Arrays.equals(names, result.get().toArray())); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommandTest.java new file mode 100644 index 0000000..bba1e7e --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamInviteCommandTest.java @@ -0,0 +1,213 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island.team; + +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandTeamInviteCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + // Player Manager + pm = mock(PlayersManager.class); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + } + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoIsland() { + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.no-island")); + } + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNotTeamLeader() { + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(notUUID); + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-leader")); + } + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTarget() { + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteOfflinePlayer() { + PowerMockito.mockStatic(User.class); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(user); + when(user.isOnline()).thenReturn(false); + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(uuid); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.offline-player")); + } + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteSamePlayer() { + PowerMockito.mockStatic(User.class); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(user); + when(user.isOnline()).thenReturn(true); + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(uuid); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.invite.cannot-invite-self")); + } + + + /** + * Test method for {@link IslandTeamInviteCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteDifferentPlayerInTeam() { + PowerMockito.mockStatic(User.class); + when(User.getInstance(Mockito.any(UUID.class))).thenReturn(user); + when(user.isOnline()).thenReturn(true); + IslandTeamInviteCommand itl = new IslandTeamInviteCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.invite.already-on-team")); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommandTest.java new file mode 100644 index 0000000..c47da84 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamKickCommandTest.java @@ -0,0 +1,268 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandTeamKickCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private Settings s; + private IslandsManager im; + private PlayersManager pm; + private UUID notUUID; + private IslandWorldManager iwm; + private Player player; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + // Has team + pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // IWM friendly name + iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTeam() { + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.no-team")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNotTeamLeader() { + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(notUUID); + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-leader")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTarget() { + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Show help + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteUnknownPlayer() { + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(null); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.unknown-player")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteSamePlayer() { + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(uuid); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.kick.cannot-kick")); + } + + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteDifferentPlayerNotInTeam() { + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(new HashSet<>()); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.not-in-team")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoConfirmation() { + when(s.isKickConfirmation()).thenReturn(false); + + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(im).removePlayer(Mockito.any(), Mockito.eq(notUUID)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteWithConfirmation() { + when(s.isKickConfirmation()).thenReturn(true); + + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + // Confirmation required + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.kick.type-again")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteTestResets() { + when(s.isKickConfirmation()).thenReturn(false); + + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + + // Require resets + when(iwm.isOnLeaveResetEnderChest(Mockito.any())).thenReturn(true); + Inventory enderChest = mock(Inventory.class); + when(player.getEnderChest()).thenReturn(enderChest); + when(iwm.isOnLeaveResetInventory(Mockito.any())).thenReturn(true); + PlayerInventory inv = mock(PlayerInventory.class); + when(player.getInventory()).thenReturn(inv); + when(iwm.isOnLeaveResetMoney(Mockito.any())).thenReturn(true); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertTrue(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(im).removePlayer(Mockito.any(), Mockito.eq(notUUID)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + + Mockito.verify(enderChest).clear(); + Mockito.verify(inv).clear(); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommandTest.java b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommandTest.java new file mode 100644 index 0000000..b5df3c9 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/commands/island/team/IslandTeamLeaveCommandTest.java @@ -0,0 +1,193 @@ +/** + * + */ +package us.tastybento.bskyblock.commands.island.team; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.commands.IslandCommand; +import us.tastybento.bskyblock.managers.CommandsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class IslandTeamLeaveCommandTest { + + private IslandCommand ic; + private UUID uuid; + private User user; + private Settings s; + private IslandsManager im; + private IslandWorldManager iwm; + private Player player; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + + // Parent command has no aliases + ic = mock(IslandCommand.class); + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(plugin.getIslands()).thenReturn(im); + + // Has team + PlayersManager pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + } + + /** + * Test method for {@link IslandTeamLeaveCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoTeam() { + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + IslandTeamLeaveCommand itl = new IslandTeamLeaveCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("general.errors.no-team")); + } + + /** + * Test method for {@link IslandTeamLeaveCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteInTeamLeader() { + IslandTeamLeaveCommand itl = new IslandTeamLeaveCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(user).sendMessage(Mockito.eq("commands.island.team.leave.cannot-leave")); + } + + /** + * Test method for {@link IslandTeamLeaveCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteNoConfirmation() { + when(s.isLeaveConfirmation()).thenReturn(false); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Add a team leader - null + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(null); + + IslandTeamLeaveCommand itl = new IslandTeamLeaveCommand(ic); + assertTrue(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(im).setLeaveTeam(Mockito.any(), Mockito.eq(uuid)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + } + + /** + * Test method for {@link IslandTeamLeaveCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteWithConfirmation() { + when(s.isLeaveConfirmation()).thenReturn(true); + // 3 second timeout + when(s.getLeaveWait()).thenReturn(3L); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Add a team leader - null + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(null); + + IslandTeamLeaveCommand itl = new IslandTeamLeaveCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), new ArrayList<>())); + // Confirmation required + Mockito.verify(user).sendMessage(Mockito.eq("general.confirm"), Mockito.eq("[seconds]"), Mockito.eq("0")); + } + + /** + * Test method for {@link IslandTeamLeaveCommand#execute(us.tastybento.bskyblock.api.user.User, java.util.List)}. + */ + @Test + public void testExecuteTestResets() { + when(s.isLeaveConfirmation()).thenReturn(false); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + // Add a team leader - null + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(null); + + // Require resets + when(iwm.isOnLeaveResetEnderChest(Mockito.any())).thenReturn(true); + Inventory enderChest = mock(Inventory.class); + when(player.getEnderChest()).thenReturn(enderChest); + when(iwm.isOnLeaveResetInventory(Mockito.any())).thenReturn(true); + PlayerInventory inv = mock(PlayerInventory.class); + when(player.getInventory()).thenReturn(inv); + when(iwm.isOnLeaveResetMoney(Mockito.any())).thenReturn(true); + + IslandTeamLeaveCommand itl = new IslandTeamLeaveCommand(ic); + assertTrue(itl.execute(user, itl.getLabel(), new ArrayList<>())); + Mockito.verify(im).setLeaveTeam(Mockito.any(), Mockito.eq(uuid)); + Mockito.verify(user).sendMessage(Mockito.eq("general.success")); + + Mockito.verify(enderChest).clear(); + Mockito.verify(inv).clear(); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java b/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java new file mode 100644 index 0000000..2e507c8 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java @@ -0,0 +1,157 @@ +package us.tastybento.bskyblock.database.mysql; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.plugin.PluginManager; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.database.objects.Players; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { BSkyBlock.class, Util.class }) +public class MySQLDatabaseHandlerTest { + + private static MySQLDatabaseHandler handler; + private static Island instance; + private static String UNIQUE_ID = "xyz"; + private static MySQLDatabaseConnecter dbConn; + private static World world; + @Mock + static BSkyBlock plugin = mock(BSkyBlock.class); + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Settings settings = mock(Settings.class); + + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDeathsMax()).thenReturn(10); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + dbConn = mock(MySQLDatabaseConnecter.class); + Connection connection = mock(Connection.class); + when(dbConn.createConnection()).thenReturn(connection); + PreparedStatement ps = mock(PreparedStatement.class); + when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps); + Statement statement = mock(Statement.class); + when(connection.createStatement()).thenReturn(statement); + ResultSet rs = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(rs); + when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); + instance = new Island(); + instance.setUniqueId(UNIQUE_ID); + handler = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); + + PowerMockito.mockStatic(Util.class); + when(Util.sameWorld(Mockito.any(), Mockito.any())).thenReturn(true); + + } + + @Test + public void testSaveObject() { + handler.saveObject(instance); + BSkyBlock plugin = mock(BSkyBlock.class); + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDeathsMax()).thenReturn(10); + Players players = new Players(); + players.setUniqueId(UUID.randomUUID().toString()); + players.setDeaths(23); + Location location = mock(Location.class); + Mockito.when(location.getWorld()).thenReturn(world); + Mockito.when(location.getBlockX()).thenReturn(0); + Mockito.when(location.getBlockY()).thenReturn(0); + Mockito.when(location.getBlockZ()).thenReturn(0); + + players.setHomeLocation(location); + players.setHomeLocation(location, 1); + players.setHomeLocation(location, 2); + Map map = new HashMap<>(); + map.put(location, 324L); + players.setKickedList(map); + players.setLocale("sdfsd"); + players.setPlayerName("name"); + players.setPlayerUUID(UUID.randomUUID()); + players.setResets(world, 3); + + + MySQLDatabaseHandler h = new MySQLDatabaseHandler<>(plugin, Players.class, dbConn); + h.saveObject(players); + + Island island = new Island(); + island.setUniqueId(UNIQUE_ID); + island.setCenter(location); + Map flags = new HashMap<>(); + for (Flag fl : Flags.values()) { + flags.put(fl, 100); + } + island.setFlags(flags); + island.setLevelHandicap(10); + Map members = new HashMap<>(); + for (int i = 0; i < 10; i++) { + members.put(UUID.randomUUID(), i); + } + island.setMembers(members); + island.setName("ytasdgfsdfg"); + island.setOwner(UUID.randomUUID()); + island.setProtectionRange(100); + island.setPurgeProtected(true); + island.setRange(100); + island.setSpawn(true); + island.setSpawnPoint(Environment.NORMAL, location); + island.setWorld(world); + + MySQLDatabaseHandler ih = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); + ih.saveObject(island); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/database/objects/NamesTest.java b/src/test/java/us/tastybento/bskyblock/database/objects/NamesTest.java new file mode 100644 index 0000000..1e275c0 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/database/objects/NamesTest.java @@ -0,0 +1,61 @@ +/** + * + */ +package us.tastybento.bskyblock.database.objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.UUID; + +import org.junit.Test; + +/** + * @author tastybento + * + */ +public class NamesTest { + + /** + * Test method for {@link us.tastybento.bskyblock.database.objects.Names#Names()}. + */ + @Test + public void testNames() { + assertNotNull(new Names()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.database.objects.Names#Names(java.lang.String, java.util.UUID)}. + */ + @Test + public void testNamesStringUUID() { + assertNotNull(new Names("name", UUID.randomUUID())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.database.objects.Names#getUniqueId()}. + */ + @Test + public void testGetUniqueId() { + Names name = new Names("name", UUID.randomUUID()); + assertEquals("name", name.getUniqueId()); + name.setUniqueId("random"); + assertEquals("random", name.getUniqueId()); + } + /** + * Test method for {@link us.tastybento.bskyblock.database.objects.Names#getUuid()}. + */ + @Test + public void testGetUuid() { + + Names name = new Names(); + assertNull(name.getUuid()); + UUID t = UUID.randomUUID(); + name.setUuid(t); + assertEquals(t, name.getUuid()); + } + + + +} diff --git a/src/test/java/us/tastybento/bskyblock/database/objects/PlayersTest.java b/src/test/java/us/tastybento/bskyblock/database/objects/PlayersTest.java new file mode 100644 index 0000000..2a2f713 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/database/objects/PlayersTest.java @@ -0,0 +1,113 @@ +package us.tastybento.bskyblock.database.objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class}) +public class PlayersTest { + + private Settings s; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + s = mock(Settings.class); + when(s.getResetLimit()).thenReturn(3); + when(s.getDeathsMax()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + Server server = mock(Server.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + } + + private BSkyBlock plugin; + + @Test + public void testPlayersBSkyBlockUUID() { + assertNotNull(new Players(plugin, UUID.randomUUID())); + } + + @Test + public void testSetHomeLocationLocation() { + Players p = new Players(plugin, UUID.randomUUID()); + Location l = mock(Location.class); + World w = mock(World.class); + when(w.getName()).thenReturn("world"); + when(l.getWorld()).thenReturn(w); + p.setHomeLocation(l, 5); + assertEquals(l, p.getHomeLocation(w, 5)); + assertNotEquals(l, p.getHomeLocation(w, 0)); + p.clearHomeLocations(w); + assertTrue(p.getHomeLocations(w).isEmpty()); + } + + @Test + public void testDeaths() { + Players p = new Players(plugin, UUID.randomUUID()); + assertTrue(p.getDeaths() == 0); + p.addDeath(); + assertTrue(p.getDeaths() == 1); + p.addDeath(); + assertTrue(p.getDeaths() == 2); + p.addDeath(); + assertTrue(p.getDeaths() == 3); + p.addDeath(); + assertTrue(p.getDeaths() == 3); + p.addDeath(); + assertTrue(p.getDeaths() == 3); + p.setDeaths(10); + assertTrue(p.getDeaths() == 3); + p.setDeaths(0); + assertTrue(p.getDeaths() == 0); + } + + @Test + public void testInviteCoolDownTime() throws InterruptedException { + when(s.getInviteWait()).thenReturn(1); + Players p = new Players(plugin, UUID.randomUUID()); + // Check a null location + assertTrue(p.getInviteCoolDownTime(null) == 0); + // Real location + Location l = mock(Location.class); + // Should be no cooldown + assertTrue(p.getInviteCoolDownTime(l) == 0); + // Start the timer + p.startInviteCoolDownTimer(l); + // More than 0 cooldown + assertTrue(p.getInviteCoolDownTime(l) > 0); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/BannedVisitorCommandsTest.java b/src/test/java/us/tastybento/bskyblock/listeners/BannedVisitorCommandsTest.java new file mode 100644 index 0000000..ec2a205 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/BannedVisitorCommandsTest.java @@ -0,0 +1,219 @@ +package us.tastybento.bskyblock.listeners; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, Bukkit.class }) +public class BannedVisitorCommandsTest { + + private IslandWorldManager iwm; + private Player player; + private BSkyBlock plugin; + private IslandsManager im; + + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(new ArrayList<>()); + when(plugin.getIWM()).thenReturn(iwm); + + // Player + player = mock(Player.class); + when(player.isOp()).thenReturn(false); + when(player.hasPermission(Mockito.anyString())).thenReturn(false); + when(player.getWorld()).thenReturn(mock(World.class)); + when(player.getLocation()).thenReturn(mock(Location.class)); + User.getInstance(player); + Server server = mock(Server.class); + Set onlinePlayers = new HashSet<>(); + for (int j = 0; j < 10; j++) { + Player p = mock(Player.class); + UUID uuid = UUID.randomUUID(); + when(p.getUniqueId()).thenReturn(uuid); + when(p.getName()).thenReturn(uuid.toString()); + onlinePlayers.add(p); + } + when(server.getOnlinePlayers()).then(new Answer>() { + + @Override + public Set answer(InvocationOnMock invocation) throws Throwable { + return onlinePlayers; + } + + }); + when(player.getServer()).thenReturn(server); + + // Island manager + im = mock(IslandsManager.class); + // Default not on island, so is a visitor + when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Notifier + Notifier notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + + + } + + @Test + public void testBannedVisitorCommands() { + assertNotNull(new BannedVisitorCommands(plugin)); + } + + @Test + public void testInstantReturn() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/blah"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + + // Not in world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + + // In world + when(iwm.inWorld(Mockito.any())).thenReturn(true); + // Op + when(player.isOp()).thenReturn(true); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + + // Not op + when(player.isOp()).thenReturn(false); + // Has bypass perm + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + + // Does not have perm + when(player.hasPermission(Mockito.anyString())).thenReturn(false); + // Not a visitor + when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testEmptyBannedCommands() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/blah"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testBannedCommands() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/blah"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + List banned = new ArrayList<>(); + banned.add("banned_command"); + banned.add("another_banned_command"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(banned); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + Mockito.verify(iwm).getVisitorBannedCommands(Mockito.any()); + } + + @Test + public void testBannedCommandsWithExtra() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/blah with extra stuff"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + List banned = new ArrayList<>(); + banned.add("banned_command"); + banned.add("another_banned_command"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(banned); + bvc.onVisitorCommand(e); + assertFalse(e.isCancelled()); + Mockito.verify(iwm).getVisitorBannedCommands(Mockito.any()); + } + + @Test + public void testBannedCommandsWithBannedCommand() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/banned_command"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + List banned = new ArrayList<>(); + banned.add("banned_command"); + banned.add("another_banned_command"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(banned); + bvc.onVisitorCommand(e); + Mockito.verify(iwm).getVisitorBannedCommands(Mockito.any()); + assertTrue(e.isCancelled()); + + } + + @Test + public void testBannedCommandsWithBannedCommandWithExtra() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/banned_command with extra stuff"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + List banned = new ArrayList<>(); + banned.add("banned_command"); + banned.add("another_banned_command"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(banned); + bvc.onVisitorCommand(e); + Mockito.verify(iwm).getVisitorBannedCommands(Mockito.any()); + assertTrue(e.isCancelled()); + + } + + @Test + public void testAnotherBannedCommandsWithBannedCommandWithExtra() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/another_banned_command with extra stuff"); + BannedVisitorCommands bvc = new BannedVisitorCommands(plugin); + List banned = new ArrayList<>(); + banned.add("banned_command"); + banned.add("another_banned_command"); + when(iwm.getVisitorBannedCommands(Mockito.any())).thenReturn(banned); + bvc.onVisitorCommand(e); + Mockito.verify(iwm).getVisitorBannedCommands(Mockito.any()); + assertTrue(e.isCancelled()); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/BlockEndDragonTest.java b/src/test/java/us/tastybento/bskyblock/listeners/BlockEndDragonTest.java new file mode 100644 index 0000000..82a26d8 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/BlockEndDragonTest.java @@ -0,0 +1,96 @@ +package us.tastybento.bskyblock.listeners; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class, Util.class }) +public class BlockEndDragonTest { + + @Test + public void testBlockEndDragon() { + BSkyBlock plugin = mock(BSkyBlock.class); + assertNotNull(new BlockEndDragon(plugin)); + } + + @Test + public void testOnDragonSpawnWrongEntityOkayToSpawn() { + LivingEntity le = mock(LivingEntity.class); + when(le.getType()).thenReturn(EntityType.AREA_EFFECT_CLOUD); + CreatureSpawnEvent event = new CreatureSpawnEvent(le, null); + BSkyBlock plugin = mock(BSkyBlock.class); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + // allow dragon spawn + when(iwm.isDragonSpawn(Mockito.any())).thenReturn(true); + BlockEndDragon bed = new BlockEndDragon(plugin); + assertTrue(bed.onDragonSpawn(event)); + assertFalse(event.isCancelled()); + } + + @Test + public void testOnDragonSpawnWrongEntityNoDragonSpawn() { + LivingEntity le = mock(LivingEntity.class); + when(le.getType()).thenReturn(EntityType.AREA_EFFECT_CLOUD); + CreatureSpawnEvent event = new CreatureSpawnEvent(le, null); + BSkyBlock plugin = mock(BSkyBlock.class); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + // allow dragon spawn + when(iwm.isDragonSpawn(Mockito.any())).thenReturn(false); + BlockEndDragon bed = new BlockEndDragon(plugin); + assertTrue(bed.onDragonSpawn(event)); + assertFalse(event.isCancelled()); + } + + @Test + public void testOnDragonSpawnRightEntityOkayToSpawn() { + LivingEntity le = mock(LivingEntity.class); + when(le.getType()).thenReturn(EntityType.ENDER_DRAGON); + CreatureSpawnEvent event = new CreatureSpawnEvent(le, null); + BSkyBlock plugin = mock(BSkyBlock.class); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + // allow dragon spawn + when(iwm.isDragonSpawn(Mockito.any())).thenReturn(true); + BlockEndDragon bed = new BlockEndDragon(plugin); + assertTrue(bed.onDragonSpawn(event)); + assertFalse(event.isCancelled()); + } + + @Test + public void testOnDragonSpawnRightEntityNotOkayToSpawn() { + LivingEntity le = mock(LivingEntity.class); + when(le.getType()).thenReturn(EntityType.ENDER_DRAGON); + CreatureSpawnEvent event = new CreatureSpawnEvent(le, null); + BSkyBlock plugin = mock(BSkyBlock.class); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + // allow dragon spawn + when(iwm.isDragonSpawn(Mockito.any())).thenReturn(false); + BlockEndDragon bed = new BlockEndDragon(plugin); + assertFalse(bed.onDragonSpawn(event)); + Mockito.verify(le).remove(); + Mockito.verify(le).setHealth(0); + assertTrue(event.isCancelled()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/NetherPortalsTest.java b/src/test/java/us/tastybento/bskyblock/listeners/NetherPortalsTest.java new file mode 100644 index 0000000..46e4f84 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/NetherPortalsTest.java @@ -0,0 +1,744 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.event.world.StructureGrowEvent; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class, Util.class }) +public class NetherPortalsTest { + + private BSkyBlock plugin; + private IslandsManager im; + private PlayersManager pm; + private IslandWorldManager iwm; + private World world; + private World nether; + private World end; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // island world mgr + iwm = mock(IslandWorldManager.class); + world = mock(World.class); + when(world.getEnvironment()).thenReturn(Environment.NORMAL); + nether = mock(World.class); + when(nether.getEnvironment()).thenReturn(Environment.NETHER); + when(nether.getSpawnLocation()).thenReturn(mock(Location.class)); + end = mock(World.class); + when(end.getEnvironment()).thenReturn(Environment.THE_END); + when(iwm.getEndWorld(Mockito.any())).thenReturn(end); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getNetherWorld(Mockito.any())).thenReturn(nether); + when(iwm.inWorld(any())).thenReturn(true); + when(iwm.getNetherSpawnRadius(Mockito.any())).thenReturn(100); + when(plugin.getIWM()).thenReturn(iwm); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // Settings + Settings s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Set up spawn + Location netherSpawn = mock(Location.class); + when(netherSpawn.toVector()).thenReturn(new Vector(0,0,0)); + when(nether.getSpawnLocation()).thenReturn(netherSpawn); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + User user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + UUID notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + Optional optionalIsland = Optional.empty(); + when(im.getIslandAt(Mockito.any())).thenReturn(optionalIsland); + when(plugin.getIslands()).thenReturn(im); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // Normally in world + Util.setPlugin(plugin); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#NetherPortals(us.tastybento.bskyblock.BSkyBlock)}. + */ + @Test + public void testNetherPortals() { + assertNotNull(new NetherPortals(plugin)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. + */ + @Test + public void testOnBlockBreakNoAction() { + NetherPortals np = new NetherPortals(plugin); + Block block = mock(Block.class); + Player player = mock(Player.class); + // Ops can do anything + when(player.isOp()).thenReturn(true); + BlockBreakEvent e = new BlockBreakEvent(block, player); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + // not op, but not in right world + when(player.isOp()).thenReturn(false); + World w = mock(World.class); + when(w.getEnvironment()).thenReturn(Environment.NORMAL); + when(player.getWorld()).thenReturn(w); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + // not op, island world + when(player.getWorld()).thenReturn(world); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + // Nether, but not standard nether + when(player.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(Mockito.any())).thenReturn(true); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + // End, but not standard end + when(player.getWorld()).thenReturn(nether); + when(iwm.isEndIslands(Mockito.any())).thenReturn(true); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. + */ + @Test + public void testOnBlockBreakActionAwayFromSpawn() { + NetherPortals np = new NetherPortals(plugin); + Block block = mock(Block.class); + Location far = mock(Location.class); + when(far.toVector()).thenReturn(new Vector(10000, 56, 2000)); + when(far.getWorld()).thenReturn(nether); + when(block.getLocation()).thenReturn(far); + Player player = mock(Player.class); + // Standard nether + when(player.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + + BlockBreakEvent e = new BlockBreakEvent(block, player); + np.onBlockBreak(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. + */ + @Test + public void testOnBlockBreakActionAtSpawn() { + NetherPortals np = new NetherPortals(plugin); + Block block = mock(Block.class); + Location near = mock(Location.class); + when(near.toVector()).thenReturn(new Vector(0, 56, 0)); + when(near.getWorld()).thenReturn(nether); + when(block.getLocation()).thenReturn(near); + Player player = mock(Player.class); + // Standard nether + when(player.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + + BlockBreakEvent e = new BlockBreakEvent(block, player); + np.onBlockBreak(e); + Mockito.verify(block).getLocation(); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onBucketEmpty(org.bukkit.event.player.PlayerBucketEmptyEvent)}. + */ + @Test + public void testOnBucketEmpty() { + NetherPortals np = new NetherPortals(plugin); + Block block = mock(Block.class); + Location near = mock(Location.class); + when(near.toVector()).thenReturn(new Vector(0, 56, 0)); + when(near.getWorld()).thenReturn(nether); + when(block.getLocation()).thenReturn(near); + Player player = mock(Player.class); + // Standard nether + when(player.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + + PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, block, null, null, null); + np.onBucketEmpty(e); + Mockito.verify(block).getLocation(); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnEndIslandPortalNotEnd() { + NetherPortals np = new NetherPortals(plugin); + // Wrong cause + PlayerPortalEvent e = new PlayerPortalEvent(null, null, null, null, TeleportCause.CHORUS_FRUIT); + np.onEndIslandPortal(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnEndIslandPortalWrongWorld() { + NetherPortals np = new NetherPortals(plugin); + Location loc = mock(Location.class); + + // Right cause, end exists, wrong world + when(loc.getWorld()).thenReturn(mock(World.class)); + when(iwm.inWorld(any())).thenReturn(false); + PlayerPortalEvent e = new PlayerPortalEvent(null, loc, null, null, TeleportCause.END_PORTAL); + when(iwm.isEndGenerate(world)).thenReturn(true); + np.onEndIslandPortal(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnEndIslandPortalHome() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from end + when(from.getWorld()).thenReturn(end); + + // Player has no island + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(UUID.randomUUID()); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + // Right cause, end exists, right world + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, null, TeleportCause.END_PORTAL); + when(iwm.isEndGenerate(world)).thenReturn(true); + np.onEndIslandPortal(e); + assertFalse(e.isCancelled()); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + np.onEndIslandPortal(e); + assertTrue(e.isCancelled()); + Mockito.verify(im).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onEntityPortal(org.bukkit.event.entity.EntityPortalEvent)}. + */ + @Test + public void testOnEntityPortal() { + NetherPortals np = new NetherPortals(plugin); + Entity ent = mock(Entity.class); + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(mock(World.class)); + // Not in world + when(iwm.inWorld(any())).thenReturn(false); + EntityPortalEvent e = new EntityPortalEvent(ent, from, null, null); + np.onEntityPortal(e); + assertFalse(e.isCancelled()); + // In world + when(iwm.inWorld(any())).thenReturn(true); + e = new EntityPortalEvent(ent, from, null, null); + np.onEntityPortal(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionNotInWorld() { + NetherPortals np = new NetherPortals(plugin); + // Null entity + Entity en = null; + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(mock(World.class)); + // Not in world + when(iwm.inWorld(any())).thenReturn(false); + + EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); + + assertFalse(np.onExplosion(e)); + } + + + @Test + public void testOnExplosionInWorldNotNetherOrEnd() { + NetherPortals np = new NetherPortals(plugin); + // Null entity + Entity en = null; + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(mock(World.class)); + EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); + assertFalse(np.onExplosion(e)); + } + + @Test + public void testOnExplosionIslands() { + NetherPortals np = new NetherPortals(plugin); + // Null entity + Entity en = null; + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + + // In world, in nether, nether islands + when(from.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(true); + EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); + assertFalse(np.onExplosion(e)); + + // In world, in end, end islands + when(from.getWorld()).thenReturn(end); + when(iwm.isNetherIslands(world)).thenReturn(false); + when(iwm.isEndIslands(world)).thenReturn(true); + assertFalse(np.onExplosion(e)); + } + + @Test + public void testOnExplosionNullEntity() { + NetherPortals np = new NetherPortals(plugin); + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + // In world, in nether, nether islands + when(from.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + EntityExplodeEvent e = new EntityExplodeEvent(null, from, affectedBlocks, 0); + assertFalse(np.onExplosion(e)); + } + + @Test + public void testOnExplosionAwayFromSpawn() { + NetherPortals np = new NetherPortals(plugin); + // Null entity + Entity en = mock(Entity.class); + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + + // In world, in nether, standard nether, null entity + when(from.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + + EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); + // Real entity, away from spawn + assertTrue(np.onExplosion(e)); + // Block should still exist because it is away from spawn + assertFalse(e.blockList().isEmpty()); + } + + @Test + public void testOnExplosion() { + NetherPortals np = new NetherPortals(plugin); + // Null entity + Entity en = null; + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + Location blockLoc = mock(Location.class); + when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); + when(block.getLocation()).thenReturn(blockLoc); + when(blockLoc.getWorld()).thenReturn(nether); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(mock(World.class)); + // In world, in nether, standard nether, null entity + when(from.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + + + // Real entity, next to spawn + en = mock(Entity.class); + when(blockLoc.toVector()).thenReturn(new Vector(0,0,0)); + EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); + // Block exists before + assertFalse(e.blockList().isEmpty()); + assertTrue(np.onExplosion(e)); + // Block removed + assertTrue(e.blockList().isEmpty()); + + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalNotPortal() { + NetherPortals np = new NetherPortals(plugin); + PlayerPortalEvent e = new PlayerPortalEvent(null, null, null, null, TeleportCause.COMMAND); + assertFalse(np.onNetherPortal(e)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalWrongWorld() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(mock(World.class)); + when(iwm.inWorld(any())).thenReturn(false); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + assertFalse(np.onNetherPortal(e)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalFromWorldToNetherIsland() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from world to nether + when(from.getWorld()).thenReturn(world); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands active + when(iwm.isNetherIslands(world)).thenReturn(true); + when(iwm.isNetherGenerate(world)).thenReturn(true); + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If nether islands, then to = from but in nether + Mockito.verify(from).toVector(); + // Do not go to spawn + Mockito.verify(nether, Mockito.never()).getSpawnLocation(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalFromWorldToNetherIslandWithSpawnDefined() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from world to nether + when(from.getWorld()).thenReturn(world); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands active + when(iwm.isNetherIslands(world)).thenReturn(true); + when(iwm.isNetherGenerate(world)).thenReturn(true); + + Island island = mock(Island.class); + Location spawnLoc = mock(Location.class); + when(island.getSpawnPoint(Mockito.any())).thenReturn(spawnLoc); + Optional optionalIsland = Optional.of(island); + // Island exists at location + when(im.getIslandAt(Mockito.any())).thenReturn(optionalIsland); + + + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If nether islands, then to = from but in nether + Mockito.verify(from).toVector(); + // Do not go to spawn + Mockito.verify(nether, Mockito.never()).getSpawnLocation(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalFromWorldToNetherIslandWithNoSpawnDefined() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from world to nether + when(from.getWorld()).thenReturn(world); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands active + when(iwm.isNetherIslands(world)).thenReturn(true); + when(iwm.isNetherGenerate(world)).thenReturn(true); + + Island island = mock(Island.class); + when(island.getSpawnPoint(Mockito.any())).thenReturn(null); + Optional optionalIsland = Optional.of(island); + // Island exists at location + when(im.getIslandAt(Mockito.any())).thenReturn(optionalIsland); + + + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If nether islands, then to = from but in nether + Mockito.verify(from).toVector(); + // Do not go to spawn + Mockito.verify(nether, Mockito.never()).getSpawnLocation(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalFromWorldToNetherStandard() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from world to nether + when(from.getWorld()).thenReturn(world); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands inactive + when(iwm.isNetherIslands(world)).thenReturn(false); + when(iwm.isNetherGenerate(world)).thenReturn(true); + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If regular nether, then to = spawn point of nether + Mockito.verify(from, Mockito.never()).toVector(); + Mockito.verify(nether).getSpawnLocation(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * @throws Exception + */ + @Test + public void testOnNetherPortalFromNetherStandard() throws Exception { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from nether to world + when(from.getWorld()).thenReturn(nether); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + Player p = mock(Player.class); + when(p.getUniqueId()).thenReturn(UUID.randomUUID()); + + PlayerPortalEvent e = new PlayerPortalEvent(p, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands inactive + when(iwm.isNetherIslands(world)).thenReturn(false); + when(iwm.isNetherGenerate(world)).thenReturn(true); + + // Player should be teleported to their island + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If regular nether, then to = island location + Mockito.verify(from, Mockito.never()).toVector(); + Mockito.verify(im).getIslandLocation(Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + */ + @Test + public void testOnNetherPortalFromNetherIsland() { + NetherPortals np = new NetherPortals(plugin); + Location from = mock(Location.class); + // Teleport from nether to world + when(from.getWorld()).thenReturn(nether); + when(from.toVector()).thenReturn(new Vector(1,2,3)); + PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, null, TeleportCause.NETHER_PORTAL); + // Nether islands active + when(iwm.isNetherIslands(world)).thenReturn(true); + when(iwm.isNetherGenerate(world)).thenReturn(true); + assertTrue(np.onNetherPortal(e)); + // Verify + assertTrue(e.isCancelled()); + // If regular nether, then to = island location + Mockito.verify(from).toVector(); + Mockito.verify(im, Mockito.never()).getIslandLocation(Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onPlayerBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. + */ + @Test + public void testOnPlayerBlockPlace() { + NetherPortals np = new NetherPortals(plugin); + Block block = mock(Block.class); + Location near = mock(Location.class); + when(near.toVector()).thenReturn(new Vector(0, 56, 0)); + when(near.getWorld()).thenReturn(nether); + when(block.getLocation()).thenReturn(near); + Player player = mock(Player.class); + // Standard nether + when(player.getWorld()).thenReturn(nether); + when(iwm.isNetherIslands(world)).thenReturn(false); + when(iwm.isNetherGenerate(world)).thenReturn(true); + BlockPlaceEvent e = new BlockPlaceEvent(block, null, block, null, player, false, null); + np.onPlayerBlockPlace(e); + Mockito.verify(block).getLocation(); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.NetherPortals#onTreeGrow(org.bukkit.event.world.StructureGrowEvent)}. + */ + @Test + public void testOnTreeGrow() { + NetherPortals np = new NetherPortals(plugin); + Location loc = mock(Location.class); + // Wrong world to start + when(loc.getWorld()).thenReturn(world); + BlockState log = mock(BlockState.class); + when(log.getType()).thenReturn(Material.LOG); + BlockState log2 = mock(BlockState.class); + when(log2.getType()).thenReturn(Material.LOG_2); + BlockState leaves = mock(BlockState.class); + when(leaves.getType()).thenReturn(Material.LEAVES); + BlockState leaves2 = mock(BlockState.class); + when(leaves2.getType()).thenReturn(Material.LEAVES_2); + List blocks = new ArrayList<>(); + blocks.add(log); + blocks.add(log2); + blocks.add(leaves); + blocks.add(leaves2); + StructureGrowEvent e = new StructureGrowEvent(loc, TreeType.ACACIA, false, null, blocks); + // No nether trees + when(iwm.isNetherTrees(world)).thenReturn(false); + assertFalse(np.onTreeGrow(e)); + // nether trees, wrong world + e = new StructureGrowEvent(loc, TreeType.ACACIA, false, null, blocks); + when(iwm.isNetherTrees(world)).thenReturn(true); + assertFalse(np.onTreeGrow(e)); + // Make the world nether + when(iwm.isNetherTrees(nether)).thenReturn(true); + when(loc.getWorld()).thenReturn(nether); + e = new StructureGrowEvent(loc, TreeType.ACACIA, false, null, blocks); + assertTrue(np.onTreeGrow(e)); + Mockito.verify(log).setType(Material.GRAVEL); + Mockito.verify(log2).setType(Material.GRAVEL); + Mockito.verify(leaves).setType(Material.GLOWSTONE); + Mockito.verify(leaves2).setType(Material.GLOWSTONE); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/ObsidianToLavaTest.java b/src/test/java/us/tastybento/bskyblock/listeners/ObsidianToLavaTest.java new file mode 100644 index 0000000..f76a204 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/ObsidianToLavaTest.java @@ -0,0 +1,217 @@ +package us.tastybento.bskyblock.listeners; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({PlayerEvent.class, PlayerInteractEvent.class}) +public class ObsidianToLavaTest { + + private static World world; + + @Test + public void testObsidianToLava() { + BSkyBlock plugin = mock(BSkyBlock.class); + assertNotNull(new ObsidianToLava(plugin)); + } + + @Test + public void testOnPlayerInteract() { + // Mock world + world = mock(World.class); + + // Mock server + Server server = mock(Server.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + // Mock item factory (for itemstacks) + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + // Set the server to the mock + Bukkit.setServer(server); + + // Mock plugin + BSkyBlock plugin = mock(BSkyBlock.class); + + // Create new object + ObsidianToLava listener = new ObsidianToLava(plugin); + + // Mock settings + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.isAllowObsidianScooping()).thenReturn(false); + // Allow scooping + when(settings.isAllowObsidianScooping()).thenReturn(true); + + // Mock player + Player who = mock(Player.class); + when(who.getWorld()).thenReturn(world); + + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + when(who.getLocation()).thenReturn(location); + + // Worlds + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getNetherWorld(Mockito.any())).thenReturn(world); + when(iwm.getEndWorld(Mockito.any())).thenReturn(world); + + // Mock up IslandsManager + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + // Mock up items and blocks + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getX()).thenReturn(0); + when(clickedBlock.getY()).thenReturn(0); + when(clickedBlock.getZ()).thenReturn(0); + when(clickedBlock.getWorld()).thenReturn(world); + + // Users + User.setPlugin(plugin); + + // Put player in world + when(iwm.inWorld(Mockito.any())).thenReturn(true); + // Put player on island + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + // Set as survival + when(who.getGameMode()).thenReturn(GameMode.SURVIVAL); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + + // Test all possible actions + //for (Action action: Action.values()) { + Action action = Action.RIGHT_CLICK_BLOCK; + // Test incorrect items + Material inHand = Material.ACACIA_DOOR; + Material block = Material.BROWN_MUSHROOM; + when(item.getType()).thenReturn(inHand); + when(clickedBlock.getType()).thenReturn(block); + // Create the event + testEvent(plugin, who, action, item, clickedBlock); + // Test with bucket in hand + inHand = Material.BUCKET; + block = Material.BROWN_MUSHROOM; + when(item.getType()).thenReturn(inHand); + when(clickedBlock.getType()).thenReturn(block); + // Create the event + testEvent(plugin, who, action, item, clickedBlock); + // Test with obsidian in hand + inHand = Material.ANVIL; + block = Material.OBSIDIAN; + when(item.getType()).thenReturn(inHand); + when(clickedBlock.getType()).thenReturn(block); + // Create the event + testEvent(plugin, who, action, item, clickedBlock); + // Test positive + inHand = Material.BUCKET; + block = Material.OBSIDIAN; + when(item.getType()).thenReturn(inHand); + when(clickedBlock.getType()).thenReturn(block); + // Create the event + testEvent(plugin, who, action, item, clickedBlock); + + + PlayerInteractEvent event = new PlayerInteractEvent(who, action, item, clickedBlock, BlockFace.EAST); + + // Test not in world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + assertFalse(listener.onPlayerInteract(event)); + // Put player in world + when(iwm.inWorld(Mockito.any())).thenReturn(true); + + // Test different game modes + for (GameMode gm : GameMode.values()) { + when(who.getGameMode()).thenReturn(gm); + if (!gm.equals(GameMode.SURVIVAL)) { + assertFalse(listener.onPlayerInteract(event)); + } + } + // Set as survival + when(who.getGameMode()).thenReturn(GameMode.SURVIVAL); + + // Test when player is not on island + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + assertFalse(listener.onPlayerInteract(event)); + + + } + + private void testEvent(BSkyBlock plugin, Player who, Action action, ItemStack item, Block clickedBlock) { + Block obsidianBlock = mock(Block.class); + when(obsidianBlock.getType()).thenReturn(Material.OBSIDIAN); + Block airBlock = mock(Block.class); + when(airBlock.getType()).thenReturn(Material.AIR); + + ObsidianToLava listener = new ObsidianToLava(plugin); + PlayerInteractEvent event = new PlayerInteractEvent(who, action, item, clickedBlock, BlockFace.EAST); + if (!action.equals(Action.RIGHT_CLICK_BLOCK) || !item.getType().equals(Material.BUCKET) || !clickedBlock.getType().equals(Material.OBSIDIAN)) { + assertFalse(listener.onPlayerInteract(event)); + } else { + // Test with obby close by in any of the possible locations + for (int x = -2; x <= 2; x++) { + for (int y = -2; y <= 2; y++) { + for (int z = -2; z <= 2; z++) { + when(world.getBlockAt(Mockito.eq(x), Mockito.eq(y), Mockito.eq(z))).thenReturn(obsidianBlock); + assertFalse(listener.onPlayerInteract(event)); + } + } + } + // Test where the area is free of obby + when(world.getBlockAt(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(airBlock); + assertTrue(listener.onPlayerInteract(event)); + } + + + } +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/PanelListenerManagerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/PanelListenerManagerTest.java new file mode 100644 index 0000000..4180ce1 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/PanelListenerManagerTest.java @@ -0,0 +1,241 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.panels.PanelItem.ClickHandler; +import us.tastybento.bskyblock.api.panels.PanelListener; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.util.Util; + +/** + * Test class for PanelListenerManager.java + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, Bukkit.class }) +public class PanelListenerManagerTest { + + private Player player; + private InventoryView view; + private SlotType type; + private ClickType click; + private InventoryAction inv; + private PanelListenerManager plm; + private UUID uuid; + private Panel panel; + private Panel wrongPanel; + private Inventory anotherInv; + private PanelListener pl; + private ClickHandler ch; + private Settings settings; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Settings + settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.isClosePanelOnClickOutside()).thenReturn(true); + + uuid = UUID.randomUUID(); + player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + view = mock(InventoryView.class); + when(view.getPlayer()).thenReturn(player); + + User.getInstance(player); + Inventory top = mock(Inventory.class); + when(top.getSize()).thenReturn(9); + when(view.getTopInventory()).thenReturn(top); + type = SlotType.CONTAINER; + click = ClickType.LEFT; + inv = InventoryAction.UNKNOWN; + + plm = new PanelListenerManager(); + + // Panel + panel = mock(Panel.class); + pl = mock(PanelListener.class); + Optional opl = Optional.of(pl); + when(panel.getListener()).thenReturn(opl); + when(panel.getInventory()).thenReturn(top); + when(top.getName()).thenReturn("name"); + Map map = new HashMap<>(); + PanelItem panelItem = mock(PanelItem.class); + ch = mock(ClickHandler.class); + //when(ch.onClick(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true); + Optional och = Optional.of(ch); + when(panelItem.getClickHandler()).thenReturn(och); + map.put(0, panelItem); + when(panel.getItems()).thenReturn(map); + + wrongPanel = mock(Panel.class); + anotherInv = mock(Inventory.class); + when(anotherInv.getName()).thenReturn("another_name"); + when(wrongPanel.getInventory()).thenReturn(anotherInv); + + // Clear the static panels + PanelListenerManager.getOpenPanels().clear(); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent)}. + */ + @Test + public void testOnInventoryClickOutside() { + SlotType type = SlotType.OUTSIDE; + InventoryClickEvent e = new InventoryClickEvent(view, type, 0, click, inv); + plm.onInventoryClick(e); + Mockito.verify(player).closeInventory(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent)}. + */ + @Test + public void testOnInventoryClickNoOpenPanels() { + InventoryClickEvent e = new InventoryClickEvent(view, type, 0, click, inv); + plm.onInventoryClick(e); + // Nothing should happen + Mockito.verify(player, Mockito.never()).closeInventory(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent)}. + */ + @Test + public void testOnInventoryClickOpenPanelsWrongPanel() { + PanelListenerManager.getOpenPanels().put(uuid, panel); + when(view.getTopInventory()).thenReturn(anotherInv); + InventoryClickEvent e = new InventoryClickEvent(view, type, 0, click, inv); + plm.onInventoryClick(e); + // Panel should be removed + assertTrue(PanelListenerManager.getOpenPanels().isEmpty()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent)}. + */ + @Test + public void testOnInventoryClickOpenPanelsRightPanelWrongSlot() { + PanelListenerManager.getOpenPanels().put(uuid, panel); + // Click on 1 instead of 0 + InventoryClickEvent e = new InventoryClickEvent(view, type, 1, click, inv); + plm.onInventoryClick(e); + assertTrue(e.isCancelled()); + Mockito.verify(pl).onInventoryClick(Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent)}. + */ + @Test + public void testOnInventoryClickOpenPanelsRightPanelRightSlot() { + PanelListenerManager.getOpenPanels().put(uuid, panel); + // Click on 0 + InventoryClickEvent e = new InventoryClickEvent(view, type, 0, click, inv); + plm.onInventoryClick(e); + // Check that the onClick is called + Mockito.verify(ch).onClick(Mockito.eq(panel), Mockito.any(User.class), Mockito.eq(click), Mockito.eq(0)); + Mockito.verify(pl).onInventoryClick(Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClose(org.bukkit.event.inventory.InventoryCloseEvent)}. + */ + @Test + public void testOnInventoryCloseNoPanels() { + // Add a panel for another player + PanelListenerManager.getOpenPanels().put(UUID.randomUUID(), panel); + // No panels for this player + InventoryCloseEvent event = new InventoryCloseEvent(view); + plm.onInventoryClose(event); + assertTrue(PanelListenerManager.getOpenPanels().size() == 1); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onInventoryClose(org.bukkit.event.inventory.InventoryCloseEvent)}. + */ + @Test + public void testOnInventoryClosePanels() { + // Add a panel for player + PanelListenerManager.getOpenPanels().put(uuid, panel); + InventoryCloseEvent event = new InventoryCloseEvent(view); + plm.onInventoryClose(event); + assertTrue(PanelListenerManager.getOpenPanels().isEmpty()); + Mockito.verify(pl).onInventoryClose(event); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#onLogOut(org.bukkit.event.player.PlayerQuitEvent)}. + */ + @Test + public void testOnLogOut() { + // Add a panel for player + PanelListenerManager.getOpenPanels().put(uuid, panel); + // Unknown player logs out + + Player unknown = mock(Player.class); + when(unknown.getUniqueId()).thenReturn(UUID.randomUUID()); + PlayerQuitEvent event = new PlayerQuitEvent(unknown, ""); + plm.onLogOut(event); + assertFalse(PanelListenerManager.getOpenPanels().isEmpty()); + + // Real log out + event = new PlayerQuitEvent(player, ""); + plm.onLogOut(event); + assertTrue(PanelListenerManager.getOpenPanels().isEmpty()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.PanelListenerManager#getOpenPanels()}. + */ + @Test + public void testGetOpenPanels() { + PanelListenerManager.getOpenPanels().put(uuid, panel); + assertEquals(panel, PanelListenerManager.getOpenPanels().get(uuid)); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListenerTest.java new file mode 100644 index 0000000..2254ecb --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/ChestDamageListenerTest.java @@ -0,0 +1,241 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Zombie; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class} ) +public class ChestDamageListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static IslandWorldManager iwm; + private static IslandsManager im; + private static World world; + + @BeforeClass + public static void setUpClass() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + + // Worlds + iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Monsters and animals + Zombie zombie = mock(Zombie.class); + when(zombie.getLocation()).thenReturn(location); + Slime slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + Cow cow = mock(Cow.class); + when(cow.getLocation()).thenReturn(location); + + // Fake players + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet()); + + // Users + //User user = mock(User.class); + ///user.setPlugin(plugin); + User.setPlugin(plugin); + + + // Locales - final + + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return (String)Arrays.asList(invocation.getArguments()).get(1); + } + + }; + when(lm.get(any(), any())).thenAnswer(answer); + + // Player name + PlayersManager pm = mock(PlayersManager.class); + when(pm.getName(Mockito.any())).thenReturn("tastybento"); + when(plugin.getPlayers()).thenReturn(pm); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + // Island manager + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + Optional optional = Optional.of(island); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(optional); + + } + + @Before + public void setUp() { + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ChestDamageListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionChestDamageNotAllowed() { + Entity entity = mock(Entity.class); + when(entity.getType()).thenReturn(EntityType.PRIMED_TNT); + List list = new ArrayList<>(); + Block chest = mock(Block.class); + when(chest.getType()).thenReturn(Material.CHEST); + when(chest.getLocation()).thenReturn(location); + Block trappedChest = mock(Block.class); + when(trappedChest.getType()).thenReturn(Material.TRAPPED_CHEST); + when(trappedChest.getLocation()).thenReturn(location); + Block stone = mock(Block.class); + when(stone.getType()).thenReturn(Material.STONE); + when(stone.getLocation()).thenReturn(location); + list.add(chest); + list.add(trappedChest); + list.add(stone); + EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0); + ChestDamageListener listener = new ChestDamageListener(); + listener.setPlugin(plugin); + listener.onExplosion(e); + assertFalse(e.isCancelled()); + assertEquals(1, e.blockList().size()); + assertFalse(e.blockList().contains(chest)); + assertFalse(e.blockList().contains(trappedChest)); + assertTrue(e.blockList().contains(stone)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ChestDamageListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionChestDamageAllowed() { + Flags.CHEST_DAMAGE.setSetting(world, true); + Entity entity = mock(Entity.class); + when(entity.getType()).thenReturn(EntityType.PRIMED_TNT); + List list = new ArrayList<>(); + Block chest = mock(Block.class); + when(chest.getType()).thenReturn(Material.CHEST); + when(chest.getLocation()).thenReturn(location); + Block trappedChest = mock(Block.class); + when(trappedChest.getType()).thenReturn(Material.TRAPPED_CHEST); + when(trappedChest.getLocation()).thenReturn(location); + Block stone = mock(Block.class); + when(stone.getType()).thenReturn(Material.STONE); + when(stone.getLocation()).thenReturn(location); + list.add(chest); + list.add(trappedChest); + list.add(stone); + EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0); + ChestDamageListener listener = new ChestDamageListener(); + listener.setPlugin(plugin); + listener.onExplosion(e); + assertFalse(e.isCancelled()); + assertEquals(3, e.blockList().size()); + assertTrue(e.blockList().contains(chest)); + assertTrue(e.blockList().contains(trappedChest)); + assertTrue(e.blockList().contains(stone)); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListenerTest.java new file mode 100644 index 0000000..86b5d2d --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/CleanSuperFlatListenerTest.java @@ -0,0 +1,170 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, Util.class }) +public class CleanSuperFlatListenerTest { + + private World world; + private Block block; + private Chunk chunk; + private IslandWorldManager iwm; + private BSkyBlock plugin; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + when(plugin.isLoaded()).thenReturn(true); + + // World + world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + when(iwm.isNetherGenerate(Mockito.any())).thenReturn(true); + when(iwm.isEndGenerate(Mockito.any())).thenReturn(true); + when(iwm.isNetherIslands(Mockito.any())).thenReturn(true); + when(iwm.isEndIslands(Mockito.any())).thenReturn(true); + + + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemF = mock(ItemFactory.class); + ItemMeta im = mock(ItemMeta.class); + when(itemF.getItemMeta(Mockito.any())).thenReturn(im); + when(Bukkit.getItemFactory()).thenReturn(itemF); + + Flags.CLEAN_SUPER_FLAT.setSetting(world, true); + + chunk = mock(Chunk.class); + when(chunk.getWorld()).thenReturn(world); + block = mock(Block.class); + when(block.getType()).thenReturn(Material.BEDROCK); + when(chunk.getBlock(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(block); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener#onChunkLoad(org.bukkit.event.world.ChunkLoadEvent)}. + */ + @Test + public void testOnChunkLoadNotBedrockNoFlsg() { + when(block.getType()).thenReturn(Material.AIR); + Flags.CLEAN_SUPER_FLAT.setSetting(world, false); + + ChunkLoadEvent e = new ChunkLoadEvent(chunk, false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world, Mockito.never()).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener#onChunkLoad(org.bukkit.event.world.ChunkLoadEvent)}. + */ + @Test + public void testOnChunkLoadBedrock() { + ChunkLoadEvent e = new ChunkLoadEvent(chunk, false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener#onChunkLoad(org.bukkit.event.world.ChunkLoadEvent)}. + */ + @Test + public void testOnChunkLoadBedrockNoClean() { + Flags.CLEAN_SUPER_FLAT.setSetting(world, false); + + ChunkLoadEvent e = new ChunkLoadEvent(chunk, false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world, Mockito.never()).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener#onChunkLoad(org.bukkit.event.world.ChunkLoadEvent)}. + */ + @Test + public void testOnChunkLoadBedrockNether() { + when(world.getEnvironment()).thenReturn(World.Environment.NETHER); + ChunkLoadEvent e = new ChunkLoadEvent(chunk, false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); + when(iwm.isNetherGenerate(Mockito.any())).thenReturn(false); + when(iwm.isNetherIslands(Mockito.any())).thenReturn(true); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); // No more than once + when(iwm.isNetherGenerate(Mockito.any())).thenReturn(true); + when(iwm.isNetherIslands(Mockito.any())).thenReturn(false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); // No more than once + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.CleanSuperFlatListener#onChunkLoad(org.bukkit.event.world.ChunkLoadEvent)}. + */ + @Test + public void testOnChunkLoadBedrockEnd() { + when(world.getEnvironment()).thenReturn(World.Environment.THE_END); + ChunkLoadEvent e = new ChunkLoadEvent(chunk, false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); + when(iwm.isEndGenerate(Mockito.any())).thenReturn(false); + when(iwm.isEndIslands(Mockito.any())).thenReturn(true); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); // No more than once + when(iwm.isEndGenerate(Mockito.any())).thenReturn(true); + when(iwm.isEndIslands(Mockito.any())).thenReturn(false); + new CleanSuperFlatListener().onChunkLoad(e); + Mockito.verify(world).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); // No more than once + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/EnderChestListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnderChestListenerTest.java new file mode 100644 index 0000000..ad85ce7 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnderChestListenerTest.java @@ -0,0 +1,260 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, User.class }) +public class EnderChestListenerTest { + + private Island island; + private IslandsManager im; + private World world; + private Location inside; + private UUID uuid; + private Player player; + private IslandWorldManager iwm; + + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Owner + uuid = UUID.randomUUID(); + + // Island initialization + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + inside = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + // On island + when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + // By default everything is in world + when(iwm.inWorld(Mockito.any())).thenReturn(true); + + // Ender chest use is not allowed by default + Flags.ENDER_CHEST.setSetting(world, false); + + // Sometimes use Mockito.withSettings().verboseLogging() + player = mock(Player.class); + UUID uuid = UUID.randomUUID(); + when(player.getUniqueId()).thenReturn(uuid); + when(player.isOp()).thenReturn(false); + // No special perms + when(player.hasPermission(Mockito.anyString())).thenReturn(false); + when(player.getWorld()).thenReturn(world); + + // Locales - this returns the string that was requested for translation + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenAnswer(new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + }}); + + } + + @Test + public void testOnEnderChestOpenNotRightClick() { + Action action = Action.LEFT_CLICK_AIR; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenNotEnderChest() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.STONE); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenEnderChestNotInWorld() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.ENDER_CHEST); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + // Not in world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenEnderChestOpPlayer() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.ENDER_CHEST); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + // Op player + when(player.isOp()).thenReturn(true); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenEnderChestHasBypassPerm() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.ENDER_CHEST); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + // Has bypass perm + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenEnderChestOkay() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.ENDER_CHEST); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + // Enderchest use is okay + Flags.ENDER_CHEST.setSetting(world, true); + new EnderChestListener().onEnderChestOpen(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnEnderChestOpenEnderChestBlocked() { + Action action = Action.RIGHT_CLICK_BLOCK; + ItemStack item = mock(ItemStack.class); + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.ENDER_CHEST); + BlockFace clickedBlockFace = BlockFace.EAST; + PlayerInteractEvent e = new PlayerInteractEvent(player, action, item, clickedBlock, clickedBlockFace); + // Enderchest use is okay + Flags.ENDER_CHEST.setSetting(world, false); + new EnderChestListener().onEnderChestOpen(e); + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage("protection.protected"); + } + + @Test + public void testOnCraftNotEnderChest() { + Recipe recipe = mock(Recipe.class); + ItemStack item = mock(ItemStack.class); + when(item.getType()).thenReturn(Material.STONE); + when(recipe.getResult()).thenReturn(item); + InventoryView view = mock(InventoryView.class); + when(view.getPlayer()).thenReturn(player); + Inventory top = mock(Inventory.class); + when(top.getSize()).thenReturn(9); + when(view.getTopInventory()).thenReturn(top); + SlotType type = SlotType.RESULT; + ClickType click = ClickType.LEFT; + InventoryAction action = InventoryAction.PICKUP_ONE; + CraftItemEvent e = new CraftItemEvent(recipe, view, type, 0, click, action); + new EnderChestListener().onCraft(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testOnCraftEnderChest() { + Recipe recipe = mock(Recipe.class); + ItemStack item = mock(ItemStack.class); + when(item.getType()).thenReturn(Material.ENDER_CHEST); + when(recipe.getResult()).thenReturn(item); + InventoryView view = mock(InventoryView.class); + when(view.getPlayer()).thenReturn(player); + Inventory top = mock(Inventory.class); + when(top.getSize()).thenReturn(9); + when(view.getTopInventory()).thenReturn(top); + SlotType type = SlotType.RESULT; + ClickType click = ClickType.LEFT; + InventoryAction action = InventoryAction.PICKUP_ONE; + CraftItemEvent e = new CraftItemEvent(recipe, view, type, 0, click, action); + new EnderChestListener().onCraft(e); + assertTrue(e.isCancelled()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/EndermanListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/EndermanListenerTest.java new file mode 100644 index 0000000..a1614ba --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/EndermanListenerTest.java @@ -0,0 +1,274 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.Slime; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +/** + * Tests enderman related listeners + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class, Bukkit.class} ) +public class EndermanListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static IslandWorldManager iwm; + private static IslandsManager im; + private static World world; + private static Enderman enderman; + private static Slime slime; + + @Before + public void setUp() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + + // Worlds + iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Monsters and animals + enderman = mock(Enderman.class); + when(enderman.getLocation()).thenReturn(location); + when(enderman.getWorld()).thenReturn(world); + when(enderman.getCarriedMaterial()).thenReturn(new MaterialData(Material.STONE)); + slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + + // Fake players + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet()); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + // Island manager + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + Optional optional = Optional.of(island); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(optional); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + // Not allowed to start + Flags.ENDERMAN_GRIEFING.setSetting(world, false); + // Allowed to start + Flags.ENDERMAN_DEATH_DROP.setSetting(world, true); + + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanGrief(org.bukkit.event.entity.EntityChangeBlockEvent)}. + */ + @Test + public void testNotEnderman() { + EndermanListener listener = new EndermanListener(); + Block to = mock(Block.class); + Material block = Material.ACACIA_DOOR; + byte data = 0; + @SuppressWarnings("deprecation") + EntityChangeBlockEvent e = new EntityChangeBlockEvent(slime, to, block, data); + listener.onEndermanGrief(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanGrief(org.bukkit.event.entity.EntityChangeBlockEvent)}. + */ + @Test + public void testOnEndermanGriefWrongWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + EndermanListener listener = new EndermanListener(); + Block to = mock(Block.class); + Material block = Material.ACACIA_DOOR; + byte data = 0; + @SuppressWarnings("deprecation") + EntityChangeBlockEvent e = new EntityChangeBlockEvent(enderman, to, block, data); + listener.onEndermanGrief(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanGrief(org.bukkit.event.entity.EntityChangeBlockEvent)}. + */ + @Test + public void testOnEndermanGriefAllowed() { + Flags.ENDERMAN_GRIEFING.setSetting(world, true); + EndermanListener listener = new EndermanListener(); + Block to = mock(Block.class); + Material block = Material.ACACIA_DOOR; + byte data = 0; + @SuppressWarnings("deprecation") + EntityChangeBlockEvent e = new EntityChangeBlockEvent(enderman, to, block, data); + listener.onEndermanGrief(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanGrief(org.bukkit.event.entity.EntityChangeBlockEvent)}. + */ + @Test + public void testOnEndermanGrief() { + EndermanListener listener = new EndermanListener(); + Block to = mock(Block.class); + Material block = Material.ACACIA_DOOR; + byte data = 0; + @SuppressWarnings("deprecation") + EntityChangeBlockEvent e = new EntityChangeBlockEvent(enderman, to, block, data); + listener.onEndermanGrief(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnNotEndermanDeath() { + EndermanListener listener = new EndermanListener(); + EntityDeathEvent e = new EntityDeathEvent(slime, new ArrayList()); + listener.onEndermanDeath(e); + Mockito.verify(world, Mockito.never()).dropItemNaturally(Mockito.any(), Mockito.any()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnEndermanDeathCarryAir() { + when(enderman.getCarriedMaterial()).thenReturn(new MaterialData(Material.AIR)); + EndermanListener listener = new EndermanListener(); + EntityDeathEvent e = new EntityDeathEvent(enderman, new ArrayList()); + listener.onEndermanDeath(e); + Mockito.verify(world, Mockito.never()).dropItemNaturally(Mockito.any(), Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnEndermanDeathNotInWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + EndermanListener listener = new EndermanListener(); + EntityDeathEvent e = new EntityDeathEvent(enderman, new ArrayList()); + listener.onEndermanDeath(e); + Mockito.verify(world, Mockito.never()).dropItemNaturally(Mockito.any(), Mockito.any()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnEndermanDeathNoFlag() { + Flags.ENDERMAN_DEATH_DROP.setSetting(world, false); + EndermanListener listener = new EndermanListener(); + EntityDeathEvent e = new EntityDeathEvent(enderman, new ArrayList()); + listener.onEndermanDeath(e); + Mockito.verify(world, Mockito.never()).dropItemNaturally(Mockito.any(), Mockito.any()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EndermanListener#onEndermanDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnEndermanDeath() { + EndermanListener listener = new EndermanListener(); + EntityDeathEvent e = new EntityDeathEvent(enderman, new ArrayList()); + listener.onEndermanDeath(e); + Mockito.verify(world).dropItemNaturally(Mockito.any(), Mockito.any()); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java new file mode 100644 index 0000000..aa19e93 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java @@ -0,0 +1,249 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, Util.class }) +public class EnterExitListenerTest { + + private static final Integer PROTECTION_RANGE = 200; + private static final Integer X = 600; + private static final Integer Y = 120; + private static final Integer Z = 10000; + private UUID uuid; + private User user; + private IslandsManager im; + private Island island; + private World world; + private Location outside; + private Location inside; + private Notifier notifier; + private Location inside2; + private EnterExitListener listener; + private LocalesManager lm; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Settings + Settings s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + User.setPlugin(plugin); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + // Locales + lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Notifier + notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + + // Island initialization + island = mock(Island.class); + Location loc = mock(Location.class); + when(loc.getWorld()).thenReturn(world); + when(loc.getBlockX()).thenReturn(X); + when(loc.getBlockY()).thenReturn(Y); + when(loc.getBlockZ()).thenReturn(Z); + when(island.getCenter()).thenReturn(loc); + when(island.getProtectionRange()).thenReturn(PROTECTION_RANGE); + when(island.getOwner()).thenReturn(uuid); + + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // Common from to's + outside = mock(Location.class); + when(outside.getWorld()).thenReturn(world); + when(outside.getBlockX()).thenReturn(X + PROTECTION_RANGE + 1); + when(outside.getBlockY()).thenReturn(Y); + when(outside.getBlockZ()).thenReturn(Z); + when(outside.toVector()).thenReturn(new Vector(X + PROTECTION_RANGE + 1, Y, Z)); + + inside = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 1); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + when(inside.toVector()).thenReturn(new Vector(X + PROTECTION_RANGE - 1, Y, Z)); + + inside2 = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 2); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + when(inside.toVector()).thenReturn(new Vector(X + PROTECTION_RANGE -2, Y, Z)); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(inside2))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(outside))).thenReturn(Optional.empty()); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Player's manager + PlayersManager pm = mock(PlayersManager.class); + when(pm.getName(Mockito.any())).thenReturn("tastybento"); + when(plugin.getPlayers()).thenReturn(pm); + + // Listener + listener = new EnterExitListener(); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testOnMoveInsideIsland() { + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), inside, inside); + listener.onMove(e); + // Moving in the island should result in no messages to the user + Mockito.verify(user, Mockito.never()).sendMessage(Mockito.anyVararg()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testOnMoveOutsideIsland() { + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), outside, outside); + listener.onMove(e); + // Moving outside the island should result in no messages to the user + Mockito.verify(user, Mockito.never()).sendMessage(Mockito.anyVararg()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testOnGoingIntoIslandEmptyIslandName() { + when(island.getName()).thenReturn(""); + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), outside, inside); + listener.onMove(e); + // Moving into the island should show a message + Mockito.verify(lm).get(Mockito.any(), Mockito.eq("protection.flags.ENTER_EXIT_MESSAGES.now-entering")); + // The island owner needs to be checked + Mockito.verify(island, Mockito.times(3)).getOwner(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testOnGoingIntoIslandWithIslandName() { + when(island.getName()).thenReturn("fancy name"); + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), outside, inside); + listener.onMove(e); + // Moving into the island should show a message + Mockito.verify(lm).get(Mockito.any(), Mockito.eq("protection.flags.ENTER_EXIT_MESSAGES.now-entering")); + // No owner check + Mockito.verify(island, Mockito.times(2)).getOwner(); + Mockito.verify(island, Mockito.times(2)).getName(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testExitingIslandEmptyIslandName() { + when(island.getName()).thenReturn(""); + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), inside, outside); + listener.onMove(e); + // Moving into the island should show a message + Mockito.verify(lm).get(Mockito.any(), Mockito.eq("protection.flags.ENTER_EXIT_MESSAGES.now-leaving")); + // The island owner needs to be checked + Mockito.verify(island, Mockito.times(3)).getOwner(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.EnterExitListener#onMove(org.bukkit.event.player.PlayerMoveEvent)}. + */ + @Test + public void testExitingIslandWithIslandName() { + when(island.getName()).thenReturn("fancy name"); + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), inside, outside); + listener.onMove(e); + // Moving into the island should show a message + Mockito.verify(lm).get(Mockito.any(), Mockito.eq("protection.flags.ENTER_EXIT_MESSAGES.now-leaving")); + // No owner check + Mockito.verify(island, Mockito.times(2)).getOwner(); + Mockito.verify(island, Mockito.times(2)).getName(); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java new file mode 100644 index 0000000..54e7cba --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java @@ -0,0 +1,334 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Zombie; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class} ) +public class FireListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static IslandWorldManager iwm; + + @BeforeClass + public static void setUpClass() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + + // Worlds + iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Monsters and animals + Zombie zombie = mock(Zombie.class); + when(zombie.getLocation()).thenReturn(location); + Slime slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + Cow cow = mock(Cow.class); + when(cow.getLocation()).thenReturn(location); + + // Fake players + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet()); + + // Users + //User user = mock(User.class); + ///user.setPlugin(plugin); + User.setPlugin(plugin); + + + // Locales - final + + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Player name + PlayersManager pm = mock(PlayersManager.class); + when(pm.getName(Mockito.any())).thenReturn("tastybento"); + when(plugin.getPlayers()).thenReturn(pm); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + } + + @Before + public void setUp() { + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + } + + @Test + public void testCheckFire() { + // Island + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Block on fire + Block block = mock(Block.class); + when(block.getLocation()).thenReturn(location); + BlockBurnEvent e = new BlockBurnEvent(block, block); + + // Fire listener - remember to set the plugin for testing! + FireListener listener = new FireListener(); + listener.setPlugin(plugin); + + // Disallow fire + when(island.isAllowed(Mockito.any())).thenReturn(false); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(false); + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.checkFire(e, location, Flags.FIRE)); + Flags.FIRE.setDefaultSetting(true); + assertTrue(listener.checkFire(e, location, Flags.FIRE)); + + // Allow fire + when(island.isAllowed(Mockito.any())).thenReturn(true); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + Flags.FIRE.setDefaultSetting(false); + assertFalse(listener.checkFire(e, location, Flags.FIRE)); + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.checkFire(e, location, Flags.FIRE)); + + // Check with no island + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + // Fire is not allowed, so should be cancelled + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.checkFire(e, location, Flags.FIRE)); + // Fire allowed + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.checkFire(e, location, Flags.FIRE)); + } + + @Test + public void testOnBlockBurn() { + // Island + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Block on fire + Block block = mock(Block.class); + when(block.getLocation()).thenReturn(location); + BlockBurnEvent e = new BlockBurnEvent(block, block); + + // Fire listener - remember to set the plugin for testing! + FireListener listener = new FireListener(); + listener.setPlugin(plugin); + + // Disallow fire + when(island.isAllowed(Mockito.any())).thenReturn(false); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(false); + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.onBlockBurn(e)); + Flags.FIRE.setDefaultSetting(true); + assertTrue(listener.onBlockBurn(e)); + + // Allow fire + when(island.isAllowed(Mockito.any())).thenReturn(true); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + Flags.FIRE.setDefaultSetting(false); + assertFalse(listener.onBlockBurn(e)); + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.onBlockBurn(e)); + + // Check with no island + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + // Fire is not allowed, so should be cancelled + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.onBlockBurn(e)); + // Fire allowed + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.onBlockBurn(e)); + } + + @Test + public void testOnBlockSpread() { + // Island + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Block on fire spread + + Block block = mock(Block.class); + Block fire = mock(Block.class); + when(block.getLocation()).thenReturn(location); + when(fire.getLocation()).thenReturn(location); + when(fire.getType()).thenReturn(Material.FIRE); + + BlockSpreadEvent e = new BlockSpreadEvent(block, fire, null); + + // Fire listener - remember to set the plugin for testing! + FireListener listener = new FireListener(); + listener.setPlugin(plugin); + + // Disallow fire + when(island.isAllowed(Mockito.any())).thenReturn(false); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(false); + Flags.FIRE_SPREAD.setDefaultSetting(false); + assertTrue(listener.onBlockSpread(e)); + Flags.FIRE_SPREAD.setDefaultSetting(true); + assertTrue(listener.onBlockSpread(e)); + + // Allow fire spread + when(island.isAllowed(Mockito.any())).thenReturn(true); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + Flags.FIRE_SPREAD.setDefaultSetting(false); + assertFalse(listener.onBlockSpread(e)); + Flags.FIRE_SPREAD.setDefaultSetting(true); + assertFalse(listener.onBlockSpread(e)); + + // Check with no island + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + // Fire spread is not allowed, so should be cancelled + Flags.FIRE_SPREAD.setDefaultSetting(false); + assertTrue(listener.onBlockSpread(e)); + // Fire allowed + Flags.FIRE_SPREAD.setDefaultSetting(true); + assertFalse(listener.onBlockSpread(e)); + } + + @Test + public void testOnBlockIgnite() { + // Island + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Block on fire spread + + Block block = mock(Block.class); + when(block.getLocation()).thenReturn(location); + when(block.getType()).thenReturn(Material.OBSIDIAN); + + BlockIgniteEvent e = new BlockIgniteEvent(block, null, block); + + // Fire listener - remember to set the plugin for testing! + FireListener listener = new FireListener(); + listener.setPlugin(plugin); + + // Obsidian is okay to ignite + assertFalse(listener.onBlockIgnite(e)); + + // Now set to something flammable + when(block.getType()).thenReturn(Material.WOOD); + + // Disallow fire + when(island.isAllowed(Mockito.any())).thenReturn(false); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(false); + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.onBlockIgnite(e)); + Flags.FIRE.setDefaultSetting(true); + assertTrue(listener.onBlockIgnite(e)); + + // Allow fire spread + when(island.isAllowed(Mockito.any())).thenReturn(true); + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + Flags.FIRE.setDefaultSetting(false); + assertFalse(listener.onBlockIgnite(e)); + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.onBlockIgnite(e)); + + // Check with no island + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + // Fire spread is not allowed, so should be cancelled + Flags.FIRE.setDefaultSetting(false); + assertTrue(listener.onBlockIgnite(e)); + // Fire allowed + Flags.FIRE.setDefaultSetting(true); + assertFalse(listener.onBlockIgnite(e)); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListenerTest.java new file mode 100644 index 0000000..189c62e --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/InvincibleVisitorsListenerTest.java @@ -0,0 +1,240 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, Bukkit.class }) +public class InvincibleVisitorsListenerTest { + + private IslandWorldManager iwm; + private InvincibleVisitorsListener listener; + private Panel panel; + private User user; + private Flag flag; + private IslandsManager im; + private Island island; + private UUID uuid; + private List ivSettings; + private Player player; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + when(plugin.getIWM()).thenReturn(iwm); + + listener = new InvincibleVisitorsListener(); + + panel = mock(Panel.class); + when(panel.getInventory()).thenReturn(mock(Inventory.class)); + when(panel.getName()).thenReturn("panel"); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.inWorld()).thenReturn(true); + when(user.getWorld()).thenReturn(mock(World.class)); + when(user.getLocation()).thenReturn(mock(Location.class)); + player = mock(Player.class); + when(user.getPlayer()).thenReturn(player); + when(user.hasPermission(Mockito.anyString())).thenReturn(true); + when(user.getTranslation(Mockito.anyString())).thenReturn("panel"); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + FlagsManager fm = mock(FlagsManager.class); + flag = mock(Flag.class); + when(flag.isSetForWorld(Mockito.any())).thenReturn(false); + PanelItem item = mock(PanelItem.class); + when(item.getItem()).thenReturn(mock(ItemStack.class)); + when(flag.toPanelItem(Mockito.any(), Mockito.eq(user))).thenReturn(item); + when(fm.getFlagByID(Mockito.anyString())).thenReturn(flag); + when(plugin.getFlagsManager()).thenReturn(fm); + + // Island Manager + im = mock(IslandsManager.class); + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(im.getIsland(Mockito.any(World.class), Mockito.any(User.class))).thenReturn(island); + // Visitor + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // IV Settings + ivSettings = new ArrayList<>(); + ivSettings.add(EntityDamageEvent.DamageCause.CRAMMING.name()); + ivSettings.add(EntityDamageEvent.DamageCause.VOID.name()); + when(iwm.getIvSettings(Mockito.any())).thenReturn(ivSettings); + + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemF = mock(ItemFactory.class); + ItemMeta imeta = mock(ItemMeta.class); + when(itemF.getItemMeta(Mockito.any())).thenReturn(imeta); + when(Bukkit.getItemFactory()).thenReturn(itemF); + + Inventory top = mock(Inventory.class); + when(top.getSize()).thenReturn(9); + when(panel.getInventory()).thenReturn(top); + + when(Bukkit.createInventory(Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(top); + + } + + @Test + public void testOnClickWrongWorld() { + when(user.inWorld()).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.wrong-world"); + } + + @Test + public void testOnClickNoPermission() { + when(user.hasPermission(Mockito.anyString())).thenReturn(false); + listener.onClick(panel, user, ClickType.LEFT, 0); + Mockito.verify(user).sendMessage("general.errors.no-permission"); + Mockito.verify(user).sendMessage("general.errors.you-need", "[permission]", "bskyblock.admin.settings.INVINCIBLE_VISITORS"); + } + + @Test + public void testOnClickNotIVPanel() { + ClickType clickType = ClickType.LEFT; + int slot = 5; + when(panel.getName()).thenReturn("not_panel"); + listener.onClick(panel, user, clickType, slot ); + // Should open inv visitors + Mockito.verify(user).closeInventory(); + Mockito.verify(player).openInventory(Mockito.any(Inventory.class)); + } + + @Test + public void testOnClickIVPanel() { + ClickType clickType = ClickType.LEFT; + int slot = 5; // FALL damage + when(panel.getName()).thenReturn("panel"); + + // IV settings should be empty + assertFalse(ivSettings.contains("FALL")); + // Click on the FALL icon + listener.onClick(panel, user, clickType, slot ); + // Should keep panel open + Mockito.verify(user, Mockito.never()).closeInventory(); + // IV settings should now have FALL in it + assertTrue(ivSettings.contains("FALL")); + + // Click on it again + listener.onClick(panel, user, clickType, slot ); + // Should keep panel open + Mockito.verify(user, Mockito.never()).closeInventory(); + // IV settings should not have FALL in it anymore + assertFalse(ivSettings.contains("FALL")); + + + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageNotPlayer() { + LivingEntity le = mock(LivingEntity.class); + EntityDamageEvent e = new EntityDamageEvent(le, EntityDamageEvent.DamageCause.CRAMMING, 0D); + listener.onVisitorGetDamage(e); + assertFalse(e.isCancelled()); + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageNotInWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + EntityDamageEvent e = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.CRAMMING, 0D); + listener.onVisitorGetDamage(e); + assertFalse(e.isCancelled()); + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageNotInIvSettings() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + EntityDamageEvent e = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.BLOCK_EXPLOSION, 0D); + listener.onVisitorGetDamage(e); + assertFalse(e.isCancelled()); + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageNotVisitor() { + EntityDamageEvent e = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.CRAMMING, 0D); + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + listener.onVisitorGetDamage(e); + assertFalse(e.isCancelled()); + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageNotVoid() { + EntityDamageEvent e = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.CRAMMING, 0D); + listener.onVisitorGetDamage(e); + assertTrue(e.isCancelled()); + Mockito.verify(player, Mockito.never()).setGameMode(Mockito.eq(GameMode.SPECTATOR)); + } + + @SuppressWarnings("deprecation") + @Test + public void testOnVisitorGetDamageVoid() { + // For testing, have no island to teleport to + when(im.getIslandAt(Mockito.any())).thenReturn(Optional.empty()); + EntityDamageEvent e = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.VOID, 0D); + listener.onVisitorGetDamage(e); + assertTrue(e.isCancelled()); + Mockito.verify(player).setGameMode(Mockito.eq(GameMode.SPECTATOR)); + Mockito.verify(im).getIslandAt(Mockito.any()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListenerTest.java new file mode 100644 index 0000000..dc4eeab --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/IslandRespawnListenerTest.java @@ -0,0 +1,191 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.inventory.ItemStack; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class }) +public class IslandRespawnListenerTest { + + private World world; + private Player player; + private IslandsManager im; + private IslandWorldManager iwm; + private Location safeLocation; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Settings + Settings s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + when(player.getWorld()).thenReturn(world); + when(player.getUniqueId()).thenReturn(UUID.randomUUID()); + when(player.getLocation()).thenReturn(mock(Location.class)); + + // Island World Manager + iwm = mock(IslandWorldManager.class); + // All locations are in world by default + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(plugin.getIslands()).thenReturn(im); + safeLocation = mock(Location.class); + when(safeLocation.getWorld()).thenReturn(world); + when(im.getSafeHomeLocation(Mockito.any(), Mockito.any(), Mockito.anyInt())).thenReturn(safeLocation); + + // Sometimes use Mockito.withSettings().verboseLogging() + User.setPlugin(plugin); + User.getInstance(player); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.IslandRespawnListener#onPlayerDeath(org.bukkit.event.entity.PlayerDeathEvent)}. + */ + @Test + public void testOnPlayerDeath() { + List drops = new ArrayList<>(); + PlayerDeathEvent e = new PlayerDeathEvent(player, drops, 0, 0, 0, 0, ""); + new IslandRespawnListener().onPlayerDeath(e); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.IslandRespawnListener#onPlayerRespawn(org.bukkit.event.player.PlayerRespawnEvent)}. + */ + @Test + public void testOnPlayerRespawn() { + // Die + List drops = new ArrayList<>(); + PlayerDeathEvent e = new PlayerDeathEvent(player, drops, 0, 0, 0, 0, ""); + IslandRespawnListener l = new IslandRespawnListener(); + l.onPlayerDeath(e); + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + // Has island + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + // Respawn + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false); + l.onPlayerRespawn(ev); + assertEquals(safeLocation, ev.getRespawnLocation()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.IslandRespawnListener#onPlayerRespawn(org.bukkit.event.player.PlayerRespawnEvent)}. + */ + @Test + public void testOnPlayerRespawnWithoutDeath() { + IslandRespawnListener l = new IslandRespawnListener(); + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + // Has island + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + // Respawn + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false); + l.onPlayerRespawn(ev); + assertEquals(location, ev.getRespawnLocation()); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.IslandRespawnListener#onPlayerRespawn(org.bukkit.event.player.PlayerRespawnEvent)}. + */ + @Test + public void testOnPlayerRespawnWrongWorld() { + when(iwm.inWorld(Mockito.any())).thenReturn(false); + // Die + List drops = new ArrayList<>(); + PlayerDeathEvent e = new PlayerDeathEvent(player, drops, 0, 0, 0, 0, ""); + IslandRespawnListener l = new IslandRespawnListener(); + l.onPlayerDeath(e); + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + // Has island + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + // Respawn + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false); + l.onPlayerRespawn(ev); + assertEquals(location, ev.getRespawnLocation()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.IslandRespawnListener#onPlayerRespawn(org.bukkit.event.player.PlayerRespawnEvent)}. + */ + @Test + public void testOnPlayerRespawnFlagNotSet() { + Flags.ISLAND_RESPAWN.setSetting(world, false); + // Die + List drops = new ArrayList<>(); + PlayerDeathEvent e = new PlayerDeathEvent(player, drops, 0, 0, 0, 0, ""); + IslandRespawnListener l = new IslandRespawnListener(); + l.onPlayerDeath(e); + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + // Has island + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + // Respawn + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false); + l.onPlayerRespawn(ev); + assertEquals(location, ev.getRespawnLocation()); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListenerTest.java new file mode 100644 index 0000000..f67bfa2 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/ItemFrameListenerTest.java @@ -0,0 +1,223 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Slime; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class, Bukkit.class} ) +public class ItemFrameListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static IslandWorldManager iwm; + private static IslandsManager im; + private static World world; + private static Enderman enderman; + private static Slime slime; + + @Before + public void setUp() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + + // Worlds + iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Monsters and animals + enderman = mock(Enderman.class); + when(enderman.getLocation()).thenReturn(location); + when(enderman.getWorld()).thenReturn(world); + when(enderman.getCarriedMaterial()).thenReturn(new MaterialData(Material.STONE)); + slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + + // Fake players + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet()); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + // Island manager + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + Optional optional = Optional.of(island); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(optional); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + // Not allowed to start + Flags.ITEM_FRAME_DAMAGE.setSetting(world, false); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ItemFrameListener#onItemFrameDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnItemFrameDamageEntityDamageByEntityEvent() { + ItemFrameListener ifl = new ItemFrameListener(); + Entity entity = mock(ItemFrame.class); + DamageCause cause = DamageCause.ENTITY_ATTACK; + @SuppressWarnings("deprecation") + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(enderman, entity, cause , 0); + ifl.onItemFrameDamage(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ItemFrameListener#onItemFrameDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testNotItemFrame() { + ItemFrameListener ifl = new ItemFrameListener(); + Entity entity = mock(Monster.class); + DamageCause cause = DamageCause.ENTITY_ATTACK; + @SuppressWarnings("deprecation") + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(enderman, entity, cause , 0); + ifl.onItemFrameDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ItemFrameListener#onItemFrameDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testProjectile() { + ItemFrameListener ifl = new ItemFrameListener(); + Entity entity = mock(ItemFrame.class); + DamageCause cause = DamageCause.ENTITY_ATTACK; + Projectile p = mock(Projectile.class); + when(p.getShooter()).thenReturn(enderman); + @SuppressWarnings("deprecation") + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(p, entity, cause , 0); + ifl.onItemFrameDamage(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ItemFrameListener#onItemFrameDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testPlayerProjectile() { + ItemFrameListener ifl = new ItemFrameListener(); + Entity entity = mock(ItemFrame.class); + DamageCause cause = DamageCause.ENTITY_ATTACK; + Projectile p = mock(Projectile.class); + Player player = mock(Player.class); + when(p.getShooter()).thenReturn(player); + @SuppressWarnings("deprecation") + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(p, entity, cause , 0); + ifl.onItemFrameDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.ItemFrameListener#onItemFrameDamage(org.bukkit.event.hanging.HangingBreakByEntityEvent)}. + */ + @Test + public void testOnItemFrameDamageHangingBreakByEntityEvent() { + ItemFrameListener ifl = new ItemFrameListener(); + Hanging hanging = mock(ItemFrame.class); + HangingBreakByEntityEvent e = new HangingBreakByEntityEvent(hanging, enderman); + ifl.onItemFrameDamage(e); + assertTrue(e.isCancelled()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListenerTest.java new file mode 100644 index 0000000..27be9ad --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/LockAndBanListenerTest.java @@ -0,0 +1,722 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.vehicle.VehicleMoveEvent; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class }) +public class LockAndBanListenerTest { + + private static final Integer PROTECTION_RANGE = 200; + private static final Integer X = 600; + private static final Integer Y = 120; + private static final Integer Z = 10000; + private UUID uuid; + private User user; + private IslandsManager im; + private Island island; + private World world; + private LockAndBanListener listener; + private Location outside; + private Location inside; + private Notifier notifier; + private Location inside2; + private BukkitScheduler sch; + private Player player; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Island world manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + + when(plugin.getIWM()).thenReturn(iwm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + User.setPlugin(plugin); + // User and player are not op + when(user.isOp()).thenReturn(false); + when(player.isOp()).thenReturn(false); + // No special perms + when(player.hasPermission(Mockito.anyString())).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + + // No island for player to begin with (set it later in the tests) + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(im.isOwner(Mockito.any(), Mockito.eq(uuid))).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Has team + PlayersManager pm = mock(PlayersManager.class); + when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Notifier + notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + + // Island Banned list initialization + island = mock(Island.class); + when(island.getBanned()).thenReturn(new HashSet<>()); + when(island.isBanned(Mockito.any())).thenReturn(false); + Location loc = mock(Location.class); + when(loc.getWorld()).thenReturn(world); + when(loc.getBlockX()).thenReturn(X); + when(loc.getBlockY()).thenReturn(Y); + when(loc.getBlockZ()).thenReturn(Z); + when(island.getCenter()).thenReturn(loc); + when(island.getProtectionRange()).thenReturn(PROTECTION_RANGE); + // Island is not locked by default + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + // Create the listener object + listener = new LockAndBanListener(); + + // Common from to's + outside = mock(Location.class); + when(outside.getWorld()).thenReturn(world); + when(outside.getBlockX()).thenReturn(X + PROTECTION_RANGE + 1); + when(outside.getBlockY()).thenReturn(Y); + when(outside.getBlockZ()).thenReturn(Z); + + inside = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 1); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + + inside2 = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + when(inside.getBlockX()).thenReturn(X + PROTECTION_RANGE - 2); + when(inside.getBlockY()).thenReturn(Y); + when(inside.getBlockZ()).thenReturn(Z); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(inside2))).thenReturn(opIsland); + when(im.getProtectedIslandAt(Mockito.eq(outside))).thenReturn(Optional.empty()); + } + + @Test + public void testTeleportToNotBannedIsland() { + // Setup location outside island, one inside banned island + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Simulate a teleport into an island + PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should not be cancelled + assertFalse(e.isCancelled()); + // User should see no message from this class + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.any()); + } + + @Test + public void testTeleportToBannedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + + // Add player to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + + // Simulate a teleport into an island + PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should be cancelled + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.any()); + } + + @Test + public void testLoginToBannedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player on the island + when(player.getLocation()).thenReturn(inside); + + // Add player to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + + // Log them in + listener.onPlayerLogin(new PlayerJoinEvent(player, "join message")); + // User should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(im).homeTeleport(Mockito.any(), Mockito.eq(player)); + // Call teleport event + PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should not be cancelled + assertFalse(e.isCancelled()); + } + + @Test + public void testVerticalMoveOnly() { + // Move vertically only + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(world); + when(from.getBlockX()).thenReturn(X); + when(from.getBlockY()).thenReturn(50); + when(from.getBlockZ()).thenReturn(Z); + Location to = mock(Location.class); + when(to.getWorld()).thenReturn(world); + when(to.getBlockX()).thenReturn(X); + when(to.getBlockY()).thenReturn(55); + when(to.getBlockZ()).thenReturn(Z); + PlayerMoveEvent e = new PlayerMoveEvent(user.getPlayer(), from, to); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + // Confirm no check is done on the island + Mockito.verify(im, Mockito.never()).getProtectedIslandAt(Mockito.any()); + } + + @Test + public void testVerticalVehicleMoveOnly() { + // Move vertically only + Location from = mock(Location.class); + when(from.getWorld()).thenReturn(world); + when(from.getBlockX()).thenReturn(X); + when(from.getBlockY()).thenReturn(50); + when(from.getBlockZ()).thenReturn(Z); + Location to = mock(Location.class); + when(to.getWorld()).thenReturn(world); + when(to.getBlockX()).thenReturn(X); + when(to.getBlockY()).thenReturn(55); + when(to.getBlockZ()).thenReturn(Z); + // Create vehicle and put two players in it. + Vehicle vehicle = mock(Vehicle.class); + Player player2 = mock(Player.class); + List passengers = new ArrayList<>(); + passengers.add(user.getPlayer()); + passengers.add(player2); + when(vehicle.getPassengers()).thenReturn(passengers); + // Move vehicle + listener.onVehicleMove(new VehicleMoveEvent(vehicle, from, to)); + // Confirm no check is done on the island + Mockito.verify(im, Mockito.never()).getProtectedIslandAt(Mockito.any()); + } + + @Test + public void testPlayerMoveIntoBannedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player just outside island + when(player.getLocation()).thenReturn(outside); + + // Add player to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside); + listener.onPlayerMove(e); + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should NOT be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testPlayerMoveInsideBannedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player inside island + when(player.getLocation()).thenReturn(inside); + + // Add player to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2); + listener.onPlayerMove(e); + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(sch).runTask(Mockito.any(), Mockito.any(Runnable.class)); + // Call teleport event + PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(ev); + // Should not be cancelled + assertFalse(ev.isCancelled()); + } + + @Test + public void testVehicleMoveIntoBannedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + + // Add player to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + + // Add the user to the ban list + when(island.isBanned(Mockito.eq(uuid))).thenReturn(true); + + // Create vehicle and put two players in it. One is banned, the other is not + Vehicle vehicle = mock(Vehicle.class); + Player player2 = mock(Player.class); + List passengers = new ArrayList<>(); + passengers.add(player); + passengers.add(player2); + when(vehicle.getPassengers()).thenReturn(passengers); + // Move vehicle + listener.onVehicleMove(new VehicleMoveEvent(vehicle, outside, inside)); + // Player should see a message and nothing should be sent to Player 2 + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(im).homeTeleport(Mockito.any(), Mockito.eq(player)); + // Player 2 should not be teleported + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player2)); + // Call teleport event + PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(ev); + // Should not be cancelled + assertFalse(ev.isCancelled()); + } + + /* + * Island lock tests + */ + + + @Test + public void testTeleportToLockedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + // Simulate a teleport into an island + PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should be cancelled + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.any()); + } + + @Test + public void testTeleportToLockedIslandAsMember() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Simulate a teleport into an island + PlayerTeleportEvent e = new PlayerTeleportEvent(player, outside, inside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should not be not cancelled + assertFalse(e.isCancelled()); + // Player should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.any()); + } + + @Test + public void testLoginToLockedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player on the island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Log them in + listener.onPlayerLogin(new PlayerJoinEvent(player, "join message")); + // User should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(im).homeTeleport(Mockito.any(), Mockito.eq(player)); + // Call teleport event + PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(e); + // Should not be cancelled + assertFalse(e.isCancelled()); + } + + @Test + public void testLoginToLockedIslandAsOp() { + // Make player + Player player = mock(Player.class); + when(player.isOp()).thenReturn(true); + + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player on the island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Log them in + listener.onPlayerLogin(new PlayerJoinEvent(player, "join message")); + // User should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.anyString()); + // User should not be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testLoginToLockedIslandWithBypassPerm() { + // Make player + Player player = mock(Player.class); + when(player.isOp()).thenReturn(false); + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player on the island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Log them in + listener.onPlayerLogin(new PlayerJoinEvent(player, "join message")); + // User should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.anyString()); + // User should not be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testLoginToLockedIslandAsMember() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player on the island + when(player.getLocation()).thenReturn(inside); + // Log them in + listener.onPlayerLogin(new PlayerJoinEvent(player, "join message")); + // User should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.anyString()); + // User should not be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testPlayerMoveIntoLockedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player just outside island + when(player.getLocation()).thenReturn(outside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside); + listener.onPlayerMove(e); + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should NOT be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testPlayerMoveIntoLockedIslandAsOp() { + // Make player + Player player = mock(Player.class); + when(player.isOp()).thenReturn(true); + + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player just outside island + when(player.getLocation()).thenReturn(outside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testPlayerMoveIntoLockedIslandWithBypass() { + // Make player + Player player = mock(Player.class); + when(player.isOp()).thenReturn(false); + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player just outside island + when(player.getLocation()).thenReturn(outside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + } + + + @Test + public void testPlayerMoveIntoLockedIslandAsMember() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player just outside island + when(player.getLocation()).thenReturn(outside); + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, outside, inside); + listener.onPlayerMove(e); + // Should not be cancelled + assertFalse(e.isCancelled()); + // Player should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.anyString()); + // User should NOT be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testPlayerMoveInsideLockedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player inside island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2); + listener.onPlayerMove(e); + assertTrue(e.isCancelled()); + // Player should see a message + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(sch).runTask(Mockito.any(), Mockito.any(Runnable.class)); + // Call teleport event + PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(ev); + // Should not be cancelled + assertFalse(ev.isCancelled()); + } + + @Test + public void testPlayerMoveInsideLockedIslandAsOp() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + when(player.isOp()).thenReturn(true); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player inside island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + } + + @Test + public void testPlayerMoveInsideLockedIslandWithBypass() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + when(player.isOp()).thenReturn(false); + when(player.hasPermission(Mockito.anyString())).thenReturn(true); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player inside island + when(player.getLocation()).thenReturn(inside); + + // Lock island for player + when(island.isAllowed(Mockito.any(), Mockito.eq(Flags.LOCK))).thenReturn(false); + + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + } + + + @Test + public void testPlayerMoveInsideLockedIslandAsMember() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + // Place the player inside island + when(player.getLocation()).thenReturn(inside); + // Move player + PlayerMoveEvent e = new PlayerMoveEvent(player, inside, inside2); + listener.onPlayerMove(e); + assertFalse(e.isCancelled()); + // Player should not see a message + Mockito.verify(notifier, Mockito.never()).notify(Mockito.any(), Mockito.anyString()); + // User should not be teleported somewhere + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player)); + } + + @Test + public void testVehicleMoveIntoLockedIsland() { + // Make player + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(uuid); + // Give player an island + when(im.hasIsland(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + Player player2 = mock(Player.class); + UUID uuid2 = UUID.randomUUID(); + when(player2.getUniqueId()).thenReturn(uuid2); + + // Player 1 is not a member, player 2 is an island member + when(island.isAllowed(Mockito.any(User.class), Mockito.any())).thenAnswer(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(0, User.class).getUniqueId().equals(uuid2); + } + + }); + + // Create vehicle and put two players in it. One is a member, the other is not + Vehicle vehicle = mock(Vehicle.class); + List passengers = new ArrayList<>(); + passengers.add(player); + passengers.add(player2); + when(vehicle.getPassengers()).thenReturn(passengers); + // Move vehicle + listener.onVehicleMove(new VehicleMoveEvent(vehicle, outside, inside)); + // Player should see a message and nothing should be sent to Player 2 + Mockito.verify(notifier).notify(Mockito.any(), Mockito.anyString()); + // User should be teleported somewhere + Mockito.verify(im).homeTeleport(Mockito.any(), Mockito.eq(player)); + // Player 2 should not be teleported + Mockito.verify(im, Mockito.never()).homeTeleport(Mockito.any(), Mockito.eq(player2)); + // Call teleport event + PlayerTeleportEvent ev = new PlayerTeleportEvent(player, inside, outside); + // Pass to event listener + listener.onPlayerTeleport(ev); + // Should not be cancelled + assertFalse(ev.isCancelled()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java new file mode 100644 index 0000000..b8c41f7 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java @@ -0,0 +1,314 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Cow; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Zombie; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class }) +public class MobSpawnListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static Zombie zombie; + private static Slime slime; + private static Cow cow; + private static IslandWorldManager iwm; + private static World world; + + @BeforeClass + public static void setUpBeforeClass() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + // Monsters and animals + zombie = mock(Zombie.class); + when(zombie.getLocation()).thenReturn(location); + slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + cow = mock(Cow.class); + when(cow.getLocation()).thenReturn(location); + + } + + @Before + public void setUp() { + // Worlds + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any(Location.class))).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + // Default - plugin is loaded + when(plugin.isLoaded()).thenReturn(true); + } + + @Test + public void testNotLoaded() { + when(plugin.isLoaded()).thenReturn(false); + CreatureSpawnEvent e = new CreatureSpawnEvent(null, SpawnReason.NATURAL); + MobSpawnListener l = new MobSpawnListener(); + assertFalse(l.onNaturalMobSpawn(e)); + assertFalse(e.isCancelled()); + } + + @Test + public void testNullEntity() { + CreatureSpawnEvent e = new CreatureSpawnEvent(null, SpawnReason.NATURAL); + MobSpawnListener l = new MobSpawnListener(); + assertFalse(l.onNaturalMobSpawn(e)); + assertFalse(e.isCancelled()); + } + + @Test + public void testNotInWorld() { + when(iwm.inWorld(any(Location.class))).thenReturn(false); + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Set up entity + LivingEntity entity = mock(LivingEntity.class); + when(entity.getLocation()).thenReturn(null); + + // Setup event + CreatureSpawnEvent e = mock(CreatureSpawnEvent.class); + when(e.getLocation()).thenReturn(location); + + // Setup the listener + MobSpawnListener l = new MobSpawnListener(); + l.setPlugin(plugin); + + // Check monsters + when(e.getEntity()).thenReturn(entity); + + // Should not be canceled + assertFalse(l.onNaturalMobSpawn(e)); + } + + @Test + public void testOnNaturalMonsterSpawnBlocked() { + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Block mobs + when(island.isAllowed(Mockito.any())).thenReturn(false); + + // Setup event + CreatureSpawnEvent e = mock(CreatureSpawnEvent.class); + when(e.getLocation()).thenReturn(location); + + // Setup the listener + MobSpawnListener l = new MobSpawnListener(); + l.setPlugin(plugin); + + // Check monsters + when(e.getEntity()).thenReturn(zombie); + checkBlocked(e,l); + when(e.getEntity()).thenReturn(slime); + checkBlocked(e,l); + // Check animal + when(e.getEntity()).thenReturn(cow); + checkBlocked(e,l); + + } + + private void checkBlocked(CreatureSpawnEvent e, MobSpawnListener l) { + for (SpawnReason reason: SpawnReason.values()) { + when(e.getSpawnReason()).thenReturn(reason); + if (reason.equals(SpawnReason.NATURAL) + || reason.equals(SpawnReason.JOCKEY) + || reason.equals(SpawnReason.CHUNK_GEN) + || reason.equals(SpawnReason.DEFAULT) + || reason.equals(SpawnReason.MOUNT) + || reason.equals(SpawnReason.NETHER_PORTAL)) { + assertTrue(l.onNaturalMobSpawn(e)); + } else { + assertFalse(l.onNaturalMobSpawn(e)); + } + } + + } + + @Test + public void testOnNaturalMobSpawnUnBlocked() { + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + + // Allow mobs + when(island.isAllowed(Mockito.any())).thenReturn(true); + + // Setup event + CreatureSpawnEvent e = mock(CreatureSpawnEvent.class); + when(e.getLocation()).thenReturn(location); + + // Setup the listener + MobSpawnListener l = new MobSpawnListener(); + l.setPlugin(plugin); + + // Check monsters + when(e.getEntity()).thenReturn(zombie); + checkUnBlocked(e,l); + when(e.getEntity()).thenReturn(slime); + checkUnBlocked(e,l); + // Check animal + when(e.getEntity()).thenReturn(cow); + checkUnBlocked(e,l); + + } + + private void checkUnBlocked(CreatureSpawnEvent e, MobSpawnListener l) { + for (SpawnReason reason: SpawnReason.values()) { + when(e.getSpawnReason()).thenReturn(reason); + assertFalse(l.onNaturalMobSpawn(e)); + } + + } + + @Test + public void testOnNaturalMonsterSpawnBlockedNoIsland() { + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + + // Block mobs + Flags.MONSTER_SPAWN.setDefaultSetting(false); + Flags.ANIMAL_SPAWN.setDefaultSetting(false); + // Setup event + CreatureSpawnEvent e = mock(CreatureSpawnEvent.class); + when(e.getLocation()).thenReturn(location); + + // Setup the listener + MobSpawnListener l = new MobSpawnListener(); + l.setPlugin(plugin); + + // Check monsters + when(e.getEntity()).thenReturn(zombie); + checkBlocked(e,l); + when(e.getEntity()).thenReturn(slime); + checkBlocked(e,l); + // Check animal + when(e.getEntity()).thenReturn(cow); + checkBlocked(e,l); + + } + + @Test + public void testOnNaturalMobSpawnUnBlockedNoIsland() { + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.empty()); + + // Block mobs + Flags.MONSTER_SPAWN.setDefaultSetting(true); + Flags.ANIMAL_SPAWN.setDefaultSetting(true); + + // Setup event + CreatureSpawnEvent e = mock(CreatureSpawnEvent.class); + when(e.getLocation()).thenReturn(location); + + // Setup the listener + MobSpawnListener l = new MobSpawnListener(); + l.setPlugin(plugin); + + // Check monsters + when(e.getEntity()).thenReturn(zombie); + checkUnBlocked(e,l); + when(e.getEntity()).thenReturn(slime); + checkUnBlocked(e,l); + // Check animal + when(e.getEntity()).thenReturn(cow); + checkUnBlocked(e,l); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListenerTest.java new file mode 100644 index 0000000..01c5567 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/OfflineRedstoneListenerTest.java @@ -0,0 +1,152 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, Bukkit.class }) +public class OfflineRedstoneListenerTest { + + private World world; + private UUID uuid; + private Island island; + private IslandsManager im; + private Location inside; + private Block block; + + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + // Owner + uuid = UUID.randomUUID(); + + // Island initialization + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + // Add members + Builder set = new ImmutableSet.Builder<>(); + set.add(UUID.randomUUID()); + set.add(UUID.randomUUID()); + set.add(UUID.randomUUID()); + set.add(UUID.randomUUID()); + when(island.getMemberSet()).thenReturn(set.build()); + + + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + inside = mock(Location.class); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + + // Blocks + block = mock(Block.class); + when(block.getWorld()).thenReturn(world); + when(block.getLocation()).thenReturn(inside); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + PowerMockito.mockStatic(Bukkit.class); + } + + @Test + public void testOnBlockRedstoneDoNothing() { + // Make an event to give some current to block + BlockRedstoneEvent e = new BlockRedstoneEvent(block, 0, 10); + OfflineRedstoneListener orl = new OfflineRedstoneListener(); + Flags.OFFLINE_REDSTONE.setSetting(world, true); + orl.onBlockRedstone(e); + // Current remains 10 + assertEquals(10, e.getNewCurrent()); + } + + @Test + public void testOnBlockRedstoneMembersOnline() { + // Make an event to give some current to block + BlockRedstoneEvent e = new BlockRedstoneEvent(block, 0, 10); + OfflineRedstoneListener orl = new OfflineRedstoneListener(); + // Offline redstone not allowed + Flags.OFFLINE_REDSTONE.setSetting(world, false); + // Members are online + when(Bukkit.getPlayer(Mockito.any(UUID.class))).thenReturn(mock(Player.class)); + + orl.onBlockRedstone(e); + // Current remains 10 + assertEquals(10, e.getNewCurrent()); + } + + @Test + public void testOnBlockRedstoneMembersOffline() { + // Make an event to give some current to block + BlockRedstoneEvent e = new BlockRedstoneEvent(block, 0, 10); + OfflineRedstoneListener orl = new OfflineRedstoneListener(); + // Offline redstone not allowed + Flags.OFFLINE_REDSTONE.setSetting(world, false); + // Members are online + when(Bukkit.getPlayer(Mockito.any(UUID.class))).thenReturn(null); + + orl.onBlockRedstone(e); + // Current will be 0 + assertEquals(0, e.getNewCurrent()); + } + + @Test + public void testOnBlockRedstoneNonIsland() { + // Make an event to give some current to block + BlockRedstoneEvent e = new BlockRedstoneEvent(block, 0, 10); + OfflineRedstoneListener orl = new OfflineRedstoneListener(); + Flags.OFFLINE_REDSTONE.setSetting(world, false); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(Optional.empty()); + orl.onBlockRedstone(e); + // Current remains 10 + assertEquals(10, e.getNewCurrent()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/PVPListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/PVPListenerTest.java new file mode 100644 index 0000000..0ed7039 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/PVPListenerTest.java @@ -0,0 +1,949 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Fish; +import org.bukkit.entity.LingeringPotion; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.Witch; +import org.bukkit.entity.Zombie; +import org.bukkit.event.entity.AreaEffectCloudApplyEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableMap; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.panels.Panel; +import us.tastybento.bskyblock.api.panels.PanelItem; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class, Bukkit.class }) +public class PVPListenerTest { + + private IslandWorldManager iwm; + private Panel panel; + private Flag flag; + private IslandsManager im; + private Settings s; + private Island island; + private Player player; + private Player player2; + private Location loc; + private Zombie zombie; + private Creeper creeper; + private World world; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + // Island World Manager + iwm = mock(IslandWorldManager.class); + when(iwm.inWorld(Mockito.any())).thenReturn(true); + when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("bskyblock"); + // No visitor protection right now + when(iwm.getIvSettings(Mockito.any())).thenReturn(new ArrayList<>()); + when(plugin.getIWM()).thenReturn(iwm); + + panel = mock(Panel.class); + when(panel.getInventory()).thenReturn(mock(Inventory.class)); + + // Sometimes use Mockito.withSettings().verboseLogging() + player = mock(Player.class); + UUID uuid = UUID.randomUUID(); + when(player.getUniqueId()).thenReturn(uuid); + + world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(player.getWorld()).thenReturn(world); + + loc = mock(Location.class); + when(loc.getWorld()).thenReturn(world); + + when(player.getLocation()).thenReturn(loc); + User.getInstance(player); + + // Sometimes use Mockito.withSettings().verboseLogging() + player2 = mock(Player.class); + UUID uuid2 = UUID.randomUUID(); + when(player2.getUniqueId()).thenReturn(uuid2); + + when(player2.getWorld()).thenReturn(world); + when(player2.getLocation()).thenReturn(loc); + User.getInstance(player2); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + + FlagsManager fm = mock(FlagsManager.class); + flag = mock(Flag.class); + when(flag.isSetForWorld(Mockito.any())).thenReturn(false); + PanelItem item = mock(PanelItem.class); + when(item.getItem()).thenReturn(mock(ItemStack.class)); + when(flag.toPanelItem(Mockito.any(), Mockito.any())).thenReturn(item); + when(fm.getFlagByID(Mockito.anyString())).thenReturn(flag); + when(plugin.getFlagsManager()).thenReturn(fm); + + im = mock(IslandsManager.class); + // Default is that player in on their island + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + island = mock(Island.class); + when(im.getIslandAt(Mockito.any())).thenReturn(Optional.of(island)); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(Optional.of(island)); + // All flags are disallowed by default. + when(island.isAllowed(Mockito.any())).thenReturn(false); + when(plugin.getIslands()).thenReturn(im); + + // Settings + s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Locales - this returns the string that was requested for translation + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenAnswer(new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgumentAt(1, String.class); + }}); + + // Create some entities + zombie = mock(Zombie.class); + when(zombie.getWorld()).thenReturn(world); + when(zombie.getUniqueId()).thenReturn(UUID.randomUUID()); + creeper = mock(Creeper.class); + when(creeper.getWorld()).thenReturn(world); + when(creeper.getUniqueId()).thenReturn(UUID.randomUUID()); + + // Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageNotPlayer() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Creeper.class); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageSelfDamage() { + Entity damager = mock(Player.class); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damager, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnPlayerByZombie() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Player.class); + World world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(damager.getWorld()).thenReturn(world); + when(damagee.getWorld()).thenReturn(world); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + + // Different attack type + e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + + // Wrong world + e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnPlayerByZombieVisitorProtected() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Player.class); + World world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(damager.getWorld()).thenReturn(world); + when(damagee.getWorld()).thenReturn(world); + + // Protect visitors + List visitorProtectionList = new ArrayList<>(); + visitorProtectionList.add("ENTITY_ATTACK"); + when(iwm.getIvSettings(world)).thenReturn(visitorProtectionList); + // This player is on their island, i.e., not a visitor + + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + // Wrong world + e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnVisitorByZombieVisitorProtected() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Player.class); + World world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(damager.getWorld()).thenReturn(world); + when(damagee.getWorld()).thenReturn(world); + + // Protect visitors + List visitorProtectionList = new ArrayList<>(); + visitorProtectionList.add("ENTITY_ATTACK"); + when(iwm.getIvSettings(world)).thenReturn(visitorProtectionList); + // This player is a visitor + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertTrue(e.isCancelled()); + // Wrong world + e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnVisitorByZombieVisitorProtectedWrongDamage() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Player.class); + World world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(damager.getWorld()).thenReturn(world); + when(damagee.getWorld()).thenReturn(world); + + // Protect visitors + List visitorProtectionList = new ArrayList<>(); + visitorProtectionList.add("ENTITY_ATTACK"); + when(iwm.getIvSettings(world)).thenReturn(visitorProtectionList); + // This player is a visitor + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + // Damage is not entity attack + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.THORNS, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnVisitorByZombieVisitorNotProtected() { + Entity damager = mock(Zombie.class); + Entity damagee = mock(Player.class); + World world = mock(World.class); + when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); + when(damager.getWorld()).thenReturn(world); + when(damagee.getWorld()).thenReturn(world); + + // This player is a visitor + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + // Wrong world + e = new EntityDamageByEntityEvent(damager, damagee, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + when(iwm.inWorld(Mockito.any())).thenReturn(false); + new PVPListener().onEntityDamage(e); + assertFalse(e.isCancelled()); + } + + // PVP TESTS + /* + * PVP Tests + * + * Variables: + * PVP on/off -> Direct hit / Projectile + * Visitor protection on/off -> protection type correct/incorrect + * + */ + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamagePVPNotAllowed() { + + // No visitor protection + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(player, player2, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + // PVP should be banned + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + // Enable visitor protection + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onEntityDamage(e); + // visitor should be protected + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnPVPAllowed() { + // PVP is allowed + when(island.isAllowed(Mockito.any())).thenReturn(true); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(player, player2, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + // PVP should be allowed + assertFalse(e.isCancelled()); + Mockito.verify(player, Mockito.never()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + // Enable visitor protection + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onEntityDamage(e); + // visitor should be protected + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageOnPVPNotAllowedProjectile() { + Projectile p = mock(Projectile.class); + when(p.getShooter()).thenReturn(player); + when(p.getLocation()).thenReturn(loc); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(p, player2, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + // PVP should be banned + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + // Visitor protection + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onEntityDamage(e); + // visitor should be protected + assertTrue(e.isCancelled()); + // PVP trumps visitor protection + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamageSelfDamageProjectile() { + Projectile p = mock(Projectile.class); + when(p.getShooter()).thenReturn(player); + when(p.getLocation()).thenReturn(loc); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(p, player, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + // Self damage okay + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onEntityDamage(org.bukkit.event.entity.EntityDamageByEntityEvent)}. + */ + @Test + public void testOnEntityDamagePVPAllowedProjectile() { + Projectile p = mock(Projectile.class); + when(p.getShooter()).thenReturn(player); + when(p.getLocation()).thenReturn(loc); + // PVP is allowed + when(island.isAllowed(Mockito.any())).thenReturn(true); + EntityDamageByEntityEvent e = new EntityDamageByEntityEvent(p, player2, EntityDamageEvent.DamageCause.ENTITY_ATTACK, + new EnumMap(ImmutableMap.of(DamageModifier.BASE, 0D)), + new EnumMap>(ImmutableMap.of(DamageModifier.BASE, Functions.constant(-0.0)))); + new PVPListener().onEntityDamage(e); + // PVP should be allowed + assertFalse(e.isCancelled()); + Mockito.verify(player, Mockito.never()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + // Enable visitor protection + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onEntityDamage(e); + // visitor should be protected + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishing() { + // Fish hook + Fish hook = mock(Fish.class); + // Catch a zombie - fine + Entity caught = mock(Zombie.class); + PlayerFishEvent pfe = new PlayerFishEvent(player, caught, hook, null); + new PVPListener().onFishing(pfe); + assertFalse(pfe.isCancelled()); + + // Catch a player + pfe = new PlayerFishEvent(player, player2, hook, null); + new PVPListener().onFishing(pfe); + + // PVP should be banned + assertTrue(pfe.isCancelled()); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + // Hook should be removed + Mockito.verify(hook).remove(); + + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + pfe = new PlayerFishEvent(player, player2, hook, null); + new PVPListener().onFishing(pfe); + assertFalse(pfe.isCancelled()); + + // Correct world + when(iwm.inWorld(Mockito.any())).thenReturn(true); + + // Allow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + pfe = new PlayerFishEvent(player, player2, hook, null); + new PVPListener().onFishing(pfe); + assertFalse(pfe.isCancelled()); + + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + pfe = new PlayerFishEvent(player, player2, hook, null); + new PVPListener().onFishing(pfe); + assertFalse(pfe.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishingProtectVisitors() { + // Fish hook + Fish hook = mock(Fish.class); + // Catch a player + PlayerFishEvent pfe = new PlayerFishEvent(player, player2, hook, null); + + // Allow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + + // Protect visitors + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onFishing(pfe); + // visitor should be protected + assertTrue(pfe.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishingSelfDamage() { + // Fish hook + Fish hook = mock(Fish.class); + // Catch a player + PlayerFishEvent pfe = new PlayerFishEvent(player, player, hook, null); + assertFalse(pfe.isCancelled()); + Mockito.verify(player, Mockito.never()).sendMessage(Mockito.anyString()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onFishing(org.bukkit.event.player.PlayerFishEvent)}. + */ + @Test + public void testOnFishingNoPVPProtectVisitors() { + // Fish hook + Fish hook = mock(Fish.class); + // Catch a player + PlayerFishEvent pfe = new PlayerFishEvent(player, player2, hook, null); + + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(false); + + // Protect visitors + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onFishing(pfe); + // visitor should be protected + assertTrue(pfe.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplashWitch() { + ThrownPotion tp = mock(ThrownPotion.class); + ProjectileSource witch = mock(Witch.class); + when(tp.getShooter()).thenReturn(witch); + PotionSplashEvent e = new PotionSplashEvent(tp, new HashMap<>()); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplashNoPlayers() { + ThrownPotion tp = mock(ThrownPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + // Create a damage map + Map map = new HashMap<>(); + map.put(zombie, 100D); + map.put(creeper, 10D); + PotionSplashEvent e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplash() { + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(false); + + ThrownPotion tp = mock(ThrownPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + // Create a damage map + Map map = new HashMap<>(); + map.put(player2, 100D); + map.put(zombie, 100D); + map.put(creeper, 10D); + PotionSplashEvent e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplashSelfInflicted() { + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(false); + + ThrownPotion tp = mock(ThrownPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + // Create a damage map + Map map = new HashMap<>(); + map.put(player, 100D); + map.put(zombie, 100D); + map.put(creeper, 10D); + PotionSplashEvent e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplashAllowPVP() { + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + + ThrownPotion tp = mock(ThrownPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + // Create a damage map + Map map = new HashMap<>(); + map.put(player2, 100D); + map.put(zombie, 100D); + map.put(creeper, 10D); + PotionSplashEvent e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + Mockito.verify(player, Mockito.never()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onSplashPotionSplash(org.bukkit.event.entity.PotionSplashEvent)}. + */ + @Test + public void testOnSplashPotionSplashAllowPVPProtectVisitors() { + // Allow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + + ThrownPotion tp = mock(ThrownPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + // Create a damage map + Map map = new HashMap<>(); + map.put(player2, 100D); + map.put(zombie, 100D); + map.put(creeper, 10D); + PotionSplashEvent e = new PotionSplashEvent(tp, map); + // Protect visitors + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + new PVPListener().onSplashPotionSplash(e); + // visitor should be protected + assertTrue(e.isCancelled()); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + e = new PotionSplashEvent(tp, map); + new PVPListener().onSplashPotionSplash(e); + assertFalse(e.isCancelled()); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionSplash(org.bukkit.event.entity.LingeringPotionSplashEvent)}. + */ + @Test + public void testOnLingeringPotionSplash() { + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + new PVPListener().onLingeringPotionSplash(e); + // Verify + Mockito.verify(player, Mockito.times(4)).getUniqueId(); + Mockito.verify(cloud).getEntityId(); + Mockito.verify(tp, Mockito.times(2)).getShooter(); + PowerMockito.verifyStatic(Bukkit.class); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionSplash(org.bukkit.event.entity.LingeringPotionSplashEvent)}. + */ + @Test + public void testOnLingeringPotionSplashNonHuman() { + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(creeper); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + new PVPListener().onLingeringPotionSplash(e); + // Verify + Mockito.verify(cloud, Mockito.never()).getEntityId(); + Mockito.verify(tp).getShooter(); + PowerMockito.verifyStatic(Bukkit.class, Mockito.never()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionDamage(org.bukkit.event.entity.AreaEffectCloudApplyEvent)}. + */ + @Test + public void testOnLingeringPotionDamageNoPVP() { + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(false); + // Throw a potion + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + when(cloud.getWorld()).thenReturn(world); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + PVPListener listener = new PVPListener(); + listener.onLingeringPotionSplash(e); + List list = new ArrayList<>(); + list.add(player); // This player will still suffer + list.add(creeper); + list.add(player2); + list.add(zombie); + // See who it affects + AreaEffectCloudApplyEvent ae = new AreaEffectCloudApplyEvent(cloud, list); + listener.onLingeringPotionDamage(ae); + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onLingeringPotionSplash(e); + // No change to results + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionDamage(org.bukkit.event.entity.AreaEffectCloudApplyEvent)}. + */ + @Test + public void testOnLingeringPotionDamagePVP() { + // Allow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + // Throw a potion + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + when(cloud.getWorld()).thenReturn(world); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + PVPListener listener = new PVPListener(); + listener.onLingeringPotionSplash(e); + List list = new ArrayList<>(); + list.add(player); // This player will still suffer + list.add(creeper); + list.add(player2); + list.add(zombie); + // See who it affects + AreaEffectCloudApplyEvent ae = new AreaEffectCloudApplyEvent(cloud, list); + listener.onLingeringPotionDamage(ae); + assertEquals(4, ae.getAffectedEntities().size()); + Mockito.verify(player, Mockito.never()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onLingeringPotionSplash(e); + assertEquals(4, ae.getAffectedEntities().size()); + Mockito.verify(player, Mockito.never()).sendMessage(Flags.PVP_OVERWORLD.getHintReference()); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionDamage(org.bukkit.event.entity.AreaEffectCloudApplyEvent)}. + */ + @Test + public void testOnLingeringPotionDamageNoPVPVisitor() { + // Disallow PVP + when(island.isAllowed(Mockito.any())).thenReturn(false); + // Throw a potion + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + when(cloud.getWorld()).thenReturn(world); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + PVPListener listener = new PVPListener(); + listener.onLingeringPotionSplash(e); + List list = new ArrayList<>(); + list.add(player); // This player will still suffer + list.add(creeper); + list.add(player2); + list.add(zombie); + // Protect visitor + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + + // See who it affects + AreaEffectCloudApplyEvent ae = new AreaEffectCloudApplyEvent(cloud, list); + listener.onLingeringPotionDamage(ae); + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onLingeringPotionSplash(e); + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.PVPListener#onLingeringPotionDamage(org.bukkit.event.entity.AreaEffectCloudApplyEvent)}. + */ + @Test + public void testOnLingeringPotionDamagePVPVisitor() { + // Allow PVP + when(island.isAllowed(Mockito.any())).thenReturn(true); + // Throw a potion + LingeringPotion tp = mock(LingeringPotion.class); + when(tp.getShooter()).thenReturn(player); + when(tp.getWorld()).thenReturn(world); + AreaEffectCloud cloud = mock(AreaEffectCloud.class); + when(cloud.getWorld()).thenReturn(world); + LingeringPotionSplashEvent e = new LingeringPotionSplashEvent(tp, cloud); + PVPListener listener = new PVPListener(); + listener.onLingeringPotionSplash(e); + List list = new ArrayList<>(); + list.add(player); // This player will still suffer + list.add(creeper); + list.add(player2); + list.add(zombie); + // Protect visitor + // This player is a visitor and any damage is not allowed + when(im.userIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + when(iwm.getIvSettings(Mockito.any())).thenReturn(Arrays.asList("ENTITY_ATTACK")); + + // See who it affects + AreaEffectCloudApplyEvent ae = new AreaEffectCloudApplyEvent(cloud, list); + listener.onLingeringPotionDamage(ae); + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + // Wrong world + when(iwm.inWorld(Mockito.any())).thenReturn(false); + listener.onLingeringPotionSplash(e); + assertEquals(3, ae.getAffectedEntities().size()); + assertFalse(ae.getAffectedEntities().contains(player2)); + Mockito.verify(player).sendMessage(Flags.INVINCIBLE_VISITORS.getHintReference()); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java new file mode 100644 index 0000000..cf4c46a --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java @@ -0,0 +1,141 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class }) +public class PistonPushListenerTest { + + private Island island; + private IslandsManager im; + private World world; + private Location inside; + private UUID uuid; + private Block block; + private List blocks; + private Block blockPushed; + + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Owner + uuid = UUID.randomUUID(); + + // Island initialization + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + inside = mock(Location.class); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + + // Blocks + block = mock(Block.class); + when(block.getWorld()).thenReturn(world); + when(block.getLocation()).thenReturn(inside); + + blockPushed = mock(Block.class); + + when(block.getRelative(Mockito.any(BlockFace.class))).thenReturn(blockPushed); + + // The blocks in the pushed list are all inside the island + when(blockPushed.getLocation()).thenReturn(inside); + + // Make a list of ten blocks + blocks = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + blocks.add(block); + } + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + } + + @Test + public void testOnPistonExtendFlagNotSet() { + Flags.PISTON_PUSH.setSetting(world, false); + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because flag is not set + assertFalse(e.isCancelled()); + } + + @Test + public void testOnPistonExtendFlagSetOnIsland() { + + // The blocks in the pushed list are all inside the island + when(island.onIsland(Mockito.any())).thenReturn(true); + + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because on island + assertFalse(e.isCancelled()); + } + + @Test + public void testOnPistonExtendFlagSetOffIsland() { + // The blocks in the pushed list are all outside the island + when(island.onIsland(Mockito.any())).thenReturn(false); + + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because on island + assertTrue(e.isCancelled()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java new file mode 100644 index 0000000..64967d2 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java @@ -0,0 +1,133 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class }) +public class RemoveMobsListenerTest { + + private Island island; + private IslandsManager im; + private World world; + private Location inside; + private UUID uuid; + private Player player; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BSkyBlock plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // World + world = mock(World.class); + + // Owner + uuid = UUID.randomUUID(); + + // Island initialization + island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + inside = mock(Location.class); + when(inside.getWorld()).thenReturn(world); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + // On island + when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(true); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // World Settings + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + Flags.REMOVE_MOBS.setSetting(world, true); + + // Sometimes use Mockito.withSettings().verboseLogging() + player = mock(Player.class); + UUID uuid = UUID.randomUUID(); + when(player.getUniqueId()).thenReturn(uuid); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.RemoveMobsListener#onUserTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. + */ + @Test + public void testOnUserTeleport() { + PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); + new RemoveMobsListener().onUserTeleport(e); + Mockito.verify(im).clearArea(Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.RemoveMobsListener#onUserTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. + */ + @Test + public void testOnUserTeleportDoNotRemove() { + Flags.REMOVE_MOBS.setSetting(world, false); + PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); + new RemoveMobsListener().onUserTeleport(e); + Mockito.verify(im, Mockito.never()).clearArea(Mockito.any()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.flags.RemoveMobsListener#onUserTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. + */ + @Test + public void testOnUserTeleportToNotIsland() { + // Not on island + when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); + PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); + new RemoveMobsListener().onUserTeleport(e); + Mockito.verify(im, Mockito.never()).clearArea(Mockito.any()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/TNTListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/TNTListenerTest.java new file mode 100644 index 0000000..dd1df21 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/TNTListenerTest.java @@ -0,0 +1,317 @@ +package us.tastybento.bskyblock.listeners.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Slime; +import org.bukkit.entity.WitherSkeleton; +import org.bukkit.entity.Zombie; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.Notifier; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Util.class} ) +public class TNTListenerTest { + + private static Location location; + private static BSkyBlock plugin; + private static IslandWorldManager iwm; + private static IslandsManager im; + private static Notifier notifier; + + @BeforeClass + public static void setUpClass() { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + PowerMockito.mockStatic(Flags.class); + + FlagsManager flagsManager = new FlagsManager(plugin); + when(plugin.getFlagsManager()).thenReturn(flagsManager); + + + // Worlds + iwm = mock(IslandWorldManager.class); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Monsters and animals + Zombie zombie = mock(Zombie.class); + when(zombie.getLocation()).thenReturn(location); + Slime slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + Cow cow = mock(Cow.class); + when(cow.getLocation()).thenReturn(location); + + // Fake players + Settings settings = mock(Settings.class); + Mockito.when(plugin.getSettings()).thenReturn(settings); + Mockito.when(settings.getFakePlayers()).thenReturn(new HashSet()); + + // Users + //User user = mock(User.class); + ///user.setPlugin(plugin); + User.setPlugin(plugin); + + + // Locales - final + + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + Answer answer = new Answer() { + + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return (String)Arrays.asList(invocation.getArguments()).get(1); + } + + }; + when(lm.get(any(), any())).thenAnswer(answer); + + // Player name + PlayersManager pm = mock(PlayersManager.class); + when(pm.getName(Mockito.any())).thenReturn("tastybento"); + when(plugin.getPlayers()).thenReturn(pm); + + // World Settings + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + // Island manager + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + Optional optional = Optional.of(island); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(optional); + + // Notifier + notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + } + + @Before + public void setUp() { + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class)); + } + + @Test + public void testOnTNTPriming() { + BlockFace clickedFace = BlockFace.DOWN; + Block clickedBlock = mock(Block.class); + when(clickedBlock.getType()).thenReturn(Material.TNT); + when(clickedBlock.getLocation()).thenReturn(location); + ItemStack item = new ItemStack(Material.FLINT_AND_STEEL); + Action action = Action.RIGHT_CLICK_BLOCK; + Player player = mock(Player.class); + PlayerInteractEvent e = new PlayerInteractEvent(player , action, item, clickedBlock, clickedFace); + + TNTListener listener = new TNTListener(); + listener.setPlugin(plugin); + listener.onTNTPriming(e); + assertTrue(e.isCancelled()); + Mockito.verify(notifier).notify(Mockito.any(), Mockito.eq("protection.protected")); + } + + @Test + public void testOnExplosion() { + Entity entity = mock(Entity.class); + when(entity.getType()).thenReturn(EntityType.PRIMED_TNT); + List list = new ArrayList<>(); + Block block = mock(Block.class); + when(block.getLocation()).thenReturn(location); + list.add(block); + EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0); + TNTListener listener = new TNTListener(); + listener.setPlugin(plugin); + listener.onExplosion(e); + assertTrue(e.isCancelled()); + assertTrue(list.isEmpty()); + } + + @Test + public void testOnTNTDamage() { + // Notifier + Notifier notifier = mock(Notifier.class); + when(plugin.getNotifier()).thenReturn(notifier); + + // Island + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + Island island = mock(Island.class); + when(im.getIslandAt(Matchers.any())).thenReturn(Optional.of(island)); + when(im.getProtectedIslandAt(Mockito.any())).thenReturn(Optional.of(island)); + + // Block on fire + Block block = mock(Block.class); + when(block.getLocation()).thenReturn(location); + when(block.getType()).thenReturn(Material.OBSIDIAN); + EntityChangeBlockEvent e = mock(EntityChangeBlockEvent.class); + when(e.getBlock()).thenReturn(block); + + + // TNT listener + TNTListener listener = new TNTListener(); + listener.setPlugin(plugin); + + // Obsidian is not TNT + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + // Out of world + when(block.getLocation()).thenReturn(null); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + + // Now set to TNT + when(block.getType()).thenReturn(Material.TNT); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + + // Back in world + when(block.getLocation()).thenReturn(location); + + // Entity is not a projectile + Player player = mock(Player.class); + when(e.getEntity()).thenReturn(player); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + + // Entity is an arrow + Arrow arrow = mock(Arrow.class); + // Shooter is a skeleton + WitherSkeleton skeleton = mock(WitherSkeleton.class); + when(arrow.getShooter()).thenReturn(skeleton); + // No fire arrow + when(arrow.getFireTicks()).thenReturn(0); + when(e.getEntity()).thenReturn(arrow); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + // Fire arrow + when(arrow.getFireTicks()).thenReturn(10); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + + // Shooter is a player + when(arrow.getShooter()).thenReturn(player); + // No fire arrow + when(arrow.getFireTicks()).thenReturn(0); + when(e.getEntity()).thenReturn(arrow); + listener.onTNTDamage(e); + assertFalse(e.isCancelled()); + + // Fire arrow + when(arrow.getFireTicks()).thenReturn(10); + + /* + // Break blocks not allowed, general flag should have no effect + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(false); + Flags.BREAK_BLOCKS.setDefaultSetting(false); + assertTrue(listener.onTNTDamage(e)); + Flags.BREAK_BLOCKS.setDefaultSetting(true); + assertTrue(listener.onTNTDamage(e)); + + // Allow BREAK_BLOCKS spread + when(island.isAllowed(Mockito.any(), Mockito.any())).thenReturn(true); + Flags.BREAK_BLOCKS.setDefaultSetting(false); + assertFalse(listener.onTNTDamage(e)); + Flags.BREAK_BLOCKS.setDefaultSetting(true); + assertFalse(listener.onTNTDamage(e)); + + // Check with no island + when(im.getProtectedIslandAt(Matchers.any())).thenReturn(Optional.empty()); + // BREAK_BLOCKS spread is not allowed, so should be cancelled + Flags.BREAK_BLOCKS.setDefaultSetting(false); + assertTrue(listener.onTNTDamage(e)); + // BREAK_BLOCKS allowed + Flags.BREAK_BLOCKS.setDefaultSetting(true); + assertFalse(listener.onTNTDamage(e)); + */ + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEventsTest.java b/src/test/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEventsTest.java new file mode 100644 index 0000000..9af5196 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/protection/FlyingMobEventsTest.java @@ -0,0 +1,384 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.protection; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Wither; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class, Util.class }) +public class FlyingMobEventsTest { + + private BSkyBlock plugin; + private IslandsManager im; + private PlayersManager pm; + private BukkitScheduler sch; + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Settings + Settings s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + User user = mock(User.class); + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + UUID notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Player has island to begin with + im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // Normally in world + Util.setPlugin(plugin); + + // Worlds + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(any())).thenReturn(true); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#FlyingMobEvents(us.tastybento.bskyblock.BSkyBlock)}. + */ + @Test + public void testFlyingMobEvents() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + assertNotNull(fme); + Mockito.verify(sch).runTaskTimer(Mockito.eq(plugin), Mockito.any(Runnable.class), Mockito.eq(20L), Mockito.eq(20L)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobSpawn(org.bukkit.event.entity.CreatureSpawnEvent)}. + */ + @Test + public void testOnMobSpawnNotInWorld() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + LivingEntity le = mock(LivingEntity.class); + CreatureSpawnEvent e = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + // Not in world + when(iwm.inWorld(any())).thenReturn(false); + fme.onMobSpawn(e); + Mockito.verify(im, Mockito.never()).getIslandAt(Mockito.any(Location.class)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobSpawn(org.bukkit.event.entity.CreatureSpawnEvent)}. + */ + @Test + public void testOnMobSpawnInWorldWrongType() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + LivingEntity le = mock(LivingEntity.class); + when(le.getType()).thenReturn(EntityType.AREA_EFFECT_CLOUD); + CreatureSpawnEvent e = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(e); + Mockito.verify(im, Mockito.never()).getIslandAt(Mockito.any(Location.class)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobSpawn(org.bukkit.event.entity.CreatureSpawnEvent)}. + */ + @Test + public void testOnMobSpawnInWorldCorrectType() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + LivingEntity le = mock(LivingEntity.class); + when(le.getLocation()).thenReturn(mock(Location.class)); + Optional oi = Optional.of(mock(Island.class)); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(le.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent e = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(e); + Mockito.verify(im).getIslandAt(Mockito.any(Location.class)); + // Blaze + when(le.getType()).thenReturn(EntityType.BLAZE); + e = new CreatureSpawnEvent(le, SpawnReason.NATURAL); + fme.onMobSpawn(e); + Mockito.verify(im, Mockito.times(2)).getIslandAt(Mockito.any(Location.class)); + // Ghast + when(le.getType()).thenReturn(EntityType.GHAST); + e = new CreatureSpawnEvent(le, SpawnReason.NATURAL); + fme.onMobSpawn(e); + Mockito.verify(im, Mockito.times(3)).getIslandAt(Mockito.any(Location.class)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnMobExplosionFail() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Entity, Location, list of Blocks, yield + EntityExplodeEvent e = new EntityExplodeEvent(null, null, null, 0); + // null entity + assertFalse(fme.onMobExplosion(e)); + + // Not in world + Entity ent = mock(Entity.class); + when(iwm.inWorld(any())).thenReturn(false); + e = new EntityExplodeEvent(ent, null, null, 0); + assertFalse(fme.onMobExplosion(e)); + + // Unknown entity (not in the list) + when(iwm.inWorld(any())).thenReturn(true); + assertFalse(fme.onMobExplosion(e)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnMobExplosionOnIsland() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn an entity + LivingEntity le = mock(LivingEntity.class); + when(le.getLocation()).thenReturn(mock(Location.class)); + Island island = mock(Island.class); + // Start with ghast exploding in island space + when(island.inIslandSpace(Mockito.any(Location.class))).thenReturn(true); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(le.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(cee); + // Make the wither explode + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + // Create event + EntityExplodeEvent e = new EntityExplodeEvent(le, mock(Location.class), affectedBlocks, 0); + // Nothing blocked + assertFalse(fme.onMobExplosion(e)); + assertFalse(e.isCancelled()); + assertFalse(e.blockList().isEmpty()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnMobExplosionOffIsland() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn an entity + LivingEntity le = mock(LivingEntity.class); + when(le.getLocation()).thenReturn(mock(Location.class)); + Island island = mock(Island.class); + // Ghast exploding outside of island space + when(island.inIslandSpace(Mockito.any(Location.class))).thenReturn(false); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(le.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(cee); + // Make the wither explode + // Entity, Location, list of Blocks, yield + Block block = mock(Block.class); + // One block will be blown up by the wither + List affectedBlocks = new ArrayList<>(); + affectedBlocks.add(block); + // Create event + EntityExplodeEvent e = new EntityExplodeEvent(le, mock(Location.class), affectedBlocks, 0); + // Blocked + assertTrue(fme.onMobExplosion(e)); + assertTrue(e.isCancelled()); + assertTrue(e.blockList().isEmpty()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onWitherExplode(org.bukkit.event.entity.ExplosionPrimeEvent)}. + */ + @Test + public void testOnWitherExplode() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn an entity + LivingEntity le = mock(LivingEntity.class); + when(le.getLocation()).thenReturn(mock(Location.class)); + Island island = mock(Island.class); + // Ghast exploding outside of island space + when(island.inIslandSpace(Mockito.any(Location.class))).thenReturn(false); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(le.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(le, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(cee); + // Make the wither explode + // Create event + ExplosionPrimeEvent e = new ExplosionPrimeEvent(le, 0, false); + // Blocked + assertTrue(fme.onWitherExplode(e)); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onWitherExplode(org.bukkit.event.entity.ExplosionPrimeEvent)}. + */ + @Test + public void testOnWitherSkullExplode() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn a wither + Wither wither = mock(Wither.class); + when(wither.getLocation()).thenReturn(mock(Location.class)); + Island island = mock(Island.class); + // Ghast exploding outside of island space + when(island.inIslandSpace(Mockito.any(Location.class))).thenReturn(false); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(wither.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(wither, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(cee); + // Make the wither shoot a skull + Projectile skull = mock(Projectile.class); + when(skull.getType()).thenReturn(EntityType.WITHER_SKULL); + when(skull.getShooter()).thenReturn(wither); + + // Create event + ExplosionPrimeEvent e = new ExplosionPrimeEvent(skull, 0, false); + // Blocked + assertTrue(fme.onWitherExplode(e)); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onWitherChangeBlocks(org.bukkit.event.entity.EntityChangeBlockEvent)}. + */ + @Test + public void testOnWitherChangeBlocks() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn a wither + Wither wither = mock(Wither.class); + when(wither.getLocation()).thenReturn(mock(Location.class)); + Island island = mock(Island.class); + // Ghast exploding outside of island space + when(island.inIslandSpace(Mockito.any(Location.class))).thenReturn(false); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + // Wither + when(wither.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(wither, SpawnReason.BUILD_WITHER); + fme.onMobSpawn(cee); + // Create event + /** + * + * @param what the Entity causing the change + * @param block the block (before the change) + * @param to the future material being changed to + * @param data the future block data + * @deprecated Magic value + */ + @SuppressWarnings("deprecation") + EntityChangeBlockEvent e = new EntityChangeBlockEvent(wither, mock(Block.class), Material.AIR, (byte) 0); + // Blocked + fme.onWitherChangeBlocks(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.listeners.protection.FlyingMobEvents#onMobDeath(org.bukkit.event.entity.EntityDeathEvent)}. + */ + @Test + public void testOnMobDeath() { + FlyingMobEvents fme = new FlyingMobEvents(plugin); + // Spawn a wither + Wither wither = mock(Wither.class); + // Wither + when(wither.getType()).thenReturn(EntityType.WITHER); + CreatureSpawnEvent cee = new CreatureSpawnEvent(wither, SpawnReason.BUILD_WITHER); + Island island = mock(Island.class); + Optional oi = Optional.of(island); + when(im.getIslandAt(Mockito.any(Location.class))).thenReturn(oi); + fme.onMobSpawn(cee); + // Kill it + EntityDeathEvent e = new EntityDeathEvent(wither, null); + assertNotNull(fme.onMobDeath(e)); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/managers/FlagsManagerTest.java b/src/test/java/us/tastybento/bskyblock/managers/FlagsManagerTest.java new file mode 100644 index 0000000..7c51f7a --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/managers/FlagsManagerTest.java @@ -0,0 +1,159 @@ +package us.tastybento.bskyblock.managers; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Comparator; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.api.flags.FlagBuilder; +import us.tastybento.bskyblock.listeners.flags.BreakBlocksListener; +import us.tastybento.bskyblock.lists.Flags; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BSkyBlock.class, Flags.class, Bukkit.class} ) +public class FlagsManagerTest { + + + private static BSkyBlock plugin; + private static Server server; + + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Plugin is loaded + when(plugin.isLoaded()).thenReturn(true); + + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + + server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + SkullMeta skullMeta = mock(SkullMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(skullMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + PowerMockito.mockStatic(Flags.class); + + } + + @Test + public void testFlagsManager() { + assertNotNull(new FlagsManager(plugin)); + } + + @Test + public void testRegisterDuplicateFlag() { + FlagsManager fm = new FlagsManager(plugin); + // Try to register every single flag - it should fail every time + Flags.values().forEach(dupe -> assertFalse(fm.registerFlag(dupe))); + } + + @Test + public void testRegisterDuplicateFlagIcons() { + FlagsManager fm = new FlagsManager(plugin); + // Change the ID to something random, but use every icon that is already used + Flags.values().forEach(dupe -> { + assertFalse(fm.registerFlag(new FlagBuilder() + .id(UUID.randomUUID().toString()) + .icon(dupe.getIcon()) + .listener(new BreakBlocksListener()) + .build())); + }); + } + + @Test + public void testRegisteroriginalFlagPluginNotLoaded() { + when(plugin.isLoaded()).thenReturn(false); + FlagsManager fm = new FlagsManager(plugin); + // This should pass + Flag originalFlag = new FlagBuilder().id("ORIGINAL").icon(Material.EMERALD_BLOCK).listener(new BreakBlocksListener()).build(); + assertTrue(fm.registerFlag(originalFlag)); + // Verify no Bukkit listener registered + Mockito.verify(server, Mockito.never()).getPluginManager(); + } + + + @Test + public void testRegisteroriginalFlagPluginLoadedOriginalListener() { + when(plugin.isLoaded()).thenReturn(true); + FlagsManager fm = new FlagsManager(plugin); + // This should pass + OriginalListener ol = new OriginalListener(); + Flag originalFlag = new FlagBuilder().id("ORIGINAL").icon(Material.EMERALD_BLOCK).listener(ol).build(); + assertTrue(fm.registerFlag(originalFlag)); + // Verify registered + Mockito.verify(server).getPluginManager(); + // Register another flag with same listener + Flag originalFlag2 = new FlagBuilder().id("ORIGINAL2").icon(Material.COAL_ORE).listener(ol).build(); + assertTrue(fm.registerFlag(originalFlag2)); + // Verify registered only once + Mockito.verify(server).getPluginManager(); + } + + class OriginalListener implements Listener { + // nothing here + } + + + @Test + public void testGetFlags() { + FlagsManager fm = new FlagsManager(plugin); + assertThat(fm.getFlags(), is(Flags.values())); + } + + @Test + public void testGetFlagByID() { + FlagsManager fm = new FlagsManager(plugin); + // Test in forward and reverse order so that any duplicates are caught + Flags.values().stream().sorted().forEach(flag -> assertEquals(flag, fm.getFlagByID(flag.getID()))); + Flags.values().stream().sorted(Comparator.reverseOrder()).forEach(flag -> assertEquals(flag, fm.getFlagByID(flag.getID()))); + + } + + +} diff --git a/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java b/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java new file mode 100644 index 0000000..cda8975 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java @@ -0,0 +1,885 @@ +package us.tastybento.bskyblock.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Wither; +import org.bukkit.entity.Zombie; +import org.bukkit.material.MaterialData; +import org.bukkit.material.TrapDoor; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.island.IslandCache; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest( { Bukkit.class, BSkyBlock.class, IslandsManager.class, Util.class, Location.class }) +public class IslandsManagerTest { + + private static BSkyBlock plugin; + private UUID uuid; + private User user; + private Settings s; + private PlayersManager pm; + private Player player; + private static World world; + private IslandsManager manager; + private Block space1; + private Block ground; + private Block space2; + private Location location; + private BlockState blockState; + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // World + world = mock(World.class); + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + s = mock(Settings.class); + when(s.getResetWait()).thenReturn(0L); + when(s.getResetLimit()).thenReturn(3); + when(plugin.getSettings()).thenReturn(s); + + // Player + player = mock(Player.class); + // Sometimes use: Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + User.setPlugin(plugin); + // Set up user already + User.getInstance(player); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + + // Has team + pm = mock(PlayersManager.class); + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Standard location + manager = new IslandsManager(plugin); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + space1 = mock(Block.class); + ground = mock(Block.class); + space2 = mock(Block.class); + when(location.getBlock()).thenReturn(space1); + when(space1.getRelative(BlockFace.DOWN)).thenReturn(ground); + when(space1.getRelative(BlockFace.UP)).thenReturn(space2); + // A safe spot + when(ground.getType()).thenReturn(Material.STONE); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + // Neutral BlockState + blockState = mock(BlockState.class); + when(ground.getState()).thenReturn(blockState); + MaterialData md = mock(MaterialData.class); + when(blockState.getData()).thenReturn(md); + + // Online players + // Return a set of online players + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getOnlinePlayers()).then(new Answer>() { + + @Override + public Set answer(InvocationOnMock invocation) throws Throwable { + + return new HashSet<>(); + } + + }); + + // Worlds + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // Scheduler + when(Bukkit.getScheduler()).thenReturn(mock(BukkitScheduler.class)); + } + + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationSafe() { + IslandsManager manager = new IslandsManager(plugin); + assertTrue(manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationNull() { + IslandsManager manager = new IslandsManager(plugin); + assertFalse(manager.isSafeLocation(null)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationNonSolidGround() { + when(ground.getType()).thenReturn(Material.WATER); + assertFalse(manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationSubmerged() { + when(ground.getType()).thenReturn(Material.STONE); + when(space1.isLiquid()).thenReturn(true); + when(space2.isLiquid()).thenReturn(true); + assertFalse(manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationPortals() { + when(ground.getType()).thenReturn(Material.STONE); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.PORTAL); + assertFalse(manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.STONE); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.ENDER_PORTAL); + assertFalse(manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.STONE); + when(space1.getType()).thenReturn(Material.PORTAL); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse(manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.STONE); + when(space1.getType()).thenReturn(Material.ENDER_PORTAL); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse(manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.PORTAL); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse(manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.ENDER_PORTAL); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse(manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testIsSafeLocationLava() { + when(ground.getType()).thenReturn(Material.LAVA); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse("In lava", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.AIR); + when(space1.getType()).thenReturn(Material.LAVA); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse("In lava", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.AIR); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.LAVA); + assertFalse("In lava", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.STATIONARY_LAVA); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse("In lava", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.AIR); + when(space1.getType()).thenReturn(Material.STATIONARY_LAVA); + when(space2.getType()).thenReturn(Material.AIR); + assertFalse("In lava", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.AIR); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.STATIONARY_LAVA); + assertFalse("In lava", manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testTrapDoor() { + when(ground.getType()).thenReturn(Material.TRAP_DOOR); + + // Open trapdoor + TrapDoor trapDoor = mock(TrapDoor.class); + when(trapDoor.isOpen()).thenReturn(true); + when(blockState.getData()).thenReturn(trapDoor); + + assertFalse("Open trapdoor", manager.isSafeLocation(location)); + + when(trapDoor.isOpen()).thenReturn(false); + assertTrue("Closed trapdoor", manager.isSafeLocation(location)); + + when(ground.getType()).thenReturn(Material.IRON_TRAPDOOR); + assertTrue("Closed iron trapdoor", manager.isSafeLocation(location)); + when(trapDoor.isOpen()).thenReturn(true); + assertFalse("Open iron trapdoor", manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testBadBlocks() { + // Fences + Arrays.stream(Material.values()).filter(m -> m.toString().contains("FENCE")).forEach(m -> { + when(ground.getType()).thenReturn(m); + assertFalse("Fence :" + m.toString(), manager.isSafeLocation(location)); + }); + // Signs + when(ground.getType()).thenReturn(Material.SIGN_POST); + assertFalse("Sign", manager.isSafeLocation(location)); + when(ground.getType()).thenReturn(Material.WALL_SIGN); + assertFalse("Sign", manager.isSafeLocation(location)); + // Bad Blocks + Material[] badMats = {Material.CACTUS, Material.BOAT}; + Arrays.asList(badMats).forEach(m -> { + when(ground.getType()).thenReturn(m); + assertFalse("Bad mat :" + m.toString(), manager.isSafeLocation(location)); + }); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. + */ + @Test + public void testSolidBlocks() { + when(space1.getType()).thenReturn(Material.STONE); + assertFalse("Solid", manager.isSafeLocation(location)); + + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.STONE); + assertFalse("Solid", manager.isSafeLocation(location)); + + when(space1.getType()).thenReturn(Material.WALL_SIGN); + when(space2.getType()).thenReturn(Material.AIR); + assertTrue("Wall sign 1", manager.isSafeLocation(location)); + + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.WALL_SIGN); + assertTrue("Wall sign 2", manager.isSafeLocation(location)); + + when(space1.getType()).thenReturn(Material.SIGN_POST); + when(space2.getType()).thenReturn(Material.AIR); + assertTrue("Wall sign 1", manager.isSafeLocation(location)); + + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.SIGN_POST); + assertTrue("Wall sign 2", manager.isSafeLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#IslandsManager(us.tastybento.bskyblock.BSkyBlock)}. + */ + @Test + public void testIslandsManager() { + assertNotNull(new IslandsManager(plugin)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#bigScan(org.bukkit.Location, int)}. + */ + @Test + public void testBigScan() throws Exception { + Settings settings = mock(Settings.class); + + when(plugin.getSettings()).thenReturn(settings); + + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getBSBIslandWorld()).thenReturn(world); + + + IslandsManager manager = new IslandsManager(plugin); + + Location location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + + Block space1 = mock(Block.class); + Block ground = mock(Block.class); + Block space2 = mock(Block.class); + + when(location.getBlock()).thenReturn(space1); + + when(ground.getType()).thenReturn(Material.GRASS); + when(space1.getType()).thenReturn(Material.AIR); + when(space2.getType()).thenReturn(Material.AIR); + when(space1.getRelative(BlockFace.DOWN)).thenReturn(ground); + when(space1.getRelative(BlockFace.UP)).thenReturn(space2); + + BlockState blockState = mock(BlockState.class); + when(ground.getState()).thenReturn(blockState); + + // Negative value = full island scan + // Null location should get a null response + assertNull(manager.bigScan(null, -1)); + // No island here yet + assertNull(manager.bigScan(location, -1)); + // Try null location, > 0 scan value + assertNull(manager.bigScan(null, 10)); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#createIsland(org.bukkit.Location)}. + */ + @Test + public void testCreateIslandLocation() { + IslandsManager im = new IslandsManager(plugin); + Island island = im.createIsland(location); + assertNotNull(island); + assertEquals(island.getCenter().getWorld(), location.getWorld()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#createIsland(org.bukkit.Location, java.util.UUID)}. + */ + @Test + public void testCreateIslandLocationUUID() { + UUID owner = UUID.randomUUID(); + IslandsManager im = new IslandsManager(plugin); + Island island = im.createIsland(location, owner); + assertNotNull(island); + assertEquals(island.getCenter().getWorld(), location.getWorld()); + assertEquals(owner, island.getOwner()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#deleteIsland(us.tastybento.bskyblock.database.objects.Island, boolean)}. + */ + @Test + public void testDeleteIslandIslandBoolean() { + IslandsManager im = new IslandsManager(plugin); + + im.deleteIsland((Island)null, true); + UUID owner = UUID.randomUUID(); + Island island = im.createIsland(location, owner); + im.deleteIsland(island, false); + island = im.createIsland(location, owner); + im.deleteIsland(island, true); + assertNull(island); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getCount()}. + */ + @Test + public void testGetCount() { + IslandsManager im = new IslandsManager(plugin); + assertTrue(im.getCount() == 0); + im.createIsland(location, UUID.randomUUID()); + assertTrue(im.getCount() == 1); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getIsland(java.util.UUID)}. + */ + @Test + public void testGetIsland() { + UUID owner = UUID.randomUUID(); + IslandsManager im = new IslandsManager(plugin); + Island island = im.createIsland(location, owner); + assertEquals(island, im.getIsland(world, owner)); + assertNull(im.getIsland(world, UUID.randomUUID())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getIslandAt(org.bukkit.Location)}. + * @throws Exception + */ + @Test + public void testGetIslandAtLocation() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Island is = mock(Island.class); + when(ic.getIslandAt(Mockito.any(Location.class))).thenReturn(is); + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + + IslandsManager im = new IslandsManager(plugin); + // In world, correct island + Optional oi = Optional.ofNullable(is); + assertEquals(oi, im.getIslandAt(location)); + + // in world, wrong island + when(ic.getIslandAt(Mockito.any(Location.class))).thenReturn(null); + assertEquals(Optional.empty(), im.getIslandAt(new Location(world, 100000, 120, -100000))); + + // not in world + when(iwm.inWorld(any())).thenReturn(true); + assertEquals(Optional.empty(), im.getIslandAt(new Location(world, 100000, 120, -100000))); + assertEquals(Optional.empty(), im.getIslandAt(location)); + + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getIslandLocation(java.util.UUID)}. + */ + @Test + public void testGetIslandLocation() { + IslandsManager im = new IslandsManager(plugin); + im.createIsland(location, uuid); + assertEquals(world, im.getIslandLocation(world, uuid).getWorld()); + assertNull(im.getIslandLocation(world, UUID.randomUUID())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getLast()}. + */ + @Test + public void testGetLast() { + IslandsManager im = new IslandsManager(plugin); + im.setLast(location); + assertEquals(location, im.getLast(world)); + assertNull(im.getLast(null)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getMembers(java.util.UUID)}. + * @throws Exception + */ + @Test + public void testGetMembers() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Set members = new HashSet<>(); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + when(ic.getMembers(Mockito.any(), Mockito.any())).thenReturn(members); + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + IslandsManager im = new IslandsManager(plugin); + assertEquals(members, im.getMembers(world, UUID.randomUUID())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getProtectedIslandAt(org.bukkit.Location)}. + * @throws Exception + */ + @Test + public void testGetProtectedIslandAt() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Island is = mock(Island.class); + + when(ic.getIslandAt(Mockito.any(Location.class))).thenReturn(is); + + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + + // In world + IslandsManager im = new IslandsManager(plugin); + Optional oi = Optional.ofNullable(is); + // In world, correct island + when(is.onIsland(Mockito.any())).thenReturn(true); + assertEquals(oi, im.getProtectedIslandAt(location)); + + // Not in protected space + when(is.onIsland(Mockito.any())).thenReturn(false); + assertEquals(Optional.empty(), im.getProtectedIslandAt(location)); + + im.setSpawn(is); + // In world, correct island + when(is.onIsland(Mockito.any())).thenReturn(true); + assertEquals(oi, im.getProtectedIslandAt(location)); + + // Not in protected space + when(is.onIsland(Mockito.any())).thenReturn(false); + assertEquals(Optional.empty(), im.getProtectedIslandAt(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getSafeHomeLocation(java.util.UUID, int)}. + */ + @Test + public void testGetSafeHomeLocation() { + IslandsManager im = new IslandsManager(plugin); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(0))).thenReturn(null); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(1))).thenReturn(location); + assertEquals(location, im.getSafeHomeLocation(world, user, 0)); + // Change location so that it is not safe + // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#getSpawnPoint()}. + */ + @Test + public void testGetSpawnPoint() { + IslandsManager im = new IslandsManager(plugin); + assertNull(im.getSpawnPoint(world)); + // Create a spawn island for this world + Island island = mock(Island.class); + when(island.getWorld()).thenReturn(world); + // Make a spawn position on the island + when(island.getSpawnPoint(Mockito.any())).thenReturn(location); + // Set the spawn island + im.setSpawn(island); + assertEquals(location,im.getSpawnPoint(world)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#homeTeleport(org.bukkit.entity.Player, int)}. + */ + @Test + public void testHomeTeleportPlayerInt() { + when(iwm.getDefaultGameMode(world)).thenReturn(GameMode.SURVIVAL); + IslandsManager im = new IslandsManager(plugin); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(0))).thenReturn(null); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(1))).thenReturn(location); + when(player.getGameMode()).thenReturn(GameMode.SPECTATOR); + im.homeTeleport(world, player, 0); + Mockito.verify(player).teleport(location); + Mockito.verify(player).setGameMode(GameMode.SURVIVAL); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#homeTeleport(org.bukkit.entity.Player, int)}. + */ + @Test + public void testHomeTeleportPlayerIntDifferentGameMode() { + when(iwm.getDefaultGameMode(world)).thenReturn(GameMode.CREATIVE); + IslandsManager im = new IslandsManager(plugin); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(0))).thenReturn(null); + when(pm.getHomeLocation(Mockito.any(), Mockito.any(User.class), Mockito.eq(1))).thenReturn(location); + when(player.getGameMode()).thenReturn(GameMode.SPECTATOR); + im.homeTeleport(world, player, 0); + Mockito.verify(player).teleport(location); + Mockito.verify(player).setGameMode(GameMode.CREATIVE); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isAtSpawn(org.bukkit.Location)}. + */ + @Test + public void testIsAtSpawn() { + IslandsManager im = new IslandsManager(plugin); + assertFalse(im.isAtSpawn(location)); + Island island = mock(Island.class); + when(island.getWorld()).thenReturn(world); + when(island.onIsland(Mockito.any())).thenReturn(true); + im.setSpawn(island); + assertTrue(im.isAtSpawn(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#isOwner(java.util.UUID)}. + * @throws Exception + */ + @Test + public void testIsOwner() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Island is = mock(Island.class); + + when(ic.getIslandAt(Mockito.any())).thenReturn(is); + + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + + IslandsManager im = new IslandsManager(plugin); + assertFalse(im.isOwner(world, null)); + + when(ic.hasIsland(Mockito.any(), Mockito.any())).thenReturn(false); + assertFalse(im.isOwner(world, UUID.randomUUID())); + + when(ic.hasIsland(Mockito.any(), Mockito.any())).thenReturn(true); + when(ic.get(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + UUID owner = UUID.randomUUID(); + when(is.getOwner()).thenReturn(owner); + UUID notOwner = UUID.randomUUID(); + while (owner.equals(notOwner)) { + notOwner = UUID.randomUUID(); + } + assertFalse(im.isOwner(world, notOwner)); + assertTrue(im.isOwner(world, owner)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#load()}. + */ + @Test + public void testLoad() { + //IslandsManager im = new IslandsManager(plugin); + //im.load(); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#locationIsOnIsland(org.bukkit.entity.Player, org.bukkit.Location)}. + * @throws Exception + */ + @Test + public void testLocationIsOnIsland() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Island is = mock(Island.class); + + when(ic.getIslandAt(Mockito.any(Location.class))).thenReturn(is); + + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + + // In world + when(is.onIsland(Mockito.any())).thenReturn(true); + + Builder members = new ImmutableSet.Builder<>(); + members.add(uuid); + when(is.getMemberSet()).thenReturn(members.build()); + + when(player.getUniqueId()).thenReturn(uuid); + + IslandsManager im = new IslandsManager(plugin); + assertFalse(im.locationIsOnIsland(null, null)); + + assertTrue(im.locationIsOnIsland(player, location)); + + // No members + Builder mem = new ImmutableSet.Builder<>(); + when(is.getMemberSet()).thenReturn(mem.build()); + assertFalse(im.locationIsOnIsland(player, location)); + + // Not on island + when(is.getMemberSet()).thenReturn(members.build()); + when(is.onIsland(Mockito.any())).thenReturn(false); + assertFalse(im.locationIsOnIsland(player, location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#userIsOnIsland(us.tastybento.bskyblock.api.user.User)}. + * @throws Exception + */ + @Test + public void testUserIsOnIsland() throws Exception { + // Mock island cache + IslandCache ic = mock(IslandCache.class); + Island is = mock(Island.class); + + when(ic.get(Mockito.any(), Mockito.any(UUID.class))).thenReturn(is); + + PowerMockito.whenNew(IslandCache.class).withAnyArguments().thenReturn(ic); + + IslandsManager im = new IslandsManager(plugin); + assertFalse(im.userIsOnIsland(world, null)); + + when(is.onIsland(Mockito.any())).thenReturn(false); + assertFalse(im.userIsOnIsland(world, user)); + + when(is.onIsland(Mockito.any())).thenReturn(true); + assertTrue(im.userIsOnIsland(world, user)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#removePlayer(java.util.UUID)}. + */ + @Test + public void testRemovePlayer() { + IslandsManager im = new IslandsManager(plugin); + im.removePlayer(world, uuid); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#removePlayersFromIsland(us.tastybento.bskyblock.database.objects.Island)}. + */ + @Test + public void testRemovePlayersFromIsland() { + IslandsManager im = new IslandsManager(plugin); + Island is = mock(Island.class); + im.removePlayersFromIsland(is); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#save(boolean)}. + */ + @Test + public void testSave() { + //fail("Not yet implemented"); // TODO - warning saving stuff will go on the file system + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#setIslandName(java.util.UUID, java.lang.String)}. + */ + @Test + public void testSetIslandName() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#setJoinTeam(us.tastybento.bskyblock.database.objects.Island, java.util.UUID)}. + */ + @Test + public void testSetJoinTeam() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#setLast(org.bukkit.Location)}. + */ + @Test + public void testSetLast() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#setLeaveTeam(java.util.UUID)}. + */ + @Test + public void testSetLeaveTeam() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#shutdown()}. + */ + @Test + public void testShutdown() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#clearArea(Location)}. + */ + @Test + public void testClearArea() { + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + Flags.REMOVE_MOBS.setSetting(world, true); + // Default whitelist + Set whitelist = new HashSet<>(); + whitelist.add(EntityType.ENDERMAN); + whitelist.add(EntityType.WITHER); + whitelist.add(EntityType.ZOMBIE_VILLAGER); + whitelist.add(EntityType.PIG_ZOMBIE); + when(iwm.getRemoveMobsWhitelist(Mockito.any())).thenReturn(whitelist); + + + // Monsters and animals + Zombie zombie = mock(Zombie.class); + when(zombie.getLocation()).thenReturn(location); + when(zombie.getType()).thenReturn(EntityType.ZOMBIE); + Slime slime = mock(Slime.class); + when(slime.getLocation()).thenReturn(location); + when(slime.getType()).thenReturn(EntityType.SLIME); + Cow cow = mock(Cow.class); + when(cow.getLocation()).thenReturn(location); + when(cow.getType()).thenReturn(EntityType.COW); + Wither wither = mock(Wither.class); + when(wither.getType()).thenReturn(EntityType.WITHER); + Creeper creeper = mock(Creeper.class); + when(creeper.getType()).thenReturn(EntityType.CREEPER); + + + Collection collection = new ArrayList<>(); + collection.add(player); + collection.add(zombie); + collection.add(cow); + collection.add(slime); + collection.add(wither); + collection.add(creeper); + when(world + .getNearbyEntities(Mockito.any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble())) + .thenReturn(collection); + + IslandsManager im = new IslandsManager(plugin); + im.clearArea(location); + + Mockito.verify(zombie).remove(); + Mockito.verify(player, Mockito.never()).remove(); + Mockito.verify(cow, Mockito.never()).remove(); + Mockito.verify(slime, Mockito.never()).remove(); + Mockito.verify(wither, Mockito.never()).remove(); + Mockito.verify(creeper).remove(); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/managers/PlayersManagerTest.java b/src/test/java/us/tastybento/bskyblock/managers/PlayersManagerTest.java new file mode 100644 index 0000000..28e47fd --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/managers/PlayersManagerTest.java @@ -0,0 +1,407 @@ +/** + * + */ +package us.tastybento.bskyblock.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.BSBDatabase; +import us.tastybento.bskyblock.database.objects.Names; +import us.tastybento.bskyblock.database.objects.Players; +import us.tastybento.bskyblock.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BSkyBlock.class, User.class, Util.class, PlayersManager.class }) +public class PlayersManagerTest { + + private BSkyBlock plugin; + private UUID uuid; + private User user; + private UUID notUUID; + private World world; + private World nether; + private World end; + private BSBDatabase db; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + // island world mgr + IslandWorldManager iwm = mock(IslandWorldManager.class); + world = mock(World.class); + when(world.getName()).thenReturn("world"); + nether = mock(World.class); + when(nether.getName()).thenReturn("world_nether"); + end = mock(World.class); + when(end.getName()).thenReturn("world_the_end"); + when(iwm.getBSBEndWorld()).thenReturn(end); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(nether); + when(iwm.inWorld(any())).thenReturn(true); + when(plugin.getIWM()).thenReturn(iwm); + + // Settings + Settings s = mock(Settings.class); + when(plugin.getSettings()).thenReturn(s); + + // Set up spawn + Location netherSpawn = mock(Location.class); + when(netherSpawn.toVector()).thenReturn(new Vector(0,0,0)); + when(nether.getSpawnLocation()).thenReturn(netherSpawn); + when(s.getNetherSpawnRadius()).thenReturn(100); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + user = mock(User.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while(notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Player has island to begin with + IslandsManager im = mock(IslandsManager.class); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(im.isOwner(Mockito.any(), Mockito.any())).thenReturn(true); + when(im.getTeamLeader(Mockito.any(), Mockito.any())).thenReturn(uuid); + when(plugin.getIslands()).thenReturn(im); + + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + // Normally in world + Util.setPlugin(plugin); + + // Mock database + db = mock(BSBDatabase.class); + PowerMockito.whenNew(BSBDatabase.class).withAnyArguments().thenReturn(db); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#PlayersManager(us.tastybento.bskyblock.BSkyBlock)}. + */ + @Test + public void testPlayersManager() { + PlayersManager pm = new PlayersManager(plugin); + assertNotNull(pm); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#load()}. + */ + @Test + public void testLoad() { + PlayersManager pm = new PlayersManager(plugin); + pm.load(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#save(boolean)}. + */ + @Test + public void testSaveBoolean() { + PlayersManager pm = new PlayersManager(plugin); + pm.save(false); + pm.save(true); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#shutdown()}. + */ + @Test + public void testShutdown() { + PlayersManager pm = new PlayersManager(plugin); + pm.shutdown(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#getPlayer(java.util.UUID)}. + */ + @Test + public void testGetPlayer() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + Players player = pm.getPlayer(uuid); + assertEquals("tasty", player.getPlayerName()); + assertEquals(uuid.toString(), player.getUniqueId()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#addPlayer(java.util.UUID)}. + */ + @Test + public void testAddPlayer() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + pm.addPlayer(null); + // Add twice + assertFalse(pm.isKnown(uuid)); + pm.addPlayer(uuid); + assertTrue(pm.isKnown(uuid)); + pm.addPlayer(uuid); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#removeOnlinePlayer(java.util.UUID)}. + */ + @Test + public void testRemoveOnlinePlayer() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + assertFalse(pm.isKnown(uuid)); + pm.removeOnlinePlayer(uuid); + pm.addPlayer(uuid); + pm.removeOnlinePlayer(uuid); + assertFalse(pm.isKnown(uuid)); + pm.removeOnlinePlayer(uuid); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#removeAllPlayers()}. + */ + @Test + public void testRemoveAllPlayers() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + pm.addPlayer(uuid); + pm.addPlayer(notUUID); + + pm.removeAllPlayers(); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#isKnown(java.util.UUID)}. + */ + @Test + public void testIsKnown() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + pm.addPlayer(uuid); + pm.addPlayer(notUUID); + + assertFalse(pm.isKnown(null)); + assertTrue(pm.isKnown(uuid)); + assertTrue(pm.isKnown(notUUID)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#setHomeLocation(User, org.bukkit.Location, int)}. + */ + @Test + public void testSetAndGetHomeLocationUserLocationInt() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + Location l = mock(Location.class); + when(l.getWorld()).thenReturn(world); + Location l2 = mock(Location.class); + when(l2.getWorld()).thenReturn(nether); + Location l3 = mock(Location.class); + when(l3.getWorld()).thenReturn(end); + + pm.setHomeLocation(uuid, l, 1); + pm.setHomeLocation(uuid, l2, 0); + pm.setHomeLocation(uuid, l3, 10); + assertEquals(l, pm.getHomeLocation(world, uuid)); + assertEquals(l2, pm.getHomeLocation(world, uuid, 0)); + assertEquals(l3, pm.getHomeLocation(world, uuid, 10)); + assertNotEquals(l, pm.getHomeLocation(world, uuid, 20)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#clearHomeLocations(java.util.UUID)}. + */ + @Test + public void testClearHomeLocations() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + Location l = mock(Location.class); + when(l.getWorld()).thenReturn(world); + Location l2 = mock(Location.class); + when(l2.getWorld()).thenReturn(nether); + Location l3 = mock(Location.class); + when(l3.getWorld()).thenReturn(end); + pm.setHomeLocation(uuid, l, 1); + pm.setHomeLocation(uuid, l2, 0); + pm.setHomeLocation(uuid, l3, 10); + assertFalse(pm.getHomeLocations(world, uuid).isEmpty()); + pm.clearHomeLocations(world, uuid); + assertTrue(pm.getHomeLocations(world, uuid).isEmpty()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#getUUID(java.lang.String)}. + */ + @Test + public void testGetUUID() { + PlayersManager pm = new PlayersManager(plugin); + assertEquals(uuid,pm.getUUID(uuid.toString())); + + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getUniqueId()).thenReturn(uuid); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + Names name = mock(Names.class); + when(name.getUuid()).thenReturn(uuid); + + // Database + when(db.objectExists(Mockito.anyString())).thenReturn(true); + when(db.loadObject(Mockito.anyString())).thenAnswer(new Answer() { + + @Override + public Names answer(InvocationOnMock invocation) throws Throwable { + + return name; + } + + }); + assertEquals(uuid, pm.getUUID("tastybento")); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#setPlayerName(us.tastybento.bskyblock.api.user.User)}. + */ + @Test + public void testSetandGetPlayerName() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + // Add a player + pm.addPlayer(uuid); + assertEquals("tasty", pm.getName(user.getUniqueId())); + pm.setPlayerName(user); + assertEquals(user.getName(), pm.getName(user.getUniqueId())); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#getResets(java.util.UUID)}. + */ + @Test + public void testGetSetResetsLeft() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + // Add a player + pm.addPlayer(uuid); + assertEquals(0, pm.getResets(world, uuid)); + pm.setResets(world, uuid, 20); + assertEquals(20, pm.getResets(world, uuid)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.PlayersManager#save(java.util.UUID)}. + */ + @Test + public void testSaveUUID() { + PlayersManager pm = new PlayersManager(plugin); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + OfflinePlayer olp = mock(OfflinePlayer.class); + when(olp.getName()).thenReturn("tasty"); + when(server.getOfflinePlayer(Mockito.any(UUID.class))).thenReturn(olp); + + // Add a player + pm.addPlayer(uuid); + pm.save(uuid); + Mockito.verify(db).saveObject(Mockito.any()); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/managers/RanksManagerTest.java b/src/test/java/us/tastybento/bskyblock/managers/RanksManagerTest.java new file mode 100644 index 0000000..454120a --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/managers/RanksManagerTest.java @@ -0,0 +1,122 @@ +/** + * + */ +package us.tastybento.bskyblock.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; + +/** + * @author tastybento + * + */ +public class RanksManagerTest { + + public static RanksManager ranksManager; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + BSkyBlock plugin = mock(BSkyBlock.class); + Settings settings = mock(Settings.class); + // Blank ranks for now + Map customRanks = new HashMap<>(); + when(plugin.getSettings()).thenReturn(settings); + when(settings.getCustomRanks()).thenReturn(customRanks); + + ranksManager = new RanksManager(plugin); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#addRank(java.lang.String, int)}. + */ + @Test + public void testAddRank() { + assertTrue(ranksManager.addRank("test.rank.reference", 750)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#removeRank(java.lang.String)}. + */ + @Test + public void testRemoveRank() { + ranksManager.addRank("test.rank.reference2", 650); + assertTrue(ranksManager.removeRank("test.rank.reference2")); + // Second time should fail + assertFalse(ranksManager.removeRank("test.rank.reference2")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#getRankValue(java.lang.String)}. + */ + @Test + public void testGetRankValue() { + ranksManager.addRank("test.rank.reference.value", 600); + assertEquals(600, ranksManager.getRankValue("test.rank.reference.value")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#getRanks()}. + */ + @Test + public void testGetRanks() { + Map ranks = ranksManager.getRanks(); + assertTrue(ranks.containsKey(RanksManager.BANNED_RANK_REF)); + assertTrue(ranks.containsKey(RanksManager.VISITOR_RANK_REF)); + assertTrue(ranks.containsKey(RanksManager.MEMBER_RANK_REF)); + assertTrue(ranks.containsKey(RanksManager.OWNER_RANK_REF)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#getRankUpValue(int)}. + */ + @Test + public void testGetNextRankValue() { + assertEquals(RanksManager.BANNED_RANK, ranksManager.getRankUpValue(-20)); + assertEquals(RanksManager.VISITOR_RANK, ranksManager.getRankUpValue(RanksManager.BANNED_RANK)); + assertEquals(RanksManager.MEMBER_RANK, ranksManager.getRankUpValue(RanksManager.VISITOR_RANK)); + assertEquals(RanksManager.OWNER_RANK, ranksManager.getRankUpValue(RanksManager.MEMBER_RANK)); + assertEquals(RanksManager.OWNER_RANK, ranksManager.getRankUpValue(RanksManager.OWNER_RANK)); + assertEquals(RanksManager.OWNER_RANK, ranksManager.getRankUpValue(2000)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#getRankDownValue(int)}. + */ + @Test + public void testGetPreviousRankValue() { + // Lowest rank is Visitor + assertEquals(RanksManager.VISITOR_RANK, ranksManager.getRankDownValue(-20)); + assertEquals(RanksManager.VISITOR_RANK, ranksManager.getRankDownValue(RanksManager.VISITOR_RANK)); + assertEquals(RanksManager.VISITOR_RANK, ranksManager.getRankDownValue(RanksManager.MEMBER_RANK)); + assertEquals(RanksManager.MEMBER_RANK, ranksManager.getRankDownValue(RanksManager.OWNER_RANK)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.managers.RanksManager#getRank(int)}. + */ + @Test + public void testGetRank() { + assertEquals(RanksManager.BANNED_RANK_REF, ranksManager.getRank(RanksManager.BANNED_RANK)); + assertEquals(RanksManager.VISITOR_RANK_REF, ranksManager.getRank(RanksManager.VISITOR_RANK)); + assertEquals(RanksManager.MEMBER_RANK_REF, ranksManager.getRank(RanksManager.MEMBER_RANK)); + assertEquals(RanksManager.OWNER_RANK_REF, ranksManager.getRank(RanksManager.OWNER_RANK)); + assertEquals("", ranksManager.getRank(-999)); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/managers/island/IslandCacheTest.java b/src/test/java/us/tastybento/bskyblock/managers/island/IslandCacheTest.java new file mode 100644 index 0000000..58bc090 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/managers/island/IslandCacheTest.java @@ -0,0 +1,280 @@ +package us.tastybento.bskyblock.managers.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(Util.class) +public class IslandCacheTest { + + BSkyBlock plugin; + private static World world; + + Island island; + UUID owner = UUID.randomUUID(); + Location location; + + + @Before + public void setUp() throws Exception { + plugin = mock(BSkyBlock.class); + + world = mock(World.class); + + // Worlds + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getBSBIslandWorld()).thenReturn(world); + when(iwm.getBSBNetherWorld()).thenReturn(world); + when(iwm.getBSBEndWorld()).thenReturn(world); + when(iwm.inWorld(any())).thenReturn(true); + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + // Mock up IslandsManager + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + + + island = mock(Island.class); + when(island.getWorld()).thenReturn(world); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + when(island.getCenter()).thenReturn(location); + when(island.getOwner()).thenReturn(owner); + Builder members = new ImmutableSet.Builder<>(); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + when(island.getMemberSet()).thenReturn(members.build()); + when(island.getMinX()).thenReturn(-200); + when(island.getMinZ()).thenReturn(-200); + + } + + @Test + public void testIslandCache() { + assertNotNull(new IslandCache()); + } + + @Test + public void testAddIsland() { + IslandCache ic = new IslandCache(); + assertTrue(ic.addIsland(island)); + // Check if they are added + assertEquals(island, ic.get(world, owner)); + assertEquals(island, ic.get(location)); + } + + @Test + public void testAddPlayer() { + IslandCache ic = new IslandCache(); + UUID playerUUID = UUID.randomUUID(); + ic.addPlayer(playerUUID, island); + // Check if they are added + assertEquals(island, ic.get(world, playerUUID)); + assertNotSame(island, ic.get(world, UUID.randomUUID())); + + } + + @Test + public void testClear() { + IslandCache ic = new IslandCache(); + ic.addIsland(island); + // Check if they are added + assertEquals(island, ic.get(world, owner)); + assertEquals(island, ic.get(location)); + ic.clear(); + assertNull(ic.get(world, owner)); + assertNull(ic.get(location)); + } + + @Test + public void testDeleteIslandFromCache() { + + IslandCache ic = new IslandCache(); + ic.addIsland(island); + // Check if they are added + assertEquals(island, ic.get(world, owner)); + assertEquals(island, ic.get(location)); + boolean result = ic.deleteIslandFromCache(island); + assertTrue(result); + assertNull(ic.get(world, owner)); + assertNull(ic.get(location)); + + // Test removing an island that is not in the cache + World world = mock(World.class); + Island island2 = mock(Island.class); + Location location2 = mock(Location.class); + when(location2.getWorld()).thenReturn(world); + when(location2.getBlockX()).thenReturn(0); + when(location2.getBlockY()).thenReturn(0); + when(location2.getBlockZ()).thenReturn(0); + when(island2.getCenter()).thenReturn(location2); + when(island2.getOwner()).thenReturn(UUID.randomUUID()); + Builder members = new ImmutableSet.Builder<>(); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + members.add(UUID.randomUUID()); + when(island2.getMemberSet()).thenReturn(members.build()); + when(island2.getMinX()).thenReturn(-400); + when(island2.getMinZ()).thenReturn(-400); + + assertFalse(ic.deleteIslandFromCache(island2)); + + } + + @Test + public void testGetLocation() { + IslandCache ic = new IslandCache(); + ic.addIsland(island); + // Check if they are added + assertEquals(island, ic.get(location)); + + } + + @Test + public void testGetUUID() { + IslandCache ic = new IslandCache(); + ic.addIsland(island); + // Check if they are added + assertEquals(island, ic.get(world, owner)); + } + + @Test + public void testGetIslandAtLocation() { + // Set coords to be in island space + when(island.inIslandSpace(Mockito.any(Integer.class), Mockito.any(Integer.class))).thenReturn(true); + // Set plugin + Util.setPlugin(plugin); + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + + // Check exact match for location + assertEquals(island, ic.getIslandAt(island.getCenter())); + + + Location location2 = mock(Location.class); + when(location2.getWorld()).thenReturn(world); + when(location2.getBlockX()).thenReturn(10); + when(location2.getBlockY()).thenReturn(10); + when(location2.getBlockZ()).thenReturn(10); + + + assertEquals(island, ic.getIslandAt(location2)); + when(island.inIslandSpace(Mockito.any(Integer.class), Mockito.any(Integer.class))).thenReturn(false); + assertNull(ic.getIslandAt(location2)); + } + + /* + @Test + public void testGetIslands() { + fail("Not yet implemented"); // TODO + } + */ + @Test + public void testgetMembers() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + + assertTrue(ic.getMembers(world, null).isEmpty()); + assertTrue(ic.getMembers(world, UUID.randomUUID()).isEmpty()); + assertFalse(ic.getMembers(world, island.getOwner()).isEmpty()); + assertEquals(3, ic.getMembers(world, island.getOwner()).size()); + + } + @Test + public void testGetTeamLeader() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + + assertEquals(owner, ic.getTeamLeader(world, owner)); + assertNull(ic.getTeamLeader(world, null)); + assertNull(ic.getTeamLeader(world, UUID.randomUUID())); + + + } + + @Test + public void testHasIsland() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + + assertTrue(ic.hasIsland(world, owner)); + assertFalse(ic.hasIsland(world, UUID.randomUUID())); + assertFalse(ic.hasIsland(world, null)); + } + + @Test + public void testRemovePlayer() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + + assertTrue(ic.hasIsland(world, owner)); + ic.removePlayer(world, null); + assertTrue(ic.hasIsland(world, owner)); + ic.removePlayer(world, UUID.randomUUID()); + assertTrue(ic.hasIsland(world, owner)); + ic.removePlayer(world, owner); + assertFalse(ic.hasIsland(world, owner)); + } + + @Test + public void testSize() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + assertEquals(1, ic.size()); + } + + @Test + public void testSetOwner() { + // New cache + IslandCache ic = new IslandCache(); + ic.addIsland(island); + UUID newOwnerUUID = UUID.randomUUID(); + ic.setOwner(island, newOwnerUUID); + + Mockito.verify(island).setOwner(newOwnerUUID); + assertEquals(island, ic.get(world, newOwnerUUID)); + assertEquals(island, ic.get(island.getCenter())); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/util/ItemParserTest.java b/src/test/java/us/tastybento/bskyblock/util/ItemParserTest.java new file mode 100644 index 0000000..22d4de2 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/util/ItemParserTest.java @@ -0,0 +1,283 @@ +package us.tastybento.bskyblock.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BannerMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.material.MaterialData; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class}) +public class ItemParserTest { + + private SpawnEggMeta spawnEggMeta; + private PotionMeta potionMeta; + private BannerMeta bannerMeta; + + @Before + public void setUp() throws Exception { + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + spawnEggMeta = mock(SpawnEggMeta.class); + when(itemFactory.getItemMeta(Mockito.eq(Material.MONSTER_EGG))).thenReturn(spawnEggMeta); + potionMeta = mock(PotionMeta.class); + when(itemFactory.getItemMeta(Mockito.eq(Material.POTION))).thenReturn(potionMeta); + when(itemFactory.getItemMeta(Mockito.eq(Material.SPLASH_POTION))).thenReturn(potionMeta); + when(itemFactory.getItemMeta(Mockito.eq(Material.LINGERING_POTION))).thenReturn(potionMeta); + when(itemFactory.getItemMeta(Mockito.eq(Material.TIPPED_ARROW))).thenReturn(potionMeta); + bannerMeta = mock(BannerMeta.class); + when(itemFactory.getItemMeta(Mockito.eq(Material.BANNER))).thenReturn(bannerMeta); + } + + @Test + public void testParseNull() { + assertNull(ItemParser.parse(null)); + } + + @Test + public void testParseBlank() { + assertNull(ItemParser.parse("")); + } + + @Test + public void testParseNoColons() { + assertNull(ItemParser.parse("NOCOLONS")); + } + + /* + * # Format POTION:NAME::::QTY + # LEVEL, EXTENDED, SPLASH, LINGER are optional. + # LEVEL is a number, 1 or 2 + # LINGER is for V1.9 servers and later + # Examples: + # POTION:STRENGTH:1:EXTENDED:SPLASH:1 + # POTION:INSTANT_DAMAGE:2::LINGER:2 + # POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1 + # POTION:WEAKNESS::::1 - any weakness potion + */ + + @Test + public void testParsePotionStrengthExtended() { + ItemStack result = ItemParser.parse("POTION:STRENGTH:1:EXTENDED::5"); + assertEquals(Material.POTION, result.getType()); + PotionType type = PotionType.STRENGTH; + boolean isExtended = true; + boolean isUpgraded = false; + PotionData data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta).setBasePotionData(Mockito.eq(data)); + assertEquals(5, result.getAmount()); + } + + @Test + public void testParsePotionStrengthNotExtended() { + ItemStack result = ItemParser.parse("POTION:STRENGTH:1:::4"); + assertEquals(Material.POTION, result.getType()); + PotionType type = PotionType.STRENGTH; + boolean isExtended = false; + boolean isUpgraded = false; + PotionData data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta).setBasePotionData(Mockito.eq(data)); + assertEquals(4, result.getAmount()); + } + + @Test + public void testParsePotionStrengthNotExtendedSplash() { + ItemStack result = ItemParser.parse("POTION:STRENGTH:1::SPLASH:3"); + assertEquals(Material.SPLASH_POTION, result.getType()); + PotionType type = PotionType.STRENGTH; + boolean isExtended = false; + boolean isUpgraded = false; + PotionData data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta).setBasePotionData(Mockito.eq(data)); + assertEquals(3, result.getAmount()); + } + + @Test + public void testParsePotionStrengthNotExtendedUpgradedSplash() { + ItemStack result = ItemParser.parse("POTION:STRENGTH:2::SPLASH:3"); + assertEquals(Material.SPLASH_POTION, result.getType()); + PotionType type = PotionType.STRENGTH; + boolean isExtended = false; + boolean isUpgraded = true; + PotionData data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta).setBasePotionData(Mockito.eq(data)); + assertEquals(3, result.getAmount()); + } + + enum ex { + NOT_EXTENDED, + EXTENDED + } + + enum ty { + NO_SPLASH, + SPLASH, + LINGER + } + + List notExtendable = Arrays.asList( + PotionType.UNCRAFTABLE, + PotionType.WATER, + PotionType.MUNDANE, + PotionType.THICK, + PotionType.AWKWARD, + PotionType.INSTANT_HEAL, + PotionType.INSTANT_DAMAGE, + PotionType.LUCK + ); + + @Test + public void testParsePotion() { + for (PotionType type : PotionType.values()) { + for (ex e : ex.values()) { + for (ty t: ty.values()) { + for (int up = 1; up < 2; up++) { + boolean isExtended = e.equals(ex.EXTENDED); + boolean isUpgraded = up > 1; + if (isExtended && notExtendable.contains(type)) { + continue; + } + String req = "POTION:" + type.name() + ":" + up + ":" + e.name() + ":"+ t.name() + ":3"; + ItemStack result = ItemParser.parse(req); + switch (t) { + case LINGER: + assertEquals(Material.LINGERING_POTION, result.getType()); + PotionData data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta, Mockito.times(3)).setBasePotionData(Mockito.eq(data)); + break; + case NO_SPLASH: + assertEquals(Material.POTION, result.getType()); + data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta).setBasePotionData(Mockito.eq(data)); + break; + case SPLASH: + assertEquals(Material.SPLASH_POTION, result.getType()); + data = new PotionData(type, isExtended, isUpgraded); + Mockito.verify(potionMeta, Mockito.times(2)).setBasePotionData(Mockito.eq(data)); + break; + default: + break; + } + + assertEquals(3, result.getAmount()); + } + } + } + } + } + + @Test + public void testParseTippedArrow() { + ItemStack result = ItemParser.parse("TIPPED_ARROW:WEAKNESS::::1"); + assertEquals(Material.TIPPED_ARROW, result.getType()); + } + + + @Test + public void testParseBannerSimple() { + ItemStack result = ItemParser.parse("BANNER:2"); + assertEquals(Material.BANNER, result.getType()); + assertEquals(2, result.getAmount()); + } + + @SuppressWarnings("deprecation") + @Test + public void testParseBannerThreeArgs() { + // Germany + ItemStack result = ItemParser.parse("BANNER:1:RED"); + assertEquals(Material.BANNER, result.getType()); + assertEquals(1, result.getAmount()); + assertEquals(new MaterialData(Material.BANNER, DyeColor.RED.getDyeData()), result.getData()); + } + + @Test + public void testParseBanner() { + // Germany - two patterns + ItemParser.parse("BANNER:1:RED:STRIPE_RIGHT:BLACK:STRIPE_LEFT:YELLOW"); + Mockito.verify(bannerMeta, Mockito.times(2)).addPattern(Mockito.any()); + } + + @Test + public void testParseBannerTooManyColons() { + ItemStack result = ItemParser.parse("BANNER:1::::::::::::::"); + Mockito.verify(bannerMeta, Mockito.never()).addPattern(Mockito.any()); + assertEquals(Material.BANNER, result.getType()); + assertEquals(1, result.getAmount()); + } + + @Test + public void testParseTwoItem() { + ItemStack result = ItemParser.parse("STONE:5"); + assertEquals(Material.STONE, result.getType()); + assertEquals(5, result.getAmount()); + } + + @Test + public void testParseTwoItemWithItemSuffixMissing() { + for (Material mat : Material.values()) { + if (mat.name().endsWith("_ITEM")) { + ItemStack r = ItemParser.parse(mat.name().replace("_ITEM", "") + ":3"); + assertEquals(mat, r.getType()); + } + } + } + + @Test + public void testParseBadTwoItem() { + assertNull(ItemParser.parse("STNE:5")); + } + + @Test + public void testParseThreeItem() { + ItemStack result = ItemParser.parse("LOG:3:2"); + assertEquals(Material.LOG, result.getType()); + assertEquals(2, result.getAmount()); + assertEquals((short)3, result.getDurability()); + } + + @Test + public void testParseBadThreeItem() { + assertNull(ItemParser.parse("STNE:5:5")); + } + + @Test + public void testParseThreeItemWithItemSuffixMissing() { + for (Material mat : Material.values()) { + if (mat.name().endsWith("_ITEM")) { + ItemStack r = ItemParser.parse(mat.name().replace("_ITEM", "") + ":3:2"); + assertEquals(mat, r.getType()); + } + } + } + + @Test + public void testParseMonsterEgg() { + ItemStack result = ItemParser.parse("MONSTER_EGG:COW:2"); + assertEquals(Material.MONSTER_EGG, result.getType()); + assertEquals(2, result.getAmount()); + Mockito.verify(spawnEggMeta).setSpawnedType(EntityType.COW); + } +} diff --git a/src/test/java/us/tastybento/bskyblock/util/PairTest.java b/src/test/java/us/tastybento/bskyblock/util/PairTest.java new file mode 100644 index 0000000..b363001 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/util/PairTest.java @@ -0,0 +1,51 @@ +package us.tastybento.bskyblock.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +public class PairTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public final void testHashCode() { + Pair pair = new Pair<>(1,2); + Pair pair2 = new Pair<>(1,2); + assertTrue(pair.hashCode() == pair2.hashCode()); + } + + @Test + public final void testPair() { + Pair pair = new Pair<>(1,2); + assertEquals(Integer.valueOf(1), pair.x); + assertEquals(Integer.valueOf(2), pair.z); + } + + @Test + public final void testToString() { + Pair pair = new Pair<>(1,2); + assertEquals("Pair [x=1, z=2]", pair.toString()); + } + + @Test + public final void testEqualsObject() { + Pair pair = new Pair<>(1,2); + Pair pair2 = new Pair<>("1","2"); + Pair pair3 = new Pair<>(1,2); + Pair pair4 = new Pair<>(1,null); + Pair pair5 = new Pair<>(null,2); + assertTrue(pair.equals(pair)); + assertTrue(pair.equals(pair3) && pair3.equals(pair)); + assertFalse(pair.equals(pair2)); + assertFalse(pair.equals(pair4)); + assertFalse(pair.equals(pair5)); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/util/UtilTest.java b/src/test/java/us/tastybento/bskyblock/util/UtilTest.java new file mode 100644 index 0000000..92e4c71 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/util/UtilTest.java @@ -0,0 +1,193 @@ +/** + * + */ +package us.tastybento.bskyblock.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.managers.IslandWorldManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { Bukkit.class }) +public class UtilTest { + + private BSkyBlock plugin; + private World world; + private IslandWorldManager iwm; + private Location location; + private Server server; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BSkyBlock.class); + Util.setPlugin(plugin); + // World + world = mock(World.class); + when(world.getName()).thenReturn("world_name"); + // Worlds + iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + location = mock(Location.class); + when(location.getWorld()).thenReturn(world); + when(location.getX()).thenReturn(500D); + when(location.getY()).thenReturn(600D); + when(location.getZ()).thenReturn(700D); + when(location.getBlockX()).thenReturn(500); + when(location.getBlockY()).thenReturn(600); + when(location.getBlockZ()).thenReturn(700); + when(location.getYaw()).thenReturn(10F); + when(location.getPitch()).thenReturn(20F); + + PowerMockito.mockStatic(Bukkit.class); + server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + when(server.getWorld(Mockito.anyString())).thenReturn(world); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getServerVersion()}. + */ + @Test + public void testGetServerVersion() { + assertEquals("bukkit",Util.getServerVersion()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getClosestIsland(org.bukkit.Location)}. + */ + @Test + public void testGetClosestIsland() throws Exception { + Util.setPlugin(plugin); + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.getIslandDistance(world)).thenReturn(100); + when(iwm.getIslandXOffset(world)).thenReturn(0); + when(iwm.getIslandZOffset(world)).thenReturn(0); + when(iwm.getIslandHeight(world)).thenReturn(120); + when(location.getBlockX()).thenReturn(456); + when(location.getBlockZ()).thenReturn(456); + Location l = Util.getClosestIsland(location); + assertEquals(500, l.getBlockX()); + assertEquals(500, l.getBlockZ()); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getLocationString(java.lang.String)}. + */ + @Test + public void testGetLocationString() { + assertNull(Util.getLocationString(null)); + assertNull(Util.getLocationString("")); + assertNull(Util.getLocationString(" ")); + Location result = Util.getLocationString("world_name:500:600:700.0:1092616192:1101004800"); + assertEquals(world, result.getWorld()); + assertTrue(result.getX() == 500.5D); + assertTrue(result.getY() == 600D); + assertTrue(result.getZ() == 700.5D); + assertTrue(result.getYaw() == 10F); + assertTrue(result.getPitch() == 20F); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getStringLocation(org.bukkit.Location)}. + */ + @Test + public void testGetStringLocation() { + assertEquals("", Util.getStringLocation(null)); + when(location.getWorld()).thenReturn(null); + assertEquals("", Util.getStringLocation(location)); + when(location.getWorld()).thenReturn(world); + assertEquals("world_name:500:600:700:1092616192:1101004800", Util.getStringLocation(location)); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#prettifyText(java.lang.String)}. + */ + @Test + public void testPrettifyText() { + assertEquals("Hello There This Is A Test", Util.prettifyText("HELLO_THERE_THIS_IS_A_TEST")); + assertEquals("All caps test", Util.prettifyText("ALL CAPS TEST")); + assertEquals("First capital letter", Util.prettifyText("first capital letter")); + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getOnlinePlayerList(us.tastybento.bskyblock.api.user.User)}. + */ + @Test + public void testGetOnlinePlayerList() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#tabLimit(java.util.List, java.lang.String)}. + */ + @Test + public void testTabLimit() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getPermValue(org.bukkit.entity.Player, java.lang.String, int)}. + */ + @Test + public void testGetPermValue() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#xyz(org.bukkit.util.Vector)}. + */ + @Test + public void testXyz() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#sameWorld(org.bukkit.World, org.bukkit.World)}. + */ + @Test + public void testSameWorld() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#getWorld(org.bukkit.World)}. + */ + @Test + public void testGetWorld() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.Util#blockFaceToFloat(org.bukkit.block.BlockFace)}. + */ + @Test + public void testBlockFaceToFloat() { + //fail("Not yet implemented"); // TODO + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleportTest.java b/src/test/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleportTest.java new file mode 100644 index 0000000..e4a306c --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/util/teleport/SafeSpotTeleportTest.java @@ -0,0 +1,127 @@ +/** + * + */ +package us.tastybento.bskyblock.util.teleport; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandWorldManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.LocalesManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { BSkyBlock.class }) +public class SafeSpotTeleportTest { + + @Mock + static BSkyBlock plugin; + @Mock + private static World world; + @Mock + private static BukkitScheduler sch; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + world = mock(World.class); + Server server = mock(Server.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + plugin = mock(BSkyBlock.class); + // Users + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + + // Island Manager + IslandsManager im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + + Island island = mock(Island.class); + when(island.getCenter()).thenReturn(mock(Location.class)); + + // Default is that there is no island around here + Optional oi = Optional.empty(); + when(im.getIslandAt(Mockito.any())).thenReturn(oi); + + // Settings + Settings settings = mock(Settings.class); + when(settings.getIslandProtectionRange()).thenReturn(1); + when(plugin.getSettings()).thenReturn(settings); + + // Island world manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getIslandProtectionRange(Mockito.any())).thenReturn(1); + when(plugin.getIWM()).thenReturn(iwm); + + // Server & Scheduler + sch = mock(BukkitScheduler.class); + when(server.getScheduler()).thenReturn(sch); + + } + + /** + * Test method for {@link us.tastybento.bskyblock.util.teleport.SafeSpotTeleport#SafeSpotTeleport(us.tastybento.bskyblock.BSkyBlock, org.bukkit.entity.Entity, org.bukkit.Location, java.lang.String, boolean, int)}. + */ + @Test + public void testSafeSpotTeleport() throws Exception { + + Player player = mock(Player.class); + when(player.getGameMode()).thenReturn(GameMode.SURVIVAL); + Location loc = mock(Location.class); + when(loc.getWorld()).thenReturn(world); + when(loc.getBlockX()).thenReturn(0); + when(loc.getBlockY()).thenReturn(120); + when(loc.getBlockZ()).thenReturn(0); + Block block = mock(Block.class); + when(loc.getBlock()).thenReturn(block); + boolean portal = false; + int homeNumber = 1; + new SafeSpotTeleport(plugin, player, loc, "failure message", portal, homeNumber); + + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilderTest.java b/src/test/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilderTest.java new file mode 100644 index 0000000..6a6491c --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/util/teleport/SafeTeleportBuilderTest.java @@ -0,0 +1,143 @@ +package us.tastybento.bskyblock.util.teleport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.LocalesManager; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(SafeTeleportBuilder.class) +public class SafeTeleportBuilderTest { + + @Mock + private SafeSpotTeleport sst; + @Mock + private BSkyBlock plugin; + @Mock + private Player player; + @Mock + private Location loc; + + @InjectMocks + private SafeTeleportBuilder stb; + + @Before + public void setUp() throws Exception { + PowerMockito.whenNew(SafeSpotTeleport.class).withAnyArguments().thenReturn(sst); + // Users + User.setPlugin(plugin); + // Locales - final + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenReturn("mock translation"); + } + + @Test + public void test() throws Exception { + stb = new SafeTeleportBuilder(plugin); + stb.build(); + SafeSpotTeleport ttt = new SafeSpotTeleport(plugin, player, loc, null, false, 0); + assertEquals(sst, ttt); + } + + @Test + public void testSafeTeleportBuilder() { + stb = new SafeTeleportBuilder(plugin); + // Should fail because no data + assertNull(stb.build()); + } + + @Test + public void testEntity() throws Exception { + // Start builder + stb = new SafeTeleportBuilder(plugin); + // Add entity + stb.entity(player); + // Test for error + assertNull(stb.build()); + // Add location + stb.location(loc); + // Build - expect success + SafeSpotTeleport result = stb.build(); + assertEquals(sst, result); + } + + @Test + public void testIsland() { + // Start builder + SafeTeleportBuilder stb = new SafeTeleportBuilder(plugin); + // Add entity + stb.entity(player); + // Add island + Island island = mock(Island.class); + when(island.getCenter()).thenReturn(loc); + stb.island(island); + // Build - expect success + SafeSpotTeleport result = stb.build(); + assertEquals(sst, result); + } + + @Test + public void testHomeNumber() { + // Start builder + SafeTeleportBuilder stb = new SafeTeleportBuilder(plugin); + // Add entity + stb.entity(player); + // Add location + stb.location(loc); + // Add home + stb.homeNumber(10); + // Build - expect success + SafeSpotTeleport result = stb.build(); + assertEquals(sst, result); + + } + + @Test + public void testPortal() { + // Start builder + SafeTeleportBuilder stb = new SafeTeleportBuilder(plugin); + // Add entity + stb.entity(player); + // Add location + stb.location(loc); + // Portal + stb.portal(); + // Build - expect success + SafeSpotTeleport result = stb.build(); + assertEquals(sst, result); + } + + @Test + public void testFailureMessage() { + // Start builder + SafeTeleportBuilder stb = new SafeTeleportBuilder(plugin); + // Add entity + stb.entity(player); + // Add location + stb.location(loc); + // Add failure + stb.failureMessage("testing 123"); + // Build - expect success + SafeSpotTeleport result = stb.build(); + assertEquals(sst, result); + } + +}