Skip to content

Commit

Permalink
Implement per-player border type (#69)
Browse files Browse the repository at this point in the history
* Rename PlayerBorder.barrier to show

* Move player event handlers to PlayerListener

* Remove PlayerBorder

* Implement PerPlayerBorderProxy

Currently defaulting to Customer (non-wbapi) border

* Add BorderTypeCommand

* Create BorderType

* Add messages to BorderTypeCommand

* Prepare PerPlayerBorderProxy for handling edge cases

The user may not have a border type set, then we return with the default.
The user's border type may be unavailable now for two reasons:
1. Border type has been disabled after a server/plugin restart
2. Border type is obsolete and does not exist anymore (forward compatibility).

* Fix message reference at BorderTypeCommand

* Use null for missing wbapiBorder

It is fine since it is never used

* Avoid wbapi "border show" spam on player move

* At command, replace args.isEmpty() with size() != 1

To also show help if more parameters are passed than necessary

* Small refactor

* Fix set-type permission in addon.yml

* Update README

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
  • Loading branch information
arphox and tastybento committed Nov 27, 2021
1 parent 620e134 commit 9ded7cc
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 97 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ show-particles: true

## Commands

There is one command that turns the border on or off. Since Version 3.0.0 it requires a permission:

/[player command] border

## Permissions

There is one permission to allow/disallow use of the border command:

`[gamemode].border.toggle`

This permission is not given by default.
### border
**Command**: `/[player command] border`
**Description**: Turns the border on/off.
**Permission**: `[gamemode].border.toggle`. Default: `op`.
**Notes**: Since Version 3.0.0 it requires a permission.

### border type {...}
**Command**: `/[player command] border type {barrier | vanilla}`
**Description**: Sets the border type.
**Permission**: `[gamemode].border.set-type`. Default: `true`.
**Example**: `/[player command] border type barrier`

## Like this addon?
[Sponsor tastybento](https://github.com/sponsors/tastybento) to get more addons like this and make this one better!
Expand Down
37 changes: 24 additions & 13 deletions src/main/java/world/bentobox/border/Border.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
package world.bentobox.border;

import java.util.ArrayList;
import java.util.List;

import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.eclipse.jdt.annotation.NonNull;

import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.util.Util;
import world.bentobox.border.commands.IslandBorderCommand;
import world.bentobox.border.listeners.PlayerBorder;
import world.bentobox.border.listeners.BorderShower;
import world.bentobox.border.listeners.PlayerListener;
import world.bentobox.border.listeners.ShowBarrier;
import world.bentobox.border.listeners.ShowWorldBorder;

import java.util.*;

public final class Border extends Addon {

private PlayerBorder playerBorder;
private BorderShower borderShower;

private Settings settings;

private Config<Settings> config = new Config<>(this, Settings.class);

private @NonNull List<GameModeAddon> gameModes = new ArrayList<>();

private final Set<BorderType> availableBorderTypes = EnumSet.of(BorderType.Barrier);

@Override
public void onLoad() {
// Save default config.yml
Expand All @@ -47,9 +49,9 @@ public void onEnable() {
this.setState(State.DISABLED);
return;
}
availableBorderTypes.add(BorderType.Vanilla);
}
gameModes.clear();
playerBorder = new PlayerBorder(this);
// Register commands
getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> {

Expand All @@ -63,8 +65,8 @@ public void onEnable() {
});

if (gameModes.size() > 0) {
borderShower = createBorder();
registerListener(new PlayerListener(this));
registerListener(playerBorder);
}
}

Expand All @@ -73,11 +75,16 @@ public void onDisable() {
// Nothing to do here
}

/**
* @return the playerBorder
*/
public PlayerBorder getPlayerBorder() {
return playerBorder;
private BorderShower createBorder() {
BorderShower customBorder = new ShowBarrier(this);
BorderShower wbapiBorder = getSettings().isUseWbapi()
? new ShowWorldBorder(this)
: null;
return new PerPlayerBorderProxy(this, customBorder, wbapiBorder);
}

public BorderShower getBorderShower() {
return borderShower;
}

/**
Expand Down Expand Up @@ -108,4 +115,8 @@ public Settings getSettings() {
public boolean inGameWorld(World world) {
return gameModes.stream().anyMatch(gm -> gm.inWorld(Util.getWorld(world)));
}

public Set<BorderType> getAvailableBorderTypesView() {
return Collections.unmodifiableSet(availableBorderTypes);
}
}
41 changes: 41 additions & 0 deletions src/main/java/world/bentobox/border/BorderType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package world.bentobox.border;

import java.util.Optional;

public enum BorderType {
Barrier((byte) 1, "barrier"),
Vanilla((byte) 2, "vanilla");
// Virtual((byte)3, "virtual"); // not supported yet

private final byte id;
private final String commandLabel;

BorderType(byte id, String commandLabel) {
this.id = id;
this.commandLabel = commandLabel;
}

public byte getId() {
return id;
}

public String getCommandLabel() {
return commandLabel;
}

public static Optional<BorderType> fromCommandLabel(String label) {
for (var bt : BorderType.values())
if (bt.commandLabel.equalsIgnoreCase(label))
return Optional.of(bt);

return Optional.empty();
}

public static Optional<BorderType> fromId(byte id) {
for (var bt : BorderType.values())
if (bt.getId() == id)
return Optional.of(bt);

return Optional.empty();
}
}
77 changes: 77 additions & 0 deletions src/main/java/world/bentobox/border/PerPlayerBorderProxy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package world.bentobox.border;

import org.bukkit.entity.Player;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.border.listeners.BorderShower;

import java.util.Optional;

public final class PerPlayerBorderProxy implements BorderShower {

public static final String BORDER_BORDERTYPE_META_DATA = "Border_bordertype";

private final Border addon;
private final BorderShower customBorder;
private final BorderShower wbapiBorder;

public PerPlayerBorderProxy(Border addon, BorderShower customBorder, BorderShower wbapiBorder) {
this.addon = addon;
this.customBorder = customBorder;
this.wbapiBorder = wbapiBorder;
}

@Override
public void showBorder(Player player, Island island) {
var user = User.getInstance(player);
var border = getBorder(user);
border.showBorder(player, island);
}

@Override
public void hideBorder(User user) {
var border = getBorder(user);
border.hideBorder(user);
}

@Override
public void clearUser(User user) {
var border = getBorder(user);
border.clearUser(user);
}

@Override
public void refreshView(User user, Island island) {
var border = getBorder(user);
border.refreshView(user, island);
}

private BorderShower getBorder(User user) {
BorderType borderType = getBorderType(user);
return switch (borderType) {
case Barrier -> customBorder;
case Vanilla -> wbapiBorder;
};
}

private BorderType getBorderType(User user) {
Optional<Byte> userTypeId = user.getMetaData(BORDER_BORDERTYPE_META_DATA)
.map(MetaDataValue::asByte);

if (userTypeId.isEmpty()) {
return getDefaultBorderType();
}

Optional<BorderType> borderType = BorderType.fromId(userTypeId.get());
if (borderType.isEmpty() || !addon.getAvailableBorderTypesView().contains(borderType.get())) {
return getDefaultBorderType();
}

return borderType.get();
}

private static BorderType getDefaultBorderType() {
return BorderType.Barrier;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package world.bentobox.border.commands;

import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.border.Border;
import world.bentobox.border.BorderType;
import world.bentobox.border.PerPlayerBorderProxy;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public final class BorderTypeCommand extends CompositeCommand {

private final Border addon;
private Island island;
private final List<String> availableTypes;

public BorderTypeCommand(Border addon, CompositeCommand parent) {
super(addon, parent, "type");
this.addon = addon;
this.availableTypes = addon.getAvailableBorderTypesView()
.stream()
.map(BorderType::getCommandLabel)
.collect(Collectors.toList());
}

@Override
public void setup() {
this.setPermission("border.set-type");
this.setDescription("border.set-type.description");
this.setOnlyPlayer(true);
// setConfigurableRankCommand(); // What is this? Should I use this? I guess not.
}

@Override
public boolean canExecute(User user, String label, List<String> args) {
island = getIslands().getIsland(getWorld(), user);
return island != null;
}

@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() != 1) {
this.showHelp(this, user);
return false;
}

if (availableTypes.stream().anyMatch(args.get(0)::equalsIgnoreCase)) {
changeBorderTypeTo(user, args.get(0));
return true;
}

user.sendMessage("border.set-type.error-unavailable-type");
return false;
}

private void changeBorderTypeTo(User user, String newBorderType) {
byte newTypeId = BorderType.fromCommandLabel(newBorderType).get().getId();

addon.getBorderShower().hideBorder(user);
user.putMetaData(PerPlayerBorderProxy.BORDER_BORDERTYPE_META_DATA, new MetaDataValue(newTypeId));
addon.getBorderShower().showBorder(user.getPlayer(), island);

user.sendMessage("border.set-type.changed",
"[type]", newBorderType);
}

@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
return Optional.of(availableTypes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public void setup() {
this.setDescription("border.toggle.description");
this.setOnlyPlayer(true);
setConfigurableRankCommand();

new BorderTypeCommand(this.getAddon(), this);
}

@Override
Expand All @@ -39,11 +41,11 @@ public boolean execute(User user, String label, List<String> args) {
if (on) {
user.sendMessage("border.toggle.border-off");
user.putMetaData(BorderShower.BORDER_STATE_META_DATA, new MetaDataValue(false));
addon.getPlayerBorder().getBorder().hideBorder(user);
addon.getBorderShower().hideBorder(user);
} else {
user.sendMessage("border.toggle.border-on");
user.putMetaData(BorderShower.BORDER_STATE_META_DATA, new MetaDataValue(true));
addon.getPlayerBorder().getBorder().showBorder(user.getPlayer(), island);
addon.getBorderShower().showBorder(user.getPlayer(), island);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ public default void clearUser(User user) {
// Do nothing
};

public default void refreshView(User user, Island island){
// Do nothing
}

}

0 comments on commit 9ded7cc

Please sign in to comment.