Add promote and demote methods to the API (#938)

This commit is contained in:
Luck 2018-04-25 19:58:38 +01:00
parent 8a5c9ddef2
commit 1312aac349
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
14 changed files with 853 additions and 208 deletions

View File

@ -26,9 +26,11 @@
package me.lucko.luckperms.api;
/**
* Represents the result of a mutation call.
* Represents the result of a data mutation call on a LuckPerms object.
*
* <p>Usually as the result to a call on a {@link PermissionHolder} or {@link Track}.</p>
*/
public enum DataMutateResult {
public enum DataMutateResult implements MutateResult {
/**
* Indicates the mutation was a success
@ -50,39 +52,14 @@ public enum DataMutateResult {
*/
FAIL(false);
private final boolean value;
private final boolean success;
DataMutateResult(boolean value) {
this.value = value;
DataMutateResult(boolean success) {
this.success = success;
}
/**
* Gets a boolean representation of the result.
*
* @return a boolean representation
*/
public boolean asBoolean() {
return this.value;
}
/**
* Gets if the result indicates a success
*
* @return if the result indicates a success
* @since 3.4
*/
@Override
public boolean wasSuccess() {
return this.value;
return this.success;
}
/**
* Gets if the result indicates a failure
*
* @return if the result indicates a failure
* @since 3.4
*/
public boolean wasFailure() {
return !this.value;
}
}

View File

@ -0,0 +1,130 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Encapsulates the result of {@link User}s demotion along a {@link Track}.
*
* @since 4.2
*/
public interface DemotionResult extends MutateResult {
/**
* Gets the status of the result.
*
* @return the status
*/
@Nonnull
Status getStatus();
@Override
default boolean wasSuccess() {
return getStatus().wasSuccess();
}
/**
* Gets the name of the group the user was demoted from, if applicable.
*
* <p>Will only be present for results with a {@link #getStatus() status} of
* {@link Status#SUCCESS} or {@link Status#REMOVED_FROM_FIRST_GROUP}.</p>
*
* <p>The value will also be set for results with the {@link Status#MALFORMED_TRACK} status,
* with this value marking the group which no longer exists.</p>
*
* @return the group the user was demoted from.
*/
@Nonnull
Optional<String> getGroupFrom();
/**
* Gets the name of the group the user was demoted from, if applicable.
*
* <p>Will only be present for results with a {@link #getStatus() status} of
* {@link Status#SUCCESS}.</p>
*
* @return the group the user was demoted to.
*/
@Nonnull
Optional<String> getGroupTo();
/**
* The result status
*/
enum Status implements MutateResult {
/**
* Indicates that the user was demoted normally.
*/
SUCCESS(true),
/**
* Indicates that the user was removed from the first group in the track.
*
* <p>This usually occurs when the user is currently on the first group, and was demoted
* "over the start" of the track.</p>
*/
REMOVED_FROM_FIRST_GROUP(true),
/**
* Indicates that the previous group in the track no longer exists.
*/
MALFORMED_TRACK(false),
/**
* Indicates that the user isn't a member of any of the groups on this track.
*/
NOT_ON_TRACK(false),
/**
* Indicates that the implementation was unable to determine the users current position on
* this track.
*
* <p>This usually occurs when the user is on more than one group on the track.</p>
*/
AMBIGUOUS_CALL(false),
/**
* An undefined failure occurred.
*/
UNDEFINED_FAILURE(false);
private final boolean success;
Status(boolean success) {
this.success = success;
}
@Override
public boolean wasSuccess() {
return this.success;
}
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Represents the result to a "mutation" on an object.
*
* @since 4.2
*/
public interface MutateResult {
/**
* Instance of {@link MutateResult} which always reports success.
*/
MutateResult GENERIC_SUCCESS = () -> true;
/**
* Instance of {@link MutateResult} which always reports failure.
*/
MutateResult GENERIC_FAILURE = () -> false;
/**
* Gets if the operation which produced this result completed successfully.
*
* @return if the result indicates a success
*/
boolean wasSuccess();
/**
* Gets if the operation which produced this result failed.
*
* @return if the result indicates a failure
*/
default boolean wasFailure() {
return !wasSuccess();
}
/**
* Gets a boolean representation of the result.
*
* <p>A value of <code>true</code> marks that the operation {@link #wasSuccess() was a success}
* and a value of <code>false</code> marks that the operation
* {@link #wasFailure() was a failure}.</p>
*
* @return a boolean representation
*/
default boolean asBoolean() {
return wasSuccess();
}
}

View File

@ -0,0 +1,130 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Encapsulates the result of {@link User}s promotion along a {@link Track}.
*
* @since 4.2
*/
public interface PromotionResult extends MutateResult {
/**
* Gets the status of the result.
*
* @return the status
*/
@Nonnull
Status getStatus();
@Override
default boolean wasSuccess() {
return getStatus().wasSuccess();
}
/**
* Gets the name of the group the user was promoted from, if applicable.
*
* <p>Will only be present for results with a {@link #getStatus() status} of
* {@link Status#SUCCESS}.</p>
*
* @return the group the user was promoted from.
*/
@Nonnull
Optional<String> getGroupFrom();
/**
* Gets the name of the group the user was promoted from, if applicable.
*
* <p>Will only be present for results with a {@link #getStatus() status} of
* {@link Status#SUCCESS} or {@link Status#ADDED_TO_FIRST_GROUP}.</p>
*
* <p>The value will also be set for results with the {@link Status#MALFORMED_TRACK} status,
* with this value marking the group which no longer exists.</p>
*
* @return the group the user was promoted to.
*/
@Nonnull
Optional<String> getGroupTo();
/**
* The result status
*/
enum Status implements MutateResult {
/**
* Indicates that the user was promoted normally.
*/
SUCCESS(true),
/**
* Indicates that the user was added to the first group in the track.
*
* <p>This usually occurs when the user isn't already on any of the groups in the track.</p>
*/
ADDED_TO_FIRST_GROUP(true),
/**
* Indicates that the next group in the track no longer exists.
*/
MALFORMED_TRACK(false),
/**
* Indicates that the user is already a member of the group at the end of the track,
* and as such cannot be promoted any further.
*/
END_OF_TRACK(false),
/**
* Indicates that the implementation was unable to determine the users current position on
* this track.
*
* <p>This usually occurs when the user is on more than one group on the track.</p>
*/
AMBIGUOUS_CALL(false),
/**
* An undefined failure occurred.
*/
UNDEFINED_FAILURE(false);
private final boolean success;
Status(boolean success) {
this.success = success;
}
@Override
public boolean wasSuccess() {
return this.success;
}
}
}

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.List;
import javax.annotation.Nonnull;
@ -88,6 +90,28 @@ public interface Track {
@Nullable
String getPrevious(@Nonnull Group current);
/**
* Promotes the given user along this track.
*
* @param user the user to promote
* @param contextSet the contexts to promote the user in
* @return the result of the action
* @since 4.2
*/
@Nonnull
PromotionResult promote(@Nonnull User user, @Nonnull ContextSet contextSet);
/**
* Demotes the given user along this track.
*
* @param user the user to demote
* @param contextSet the contexts to demote the user in
* @return the result of the action
* @since 4.2
*/
@Nonnull
DemotionResult demote(@Nonnull User user, @Nonnull ContextSet contextSet);
/**
* Appends a group to the end of this track
*

View File

@ -0,0 +1,124 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.api;
import me.lucko.luckperms.api.DemotionResult;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Utility class for creating instances of {@link DemotionResult}.
*/
public final class DemotionResults {
public static DemotionResult success(String groupFrom, String groupTo) {
return new Impl(DemotionResult.Status.SUCCESS, groupFrom, groupTo);
}
public static DemotionResult removedFromFirst(String groupFrom) {
return new Impl(DemotionResult.Status.REMOVED_FROM_FIRST_GROUP, groupFrom, null);
}
public static DemotionResult malformedTrack(String groupTo) {
return new Impl(DemotionResult.Status.MALFORMED_TRACK, null, groupTo);
}
public static DemotionResult notOnTrack() {
return new Impl(DemotionResult.Status.NOT_ON_TRACK);
}
public static DemotionResult ambiguousCall() {
return new Impl(DemotionResult.Status.AMBIGUOUS_CALL);
}
public static DemotionResult undefinedFailure() {
return new Impl(DemotionResult.Status.UNDEFINED_FAILURE);
}
private static final class Impl implements DemotionResult {
private final Status status;
private final String groupFrom;
private final String groupTo;
private Impl(Status status, String groupFrom, String groupTo) {
this.status = status;
this.groupFrom = groupFrom;
this.groupTo = groupTo;
}
private Impl(Status status) {
this(status, null, null);
}
@Nonnull
@Override
public Status getStatus() {
return this.status;
}
@Nonnull
@Override
public Optional<String> getGroupFrom() {
return Optional.ofNullable(this.groupFrom);
}
@Nonnull
@Override
public Optional<String> getGroupTo() {
return Optional.ofNullable(this.groupTo);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Impl that = (Impl) o;
return this.status == that.status &&
Objects.equals(this.groupFrom, that.groupFrom) &&
Objects.equals(this.groupTo, that.groupTo);
}
@Override
public int hashCode() {
return Objects.hash(this.status, this.groupFrom, this.groupTo);
}
@Override
public String toString() {
return "DemotionResult(" +
"status=" + this.status + ", " +
"groupFrom='" + this.groupFrom + "', " +
"groupTo='" + this.groupTo + "')";
}
}
private DemotionResults() {}
}

View File

@ -0,0 +1,124 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.api;
import me.lucko.luckperms.api.PromotionResult;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Utility class for creating instances of {@link PromotionResult}.
*/
public final class PromotionResults {
public static PromotionResult success(String groupFrom, String groupTo) {
return new Impl(PromotionResult.Status.SUCCESS, groupFrom, groupTo);
}
public static PromotionResult addedToFirst(String groupTo) {
return new Impl(PromotionResult.Status.ADDED_TO_FIRST_GROUP, null, groupTo);
}
public static PromotionResult malformedTrack(String groupTo) {
return new Impl(PromotionResult.Status.MALFORMED_TRACK, null, groupTo);
}
public static PromotionResult endOfTrack() {
return new Impl(PromotionResult.Status.END_OF_TRACK);
}
public static PromotionResult ambiguousCall() {
return new Impl(PromotionResult.Status.AMBIGUOUS_CALL);
}
public static PromotionResult undefinedFailure() {
return new Impl(PromotionResult.Status.UNDEFINED_FAILURE);
}
private static final class Impl implements PromotionResult {
private final Status status;
private final String groupFrom;
private final String groupTo;
private Impl(Status status, String groupFrom, String groupTo) {
this.status = status;
this.groupFrom = groupFrom;
this.groupTo = groupTo;
}
private Impl(Status status) {
this(status, null, null);
}
@Nonnull
@Override
public Status getStatus() {
return this.status;
}
@Nonnull
@Override
public Optional<String> getGroupFrom() {
return Optional.ofNullable(this.groupFrom);
}
@Nonnull
@Override
public Optional<String> getGroupTo() {
return Optional.ofNullable(this.groupTo);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Impl that = (Impl) o;
return this.status == that.status &&
Objects.equals(this.groupFrom, that.groupFrom) &&
Objects.equals(this.groupTo, that.groupTo);
}
@Override
public int hashCode() {
return Objects.hash(this.status, this.groupFrom, this.groupTo);
}
@Override
public String toString() {
return "PromotionResult(" +
"status=" + this.status + ", " +
"groupFrom='" + this.groupFrom + "', " +
"groupTo='" + this.groupTo + "')";
}
}
private PromotionResults() {}
}

View File

@ -28,8 +28,13 @@ package me.lucko.luckperms.common.api.delegates.model;
import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.DataMutateResult;
import me.lucko.luckperms.api.DemotionResult;
import me.lucko.luckperms.api.Group;
import me.lucko.luckperms.api.PromotionResult;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.Objects;
@ -90,6 +95,18 @@ public final class ApiTrack implements me.lucko.luckperms.api.Track {
}
}
@Nonnull
@Override
public PromotionResult promote(@Nonnull User user, @Nonnull ContextSet contextSet) {
return this.handle.promote(ApiUser.cast(user), contextSet, Predicates.alwaysTrue(), null);
}
@Nonnull
@Override
public DemotionResult demote(@Nonnull User user, @Nonnull ContextSet contextSet) {
return this.handle.demote(ApiUser.cast(user), contextSet, Predicates.alwaysTrue(), null);
}
@Override
public DataMutateResult appendGroup(@Nonnull Group group) {
Objects.requireNonNull(group, "group");

View File

@ -25,9 +25,7 @@
package me.lucko.luckperms.common.commands.user;
import com.google.common.collect.Iterables;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.DemotionResult;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.command.CommandResult;
@ -42,18 +40,14 @@ import me.lucko.luckperms.common.command.utils.TabCompletions;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.command.CommandSpec;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.storage.DataConstraints;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class UserDemote extends SubCommand<User> {
public UserDemote(LocaleManager locale) {
@ -91,69 +85,39 @@ public class UserDemote extends SubCommand<User> {
return CommandResult.NO_PERMISSION;
}
// Load applicable groups
Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
.filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet());
nodes.removeIf(g -> !track.containsGroup(g.getGroupName()));
if (nodes.isEmpty()) {
DemotionResult result = track.demote(user, context, s -> !ArgumentPermissions.checkArguments(plugin, sender, getPermission().get(), track.getName(), s), sender);
switch (result.getStatus()) {
case NOT_ON_TRACK:
Message.USER_TRACK_ERROR_NOT_CONTAIN_GROUP.send(sender, user.getFriendlyName(), track.getName());
return CommandResult.FAILURE;
}
if (nodes.size() != 1) {
case AMBIGUOUS_CALL:
Message.TRACK_AMBIGUOUS_CALL.send(sender, user.getFriendlyName());
return CommandResult.FAILURE;
}
final Node oldNode = Iterables.getFirst(nodes, null);
final String old = oldNode.getGroupName();
final String previous;
try {
previous = track.getPrevious(old);
} catch (IllegalArgumentException e) {
Message.TRACK_DOES_NOT_CONTAIN.send(sender, track.getName(), old);
return CommandResult.STATE_ERROR;
}
if (ArgumentPermissions.checkArguments(plugin, sender, getPermission().get(), track.getName(), oldNode.getGroupName())) {
case UNDEFINED_FAILURE:
Message.COMMAND_NO_PERMISSION.send(sender);
return CommandResult.NO_PERMISSION;
}
if (previous == null) {
user.unsetPermission(oldNode);
Message.USER_DEMOTE_ENDOFTRACK.send(sender, track.getName(), user.getFriendlyName(), old);
ExtendedLogEntry.build().actor(sender).acted(user)
.action("demote", track.getName(), context)
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
plugin.getEventFactory().handleUserDemote(user, track, old, null, sender);
return CommandResult.SUCCESS;
}
Group previousGroup = plugin.getStorage().loadGroup(previous).join().orElse(null);
if (previousGroup == null) {
Message.USER_DEMOTE_ERROR_MALFORMED.send(sender, previous);
case MALFORMED_TRACK:
Message.USER_DEMOTE_ERROR_MALFORMED.send(sender, result.getGroupTo().get());
return CommandResult.LOADING_ERROR;
case REMOVED_FROM_FIRST_GROUP: {
Message.USER_DEMOTE_ENDOFTRACK.send(sender, track.getName(), user.getFriendlyName(), result.getGroupFrom().get());
ExtendedLogEntry.build().actor(sender).acted(user)
.action("demote", track.getName(), context)
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
return CommandResult.SUCCESS;
}
user.unsetPermission(oldNode);
user.setPermission(NodeFactory.buildGroupNode(previousGroup.getName()).withExtraContext(context).build());
case SUCCESS: {
String groupFrom = result.getGroupFrom().get();
String groupTo = result.getGroupTo().get();
if (context.isEmpty() && user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(old)) {
user.getPrimaryGroup().setStoredValue(previousGroup.getName());
}
Message.USER_DEMOTE_SUCCESS.send(sender, user.getFriendlyName(), track.getName(), old, previousGroup.getFriendlyName(), MessageUtils.contextSetToString(context));
Message.USER_DEMOTE_SUCCESS.send(sender, user.getFriendlyName(), track.getName(), groupFrom, groupTo, MessageUtils.contextSetToString(context));
if (!silent) {
Message.EMPTY.send(sender, MessageUtils.listToArrowSep(track.getGroups(), previousGroup.getName(), old, true));
Message.EMPTY.send(sender, MessageUtils.listToArrowSep(track.getGroups(), groupTo, groupFrom, true));
}
ExtendedLogEntry.build().actor(sender).acted(user)
@ -161,10 +125,14 @@ public class UserDemote extends SubCommand<User> {
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
plugin.getEventFactory().handleUserDemote(user, track, old, previousGroup.getName(), sender);
return CommandResult.SUCCESS;
}
default:
throw new AssertionError("Unknown status: " + result.getStatus());
}
}
@Override
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return TabCompletions.getTrackTabComplete(args, plugin);

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.common.commands.user;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PromotionResult;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.command.CommandResult;
@ -40,18 +40,14 @@ import me.lucko.luckperms.common.command.utils.TabCompletions;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.command.CommandSpec;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.storage.DataConstraints;
import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class UserPromote extends SubCommand<User> {
public UserPromote(LocaleManager locale) {
@ -89,88 +85,39 @@ public class UserPromote extends SubCommand<User> {
return CommandResult.NO_PERMISSION;
}
// Load applicable groups
Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
.filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet());
nodes.removeIf(g -> !track.containsGroup(g.getGroupName()));
if (nodes.isEmpty()) {
String first = track.getGroups().get(0);
Group nextGroup = plugin.getGroupManager().getIfLoaded(first);
if (nextGroup == null) {
Message.USER_PROMOTE_ERROR_MALFORMED.send(sender, first);
PromotionResult result = track.promote(user, context, s -> !ArgumentPermissions.checkArguments(plugin, sender, getPermission().get(), track.getName(), s), sender);
switch (result.getStatus()) {
case MALFORMED_TRACK:
Message.USER_PROMOTE_ERROR_MALFORMED.send(sender, result.getGroupTo().get());
return CommandResult.LOADING_ERROR;
}
if (ArgumentPermissions.checkArguments(plugin, sender, getPermission().get(), track.getName(), nextGroup.getName())) {
case UNDEFINED_FAILURE:
Message.COMMAND_NO_PERMISSION.send(sender);
return CommandResult.NO_PERMISSION;
}
user.setPermission(NodeFactory.buildGroupNode(nextGroup.getId()).withExtraContext(context).build());
Message.USER_TRACK_ADDED_TO_FIRST.send(sender, user.getFriendlyName(), nextGroup.getFriendlyName(), MessageUtils.contextSetToString(context));
ExtendedLogEntry.build().actor(sender).acted(user)
.action("promote", track.getName(), context)
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
plugin.getEventFactory().handleUserPromote(user, track, null, first, sender);
return CommandResult.SUCCESS;
}
if (nodes.size() != 1) {
case AMBIGUOUS_CALL:
Message.TRACK_AMBIGUOUS_CALL.send(sender, user.getFriendlyName());
return CommandResult.FAILURE;
}
final Node oldNode = nodes.stream().findAny().get();
final String old = oldNode.getGroupName();
final String next;
try {
next = track.getNext(old);
} catch (IllegalArgumentException e) {
Message.TRACK_DOES_NOT_CONTAIN.send(sender, track.getName(), old);
return CommandResult.STATE_ERROR;
}
if (next == null) {
case END_OF_TRACK:
Message.USER_PROMOTE_ERROR_ENDOFTRACK.send(sender, track.getName(), user.getFriendlyName());
return CommandResult.STATE_ERROR;
case ADDED_TO_FIRST_GROUP: {
Message.USER_TRACK_ADDED_TO_FIRST.send(sender, user.getFriendlyName(), result.getGroupTo().get(), MessageUtils.contextSetToString(context));
ExtendedLogEntry.build().actor(sender).acted(user)
.action("promote", track.getName(), context)
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
return CommandResult.SUCCESS;
}
if (!plugin.getStorage().loadGroup(next).join().isPresent()) {
Message.USER_PROMOTE_ERROR_MALFORMED.send(sender, next);
return CommandResult.STATE_ERROR;
}
case SUCCESS: {
String groupFrom = result.getGroupFrom().get();
String groupTo = result.getGroupTo().get();
Group nextGroup = plugin.getGroupManager().getIfLoaded(next);
if (nextGroup == null) {
Message.USER_PROMOTE_ERROR_MALFORMED.send(sender, next);
return CommandResult.LOADING_ERROR;
}
if (ArgumentPermissions.checkArguments(plugin, sender, getPermission().get(), track.getName(), nextGroup.getName())) {
Message.COMMAND_NO_PERMISSION.send(sender);
return CommandResult.NO_PERMISSION;
}
user.unsetPermission(oldNode);
user.setPermission(NodeFactory.buildGroupNode(nextGroup.getName()).withExtraContext(context).build());
if (context.isEmpty() && user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(old)) {
user.getPrimaryGroup().setStoredValue(nextGroup.getName());
}
Message.USER_PROMOTE_SUCCESS.send(sender, user.getFriendlyName(), track.getName(), old, nextGroup.getFriendlyName(), MessageUtils.contextSetToString(context));
Message.USER_PROMOTE_SUCCESS.send(sender, user.getFriendlyName(), track.getName(), groupFrom, groupTo, MessageUtils.contextSetToString(context));
if (!silent) {
Message.EMPTY.send(sender, MessageUtils.listToArrowSep(track.getGroups(), old, nextGroup.getName(), false));
Message.EMPTY.send(sender, MessageUtils.listToArrowSep(track.getGroups(), groupFrom, groupTo, false));
}
ExtendedLogEntry.build().actor(sender).acted(user)
@ -178,10 +125,14 @@ public class UserPromote extends SubCommand<User> {
.build().submit(plugin, sender);
StorageAssistant.save(user, sender, plugin);
plugin.getEventFactory().handleUserPromote(user, track, old, nextGroup.getName(), sender);
return CommandResult.SUCCESS;
}
default:
throw new AssertionError("Unknown status: " + result.getStatus());
}
}
@Override
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return TabCompletions.getTrackTabComplete(args, plugin);

View File

@ -37,6 +37,7 @@ import me.lucko.luckperms.api.event.cause.CreationCause;
import me.lucko.luckperms.api.event.cause.DeletionCause;
import me.lucko.luckperms.api.event.log.LogBroadcastEvent;
import me.lucko.luckperms.api.event.log.LogNotifyEvent;
import me.lucko.luckperms.api.event.source.Source;
import me.lucko.luckperms.common.api.delegates.model.ApiPermissionHolder;
import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.event.impl.EventConfigReload;
@ -73,6 +74,7 @@ import me.lucko.luckperms.common.event.impl.EventUserLoginProcess;
import me.lucko.luckperms.common.event.impl.EventUserPromote;
import me.lucko.luckperms.common.event.model.EntitySourceImpl;
import me.lucko.luckperms.common.event.model.SenderEntity;
import me.lucko.luckperms.common.event.model.UnknownSource;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.Track;
@ -84,6 +86,8 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
public final class EventFactory {
private final AbstractEventBus eventBus;
@ -265,13 +269,15 @@ public final class EventFactory {
fireEvent(event);
}
public void handleUserDemote(User user, Track track, String from, String to, Sender source) {
EventUserDemote event = new EventUserDemote(track.getApiDelegate(), new ApiUser(user), from, to, new EntitySourceImpl(new SenderEntity(source)));
public void handleUserDemote(User user, Track track, String from, String to, @Nullable Sender source) {
Source s = source == null ? UnknownSource.INSTANCE : new EntitySourceImpl(new SenderEntity(source));
EventUserDemote event = new EventUserDemote(track.getApiDelegate(), new ApiUser(user), from, to, s);
fireEventAsync(event);
}
public void handleUserPromote(User user, Track track, String from, String to, Sender source) {
EventUserPromote event = new EventUserPromote(track.getApiDelegate(), new ApiUser(user), from, to, new EntitySourceImpl(new SenderEntity(source)));
public void handleUserPromote(User user, Track track, String from, String to, @Nullable Sender source) {
Source s = source == null ? UnknownSource.INSTANCE : new EntitySourceImpl(new SenderEntity(source));
EventUserPromote event = new EventUserPromote(track.getApiDelegate(), new ApiUser(user), from, to, s);
fireEventAsync(event);
}

View File

@ -30,7 +30,7 @@ import me.lucko.luckperms.api.event.source.Source;
import javax.annotation.Nonnull;
public final class UnknownSource implements Source {
private static final Source INSTANCE = new UnknownSource();
public static final Source INSTANCE = new UnknownSource();
private UnknownSource() {

View File

@ -28,15 +28,27 @@ package me.lucko.luckperms.common.model;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.DataMutateResult;
import me.lucko.luckperms.api.DemotionResult;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PromotionResult;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.api.DemotionResults;
import me.lucko.luckperms.common.api.PromotionResults;
import me.lucko.luckperms.common.api.delegates.model.ApiTrack;
import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.references.Identifiable;
import me.lucko.luckperms.common.sender.Sender;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
public final class Track implements Identifiable<String> {
@ -259,6 +271,119 @@ public final class Track implements Identifiable<String> {
this.plugin.getEventFactory().handleTrackClear(this, before);
}
public PromotionResult promote(User user, ContextSet context, Predicate<String> nextGroupPermissionChecker, @Nullable Sender sender) {
if (getSize() <= 1) {
throw new IllegalStateException("Track contains one or fewer groups, unable to promote");
}
// find all groups that are inherited by the user in the exact contexts given and applicable to this track
List<Node> nodes = user.getEnduringNodes().get(context.makeImmutable()).stream()
.filter(Node::isGroupNode)
.filter(Node::getValuePrimitive)
.filter(node -> containsGroup(node.getGroupName()))
.distinct()
.collect(Collectors.toList());
if (nodes.isEmpty()) {
String first = getGroups().get(0);
Group nextGroup = this.plugin.getGroupManager().getIfLoaded(first);
if (nextGroup == null) {
return PromotionResults.malformedTrack(first);
}
if (!nextGroupPermissionChecker.test(nextGroup.getName())) {
return PromotionResults.undefinedFailure();
}
user.setPermission(NodeFactory.buildGroupNode(nextGroup.getId()).withExtraContext(context).build());
this.plugin.getEventFactory().handleUserPromote(user, this, null, first, sender);
return PromotionResults.addedToFirst(first);
}
if (nodes.size() != 1) {
return PromotionResults.ambiguousCall();
}
Node oldNode = nodes.get(0);
String old = oldNode.getGroupName();
String next = getNext(old);
if (next == null) {
return PromotionResults.endOfTrack();
}
Group nextGroup = this.plugin.getGroupManager().getIfLoaded(next);
if (nextGroup == null) {
return PromotionResults.malformedTrack(next);
}
if (!nextGroupPermissionChecker.test(nextGroup.getName())) {
return PromotionResults.undefinedFailure();
}
user.unsetPermission(oldNode);
user.setPermission(NodeFactory.buildGroupNode(nextGroup.getName()).withExtraContext(context).build());
if (context.isEmpty() && user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(old)) {
user.getPrimaryGroup().setStoredValue(nextGroup.getName());
}
this.plugin.getEventFactory().handleUserPromote(user, this, old, nextGroup.getName(), sender);
return PromotionResults.success(old, nextGroup.getName());
}
public DemotionResult demote(User user, ContextSet context, Predicate<String> previousGroupPermissionChecker, @Nullable Sender sender) {
if (getSize() <= 1) {
throw new IllegalStateException("Track contains one or fewer groups, unable to demote");
}
// find all groups that are inherited by the user in the exact contexts given and applicable to this track
List<Node> nodes = user.getEnduringNodes().get(context.makeImmutable()).stream()
.filter(Node::isGroupNode)
.filter(Node::getValuePrimitive)
.filter(node -> containsGroup(node.getGroupName()))
.distinct()
.collect(Collectors.toList());
if (nodes.isEmpty()) {
return DemotionResults.notOnTrack();
}
if (nodes.size() != 1) {
return DemotionResults.ambiguousCall();
}
Node oldNode = nodes.get(0);
String old = oldNode.getGroupName();
String previous = getPrevious(old);
if (!previousGroupPermissionChecker.test(oldNode.getGroupName())) {
return DemotionResults.undefinedFailure();
}
if (previous == null) {
user.unsetPermission(oldNode);
this.plugin.getEventFactory().handleUserDemote(user, this, old, null, sender);
return DemotionResults.removedFromFirst(old);
}
Group previousGroup = this.plugin.getGroupManager().getIfLoaded(previous);
if (previousGroup == null) {
return DemotionResults.malformedTrack(previous);
}
user.unsetPermission(oldNode);
user.setPermission(NodeFactory.buildGroupNode(previousGroup.getName()).withExtraContext(context).build());
if (context.isEmpty() && user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(old)) {
user.getPrimaryGroup().setStoredValue(previousGroup.getName());
}
this.plugin.getEventFactory().handleUserDemote(user, this, old, previousGroup.getName(), sender);
return DemotionResults.success(old, previousGroup.getName());
}
@Override
public boolean equals(Object o) {
if (o == this) return true;

View File

@ -27,7 +27,6 @@ package me.lucko.luckperms.common.storage;
import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
@ -52,7 +51,7 @@ public final class PlayerSaveResult {
}
public static PlayerSaveResult usernameUpdated(String oldUsername) {
return new PlayerSaveResult(oldUsername, null, Status.USERNAME_UPDATED);
return new PlayerSaveResult(EnumSet.of(Status.USERNAME_UPDATED), oldUsername, null);
}
public static PlayerSaveResult determineBaseResult(String username, String oldUsername) {
@ -77,12 +76,8 @@ public final class PlayerSaveResult {
this.otherUuids = otherUuids;
}
private PlayerSaveResult(@Nullable String oldUsername, @Nullable Set<UUID> otherUuids, Status... status) {
this(EnumSet.copyOf(Arrays.asList(status)), oldUsername, otherUuids);
}
private PlayerSaveResult(Status... status) {
this(null, null, status);
private PlayerSaveResult(Status status) {
this(EnumSet.of(status), null, null);
}
/**