mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-01-01 05:57:51 +01:00
Re-implement bulk updates
This commit is contained in:
parent
4de8165c95
commit
f6f9840eb7
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.action.Action;
|
||||
import me.lucko.luckperms.common.bulkupdate.constraint.Constraint;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a query to be applied to a set of data.
|
||||
* Queries can either be applied to im-memory sets of data, or converted to SQL syntax to be executed remotely.
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
public class BulkUpdate {
|
||||
|
||||
// the data types which this query should apply to
|
||||
private final DataType dataType;
|
||||
|
||||
// the action to apply to the data which matches the constraints
|
||||
private final Action action;
|
||||
|
||||
// a set of constraints which data must match to be acted upon
|
||||
private final List<Constraint> constraints;
|
||||
|
||||
/**
|
||||
* Check to see if a Node instance satisfies the constrints of this query
|
||||
*
|
||||
* @param node the node to check
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean satisfiesConstraints(NodeModel node) {
|
||||
for (Constraint constraint : constraints) {
|
||||
if (!constraint.isSatisfiedBy(node)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this query to the given NodeModel, and returns the result.
|
||||
*
|
||||
* @param from the node to base changes from
|
||||
* @return the new nodemodel instance, or null if the node should be deleted.
|
||||
*/
|
||||
public NodeModel apply(NodeModel from) {
|
||||
if (!satisfiesConstraints(from)) {
|
||||
return from; // make no change
|
||||
}
|
||||
|
||||
return action.apply(from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this {@link BulkUpdate} to SQL syntax
|
||||
*
|
||||
* @return this query in SQL form
|
||||
*/
|
||||
public String buildAsSql() {
|
||||
// DELETE FROM {table} WHERE ...
|
||||
// UPDATE {table} SET ... WHERE ...
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// add the action
|
||||
// (DELETE FROM or UPDATE)
|
||||
sb.append(action.getAsSql());
|
||||
|
||||
// if there are no constraints, just return without a WHERE clause
|
||||
if (constraints.isEmpty()) {
|
||||
return sb.append(";").toString();
|
||||
}
|
||||
|
||||
// append constraints
|
||||
sb.append(" WHERE");
|
||||
for (int i = 0; i < constraints.size(); i++) {
|
||||
Constraint constraint = constraints.get(i);
|
||||
|
||||
sb.append(" ");
|
||||
if (i != 0) {
|
||||
sb.append("AND ");
|
||||
}
|
||||
|
||||
sb.append(constraint.getAsSql());
|
||||
}
|
||||
|
||||
return sb.append(";").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to appropriately escape a string for use in a query.
|
||||
*
|
||||
* @param s the string to escape
|
||||
* @return an escaped string
|
||||
*/
|
||||
public static String escapeStringForSql(String s) {
|
||||
if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")) {
|
||||
return s.toLowerCase();
|
||||
}
|
||||
|
||||
try {
|
||||
Integer.parseInt(s);
|
||||
return s;
|
||||
} catch (NumberFormatException e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
return "\"" + s + "\"";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.action.Action;
|
||||
import me.lucko.luckperms.common.bulkupdate.constraint.Constraint;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for building a {@link BulkUpdate}
|
||||
*/
|
||||
@ToString
|
||||
@NoArgsConstructor(staticName = "create")
|
||||
public class BulkUpdateBuilder {
|
||||
|
||||
// the data type this query should affect
|
||||
private DataType dataType = DataType.ALL;
|
||||
|
||||
// the action to apply to the data which matches the constraints
|
||||
private Action action = null;
|
||||
|
||||
// a set of constraints which data must match to be acted upon
|
||||
private Set<Constraint> constraints = new LinkedHashSet<>();
|
||||
|
||||
public BulkUpdateBuilder action(Action action) {
|
||||
this.action = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkUpdateBuilder dataType(DataType dataType) {
|
||||
this.dataType = dataType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkUpdateBuilder constraint(Constraint constraint) {
|
||||
constraints.add(constraint);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkUpdate build() {
|
||||
if (action == null) {
|
||||
throw new IllegalStateException("no action specified");
|
||||
}
|
||||
|
||||
return new BulkUpdate(dataType, action, ImmutableList.copyOf(constraints));
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents the data sets a query should apply to
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataType {
|
||||
|
||||
ALL("all", true, true),
|
||||
USERS("users", true, false),
|
||||
GROUPS("groups", false, true);
|
||||
|
||||
private final String name;
|
||||
private final boolean includingUsers;
|
||||
private final boolean includingGroups;
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.action;
|
||||
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
|
||||
public interface Action {
|
||||
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Applies this action to the given NodeModel, and returns the result.
|
||||
*
|
||||
* @param from the node to base changes from
|
||||
* @return the new nodemodel instance, or null if the node should be deleted.
|
||||
*/
|
||||
NodeModel apply(NodeModel from);
|
||||
|
||||
String getAsSql();
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.action;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
|
||||
@AllArgsConstructor(staticName = "create")
|
||||
public class DeleteAction implements Action {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel apply(NodeModel from) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsSql() {
|
||||
return "DELETE FROM {table}";
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.action;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.constraint.QueryField;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
public class UpdateAction implements Action {
|
||||
|
||||
private final QueryField field;
|
||||
private final String value;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "update";
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel apply(NodeModel from) {
|
||||
switch (field) {
|
||||
case PERMISSION:
|
||||
return from.setPermission(value);
|
||||
case SERVER:
|
||||
return from.setServer(value);
|
||||
case WORLD:
|
||||
return from.setWorld(value);
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsSql() {
|
||||
return "UPDATE {table} SET " + field.getSqlName() + "=" + BulkUpdate.escapeStringForSql(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons;
|
||||
|
||||
public interface Comparison {
|
||||
|
||||
String getSymbol();
|
||||
|
||||
boolean matches(String str, String expr);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonEqual;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonNotEqual;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonNotSimilar;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonSimilar;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum ComparisonType {
|
||||
|
||||
EQUAL("==", new ComparisonEqual()),
|
||||
NOT_EQUAL("!=", new ComparisonNotEqual()),
|
||||
SIMILAR("~~", new ComparisonSimilar()),
|
||||
NOT_SIMILAR("!~", new ComparisonNotSimilar());
|
||||
|
||||
private final String symbol;
|
||||
private final Comparison instance;
|
||||
|
||||
public String getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public Comparison get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ComparisonType parseComparison(String s) {
|
||||
for (ComparisonType t : values()) {
|
||||
if (t.getSymbol().equals(s)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons.impl;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.Comparison;
|
||||
|
||||
public class ComparisonEqual implements Comparison {
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "==";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String str, String expr) {
|
||||
return str.equalsIgnoreCase(expr);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons.impl;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.Comparison;
|
||||
|
||||
public class ComparisonNotEqual implements Comparison {
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "!=";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String str, String expr) {
|
||||
return !str.equalsIgnoreCase(expr);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons.impl;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.Comparison;
|
||||
|
||||
public class ComparisonNotSimilar implements Comparison {
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "!~";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String str, String expr) {
|
||||
// form expression
|
||||
expr = expr.toLowerCase();
|
||||
expr = expr.replace(".", "\\.");
|
||||
|
||||
// convert from SQL LIKE syntax to regex
|
||||
expr = expr.replace("_", ".");
|
||||
expr = expr.replace("%", ".*");
|
||||
|
||||
return !str.toLowerCase().matches(expr);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.comparisons.impl;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.Comparison;
|
||||
|
||||
public class ComparisonSimilar implements Comparison {
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "~~";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String str, String expr) {
|
||||
// form expression
|
||||
expr = expr.toLowerCase();
|
||||
expr = expr.replace(".", "\\.");
|
||||
|
||||
// convert from SQL LIKE syntax to regex
|
||||
expr = expr.replace("_", ".");
|
||||
expr = expr.replace("%", ".*");
|
||||
|
||||
return str.toLowerCase().matches(expr);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.constraint;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.ComparisonType;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
|
||||
/**
|
||||
* Represents a query constraint
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
public class Constraint {
|
||||
|
||||
// the field this constraint is comparing against
|
||||
private final QueryField field;
|
||||
|
||||
// the comparison type being used in this constraint
|
||||
private final ComparisonType comparison;
|
||||
|
||||
// the expression being compared against
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Returns if the given node satisfies this constraint
|
||||
*
|
||||
* @param node the node
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean isSatisfiedBy(NodeModel node) {
|
||||
switch (field) {
|
||||
case PERMISSION:
|
||||
return comparison.get().matches(node.getPermission(), value);
|
||||
case SERVER:
|
||||
return comparison.get().matches(node.getServer(), value);
|
||||
case WORLD:
|
||||
return comparison.get().matches(node.getWorld(), value);
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public String getAsSql() {
|
||||
// WHERE permission = "thing"
|
||||
// WHERE permission != ""
|
||||
// WHERE permission LIKE ""
|
||||
// WHERE permission NOT LIKE ""
|
||||
|
||||
switch (comparison) {
|
||||
case EQUAL:
|
||||
return field.getSqlName() + " = " + BulkUpdate.escapeStringForSql(value);
|
||||
case NOT_EQUAL:
|
||||
return field.getSqlName() + " != " + BulkUpdate.escapeStringForSql(value);
|
||||
case SIMILAR:
|
||||
return field.getSqlName() + " LIKE " + BulkUpdate.escapeStringForSql(value);
|
||||
case NOT_SIMILAR:
|
||||
return field.getSqlName() + " NOT LIKE " + BulkUpdate.escapeStringForSql(value);
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.bulkupdate.constraint;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents a field being used in an update
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum QueryField {
|
||||
|
||||
PERMISSION("permission"),
|
||||
SERVER("server"),
|
||||
WORLD("world");
|
||||
|
||||
private final String sqlName;
|
||||
|
||||
public static QueryField of(String s) {
|
||||
try {
|
||||
return valueOf(s.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ import me.lucko.luckperms.common.commands.impl.group.GroupMainCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.group.ListGroups;
|
||||
import me.lucko.luckperms.common.commands.impl.log.LogMainCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationMainCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.BulkUpdateCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.CheckCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.ExportCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.ImportCommand;
|
||||
@ -97,6 +98,7 @@ public class CommandManager {
|
||||
.add(new ImportCommand())
|
||||
.add(new ExportCommand())
|
||||
.add(new ReloadConfigCommand())
|
||||
.add(new BulkUpdateCommand())
|
||||
.add(new MigrationMainCommand())
|
||||
.add(new CreateGroup())
|
||||
.add(new DeleteGroup())
|
||||
@ -160,7 +162,7 @@ public class CommandManager {
|
||||
|
||||
// Check the correct number of args were given for the main command
|
||||
if (main.getArgumentCheck().test(arguments.size())) {
|
||||
main.sendUsage(sender, label);
|
||||
main.sendDetailedUsage(sender, label);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ public abstract class MainCommand<T> extends Command<Void, T> {
|
||||
|
||||
@Override
|
||||
public void sendDetailedUsage(Sender sender, String label) {
|
||||
|
||||
sendUsage(sender, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.commands.impl.misc;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.DataType;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.ComparisonType;
|
||||
import me.lucko.luckperms.common.bulkupdate.constraint.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.constraint.QueryField;
|
||||
import me.lucko.luckperms.common.commands.Arg;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.commands.utils.ArgumentUtils;
|
||||
import me.lucko.luckperms.common.constants.Message;
|
||||
import me.lucko.luckperms.common.constants.Permission;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BulkUpdateCommand extends SingleCommand {
|
||||
private final Cache<String, BulkUpdate> pendingOperations = Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build();
|
||||
|
||||
public BulkUpdateCommand() {
|
||||
super("BulkUpdate", "Execute bulk change queries on all data", "/%s bulkupdate", Permission.BULK_UPDATE, Predicates.alwaysFalse(),
|
||||
Arg.list(
|
||||
Arg.create("data type", true, "the type of data being changed. ('all', 'users' or 'groups')"),
|
||||
Arg.create("action", true, "the action to perform on the data. ('update' or 'delete')"),
|
||||
Arg.create("action field", false, "the field to act upon. only required for 'update'. ('permission', 'server' or 'world')"),
|
||||
Arg.create("action value", false, "the value to replace with. only required for 'update'."),
|
||||
Arg.create("constraint...", false, "the constraints required for the update")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) throws CommandException {
|
||||
if (args.size() == 2 && args.get(0).equalsIgnoreCase("confirm")) {
|
||||
|
||||
String id = args.get(1);
|
||||
BulkUpdate operation = pendingOperations.asMap().remove(id);
|
||||
|
||||
if (operation == null) {
|
||||
Message.BULK_UPDATE_UNKNOWN_ID.send(sender, id);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
Message.BULK_UPDATE_STARTING.send(sender);
|
||||
plugin.getStorage().applyBulkUpdate(operation).thenAccept(b -> {
|
||||
if (b) {
|
||||
Message.BULK_UPDATE_SUCCESS.send(sender);
|
||||
} else {
|
||||
Message.BULK_UPDATE_FAILURE.send(sender);
|
||||
}
|
||||
});
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
if (args.size() < 3) {
|
||||
throw new ArgumentUtils.DetailedUsageException();
|
||||
}
|
||||
|
||||
BulkUpdateBuilder bulkUpdateBuilder = BulkUpdateBuilder.create();
|
||||
|
||||
try {
|
||||
bulkUpdateBuilder.dataType(DataType.valueOf(args.remove(0).toUpperCase()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Message.BULK_UPDATE_INVALID_DATA_TYPE.send(sender);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
String action = args.remove(0).toLowerCase();
|
||||
if (action.equals("delete")) {
|
||||
bulkUpdateBuilder.action(DeleteAction.create());
|
||||
} else if (action.equals("update")) {
|
||||
if (args.size() < 2) {
|
||||
throw new ArgumentUtils.DetailedUsageException();
|
||||
}
|
||||
|
||||
String field = args.remove(0);
|
||||
QueryField queryField = QueryField.of(field);
|
||||
if (queryField == null) {
|
||||
throw new ArgumentUtils.DetailedUsageException();
|
||||
}
|
||||
String value = args.remove(0);
|
||||
|
||||
bulkUpdateBuilder.action(UpdateAction.of(queryField, value));
|
||||
} else {
|
||||
throw new ArgumentUtils.DetailedUsageException();
|
||||
}
|
||||
|
||||
for (String constraint : args) {
|
||||
String[] parts = constraint.split(" ");
|
||||
if (parts.length != 3) {
|
||||
Message.BULK_UPDATE_INVALID_CONSTRAINT.send(sender, constraint);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
QueryField field = QueryField.of(parts[0]);
|
||||
if (field == null) {
|
||||
Message.BULK_UPDATE_INVALID_CONSTRAINT.send(sender, constraint);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
ComparisonType comparison = ComparisonType.parseComparison(parts[1]);
|
||||
if (comparison == null) {
|
||||
Message.BULK_UPDATE_INVALID_COMPARISON.send(sender, parts[1]);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
String expr = parts[2];
|
||||
bulkUpdateBuilder.constraint(Constraint.of(field, comparison, expr));
|
||||
}
|
||||
|
||||
String id = "" + ThreadLocalRandom.current().nextInt(9) +
|
||||
ThreadLocalRandom.current().nextInt(9) +
|
||||
ThreadLocalRandom.current().nextInt(9) +
|
||||
ThreadLocalRandom.current().nextInt(9);
|
||||
|
||||
BulkUpdate bulkUpdate = bulkUpdateBuilder.build();
|
||||
|
||||
pendingOperations.put(id, bulkUpdate);
|
||||
|
||||
Message.BULK_UPDATE_QUEUED.send(sender, bulkUpdate.buildAsSql().replace("{table}", bulkUpdate.getDataType().getName()));
|
||||
Message.BULK_UPDATE_CONFIRM.send(sender, label, id);
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Sender sender) {
|
||||
return sender.isConsole(); // we only want console to be able to use this command
|
||||
}
|
||||
}
|
@ -261,6 +261,17 @@ public enum Message {
|
||||
UNSET_META_SUCCESS("&aUnset meta value with key &f\"{0}&f\"&a for &b{1}&a in context {2}&a.", true),
|
||||
UNSET_META_TEMP_SUCCESS("&aUnset temporary meta value with key &f\"{0}&f\"&a for &b{1}&a in context {2}&a.", true),
|
||||
|
||||
BULK_UPDATE_INVALID_DATA_TYPE("Invalid type. Was expecting 'all', 'users' or 'groups'.", true),
|
||||
BULK_UPDATE_INVALID_CONSTRAINT("Invalid constraint &4{0}&c. Constraints should be in the format '&f<field> <comparison operator> <value>&c'.", true),
|
||||
BULK_UPDATE_INVALID_COMPARISON("Invalid comparison operator '&4{0}&c'. Expected one of the following: &f== != ~~ ~!", true),
|
||||
BULK_UPDATE_QUEUED("&aBulk update operation was queued. &7(&f{0}&7)", true),
|
||||
BULK_UPDATE_CONFIRM("&aRun &b/{0} bulkupdate confirm {1} &ato execute the update.", true),
|
||||
BULK_UPDATE_UNKNOWN_ID("&aOperation with id &b{0}&a does not exist or has expired.", true),
|
||||
|
||||
BULK_UPDATE_STARTING("&aRunning bulk update.", true),
|
||||
BULK_UPDATE_SUCCESS("&bBulk update completed successfully.", true),
|
||||
BULK_UPDATE_FAILURE("&cBulk update failed. Check the console for errors.", true),
|
||||
|
||||
BULK_CHANGE_TYPE_ERROR("Invalid type. Was expecting 'server' or 'world'.", true),
|
||||
BULK_CHANGE_SUCCESS("&aApplied bulk change successfully. {0} records were changed.", true),
|
||||
|
||||
|
@ -44,6 +44,7 @@ public enum Permission {
|
||||
IMPORT(list("import"), Type.NONE),
|
||||
EXPORT(list("export"), Type.NONE),
|
||||
RELOAD_CONFIG(list("reloadconfig"), Type.NONE),
|
||||
BULK_UPDATE(list("bulkupdate"), Type.NONE),
|
||||
MIGRATION(list("migration"), Type.NONE),
|
||||
|
||||
CREATE_GROUP(list("creategroup"), Type.NONE),
|
||||
@ -88,7 +89,6 @@ public enum Permission {
|
||||
USER_SHOWTRACKS(list("showtracks"), Type.USER),
|
||||
USER_PROMOTE(list("promote"), Type.USER),
|
||||
USER_DEMOTE(list("demote"), Type.USER),
|
||||
USER_BULKCHANGE(list("bulkchange"), Type.USER),
|
||||
USER_CLEAR(list("clear"), Type.USER),
|
||||
|
||||
GROUP_INFO(list("info"), Type.GROUP),
|
||||
@ -123,7 +123,6 @@ public enum Permission {
|
||||
GROUP_LISTMEMBERS(list("listmembers"), Type.GROUP),
|
||||
GROUP_SHOWTRACKS(list("showtracks"), Type.GROUP),
|
||||
GROUP_SETWEIGHT(list("setweight"), Type.GROUP),
|
||||
GROUP_BULKCHANGE(list("bulkchange"), Type.GROUP),
|
||||
GROUP_CLEAR(list("clear"), Type.GROUP),
|
||||
GROUP_RENAME(list("rename"), Type.GROUP),
|
||||
GROUP_CLONE(list("clone"), Type.GROUP),
|
||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.api.event.cause.DeletionCause;
|
||||
import me.lucko.luckperms.common.api.delegates.StorageDelegate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
@ -91,6 +92,11 @@ public class AbstractStorage implements Storage {
|
||||
return makeFuture(backing::getLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
return makeFuture(() -> backing.applyBulkUpdate(bulkUpdate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> loadUser(UUID uuid, String username) {
|
||||
return makeFuture(() -> {
|
||||
|
@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
@ -76,6 +77,20 @@ public class SplitBacking extends AbstractBacking {
|
||||
return backing.get(types.get("log")).getLog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
String userType = types.get("user");
|
||||
String groupType = types.get("group");
|
||||
|
||||
boolean ret = backing.get(userType).applyBulkUpdate(bulkUpdate);
|
||||
if (!userType.equals(groupType)) {
|
||||
if (!backing.get(groupType).applyBulkUpdate(bulkUpdate)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadUser(UUID uuid, String username) {
|
||||
return backing.get(types.get("user")).loadUser(uuid, username);
|
||||
|
@ -27,6 +27,7 @@ import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.api.event.cause.DeletionCause;
|
||||
import me.lucko.luckperms.common.api.delegates.StorageDelegate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
@ -60,6 +61,8 @@ public interface Storage {
|
||||
|
||||
CompletableFuture<Log> getLog();
|
||||
|
||||
CompletableFuture<Boolean> applyBulkUpdate(BulkUpdate bulkUpdate);
|
||||
|
||||
CompletableFuture<Boolean> loadUser(UUID uuid, String username);
|
||||
|
||||
CompletableFuture<Boolean> saveUser(User user);
|
||||
|
@ -29,6 +29,7 @@ import lombok.Setter;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
@ -60,6 +61,8 @@ public abstract class AbstractBacking {
|
||||
|
||||
public abstract Log getLog();
|
||||
|
||||
public abstract boolean applyBulkUpdate(BulkUpdate bulkUpdate);
|
||||
|
||||
public abstract boolean loadUser(UUID uuid, String username);
|
||||
|
||||
public abstract boolean saveUser(User user);
|
||||
|
@ -34,6 +34,7 @@ import com.google.gson.JsonPrimitive;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
import me.lucko.luckperms.common.core.PriorityComparator;
|
||||
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||
@ -55,9 +56,9 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@ -81,16 +82,69 @@ public class JSONBacking extends FlatfileBacking {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean readObjectFromFile(File file, Function<JsonObject, Boolean> readOperation) {
|
||||
boolean success = false;
|
||||
public JsonObject readObjectFromFile(File file) {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
JsonObject object = gson.fromJson(reader, JsonObject.class);
|
||||
success = readOperation.apply(object);
|
||||
return gson.fromJson(reader, JsonObject.class);
|
||||
} catch (Throwable t) {
|
||||
plugin.getLog().warn("Exception whilst reading from file: " + file.getAbsolutePath());
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
return call(() -> {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".json"));
|
||||
if (files == null) return false;
|
||||
|
||||
for (File file : files) {
|
||||
registerFileAction("users", file);
|
||||
|
||||
JsonObject object = readObjectFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
object.add("permissions", serializePermissions(results));
|
||||
|
||||
writeElementToFile(file, object);
|
||||
}
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
File[] files = groupsDir.listFiles((dir, name1) -> name1.endsWith(".json"));
|
||||
if (files == null) return false;
|
||||
|
||||
for (File file : files) {
|
||||
registerFileAction("groups", file);
|
||||
|
||||
JsonObject object = readObjectFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
object.add("permissions", serializePermissions(results));
|
||||
|
||||
writeElementToFile(file, object);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,30 +157,29 @@ public class JSONBacking extends FlatfileBacking {
|
||||
registerFileAction("users", userFile);
|
||||
|
||||
if (userFile.exists()) {
|
||||
return readObjectFromFile(userFile, object -> {
|
||||
String name = object.get("name").getAsString();
|
||||
user.getPrimaryGroup().setStoredValue(object.get("primaryGroup").getAsString());
|
||||
JsonObject object = readObjectFromFile(userFile);
|
||||
String name = object.get("name").getAsString();
|
||||
user.getPrimaryGroup().setStoredValue(object.get("primaryGroup").getAsString());
|
||||
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
user.setNodes(nodes);
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
user.setNodes(nodes);
|
||||
|
||||
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
|
||||
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
|
||||
|
||||
if (user.getName() == null || user.getName().equalsIgnoreCase("null")) {
|
||||
user.setName(name);
|
||||
} else {
|
||||
if (!name.equalsIgnoreCase(user.getName())) {
|
||||
save = true;
|
||||
}
|
||||
if (user.getName() == null || user.getName().equalsIgnoreCase("null")) {
|
||||
user.setName(name);
|
||||
} else {
|
||||
if (!name.equalsIgnoreCase(user.getName())) {
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (save) {
|
||||
saveUser(user);
|
||||
}
|
||||
if (save) {
|
||||
saveUser(user);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
if (GenericUserManager.shouldSave(user)) {
|
||||
user.clearNodes();
|
||||
@ -190,11 +243,10 @@ public class JSONBacking extends FlatfileBacking {
|
||||
for (File file : files) {
|
||||
registerFileAction("users", file);
|
||||
|
||||
JsonObject object = readObjectFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
readObjectFromFile(file, object -> {
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
return true;
|
||||
});
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
|
||||
boolean shouldDelete = false;
|
||||
if (nodes.size() == 1) {
|
||||
@ -223,12 +275,11 @@ public class JSONBacking extends FlatfileBacking {
|
||||
registerFileAction("users", file);
|
||||
|
||||
UUID holder = UUID.fromString(file.getName().substring(0, file.getName().length() - 5));
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
|
||||
readObjectFromFile(file, object -> {
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
return true;
|
||||
});
|
||||
JsonObject object = readObjectFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
@ -253,12 +304,11 @@ public class JSONBacking extends FlatfileBacking {
|
||||
registerFileAction("groups", groupFile);
|
||||
|
||||
if (groupFile.exists()) {
|
||||
return readObjectFromFile(groupFile, object -> {
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
});
|
||||
JsonObject object = readObjectFromFile(groupFile);
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
groupFile.createNewFile();
|
||||
@ -290,12 +340,15 @@ public class JSONBacking extends FlatfileBacking {
|
||||
File groupFile = new File(groupsDir, name + ".json");
|
||||
registerFileAction("groups", groupFile);
|
||||
|
||||
return groupFile.exists() && readObjectFromFile(groupFile, object -> {
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
});
|
||||
if (!groupFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject object = readObjectFromFile(groupFile);
|
||||
Set<NodeModel> data = deserializePermissions(object.get("permissions").getAsJsonArray());
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
}, false);
|
||||
} finally {
|
||||
group.getIoLock().unlock();
|
||||
@ -341,11 +394,11 @@ public class JSONBacking extends FlatfileBacking {
|
||||
registerFileAction("groups", file);
|
||||
|
||||
String holder = file.getName().substring(0, file.getName().length() - 5);
|
||||
|
||||
JsonObject object = readObjectFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
readObjectFromFile(file, element -> {
|
||||
nodes.addAll(deserializePermissions(element.get("permissions").getAsJsonArray()));
|
||||
return true;
|
||||
});
|
||||
nodes.addAll(deserializePermissions(object.get("permissions").getAsJsonArray()));
|
||||
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
@ -370,14 +423,13 @@ public class JSONBacking extends FlatfileBacking {
|
||||
registerFileAction("tracks", trackFile);
|
||||
|
||||
if (trackFile.exists()) {
|
||||
return readObjectFromFile(trackFile, element -> {
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (JsonElement g : element.get("groups").getAsJsonArray()) {
|
||||
groups.add(g.getAsString());
|
||||
}
|
||||
track.setGroups(groups);
|
||||
return true;
|
||||
});
|
||||
JsonObject object = readObjectFromFile(trackFile);
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (JsonElement g : object.get("groups").getAsJsonArray()) {
|
||||
groups.add(g.getAsString());
|
||||
}
|
||||
track.setGroups(groups);
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
trackFile.createNewFile();
|
||||
@ -411,14 +463,17 @@ public class JSONBacking extends FlatfileBacking {
|
||||
File trackFile = new File(tracksDir, name + ".json");
|
||||
registerFileAction("tracks", trackFile);
|
||||
|
||||
return trackFile.exists() && readObjectFromFile(trackFile, element -> {
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (JsonElement g : element.get("groups").getAsJsonArray()) {
|
||||
groups.add(g.getAsString());
|
||||
}
|
||||
track.setGroups(groups);
|
||||
return true;
|
||||
});
|
||||
if (!trackFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject object = readObjectFromFile(trackFile);
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (JsonElement g : object.get("groups").getAsJsonArray()) {
|
||||
groups.add(g.getAsString());
|
||||
}
|
||||
track.setGroups(groups);
|
||||
return true;
|
||||
|
||||
}, false);
|
||||
} finally {
|
||||
|
@ -34,7 +34,9 @@ import com.mongodb.client.model.InsertOneOptions;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.NodeFactory;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
@ -55,6 +57,7 @@ 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 java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -224,6 +227,81 @@ public class MongoDBBacking extends AbstractBacking {
|
||||
}, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
return call(() -> {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
MongoCollection<Document> c = database.getCollection("users");
|
||||
|
||||
try (MongoCursor<Document> cursor = c.find().iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
|
||||
UUID uuid = UUID.fromString(d.getString("_id"));
|
||||
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms"));
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
|
||||
Node node = NodeFactory.fromSerializedNode(e.getKey(), e.getValue());
|
||||
nodes.add(NodeModel.fromNode(node));
|
||||
}
|
||||
|
||||
Set<Node> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.map(NodeModel::toNode)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Document permsDoc = new Document();
|
||||
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(results)).entrySet()) {
|
||||
permsDoc.append(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
d.put("perms", perms);
|
||||
c.replaceOne(new Document("_id", uuid), d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
MongoCollection<Document> c = database.getCollection("groups");
|
||||
|
||||
try (MongoCursor<Document> cursor = c.find().iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
|
||||
String holder = d.getString("_id");
|
||||
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms"));
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
|
||||
Node node = NodeFactory.fromSerializedNode(e.getKey(), e.getValue());
|
||||
nodes.add(NodeModel.fromNode(node));
|
||||
}
|
||||
|
||||
Set<Node> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.map(NodeModel::toNode)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Document permsDoc = new Document();
|
||||
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(results)).entrySet()) {
|
||||
permsDoc.append(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
d.put("perms", perms);
|
||||
c.replaceOne(new Document("_id", holder), d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadUser(UUID uuid, String username) {
|
||||
User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
|
||||
|
@ -32,6 +32,7 @@ import com.google.gson.reflect.TypeToken;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
@ -248,6 +249,42 @@ public class SQLBacking extends AbstractBacking {
|
||||
return log.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
boolean success = true;
|
||||
String queryString = bulkUpdate.buildAsSql();
|
||||
|
||||
try (Connection c = provider.getConnection()) {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
String table = prefix.apply("{prefix}user_permissions");
|
||||
|
||||
try (Statement s = c.createStatement()) {
|
||||
s.execute(queryString.replace("{table}", table));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
String table = prefix.apply("{prefix}group_permissions");
|
||||
|
||||
try (Statement s = c.createStatement()) {
|
||||
s.execute(queryString.replace("{table}", table));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadUser(UUID uuid, String username) {
|
||||
User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
|
||||
|
@ -29,6 +29,7 @@ import com.google.common.collect.Iterables;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.NodeModel;
|
||||
import me.lucko.luckperms.common.core.UserIdentifier;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
@ -55,9 +56,9 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"})
|
||||
@ -85,15 +86,67 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean readMapFromFile(File file, Function<Map<String, Object>, Boolean> readOperation) {
|
||||
boolean success = false;
|
||||
public Map<String, Object> readMapFromFile(File file) {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
success = readOperation.apply((Map<String, Object>) getYaml().load(reader));
|
||||
return (Map<String, Object>) getYaml().load(reader);
|
||||
} catch (Throwable t) {
|
||||
plugin.getLog().warn("Exception whilst reading from file: " + file.getAbsolutePath());
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
return call(() -> {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".yml"));
|
||||
if (files == null) return false;
|
||||
|
||||
for (File file : files) {
|
||||
registerFileAction("users", file);
|
||||
|
||||
Map<String, Object> values = readMapFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
values.put("permissions", serializePermissions(results));
|
||||
writeMapToFile(file, values);
|
||||
}
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
File[] files = groupsDir.listFiles((dir, name1) -> name1.endsWith(".yml"));
|
||||
if (files == null) return false;
|
||||
|
||||
for (File file : files) {
|
||||
registerFileAction("groups", file);
|
||||
|
||||
Map<String, Object> values = readMapFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(n -> Optional.ofNullable(bulkUpdate.apply(n)))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
values.put("permissions", serializePermissions(results));
|
||||
writeMapToFile(file, values);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -105,30 +158,30 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
File userFile = new File(usersDir, uuid.toString() + ".yml");
|
||||
registerFileAction("users", userFile);
|
||||
if (userFile.exists()) {
|
||||
return readMapFromFile(userFile, values -> {
|
||||
// User exists, let's load.
|
||||
String name = (String) values.get("name");
|
||||
user.getPrimaryGroup().setStoredValue((String) values.get("primary-group"));
|
||||
Map<String, Object> values = readMapFromFile(userFile);
|
||||
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
user.setNodes(nodes);
|
||||
// User exists, let's load.
|
||||
String name = (String) values.get("name");
|
||||
user.getPrimaryGroup().setStoredValue((String) values.get("primary-group"));
|
||||
|
||||
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
user.setNodes(nodes);
|
||||
|
||||
if (user.getName() == null || user.getName().equalsIgnoreCase("null")) {
|
||||
user.setName(name);
|
||||
} else {
|
||||
if (!name.equalsIgnoreCase(user.getName())) {
|
||||
save = true;
|
||||
}
|
||||
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
|
||||
|
||||
if (user.getName() == null || user.getName().equalsIgnoreCase("null")) {
|
||||
user.setName(name);
|
||||
} else {
|
||||
if (!name.equalsIgnoreCase(user.getName())) {
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (save) {
|
||||
saveUser(user);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (save) {
|
||||
saveUser(user);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (GenericUserManager.shouldSave(user)) {
|
||||
user.clearNodes();
|
||||
@ -191,11 +244,10 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
for (File file : files) {
|
||||
registerFileAction("users", file);
|
||||
|
||||
Map<String, Object> values = readMapFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
readMapFromFile(file, values -> {
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
return true;
|
||||
});
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
|
||||
boolean shouldDelete = false;
|
||||
if (nodes.size() == 1) {
|
||||
@ -224,11 +276,11 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
registerFileAction("users", file);
|
||||
|
||||
UUID holder = UUID.fromString(file.getName().substring(0, file.getName().length() - 4));
|
||||
|
||||
Map<String, Object> values = readMapFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
readMapFromFile(file, values -> {
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
return true;
|
||||
});
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
@ -253,12 +305,11 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
registerFileAction("groups", groupFile);
|
||||
|
||||
if (groupFile.exists()) {
|
||||
return readMapFromFile(groupFile, values -> {
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
});
|
||||
Map<String, Object> values = readMapFromFile(groupFile);
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
groupFile.createNewFile();
|
||||
@ -288,12 +339,15 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
File groupFile = new File(groupsDir, name + ".yml");
|
||||
registerFileAction("groups", groupFile);
|
||||
|
||||
return groupFile.exists() && readMapFromFile(groupFile, values -> {
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
});
|
||||
if (!groupFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, Object> values = readMapFromFile(groupFile);
|
||||
Set<NodeModel> data = deserializePermissions((List<Object>) values.get("permissions"));
|
||||
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
group.setNodes(nodes);
|
||||
return true;
|
||||
}, false);
|
||||
} finally {
|
||||
group.getIoLock().unlock();
|
||||
@ -339,11 +393,11 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
registerFileAction("groups", file);
|
||||
|
||||
String holder = file.getName().substring(0, file.getName().length() - 4);
|
||||
|
||||
Map<String, Object> values = readMapFromFile(file);
|
||||
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
readMapFromFile(file, values -> {
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
return true;
|
||||
});
|
||||
nodes.addAll(deserializePermissions((List<Object>) values.get("permissions")));
|
||||
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
@ -368,10 +422,9 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
registerFileAction("tracks", trackFile);
|
||||
|
||||
if (trackFile.exists()) {
|
||||
return readMapFromFile(trackFile, values -> {
|
||||
track.setGroups((List<String>) values.get("groups"));
|
||||
return true;
|
||||
});
|
||||
Map<String, Object> values = readMapFromFile(trackFile);
|
||||
track.setGroups((List<String>) values.get("groups"));
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
trackFile.createNewFile();
|
||||
@ -401,10 +454,13 @@ public class YAMLBacking extends FlatfileBacking {
|
||||
File trackFile = new File(tracksDir, name + ".yml");
|
||||
registerFileAction("tracks", trackFile);
|
||||
|
||||
return trackFile.exists() && readMapFromFile(trackFile, values -> {
|
||||
track.setGroups((List<String>) values.get("groups"));
|
||||
return true;
|
||||
});
|
||||
if (!trackFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, Object> values = readMapFromFile(trackFile);
|
||||
track.setGroups((List<String>) values.get("groups"));
|
||||
return true;
|
||||
}, false);
|
||||
} finally {
|
||||
track.getIoLock().unlock();
|
||||
|
@ -39,7 +39,6 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -69,16 +68,15 @@ public class LegacyJSONSchemaMigration implements Runnable {
|
||||
try {
|
||||
File replacementFile = new File(newGroupsDir, oldFile.getName());
|
||||
|
||||
AtomicReference<String> name = new AtomicReference<>(null);
|
||||
JsonObject values = backing.readObjectFromFile(oldFile);
|
||||
|
||||
Map<String, Boolean> perms = new HashMap<>();
|
||||
backing.readObjectFromFile(oldFile, values -> {
|
||||
name.set(values.get("name").getAsString());
|
||||
JsonObject permsSection = values.get("perms").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> e : permsSection.entrySet()) {
|
||||
perms.put(e.getKey(), e.getValue().getAsBoolean());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
String name = values.get("name").getAsString();
|
||||
JsonObject permsSection = values.get("perms").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> e : permsSection.entrySet()) {
|
||||
perms.put(e.getKey(), e.getValue().getAsBoolean());
|
||||
}
|
||||
|
||||
|
||||
Set<NodeModel> nodes = perms.entrySet().stream()
|
||||
.map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue()))
|
||||
@ -94,7 +92,7 @@ public class LegacyJSONSchemaMigration implements Runnable {
|
||||
}
|
||||
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("name", name.get());
|
||||
data.addProperty("name", name);
|
||||
data.add("permissions", JSONBacking.serializePermissions(nodes));
|
||||
backing.writeElementToFile(replacementFile, data);
|
||||
|
||||
@ -119,20 +117,16 @@ public class LegacyJSONSchemaMigration implements Runnable {
|
||||
try {
|
||||
File replacementFile = new File(newUsersDir, oldFile.getName());
|
||||
|
||||
AtomicReference<String> uuid = new AtomicReference<>(null);
|
||||
AtomicReference<String> name = new AtomicReference<>(null);
|
||||
AtomicReference<String> primaryGroup = new AtomicReference<>(null);
|
||||
JsonObject values = backing.readObjectFromFile(oldFile);
|
||||
|
||||
Map<String, Boolean> perms = new HashMap<>();
|
||||
backing.readObjectFromFile(oldFile, values -> {
|
||||
uuid.set(values.get("uuid").getAsString());
|
||||
name.set(values.get("name").getAsString());
|
||||
primaryGroup.set(values.get("primaryGroup").getAsString());
|
||||
JsonObject permsSection = values.get("perms").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> e : permsSection.entrySet()) {
|
||||
perms.put(e.getKey(), e.getValue().getAsBoolean());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
String uuid = values.get("uuid").getAsString();
|
||||
String name = values.get("name").getAsString();
|
||||
String primaryGroup = values.get("primaryGroup").getAsString();
|
||||
JsonObject permsSection = values.get("perms").getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> e : permsSection.entrySet()) {
|
||||
perms.put(e.getKey(), e.getValue().getAsBoolean());
|
||||
}
|
||||
|
||||
Set<NodeModel> nodes = perms.entrySet().stream()
|
||||
.map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue()))
|
||||
@ -148,9 +142,9 @@ public class LegacyJSONSchemaMigration implements Runnable {
|
||||
}
|
||||
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("uuid", uuid.get());
|
||||
data.addProperty("name", name.get());
|
||||
data.addProperty("primaryGroup", primaryGroup.get());
|
||||
data.addProperty("uuid", uuid);
|
||||
data.addProperty("name", name);
|
||||
data.addProperty("primaryGroup", primaryGroup);
|
||||
data.add("permissions", JSONBacking.serializePermissions(nodes));
|
||||
backing.writeElementToFile(replacementFile, data);
|
||||
|
||||
|
@ -37,7 +37,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -67,13 +66,11 @@ public class LegacyYAMLSchemaMigration implements Runnable {
|
||||
try {
|
||||
File replacementFile = new File(newGroupsDir, oldFile.getName());
|
||||
|
||||
AtomicReference<String> name = new AtomicReference<>(null);
|
||||
Map<String, Object> data = backing.readMapFromFile(oldFile);
|
||||
|
||||
Map<String, Boolean> perms = new HashMap<>();
|
||||
backing.readMapFromFile(oldFile, values -> {
|
||||
name.set((String) values.get("name"));
|
||||
perms.putAll((Map<String, Boolean>) values.get("perms"));
|
||||
return true;
|
||||
});
|
||||
String name = (String) data.get("name");
|
||||
perms.putAll((Map<String, Boolean>) data.get("perms"));
|
||||
|
||||
Set<NodeModel> nodes = perms.entrySet().stream()
|
||||
.map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue()))
|
||||
@ -89,7 +86,7 @@ public class LegacyYAMLSchemaMigration implements Runnable {
|
||||
}
|
||||
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
values.put("name", name.get());
|
||||
values.put("name", name);
|
||||
values.put("permissions", YAMLBacking.serializePermissions(nodes));
|
||||
backing.writeMapToFile(replacementFile, values);
|
||||
|
||||
@ -114,17 +111,13 @@ public class LegacyYAMLSchemaMigration implements Runnable {
|
||||
try {
|
||||
File replacementFile = new File(newUsersDir, oldFile.getName());
|
||||
|
||||
AtomicReference<String> uuid = new AtomicReference<>(null);
|
||||
AtomicReference<String> name = new AtomicReference<>(null);
|
||||
AtomicReference<String> primaryGroup = new AtomicReference<>(null);
|
||||
Map<String, Object> data = backing.readMapFromFile(oldFile);
|
||||
|
||||
Map<String, Boolean> perms = new HashMap<>();
|
||||
backing.readMapFromFile(oldFile, values -> {
|
||||
uuid.set((String) values.get("uuid"));
|
||||
name.set((String) values.get("name"));
|
||||
primaryGroup.set((String) values.get("primary-group"));
|
||||
perms.putAll((Map<String, Boolean>) values.get("perms"));
|
||||
return true;
|
||||
});
|
||||
String uuid = (String) data.get("uuid");
|
||||
String name = (String) data.get("name");
|
||||
String primaryGroup = (String) data.get("primary-group");
|
||||
perms.putAll((Map<String, Boolean>) data.get("perms"));
|
||||
|
||||
Set<NodeModel> nodes = perms.entrySet().stream()
|
||||
.map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue()))
|
||||
@ -140,9 +133,9 @@ public class LegacyYAMLSchemaMigration implements Runnable {
|
||||
}
|
||||
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
values.put("uuid", uuid.get());
|
||||
values.put("name", name.get());
|
||||
values.put("primary-group", primaryGroup.get());
|
||||
values.put("uuid", uuid);
|
||||
values.put("name", name);
|
||||
values.put("primary-group", primaryGroup);
|
||||
values.put("permissions", YAMLBacking.serializePermissions(nodes));
|
||||
backing.writeMapToFile(replacementFile, values);
|
||||
|
||||
|
@ -31,6 +31,7 @@ import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.api.event.cause.DeletionCause;
|
||||
import me.lucko.luckperms.common.api.delegates.StorageDelegate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.core.model.Group;
|
||||
import me.lucko.luckperms.common.core.model.Track;
|
||||
import me.lucko.luckperms.common.core.model.User;
|
||||
@ -96,6 +97,16 @@ public class TolerantStorage implements Storage {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
phaser.register();
|
||||
try {
|
||||
return backing.applyBulkUpdate(bulkUpdate);
|
||||
} finally {
|
||||
phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> loadUser(UUID uuid, String username) {
|
||||
phaser.register();
|
||||
|
Loading…
Reference in New Issue
Block a user