134 changes: 134 additions & 0 deletions proxy/src/main/java/dev/plex/toml/NumberValueReaderWriter.java
@@ -0,0 +1,134 @@
package dev.plex.toml;

import java.util.concurrent.atomic.AtomicInteger;

class NumberValueReaderWriter implements dev.plex.toml.ValueReader, dev.plex.toml.ValueWriter
{
static final NumberValueReaderWriter NUMBER_VALUE_READER_WRITER = new NumberValueReaderWriter();

@Override
public boolean canRead(String s)
{
char firstChar = s.charAt(0);

return firstChar == '+' || firstChar == '-' || Character.isDigit(firstChar);
}

@Override
public Object read(String s, AtomicInteger index, dev.plex.toml.Context context)
{
boolean signable = true;
boolean dottable = false;
boolean exponentable = false;
boolean terminatable = false;
boolean underscorable = false;
String type = "";
StringBuilder sb = new StringBuilder();

for (int i = index.get(); i < s.length(); i = index.incrementAndGet())
{
char c = s.charAt(i);
boolean notLastChar = s.length() > i + 1;

if (Character.isDigit(c))
{
sb.append(c);
signable = false;
terminatable = true;
if (type.isEmpty())
{
type = "integer";
dottable = true;
}
underscorable = notLastChar;
exponentable = !type.equals("exponent");
}
else if ((c == '+' || c == '-') && signable && notLastChar)
{
signable = false;
terminatable = false;
if (c == '-')
{
sb.append('-');
}
}
else if (c == '.' && dottable && notLastChar)
{
sb.append('.');
type = "float";
terminatable = false;
dottable = false;
exponentable = false;
underscorable = false;
}
else if ((c == 'E' || c == 'e') && exponentable && notLastChar)
{
sb.append('E');
type = "exponent";
terminatable = false;
signable = true;
dottable = false;
exponentable = false;
underscorable = false;
}
else if (c == '_' && underscorable && notLastChar && Character.isDigit(s.charAt(i + 1)))
{
underscorable = false;
}
else
{
if (!terminatable)
{
type = "";
}
index.decrementAndGet();
break;
}
}

if (type.equals("integer"))
{
return Long.valueOf(sb.toString());
}
else if (type.equals("float"))
{
return Double.valueOf(sb.toString());
}
else if (type.equals("exponent"))
{
String[] exponentString = sb.toString().split("E");

return Double.parseDouble(exponentString[0]) * Math.pow(10, Double.parseDouble(exponentString[1]));
}
else
{
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
errors.invalidValue(context.identifier.getName(), sb.toString(), context.line.get());
return errors;
}
}

@Override
public boolean canWrite(Object value)
{
return value instanceof Number;
}

@Override
public void write(Object value, dev.plex.toml.WriterContext context)
{
context.write(value.toString());
}

@Override
public boolean isPrimitiveType()
{
return true;
}

@Override
public String toString()
{
return "number";
}
}
95 changes: 95 additions & 0 deletions proxy/src/main/java/dev/plex/toml/ObjectValueWriter.java
@@ -0,0 +1,95 @@
package dev.plex.toml;

import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

class ObjectValueWriter implements ValueWriter
{
static final ValueWriter OBJECT_VALUE_WRITER = new ObjectValueWriter();

@Override
public boolean canWrite(Object value)
{
return true;
}

@Override
public void write(Object value, WriterContext context)
{
Map<String, Object> to = new LinkedHashMap<String, Object>();
Set<Field> fields = getFields(value.getClass());
for (Field field : fields)
{
if (field.isAnnotationPresent(SerializedName.class))
{
to.put(field.getDeclaredAnnotation(SerializedName.class).value(), getFieldValue(field, value));
}
else
{
to.put(field.getName(), getFieldValue(field, value));
}
}

MapValueWriter.MAP_VALUE_WRITER.write(to, context);
}

@Override
public boolean isPrimitiveType()
{
return false;
}

private static Set<Field> getFields(Class<?> cls)
{
Set<Field> fields = new LinkedHashSet<Field>(Arrays.asList(cls.getDeclaredFields()));
while (cls != Object.class)
{
fields.addAll(Arrays.asList(cls.getDeclaredFields()));
cls = cls.getSuperclass();
}
removeConstantsAndSyntheticFields(fields);

return fields;
}

private static void removeConstantsAndSyntheticFields(Set<Field> fields)
{
Iterator<Field> iterator = fields.iterator();
while (iterator.hasNext())
{
Field field = iterator.next();
if ((Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) || field.isSynthetic() || Modifier.isTransient(field.getModifiers()))
{
iterator.remove();
}
}
}

private static Object getFieldValue(Field field, Object o)
{
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
Object value = null;
try
{
value = field.get(o);
}
catch (IllegalAccessException ignored)
{
}
field.setAccessible(isAccessible);

return value;
}

private ObjectValueWriter()
{
}
}
63 changes: 63 additions & 0 deletions proxy/src/main/java/dev/plex/toml/PrimitiveArrayValueWriter.java
@@ -0,0 +1,63 @@
package dev.plex.toml;

import java.util.Collection;

class PrimitiveArrayValueWriter extends ArrayValueWriter
{
static final ValueWriter PRIMITIVE_ARRAY_VALUE_WRITER = new PrimitiveArrayValueWriter();

@Override
public boolean canWrite(Object value)
{
return isArrayish(value) && isArrayOfPrimitive(value);
}

@Override
public void write(Object o, WriterContext context)
{
Collection<?> values = normalize(o);

context.write('[');
context.writeArrayDelimiterPadding();

boolean first = true;
ValueWriter firstWriter = null;

for (Object value : values)
{
if (first)
{
firstWriter = ValueWriters.WRITERS.findWriterFor(value);
first = false;
}
else
{
ValueWriter writer = ValueWriters.WRITERS.findWriterFor(value);
if (writer != firstWriter)
{
throw new IllegalStateException(
context.getContextPath() +
": cannot write a heterogeneous array; first element was of type " + firstWriter +
" but found " + writer
);
}
context.write(", ");
}

ValueWriters.WRITERS.findWriterFor(value).write(value, context);
}

context.writeArrayDelimiterPadding();
context.write(']');
}

private PrimitiveArrayValueWriter()
{
}

@Override
public String toString()
{
return "primitive-array";
}
}
361 changes: 361 additions & 0 deletions proxy/src/main/java/dev/plex/toml/Results.java
@@ -0,0 +1,361 @@
package dev.plex.toml;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

class Results
{

static class Errors
{

private final StringBuilder sb = new StringBuilder();

void duplicateTable(String table, int line)
{
sb.append("Duplicate table definition on line ")
.append(line)
.append(": [")
.append(table)
.append("]");
}

public void tableDuplicatesKey(String table, AtomicInteger line)
{
sb.append("Key already exists for table defined on line ")
.append(line.get())
.append(": [")
.append(table)
.append("]");
}

public void keyDuplicatesTable(String key, AtomicInteger line)
{
sb.append("Table already exists for key defined on line ")
.append(line.get())
.append(": ")
.append(key);
}

void emptyImplicitTable(String table, int line)
{
sb.append("Invalid table definition due to empty implicit table name: ")
.append(table);
}

void invalidTable(String table, int line)
{
sb.append("Invalid table definition on line ")
.append(line)
.append(": ")
.append(table)
.append("]");
}

void duplicateKey(String key, int line)
{
sb.append("Duplicate key");
if (line > -1)
{
sb.append(" on line ")
.append(line);
}
sb.append(": ")
.append(key);
}

void invalidTextAfterIdentifier(dev.plex.toml.Identifier identifier, char text, int line)
{
sb.append("Invalid text after key ")
.append(identifier.getName())
.append(" on line ")
.append(line)
.append(". Make sure to terminate the value or add a comment (#).");
}

void invalidKey(String key, int line)
{
sb.append("Invalid key on line ")
.append(line)
.append(": ")
.append(key);
}

void invalidTableArray(String tableArray, int line)
{
sb.append("Invalid table array definition on line ")
.append(line)
.append(": ")
.append(tableArray);
}

void invalidValue(String key, String value, int line)
{
sb.append("Invalid value on line ")
.append(line)
.append(": ")
.append(key)
.append(" = ")
.append(value);
}

void unterminatedKey(String key, int line)
{
sb.append("Key is not followed by an equals sign on line ")
.append(line)
.append(": ")
.append(key);
}

void unterminated(String key, String value, int line)
{
sb.append("Unterminated value on line ")
.append(line)
.append(": ")
.append(key)
.append(" = ")
.append(value.trim());
}

public void heterogenous(String key, int line)
{
sb.append(key)
.append(" becomes a heterogeneous array on line ")
.append(line);
}

boolean hasErrors()
{
return sb.length() > 0;
}

@Override
public String toString()
{
return sb.toString();
}

public void add(Errors other)
{
sb.append(other.sb);
}
}

final Errors errors = new Errors();
private final Set<String> tables = new HashSet<String>();
private final Deque<Container> stack = new ArrayDeque<Container>();

Results()
{
stack.push(new Container.Table(""));
}

void addValue(String key, Object value, AtomicInteger line)
{
Container currentTable = stack.peek();

if (value instanceof Map)
{
String path = getInlineTablePath(key);
if (path == null)
{
startTable(key, line);
}
else if (path.isEmpty())
{
startTables(dev.plex.toml.Identifier.from(key, null), line);
}
else
{
startTables(dev.plex.toml.Identifier.from(path, null), line);
}
@SuppressWarnings("unchecked")
Map<String, Object> valueMap = (Map<String, Object>) value;
for (Map.Entry<String, Object> entry : valueMap.entrySet())
{
addValue(entry.getKey(), entry.getValue(), line);
}
stack.pop();
}
else if (currentTable.accepts(key))
{
currentTable.put(key, value);
}
else
{
if (currentTable.get(key) instanceof Container)
{
errors.keyDuplicatesTable(key, line);
}
else
{
errors.duplicateKey(key, line != null ? line.get() : -1);
}
}
}

void startTableArray(dev.plex.toml.Identifier identifier, AtomicInteger line)
{
String tableName = identifier.getBareName();
while (stack.size() > 1)
{
stack.pop();
}

dev.plex.toml.Keys.Key[] tableParts = dev.plex.toml.Keys.split(tableName);
for (int i = 0; i < tableParts.length; i++)
{
String tablePart = tableParts[i].name;
Container currentContainer = stack.peek();

if (currentContainer.get(tablePart) instanceof Container.TableArray)
{
Container.TableArray currentTableArray = (Container.TableArray) currentContainer.get(tablePart);
stack.push(currentTableArray);

if (i == tableParts.length - 1)
{
currentTableArray.put(tablePart, new Container.Table());
}

stack.push(currentTableArray.getCurrent());
currentContainer = stack.peek();
}
else if (currentContainer.get(tablePart) instanceof Container.Table && i < tableParts.length - 1)
{
Container nextTable = (Container) currentContainer.get(tablePart);
stack.push(nextTable);
}
else if (currentContainer.accepts(tablePart))
{
Container newContainer = i == tableParts.length - 1 ? new Container.TableArray() : new Container.Table();
addValue(tablePart, newContainer, line);
stack.push(newContainer);

if (newContainer instanceof Container.TableArray)
{
stack.push(((Container.TableArray) newContainer).getCurrent());
}
}
else
{
errors.duplicateTable(tableName, line.get());
break;
}
}
}

void startTables(dev.plex.toml.Identifier id, AtomicInteger line)
{
String tableName = id.getBareName();

while (stack.size() > 1)
{
stack.pop();
}

dev.plex.toml.Keys.Key[] tableParts = dev.plex.toml.Keys.split(tableName);
for (int i = 0; i < tableParts.length; i++)
{
String tablePart = tableParts[i].name;
Container currentContainer = stack.peek();
if (currentContainer.get(tablePart) instanceof Container)
{
Container nextTable = (Container) currentContainer.get(tablePart);
if (i == tableParts.length - 1 && !nextTable.isImplicit())
{
errors.duplicateTable(tableName, line.get());
return;
}
stack.push(nextTable);
if (stack.peek() instanceof Container.TableArray)
{
stack.push(((Container.TableArray) stack.peek()).getCurrent());
}
}
else if (currentContainer.accepts(tablePart))
{
startTable(tablePart, i < tableParts.length - 1, line);
}
else
{
errors.tableDuplicatesKey(tablePart, line);
break;
}
}
}

/**
* Warning: After this method has been called, this instance is no longer usable.
*/
Map<String, Object> consume()
{
Container values = stack.getLast();
stack.clear();

return ((Container.Table) values).consume();
}

private Container startTable(String tableName, AtomicInteger line)
{
Container newTable = new Container.Table(tableName);
addValue(tableName, newTable, line);
stack.push(newTable);

return newTable;
}

private Container startTable(String tableName, boolean implicit, AtomicInteger line)
{
Container newTable = new Container.Table(tableName, implicit);
addValue(tableName, newTable, line);
stack.push(newTable);

return newTable;
}

private String getInlineTablePath(String key)
{
Iterator<Container> descendingIterator = stack.descendingIterator();
StringBuilder sb = new StringBuilder();

while (descendingIterator.hasNext())
{
Container next = descendingIterator.next();
if (next instanceof Container.TableArray)
{
return null;
}

Container.Table table = (Container.Table) next;

if (table.name == null)
{
break;
}

if (sb.length() > 0)
{
sb.append('.');
}

sb.append(table.name);
}

if (sb.length() > 0)
{
sb.append('.');
}

sb.append(key)
.insert(0, '[')
.append(']');

return sb.toString();
}
}
154 changes: 154 additions & 0 deletions proxy/src/main/java/dev/plex/toml/StringValueReaderWriter.java
@@ -0,0 +1,154 @@
package dev.plex.toml;

import java.net.URI;
import java.net.URL;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class StringValueReaderWriter implements ValueReader, ValueWriter
{

static final StringValueReaderWriter STRING_VALUE_READER_WRITER = new StringValueReaderWriter();
private static final Pattern UNICODE_REGEX = Pattern.compile("\\\\[uU](.{4})");

static private final String[] specialCharacterEscapes = new String[93];

static
{
specialCharacterEscapes['\b'] = "\\b";
specialCharacterEscapes['\t'] = "\\t";
specialCharacterEscapes['\n'] = "\\n";
specialCharacterEscapes['\f'] = "\\f";
specialCharacterEscapes['\r'] = "\\r";
specialCharacterEscapes['"'] = "\\\"";
specialCharacterEscapes['\\'] = "\\\\";
}

@Override
public boolean canRead(String s)
{
return s.startsWith("\"");
}

@Override
public Object read(String s, AtomicInteger index, Context context)
{
int startIndex = index.incrementAndGet();
int endIndex = -1;

for (int i = index.get(); i < s.length(); i = index.incrementAndGet())
{
char ch = s.charAt(i);
if (ch == '"' && s.charAt(i - 1) != '\\')
{
endIndex = i;
break;
}
}

if (endIndex == -1)
{
Results.Errors errors = new Results.Errors();
errors.unterminated(context.identifier.getName(), s.substring(startIndex - 1), context.line.get());
return errors;
}

String raw = s.substring(startIndex, endIndex);
s = replaceUnicodeCharacters(raw);
s = replaceSpecialCharacters(s);

if (s == null)
{
Results.Errors errors = new Results.Errors();
errors.invalidValue(context.identifier.getName(), raw, context.line.get());
return errors;
}

return s;
}

String replaceUnicodeCharacters(String value)
{
Matcher unicodeMatcher = UNICODE_REGEX.matcher(value);

while (unicodeMatcher.find())
{
value = value.replace(unicodeMatcher.group(), new String(Character.toChars(Integer.parseInt(unicodeMatcher.group(1), 16))));
}
return value;
}

String replaceSpecialCharacters(String s)
{
for (int i = 0; i < s.length() - 1; i++)
{
char ch = s.charAt(i);
char next = s.charAt(i + 1);

if (ch == '\\' && next == '\\')
{
i++;
}
else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '\\'))
{
return null;
}
}

return s.replace("\\n", "\n")
.replace("\\\"", "\"")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\\", "\\")
.replace("\\/", "/")
.replace("\\b", "\b")
.replace("\\f", "\f");
}

@Override
public boolean canWrite(Object value)
{
return value instanceof String || value instanceof Character || value instanceof URL || value instanceof URI || value instanceof Enum;
}

@Override
public void write(Object value, WriterContext context)
{
context.write('"');
escapeUnicode(value.toString(), context);
context.write('"');
}

@Override
public boolean isPrimitiveType()
{
return true;
}

private void escapeUnicode(String in, WriterContext context)
{
for (int i = 0; i < in.length(); i++)
{
int codePoint = in.codePointAt(i);
if (codePoint < specialCharacterEscapes.length && specialCharacterEscapes[codePoint] != null)
{
context.write(specialCharacterEscapes[codePoint]);
}
else
{
context.write(in.charAt(i));
}
}
}

private StringValueReaderWriter()
{
}

@Override
public String toString()
{
return "string";
}
}
38 changes: 38 additions & 0 deletions proxy/src/main/java/dev/plex/toml/TableArrayValueWriter.java
@@ -0,0 +1,38 @@
package dev.plex.toml;

import java.util.Collection;
import static dev.plex.toml.ValueWriters.WRITERS;

class TableArrayValueWriter extends ArrayValueWriter
{
static final ValueWriter TABLE_ARRAY_VALUE_WRITER = new TableArrayValueWriter();

@Override
public boolean canWrite(Object value)
{
return isArrayish(value) && !isArrayOfPrimitive(value);
}

@Override
public void write(Object from, WriterContext context)
{
Collection<?> values = normalize(from);

WriterContext subContext = context.pushTableFromArray();

for (Object value : values)
{
WRITERS.findWriterFor(value).write(value, subContext);
}
}

private TableArrayValueWriter()
{
}

@Override
public String toString()
{
return "table-array";
}
}
500 changes: 500 additions & 0 deletions proxy/src/main/java/dev/plex/toml/Toml.java

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions proxy/src/main/java/dev/plex/toml/TomlParser.java
@@ -0,0 +1,88 @@
package dev.plex.toml;

import java.util.concurrent.atomic.AtomicInteger;

class TomlParser
{

static dev.plex.toml.Results run(String tomlString)
{
final dev.plex.toml.Results results = new dev.plex.toml.Results();

if (tomlString.isEmpty())
{
return results;
}

AtomicInteger index = new AtomicInteger();
boolean inComment = false;
AtomicInteger line = new AtomicInteger(1);
dev.plex.toml.Identifier identifier = null;
Object value = null;

for (int i = index.get(); i < tomlString.length(); i = index.incrementAndGet())
{
char c = tomlString.charAt(i);

if (results.errors.hasErrors())
{
break;
}

if (c == '#' && !inComment)
{
inComment = true;
}
else if (!Character.isWhitespace(c) && !inComment && identifier == null)
{
dev.plex.toml.Identifier id = dev.plex.toml.IdentifierConverter.IDENTIFIER_CONVERTER.convert(tomlString, index, new dev.plex.toml.Context(null, line, results.errors));

if (id != dev.plex.toml.Identifier.INVALID)
{
if (id.isKey())
{
identifier = id;
}
else if (id.isTable())
{
results.startTables(id, line);
}
else if (id.isTableArray())
{
results.startTableArray(id, line);
}
}
}
else if (c == '\n')
{
inComment = false;
identifier = null;
value = null;
line.incrementAndGet();
}
else if (!inComment && identifier != null && identifier.isKey() && value == null && !Character.isWhitespace(c))
{
value = ValueReaders.VALUE_READERS.convert(tomlString, index, new dev.plex.toml.Context(identifier, line, results.errors));

if (value instanceof dev.plex.toml.Results.Errors)
{
results.errors.add((dev.plex.toml.Results.Errors) value);
}
else
{
results.addValue(identifier.getName(), value, line);
}
}
else if (value != null && !inComment && !Character.isWhitespace(c))
{
results.errors.invalidTextAfterIdentifier(identifier, c, line.get());
}
}

return results;
}

private TomlParser()
{
}
}
186 changes: 186 additions & 0 deletions proxy/src/main/java/dev/plex/toml/TomlWriter.java
@@ -0,0 +1,186 @@
package dev.plex.toml;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.jetbrains.annotations.Nullable;
import static dev.plex.toml.ValueWriters.WRITERS;

/**
* <p>Converts Objects to TOML</p>
*
* <p>An input Object can comprise arbitrarily nested combinations of Java primitive types,
* other {@link Object}s, {@link Map}s, {@link List}s, and Arrays. {@link Object}s and {@link Map}s
* are output to TOML tables, and {@link List}s and Array to TOML arrays.</p>
*
* <p>Example usage:</p>
* <pre><code>
* class AClass {
* int anInt = 1;
* int[] anArray = { 2, 3 };
* }
*
* String tomlString = new TomlWriter().write(new AClass());
* </code></pre>
*/
public class TomlWriter
{

public static class Builder
{
private int keyIndentation;
private int tableIndentation;
private int arrayDelimiterPadding = 0;
private TimeZone timeZone = TimeZone.getTimeZone("UTC");
private boolean showFractionalSeconds = false;

public Builder indentValuesBy(int spaces)
{
this.keyIndentation = spaces;

return this;
}

public Builder indentTablesBy(int spaces)
{
this.tableIndentation = spaces;

return this;
}

public Builder timeZone(TimeZone timeZone)
{
this.timeZone = timeZone;

return this;
}

/**
* @param spaces number of spaces to put between opening square bracket and first item and between closing square bracket and last item
* @return this TomlWriter.Builder instance
*/
public Builder padArrayDelimitersBy(int spaces)
{
this.arrayDelimiterPadding = spaces;

return this;
}

public TomlWriter build()
{
return new TomlWriter(keyIndentation, tableIndentation, arrayDelimiterPadding, timeZone, showFractionalSeconds);
}

public Builder showFractionalSeconds()
{
this.showFractionalSeconds = true;
return this;
}
}

private final IndentationPolicy indentationPolicy;
private final dev.plex.toml.DatePolicy datePolicy;

/**
* Creates a TomlWriter instance.
*/
public TomlWriter()
{
this(0, 0, 0, TimeZone.getTimeZone("UTC"), false);
}

private TomlWriter(int keyIndentation, int tableIndentation, int arrayDelimiterPadding, TimeZone timeZone, boolean showFractionalSeconds)
{
this.indentationPolicy = new IndentationPolicy(keyIndentation, tableIndentation, arrayDelimiterPadding);
this.datePolicy = new dev.plex.toml.DatePolicy(timeZone, showFractionalSeconds);
}

/**
* Write an Object into TOML String.
*
* @param from the object to be written
* @return a string containing the TOML representation of the given Object
*/
public String write(Object from)
{
try
{
StringWriter output = new StringWriter();
write(from, output, null);

return output.toString();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}

/**
* Write an Object in TOML to a {@link File}. Output is encoded as UTF-8.
*
* @param from the object to be written
* @param target the File to which the TOML will be written
* @throws IOException if any file operations fail
*/
public void write(Object from, File target) throws IOException
{
OutputStream outputStream = new FileOutputStream(target, true);
try
{
write(from, outputStream, target);
}
finally
{
outputStream.close();
}
}

/**
* Write an Object in TOML to a {@link OutputStream}. Output is encoded as UTF-8.
*
* @param from the object to be written
* @param target the OutputStream to which the TOML will be written. The stream is NOT closed after being written to.
* @throws IOException if target.write() fails
*/
public void write(Object from, OutputStream target, @Nullable File file) throws IOException
{
OutputStreamWriter writer = new OutputStreamWriter(target, StandardCharsets.UTF_8);
write(from, writer, file);
writer.flush();
}

/**
* Write an Object in TOML to a {@link Writer}. You MUST ensure that the {@link Writer}s's encoding is set to UTF-8 for the TOML to be valid.
*
* @param from the object to be written. Can be a Map or a custom type. Must not be null.
* @param target the Writer to which TOML will be written. The Writer is not closed.
* @throws IOException if target.write() fails
* @throws IllegalArgumentException if from is of an invalid type
*/
public void write(Object from, Writer target, @Nullable File file) throws IOException
{
dev.plex.toml.ValueWriter valueWriter = WRITERS.findWriterFor(from);
if (valueWriter == MapValueWriter.MAP_VALUE_WRITER || valueWriter == dev.plex.toml.ObjectValueWriter.OBJECT_VALUE_WRITER)
{
WriterContext context = new WriterContext(indentationPolicy, datePolicy, target);
if (file != null && file.exists())
{
context.file = file;
}
valueWriter.write(from, context);
}
else
{
throw new IllegalArgumentException("An object of class " + from.getClass().getSimpleName() + " cannot produce valid TOML. Please pass in a Map or a custom type.");
}
}
}
21 changes: 21 additions & 0 deletions proxy/src/main/java/dev/plex/toml/ValueReader.java
@@ -0,0 +1,21 @@
package dev.plex.toml;

import java.util.concurrent.atomic.AtomicInteger;

interface ValueReader
{

/**
* @param s must already have been trimmed
*/
boolean canRead(String s);

/**
* Partial validation. Stops after type terminator, rather than at EOI.
*
* @param s must already have been validated by {@link #canRead(String)}
* @param index where to start in s
* @return a value or a {@link dev.plex.toml.Results.Errors}
*/
Object read(String s, AtomicInteger index, dev.plex.toml.Context context);
}
40 changes: 40 additions & 0 deletions proxy/src/main/java/dev/plex/toml/ValueReaders.java
@@ -0,0 +1,40 @@
package dev.plex.toml;

import java.util.concurrent.atomic.AtomicInteger;
import static dev.plex.toml.ArrayValueReader.ARRAY_VALUE_READER;
import static dev.plex.toml.BooleanValueReaderWriter.BOOLEAN_VALUE_READER_WRITER;
import static dev.plex.toml.DateValueReaderWriter.DATE_VALUE_READER_WRITER;
import static dev.plex.toml.LiteralStringValueReader.LITERAL_STRING_VALUE_READER;
import static dev.plex.toml.MultilineLiteralStringValueReader.MULTILINE_LITERAL_STRING_VALUE_READER;
import static dev.plex.toml.MultilineStringValueReader.MULTILINE_STRING_VALUE_READER;
import static dev.plex.toml.StringValueReaderWriter.STRING_VALUE_READER_WRITER;

class ValueReaders
{

static final ValueReaders VALUE_READERS = new ValueReaders();

Object convert(String value, AtomicInteger index, dev.plex.toml.Context context)
{
String substring = value.substring(index.get());
for (dev.plex.toml.ValueReader valueParser : READERS)
{
if (valueParser.canRead(substring))
{
return valueParser.read(value, index, context);
}
}

dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
errors.invalidValue(context.identifier.getName(), substring, context.line.get());
return errors;
}

private ValueReaders()
{
}

private static final dev.plex.toml.ValueReader[] READERS = {
MULTILINE_STRING_VALUE_READER, MULTILINE_LITERAL_STRING_VALUE_READER, LITERAL_STRING_VALUE_READER, STRING_VALUE_READER_WRITER, DATE_VALUE_READER_WRITER, NumberValueReaderWriter.NUMBER_VALUE_READER_WRITER, BOOLEAN_VALUE_READER_WRITER, ARRAY_VALUE_READER, InlineTableValueReader.INLINE_TABLE_VALUE_READER
};
}
10 changes: 10 additions & 0 deletions proxy/src/main/java/dev/plex/toml/ValueWriter.java
@@ -0,0 +1,10 @@
package dev.plex.toml;

interface ValueWriter
{
boolean canWrite(Object value);

void write(Object value, WriterContext context);

boolean isPrimitiveType();
}
35 changes: 35 additions & 0 deletions proxy/src/main/java/dev/plex/toml/ValueWriters.java
@@ -0,0 +1,35 @@
package dev.plex.toml;

class ValueWriters
{

static final ValueWriters WRITERS = new ValueWriters();

ValueWriter findWriterFor(Object value)
{
for (ValueWriter valueWriter : VALUE_WRITERS)
{
if (valueWriter.canWrite(value))
{
return valueWriter;
}
}

return ObjectValueWriter.OBJECT_VALUE_WRITER;
}

private ValueWriters()
{
}

private static dev.plex.toml.DateValueReaderWriter getPlatformSpecificDateConverter()
{
String specificationVersion = Runtime.class.getPackage().getSpecificationVersion();
return specificationVersion != null && specificationVersion.startsWith("1.6") ? dev.plex.toml.DateValueReaderWriter.DATE_PARSER_JDK_6 : dev.plex.toml.DateValueReaderWriter.DATE_VALUE_READER_WRITER;
}

private static final ValueWriter[] VALUE_WRITERS = {
StringValueReaderWriter.STRING_VALUE_READER_WRITER, NumberValueReaderWriter.NUMBER_VALUE_READER_WRITER, dev.plex.toml.BooleanValueReaderWriter.BOOLEAN_VALUE_READER_WRITER, getPlatformSpecificDateConverter(),
MapValueWriter.MAP_VALUE_WRITER, dev.plex.toml.PrimitiveArrayValueWriter.PRIMITIVE_ARRAY_VALUE_WRITER, TableArrayValueWriter.TABLE_ARRAY_VALUE_WRITER
};
}
185 changes: 185 additions & 0 deletions proxy/src/main/java/dev/plex/toml/WriterContext.java
@@ -0,0 +1,185 @@
package dev.plex.toml;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;

class WriterContext
{
private String arrayKey = null;
private boolean isArrayOfTable = false;
private boolean empty = true;
public final String key;
private final String currentTableIndent;
private final String currentFieldIndent;
private final Writer output;
private final dev.plex.toml.IndentationPolicy indentationPolicy;
private final dev.plex.toml.DatePolicy datePolicy;

public File file;
public String parentName;
public boolean hasRun = false;

WriterContext(dev.plex.toml.IndentationPolicy indentationPolicy, dev.plex.toml.DatePolicy datePolicy, Writer output)
{
this("", "", output, indentationPolicy, datePolicy);
}

WriterContext pushTable(String newKey)
{
String newIndent = "";
if (!key.isEmpty())
{
newIndent = growIndent(indentationPolicy);
}

String fullKey = key.isEmpty() ? newKey : key + "." + newKey;

WriterContext subContext = new WriterContext(fullKey, newIndent, output, indentationPolicy, datePolicy);
if (!empty)
{
subContext.empty = false;
}

return subContext;
}

WriterContext pushTableFromArray()
{
WriterContext subContext = new WriterContext(key, currentTableIndent, output, indentationPolicy, datePolicy);
if (!empty)
{
subContext.empty = false;
}
subContext.setIsArrayOfTable(true);

return subContext;
}

WriterContext write(String s)
{
try
{
output.write(s);
if (empty && !s.isEmpty())
{
empty = false;
}

return this;
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}

void write(char[] chars)
{
for (char c : chars)
{
write(c);
}
}

WriterContext write(char c)
{
try
{
output.write(c);
empty = false;

return this;
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}

void writeKey()
{
if (key.isEmpty())
{
return;
}

if (!empty)
{
write('\n');
}

write(currentTableIndent);

if (isArrayOfTable)
{
write("[[").write(key).write("]]\n");
}
else
{
write('[').write(key).write("]\n");
}
}

void writeArrayDelimiterPadding()
{
for (int i = 0; i < indentationPolicy.getArrayDelimiterPadding(); i++)
{
write(' ');
}
}

void indent()
{
if (!key.isEmpty())
{
write(currentFieldIndent);
}
}

dev.plex.toml.DatePolicy getDatePolicy()
{
return datePolicy;
}

WriterContext setIsArrayOfTable(boolean isArrayOfTable)
{
this.isArrayOfTable = isArrayOfTable;
return this;
}

WriterContext setArrayKey(String arrayKey)
{
this.arrayKey = arrayKey;
return this;
}

String getContextPath()
{
return key.isEmpty() ? arrayKey : key + "." + arrayKey;
}

private String growIndent(dev.plex.toml.IndentationPolicy indentationPolicy)
{
return currentTableIndent + fillStringWithSpaces(indentationPolicy.getTableIndent());
}

private String fillStringWithSpaces(int count)
{
char[] chars = new char[count];
Arrays.fill(chars, ' ');

return new String(chars);
}

private WriterContext(String key, String tableIndent, Writer output, dev.plex.toml.IndentationPolicy indentationPolicy, dev.plex.toml.DatePolicy datePolicy)
{
this.key = key;
this.output = output;
this.indentationPolicy = indentationPolicy;
this.currentTableIndent = tableIndent;
this.datePolicy = datePolicy;
this.currentFieldIndent = tableIndent + fillStringWithSpaces(this.indentationPolicy.getKeyValueIndent());
}
}
66 changes: 66 additions & 0 deletions proxy/src/main/java/dev/plex/util/PlexLog.java
@@ -0,0 +1,66 @@
package dev.plex.util;

import dev.plex.Plex;
import dev.plex.settings.ServerSettings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;

public class PlexLog
{
public static void log(String message, Object... strings)
{
for (int i = 0; i < strings.length; i++)
{
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
}
}
Plex.get().getServer().getConsoleCommandSource().sendMessage(MiniMessage.miniMessage().deserialize("<yellow>[Plex] <gray>" + message));
}

public static void log(Component component)
{
Plex.get().getServer().getConsoleCommandSource().sendMessage(Component.text("[Plex] ").color(NamedTextColor.YELLOW).append(component).colorIfAbsent(NamedTextColor.GRAY));
}

public static void error(String message, Object... strings)
{
for (int i = 0; i < strings.length; i++)
{
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
}
}
Plex.get().getServer().getConsoleCommandSource().sendMessage(MiniMessage.miniMessage().deserialize("<red>[Plex Error] <gold>" + message));
}

public static void warn(String message, Object... strings)
{
for (int i = 0; i < strings.length; i++)
{
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
}
}
Plex.get().getServer().getConsoleCommandSource().sendMessage(MiniMessage.miniMessage().deserialize("<#eb7c0e>[Plex Warning] <gold>" + message));
}

public static void debug(String message, Object... strings)
{
for (int i = 0; i < strings.length; i++)
{
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
}
}
if (Plex.get().getConfig().as(ServerSettings.class).getServer().isDebug())
{
Plex.get().getServer().getConsoleCommandSource().sendMessage(MiniMessage.miniMessage().deserialize("<dark_purple>[Plex Debug] <gold>" + message));
}
}
}
33 changes: 33 additions & 0 deletions proxy/src/main/java/dev/plex/util/RandomUtil.java
@@ -0,0 +1,33 @@
package dev.plex.util;

import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.text.format.NamedTextColor;

public class RandomUtil
{
public static NamedTextColor getRandomColor()
{
NamedTextColor[] colors = NamedTextColor.NAMES.values().stream().filter(namedTextColor -> namedTextColor != NamedTextColor.BLACK && namedTextColor != NamedTextColor.DARK_BLUE).toArray(NamedTextColor[]::new);
return colors[randomNum(colors.length)];
}

public static boolean randomBoolean()
{
return ThreadLocalRandom.current().nextBoolean();
}

public static int randomNum()
{
return ThreadLocalRandom.current().nextInt();
}

public static int randomNum(int limit)
{
return ThreadLocalRandom.current().nextInt(limit);
}

public static int randomNum(int start, int limit)
{
return ThreadLocalRandom.current().nextInt(start, limit);
}
}
Expand Up @@ -49,7 +49,7 @@ public static <T> Set<Class<? extends T>> getClassesBySubType(String packageName
{
if (clazz.getSuperclass() == subType || Arrays.asList(clazz.getInterfaces()).contains(subType))
{
classes.add((Class<? extends T>)clazz);
classes.add((Class<? extends T>) clazz);
}
});
return Collections.unmodifiableSet(classes);
Expand Down
31 changes: 31 additions & 0 deletions proxy/src/main/resources/config.toml
@@ -0,0 +1,31 @@
#############################
# #
# Plex Velocity #
# v1.1 #
# #
#############################

[server]
name = "Plexus"

# Placeholders
# %mcversion% - The Velocity Version (i.e. 3.1.2-SNAPSHOT)
# %servername% - The name provided above
# %randomgradient% - Creates a random gradient every ping of two random colors for the whole string
# Supports MiniMessage strings, no legacy & and §
motd = ["%randomgradient%%servername% - %mcversion%", "Another motd"]
colorizeMotd = false

# Enables debug messages
debug = false

# Due to game code only supporting legacy color codes for
# player samples and not components, you may only use § or & here
# for colors.
sample = ["example", "example"]

# Adds this amount to the current player count
add-player-count = 0

# The max player count will always display as +1 more than the player count
plus-one-max-count = true
140 changes: 140 additions & 0 deletions server/build.gradle
@@ -0,0 +1,140 @@
plugins {
id "net.minecrell.plugin-yml.bukkit" version "0.6.1-SNAPSHOT"
}

dependencies {
library "org.projectlombok:lombok:1.18.24"
annotationProcessor "org.projectlombok:lombok:1.18.24"
library "org.json:json:20220320"
library "commons-io:commons-io:2.11.0"
library "dev.morphia.morphia:morphia-core:2.2.6"
library "redis.clients:jedis:4.2.2"
library "org.mariadb.jdbc:mariadb-java-client:3.0.4"
library "com.zaxxer:HikariCP:5.0.1"
library "org.apache.httpcomponents:httpclient:4.5.13"
library "org.apache.commons:commons-lang3:3.12.0"
library "org.apache.maven.resolver:maven-resolver-api:1.8.0"
library "org.apache.maven.resolver:maven-resolver-impl:1.8.0"
library "org.apache.maven.resolver:maven-resolver-connector-basic:1.8.0"
library "org.apache.maven.resolver:maven-resolver-transport-http:1.8.0"
library "org.apache.maven:maven-resolver-provider:3.8.5"
library "org.eclipse.jetty:jetty-server:11.0.9"
library "org.eclipse.jetty:jetty-servlet:11.0.9"
library "org.eclipse.jetty:jetty-proxy:11.0.9"
library "com.google.code.gson:gson:2.9.0"
compileOnly "io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT"
compileOnly("com.github.MilkBowl:VaultAPI:1.7") {
exclude group: "org.bukkit", module: "bukkit"
}
implementation "org.bstats:bstats-base:3.0.0"
implementation "org.bstats:bstats-bukkit:3.0.0"
implementation project(":api")
}

group = rootProject.group
version = rootProject.version
description = "Plex-Server"

shadowJar {
archiveBaseName.set("Plex")
archiveClassifier.set("")
relocate "org.bstats", "dev.plex"
}

bukkit {
name = "Plex"
version = project.version
description = "Plex provides a new experience for freedom servers."
main = "dev.plex.Plex"
website = "https://plex.us.org"
authors = ["Telesphoreo", "taahanis", "supernt"]
softDepend = ["Vault"]
apiVersion = "1.18"
}

String getGitHash() {
def stdout = new ByteArrayOutputStream()
try {
exec {
commandLine "git", "rev-parse", "--short", "HEAD"
standardOutput = stdout
ignoreExitValue = true
}
} catch (GradleException e) {
logger.error("Couldn't determine Git head because Git is not installed. " + e.getMessage())
}
return stdout.size() > 0 ? stdout.toString().trim() : "unknown"
}

String getBuildNumber() {
def stdout = new ByteArrayOutputStream()
try {
exec {
commandLine "git", "rev-list", "HEAD", "--count"
standardOutput = stdout
ignoreExitValue = true
}
} catch (GradleException e) {
logger.error("Couldn't determine build number because Git is not installed. " + e.getMessage())
}
return stdout.size() ? stdout.toString().trim() + " (local)" : "unknown"
}

static def getDate() {
return new Date().format("MM/dd/yyyy '<light_purple>at<gold>' hh:mm:ss a z")
}

task buildProperties {
ant.propertyfile(file: "$project.projectDir/src/main/resources/build.properties") {
entry(key: "buildAuthor", default: System.getenv("JENKINS_URL") != null ? "jenkins" : "unknown")
entry(key: "buildNumber", value: System.getenv("JENKINS_URL") != null ? System.getenv("BUILD_NUMBER") + " (Jenkins)" : getBuildNumber())
entry(key: "buildDate", value: getDate())
entry(key: "buildHead", value: getGitHash())
}
}

tasks {
build {
dependsOn(shadowJar)
finalizedBy(buildProperties)
}

jar {
enabled = false
}

shadowJar {
finalizedBy(rootProject.tasks.copyJars)
}

javadoc {
options.memberLevel = JavadocMemberLevel.PRIVATE
}
}

publishing {
publications {
maven(MavenPublication) {
pom.withXml {
def dependenciesNode = asNode().appendNode("dependencies")
configurations.getByName("library").getAllDependencies().each { dependency ->
dependenciesNode.appendNode("dependency").with {
it.appendNode("groupId", dependency.group)
it.appendNode("artifactId", dependency.name)
it.appendNode("version", dependency.version)
it.appendNode("scope", "provided")
}
}
configurations.getByName("implementation").getAllDependencies().each { dependency ->
dependenciesNode.appendNode("dependency").with {
it.appendNode("groupId", dependency.group)
it.appendNode("artifactId", dependency.name)
it.appendNode("version", dependency.version)
it.appendNode("scope", "runtime")
}
}
}
artifacts = [shadowJar]
}
}
}
Expand Up @@ -2,12 +2,19 @@

import dev.plex.admin.Admin;
import dev.plex.admin.AdminList;
import dev.plex.api.PlexApi;
import dev.plex.api.PlexApiProvider;
import dev.plex.api.plugin.PlexPlugin;
import dev.plex.cache.DataUtils;
import dev.plex.cache.PlayerCache;
import dev.plex.config.Config;
import dev.plex.handlers.CommandHandler;
import dev.plex.handlers.ListenerHandler;
import dev.plex.hook.VaultHook;
import dev.plex.listener.impl.ChatListener;
import dev.plex.module.ModuleManager;
import dev.plex.permission.handler.NativePermissionHandler;
import dev.plex.permission.handler.VaultPermissionHandler;
import dev.plex.player.PlexPlayer;
import dev.plex.punishment.PunishmentManager;
import dev.plex.rank.RankManager;
Expand All @@ -22,29 +29,31 @@
import dev.plex.storage.punishment.SQLNotes;
import dev.plex.storage.punishment.SQLPunishment;
import dev.plex.util.BuildInfo;
import dev.plex.util.BungeeUtil;
import dev.plex.util.PlexLog;
import dev.plex.util.PlexUtils;
import dev.plex.util.UpdateChecker;
import dev.plex.world.CustomWorld;
import java.io.File;
import lombok.Getter;
import lombok.Setter;
import net.milkbowl.vault.permission.Permission;
import org.bson.conversions.Bson;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;

@Getter
@Setter
public class Plex extends JavaPlugin
public class Plex extends PlexPlugin implements PlexApiProvider
{
private static Plex plugin;

public Config config;
public Config messages;
public Config indefBans;
public Config commands;
public Config toggles;

private PlexProvider provider;

public File modulesFolder;
private StorageType storageType = StorageType.SQLITE;
Expand All @@ -55,6 +64,8 @@ public class Plex extends JavaPlugin
private MongoConnection mongoConnection;
private RedisConnection redisConnection;

private PlayerCache playerCache;

private MongoPlayerData mongoPlayerData;
private SQLPlayerData sqlPlayerData;

Expand All @@ -71,7 +82,6 @@ public class Plex extends JavaPlugin
private UpdateChecker updateChecker;
private String system;

private Permission permissions;

public static Plex get()
{
Expand All @@ -81,11 +91,13 @@ public static Plex get()
@Override
public void onLoad()
{
super.onLoad();
plugin = this;
config = new Config(this, "config.yml");
messages = new Config(this, "messages.yml");
indefBans = new Config(this, "indefbans.yml");
commands = new Config(this, "commands.yml");
toggles = new Config(this, "toggles.yml");
build.load(this);

modulesFolder = new File(this.getDataFolder() + File.separator + "modules");
Expand All @@ -97,22 +109,27 @@ public void onLoad()
moduleManager = new ModuleManager();
moduleManager.loadAllModules();
moduleManager.loadModules();

this.setChatHandler(new ChatListener.ChatHandlerImpl());

}

@Override
public void onEnable()
{
config.load();
messages.load();
// Don't add default entries to indefinite ban file
toggles.load();

// Don't add default entries to these files
indefBans.load(false);
commands.load(false);

sqlConnection = new SQLConnection();
mongoConnection = new MongoConnection();
redisConnection = new RedisConnection();

moduleManager.enableModules();
playerCache = new PlayerCache();

system = config.getString("system");

Expand All @@ -128,9 +145,21 @@ public void onEnable()
e.printStackTrace();
}

if (!setupPermissions() && system.equalsIgnoreCase("permissions") && !getServer().getPluginManager().isPluginEnabled("Vault"))
boolean permissions = false;
if (getServer().getPluginManager().isPluginEnabled("Vault"))
{
VaultPermissionHandler handler = new VaultPermissionHandler();
if (VaultHook.getPermission() != null)
{
this.setPermissionHandler(handler);
permissions = true;
PlexLog.debug("Enabling Vault support for permissions with a permission plugin: " + VaultHook.getPermission().getName());
}
}

if (!permissions)
{
throw new RuntimeException("Vault is required to run on the server if you use permissions!");
this.setPermissionHandler(new NativePermissionHandler());
}

updateChecker = new UpdateChecker();
Expand Down Expand Up @@ -183,14 +212,26 @@ public void onEnable()
PlexLog.log("Started " + serviceManager.serviceCount() + " services.");

reloadPlayers();
PlexLog.debug("Registered Bukkit -> BungeeCord Plugin Messaging Channel");
PlexLog.debug("Velocity Support? " + BungeeUtil.isVelocity());
PlexLog.debug("BungeeCord Support? " + BungeeUtil.isBungeeCord());
if (BungeeUtil.isBungeeCord() && BungeeUtil.isVelocity())
{
PlexLog.warn("It seems you have both velocity and bungeecord configuration options enabled! When running Velocity, you do NOT need to enable bungeecord.");
}
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");


provider = new PlexProvider();
moduleManager.enableModules();
}

@Override
public void onDisable()
{
Bukkit.getOnlinePlayers().forEach(player ->
{
PlexPlayer plexPlayer = PlayerCache.getPlexPlayerMap().get(player.getUniqueId()); //get the player because it's literally impossible for them to not have an object
PlexPlayer plexPlayer = playerCache.getPlexPlayerMap().get(player.getUniqueId()); //get the player because it's literally impossible for them to not have an object

if (plugin.getRankManager().isAdmin(plexPlayer))
{
Expand All @@ -212,6 +253,8 @@ else if (sqlPlayerData != null) //sql checking
redisConnection.getJedis().close();
}

this.getServer().getMessenger().unregisterOutgoingPluginChannel(this);

moduleManager.disableModules();
}

Expand All @@ -230,7 +273,7 @@ private void reloadPlayers()
Bukkit.getOnlinePlayers().forEach(player ->
{
PlexPlayer plexPlayer = DataUtils.getPlayer(player.getUniqueId());
PlayerCache.getPlexPlayerMap().put(player.getUniqueId(), plexPlayer); //put them into the cache
playerCache.getPlexPlayerMap().put(player.getUniqueId(), plexPlayer); //put them into the cache
if (plugin.getRankManager().isAdmin(plexPlayer))
{
Admin admin = new Admin(plexPlayer.getUuid());
Expand All @@ -241,10 +284,9 @@ private void reloadPlayers()
});
}

public boolean setupPermissions()
@Override
public PlexApi getApi()
{
RegisteredServiceProvider<Permission> rsp = Bukkit.getServicesManager().getRegistration(Permission.class);
permissions = rsp.getProvider();
return permissions != null;
return provider;
}
}
File renamed without changes.
14 changes: 14 additions & 0 deletions server/src/main/java/dev/plex/PlexProvider.java
@@ -0,0 +1,14 @@
package dev.plex;

import dev.plex.api.IPlayerCache;
import dev.plex.api.PlexApi;
import dev.plex.player.PlexPlayer;

public class PlexProvider implements PlexApi
{
@Override
public IPlayerCache<PlexPlayer> getPlayerCache()
{
return Plex.get().getPlayerCache();
}
}
File renamed without changes.
File renamed without changes.
Expand Up @@ -3,6 +3,7 @@
import dev.plex.Plex;
import dev.plex.player.PlexPlayer;
import dev.plex.storage.StorageType;
import java.util.Optional;
import java.util.UUID;

/**
Expand Down Expand Up @@ -49,9 +50,14 @@ public static boolean hasPlayedBefore(String username)
*/
public static PlexPlayer getPlayer(UUID uuid)
{
if (PlayerCache.getPlexPlayerMap().containsKey(uuid))
return getPlayer(uuid, true);
}

public static PlexPlayer getPlayer(UUID uuid, boolean loadExtraData)
{
if (Plex.get().getPlayerCache().getPlexPlayerMap().containsKey(uuid))
{
return PlayerCache.getPlexPlayerMap().get(uuid);
return Plex.get().getPlayerCache().getPlexPlayerMap().get(uuid);
}

if (Plex.get().getStorageType() == StorageType.MONGODB)
Expand All @@ -60,19 +66,30 @@ public static PlexPlayer getPlayer(UUID uuid)
}
else
{
return Plex.get().getSqlPlayerData().getByUUID(uuid);
return Plex.get().getSqlPlayerData().getByUUID(uuid, loadExtraData);
}
}

public static PlexPlayer getPlayer(String username)
{
return getPlayer(username, true);
}

public static PlexPlayer getPlayer(String username, boolean loadExtraData)
{
Optional<PlexPlayer> plexPlayer = Plex.get().getPlayerCache().getPlexPlayerMap().values().stream().filter(player -> player.getName().equalsIgnoreCase(username)).findFirst();
if (plexPlayer.isPresent())
{
return plexPlayer.get();
}

if (Plex.get().getStorageType() == StorageType.MONGODB)
{
return Plex.get().getMongoPlayerData().getByName(username);
}
else
{
return Plex.get().getSqlPlayerData().getByName(username);
return Plex.get().getSqlPlayerData().getByName(username, loadExtraData);
}
}

Expand All @@ -85,7 +102,7 @@ public static PlexPlayer getPlayer(String username)
*/
public static PlexPlayer getPlayerByIP(String ip)
{
PlexPlayer player = PlayerCache.getPlexPlayerMap().values().stream().filter(plexPlayer -> plexPlayer.getIps().contains(ip)).findFirst().orElse(null);
PlexPlayer player = Plex.get().getPlayerCache().getPlexPlayerMap().values().stream().filter(plexPlayer -> plexPlayer.getIps().contains(ip)).findFirst().orElse(null);
if (player != null)
{
return player;
Expand Down
@@ -1,6 +1,7 @@
package dev.plex.cache;

import com.google.common.collect.Maps;
import dev.plex.api.IPlayerCache;
import dev.plex.player.PlexPlayer;
import java.util.Map;
import java.util.UUID;
Expand All @@ -9,7 +10,7 @@
* Cache storage
*/

public class PlayerCache
public class PlayerCache implements IPlayerCache<PlexPlayer>
{
/**
* A key/value pair where the key is the unique ID of the Plex Player
Expand All @@ -19,13 +20,13 @@ public class PlayerCache
/**
* A key/value pair where the key is the unique ID of the Punished Player
*/
// private static final Map<UUID, PunishedPlayer> punishedPlayerMap = Maps.newHashMap();
// private static final Map<UUID, PunishedPlayer> punishedPlayerMap = Maps.newHashMap();

// public static Map<UUID, PunishedPlayer> getPunishedPlayerMap()
// {
// return punishedPlayerMap;
// }
public static Map<UUID, PlexPlayer> getPlexPlayerMap()
// public static Map<UUID, PunishedPlayer> getPunishedPlayerMap()
// {
// return punishedPlayerMap;
// }
public Map<UUID, PlexPlayer> getPlexPlayerMap()
{
return plexPlayerMap;
}
Expand All @@ -39,7 +40,7 @@ public static Map<UUID, PlexPlayer> getPlexPlayerMap()
return getPunishedPlayerMap().get(uuid);
}
*/
public static PlexPlayer getPlexPlayer(UUID uuid)
public PlexPlayer getPlexPlayer(UUID uuid)
{
return getPlexPlayerMap().get(uuid);
}
Expand Down
Expand Up @@ -2,7 +2,6 @@

import dev.plex.Plex;
import dev.plex.cache.DataUtils;
import dev.plex.cache.PlayerCache;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
Expand Down Expand Up @@ -66,7 +65,7 @@ public abstract class PlexCommand extends Command implements PluginIdentifiableC
/**
* Creates an instance of the command
*/
public PlexCommand()
public PlexCommand(boolean register)
{
super("");
this.params = getClass().getAnnotation(CommandParameters.class);
Expand All @@ -83,7 +82,15 @@ public PlexCommand()
this.level = perms.level();
this.commandSource = perms.source();

getMap().register("plex", this);
if (register)
{
getMap().register("plex", this);
}
}

public PlexCommand()
{
this(true);
}

/**
Expand Down Expand Up @@ -123,7 +130,7 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String label, Str

if (sender instanceof Player player)
{
PlexPlayer plexPlayer = PlayerCache.getPlexPlayerMap().get(player.getUniqueId());
PlexPlayer plexPlayer = plugin.getPlayerCache().getPlexPlayerMap().get(player.getUniqueId());

if (plugin.getSystem().equalsIgnoreCase("ranks"))
{
Expand All @@ -143,7 +150,7 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String label, Str
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
if (!player.hasPermission(perms.permission()))
if (!perms.permission().isEmpty() && !plugin.getPermissionHandler().hasPermission(player, perms.permission()))
{
send(sender, messageComponent("noPermissionNode", perms.permission()));
return true;
Expand Down Expand Up @@ -179,7 +186,7 @@ else if (plugin.getSystem().equalsIgnoreCase("permissions"))
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
if (!plugin.getPermissions().playerHas(null, Bukkit.getOfflinePlayer(plexPlayer.getUuid()), perms.permission()))
if (!perms.permission().isEmpty() && !plugin.getPermissionHandler().hasPermission(Bukkit.getOfflinePlayer(plexPlayer.getName()), perms.permission()))
{
send(sender, messageComponent("noPermissionNode", perms.permission()));
return true;
Expand All @@ -194,7 +201,7 @@ else if (plugin.getSystem().equalsIgnoreCase("permissions"))
}
try
{
Component component = this.execute(sender, isConsole(sender) ? null : (Player)sender, args);
Component component = this.execute(sender, isConsole(sender) ? null : (Player) sender, args);
if (component != null)
{
send(sender, component);
Expand Down Expand Up @@ -280,7 +287,7 @@ protected boolean checkRank(CommandSender sender, Rank rank, String permission)
{
if (!isConsole(sender))
{
return checkRank((Player)sender, rank, permission);
return checkRank((Player) sender, rank, permission);
}
if (!sender.getName().equalsIgnoreCase("console"))
{
Expand All @@ -298,7 +305,7 @@ protected boolean checkRank(CommandSender sender, Rank rank, String permission)
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
if (!plugin.getPermissions().playerHas(null, Bukkit.getOfflinePlayer(plexPlayer.getUuid()), permission))
if (!perms.permission().isEmpty() && !plugin.getPermissionHandler().hasPermission(Bukkit.getOfflinePlayer(plexPlayer.getName()), perms.permission()))
{
throw new CommandFailException(PlexUtils.messageString("noPermissionNode", permission));
}
Expand Down Expand Up @@ -336,7 +343,7 @@ protected boolean checkRank(Player player, Rank rank, String permission)
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
if (!player.hasPermission(permission))
if (!perms.permission().isEmpty() && !plugin.getPermissionHandler().hasPermission(player, permission))
{
throw new CommandFailException(PlexUtils.messageString("noPermissionNode", permission));
}
Expand All @@ -357,7 +364,7 @@ protected boolean silentCheckRank(Player player, Rank rank, String permission)
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
return player.hasPermission(permission);
return !perms.permission().isEmpty() && plugin.getPermissionHandler().hasPermission(player, permission);
}
return false;
}
Expand All @@ -375,7 +382,7 @@ protected boolean checkTab(CommandSender sender, Rank rank, String permission)
{
if (!isConsole(sender))
{
return checkTab((Player)sender, rank, permission);
return checkTab((Player) sender, rank, permission);
}
return true;
}
Expand All @@ -398,7 +405,7 @@ protected boolean checkTab(Player player, Rank rank, String permission)
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
return player.hasPermission(permission);
return !perms.permission().isEmpty() && plugin.getPermissionHandler().hasPermission(player, permission);
}
return true;
}
Expand Down Expand Up @@ -510,6 +517,18 @@ protected Component messageComponent(String s, Object... objects)
return PlexUtils.messageComponent(s, objects);
}

/**
* Converts a message entry from the "messages.yml" to a Component
*
* @param s The message entry
* @param objects Any objects to replace in order
* @return A Kyori Component
*/
protected Component messageComponent(String s, Component... objects)
{
return PlexUtils.messageComponent(s, objects);
}

/**
* Converts a message entry from the "messages.yml" to a String
*
Expand Down Expand Up @@ -546,6 +565,16 @@ protected Component usage(String s)

protected Player getNonNullPlayer(String name)
{
try
{
UUID uuid = UUID.fromString(name);
return Bukkit.getPlayer(uuid);
}
catch (IllegalArgumentException ignored)
{

}

Player player = Bukkit.getPlayer(name);
if (player == null)
{
Expand All @@ -557,7 +586,7 @@ protected Player getNonNullPlayer(String name)
protected PlexPlayer getOnlinePlexPlayer(String name)
{
Player player = getNonNullPlayer(name);
PlexPlayer plexPlayer = PlayerCache.getPlexPlayer(player.getUniqueId());
PlexPlayer plexPlayer = plugin.getPlayerCache().getPlexPlayer(player.getUniqueId());
if (plexPlayer == null)
{
throw new PlayerNotFoundException();
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,9 @@
package dev.plex.command.impl;

import com.google.common.collect.ImmutableList;
import dev.plex.api.event.AdminAddEvent;
import dev.plex.api.event.AdminRemoveEvent;
import dev.plex.api.event.AdminSetRankEvent;
import dev.plex.cache.DataUtils;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
Expand All @@ -9,9 +12,6 @@
import dev.plex.command.exception.ConsoleOnlyException;
import dev.plex.command.exception.PlayerNotFoundException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.event.AdminAddEvent;
import dev.plex.event.AdminRemoveEvent;
import dev.plex.event.AdminSetRankEvent;
import dev.plex.player.PlexPlayer;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
Expand Down Expand Up @@ -85,7 +85,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
throw new ConsoleOnlyException();
}

// UUID targetUUID = PlexUtils.getFromName(args[1]);
// UUID targetUUID = PlexUtils.getFromName(args[1]);

if (!DataUtils.hasPlayedBefore(args[1]))
{
Expand Down Expand Up @@ -114,7 +114,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
throw new ConsoleOnlyException();
}

// UUID targetUUID = PlexUtils.getFromName(args[1]);
// UUID targetUUID = PlexUtils.getFromName(args[1]);

if (!DataUtils.hasPlayedBefore(args[1]))
{
Expand Down
@@ -1,6 +1,5 @@
package dev.plex.command.impl;

import dev.plex.cache.PlayerCache;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
Expand Down Expand Up @@ -38,15 +37,15 @@ private void adminChat(CommandSender sender, String message)
{
if (plugin.getSystem().equalsIgnoreCase("ranks"))
{
PlexPlayer plexPlayer = PlayerCache.getPlexPlayerMap().get(player.getUniqueId());
PlexPlayer plexPlayer = plugin.getPlayerCache().getPlexPlayerMap().get(player.getUniqueId());
if (plexPlayer.getRankFromString().isAtLeast(Rank.ADMIN) && plexPlayer.isAdminActive())
{
player.sendMessage(PlexUtils.messageComponent("adminChatFormat", sender.getName(), message));
}
}
else if (plugin.getSystem().equalsIgnoreCase("permissions"))
{
if (player.hasPermission("plex.adminchat"))
if (plugin.getPermissionHandler().hasPermission(player, "plex.adminchat"))
{
player.sendMessage(PlexUtils.messageComponent("adminChatFormat", sender.getName(), message));
}
Expand Down
File renamed without changes.
@@ -1,12 +1,12 @@
package dev.plex.command.impl;

import com.google.common.collect.ImmutableList;
import dev.plex.api.event.GameModeUpdateEvent;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.event.GameModeUpdateEvent;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.util.List;
Expand All @@ -19,7 +19,7 @@
import org.jetbrains.annotations.Nullable;

@CommandPermissions(level = Rank.OP, permission = "plex.gamemode.adventure", source = RequiredCommandSource.ANY)
@CommandParameters(name = "adventure", aliases = "gma", description = "Set your own or another player's gamemode to adventure mode")
@CommandParameters(name = "adventure", aliases = "gma,egma,eadventure,adventuremode,eadventuremode", description = "Set your own or another player's gamemode to adventure mode")
public class AdventureCMD extends PlexCommand
{
@Override
Expand Down
Expand Up @@ -12,11 +12,11 @@
import dev.plex.punishment.Punishment;
import dev.plex.punishment.PunishmentType;
import dev.plex.rank.enums.Rank;
import dev.plex.util.BungeeUtil;
import dev.plex.util.PlexLog;
import dev.plex.util.PlexUtils;
import dev.plex.util.TimeUtils;
import dev.plex.util.WebUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
Expand Down Expand Up @@ -101,7 +101,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
{
if (player != null)
{
player.kick(Punishment.generateBanMessage(punishment));
BungeeUtil.kickPlayer(player, Punishment.generateBanMessage(punishment));
}
});
PlexLog.debug("(From /ban command) PunishedPlayer UUID: " + plexPlayer.getUuid());
Expand Down
Expand Up @@ -7,7 +7,6 @@
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand All @@ -30,7 +29,8 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play

if (args[0].equalsIgnoreCase("list"))
{
send(sender, "The following have block modification abilities restricted:");
send(sender, messageComponent("listOfPlayersBlocked"));

int count = 0;
for (String player : bl.blockedPlayers.stream().toList())
{
Expand All @@ -45,7 +45,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
}
else if (args[0].equalsIgnoreCase("purge"))
{
PlexUtils.broadcast(componentFromString(sender.getName() + " - Unblocking block modification abilities for all players").color(NamedTextColor.AQUA));
PlexUtils.broadcast(messageComponent("unblockingEdits", sender.getName(), "all players"));
int count = 0;
for (String player : bl.blockedPlayers.stream().toList())
{
Expand All @@ -55,11 +55,11 @@ else if (args[0].equalsIgnoreCase("purge"))
++count;
}
}
return messageComponent("unblockedEditsSize", count);
return messageComponent("blockeditSize", "Unblocked", count);
}
else if (args[0].equalsIgnoreCase("all"))
{
PlexUtils.broadcast(componentFromString(sender.getName() + " - Blocking block modification abilities for all non-admins").color(NamedTextColor.RED));
PlexUtils.broadcast(messageComponent("blockingEdits", sender.getName(), "all non-admins"));
int count = 0;
for (final Player player : Bukkit.getOnlinePlayers())
{
Expand All @@ -70,7 +70,7 @@ else if (args[0].equalsIgnoreCase("all"))
}
}

return messageComponent("blockedEditsSize", count);
return messageComponent("blockeditSize", "Blocked", count);
}

final Player player = getNonNullPlayer(args[0]);
Expand All @@ -81,16 +81,16 @@ else if (args[0].equalsIgnoreCase("all"))
send(sender, messageComponent("higherRankThanYou"));
return null;
}
PlexUtils.broadcast(messageComponent("blockingEditFor", sender.getName(), player.getName()));
PlexUtils.broadcast(messageComponent("blockingEdits", sender.getName(), player.getName()));
bl.blockedPlayers.add(player.getName());
send(player, messageComponent("yourEditsHaveBeenBlocked"));
send(player, messageComponent("editsModified", "blocked"));
send(sender, messageComponent("editsBlocked", player.getName()));
}
else
{
PlexUtils.broadcast(messageComponent("unblockingEditFor", sender.getName(), player.getName()));
PlexUtils.broadcast(messageComponent("unblockingEdits", sender.getName(), player.getName()));
bl.blockedPlayers.remove(player.getName());
send(player, messageComponent("yourEditsHaveBeenUnblocked"));
send(player, messageComponent("editsModified", "unblocked"));
send(sender, messageComponent("editsUnblocked", player.getName()));
}
return null;
Expand Down
File renamed without changes.
File renamed without changes.
@@ -1,12 +1,12 @@
package dev.plex.command.impl;

import com.google.common.collect.ImmutableList;
import dev.plex.api.event.GameModeUpdateEvent;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.event.GameModeUpdateEvent;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.util.List;
Expand All @@ -19,7 +19,7 @@
import org.jetbrains.annotations.Nullable;

@CommandPermissions(level = Rank.OP, permission = "plex.gamemode.creative", source = RequiredCommandSource.ANY)
@CommandParameters(name = "creative", aliases = "gmc", description = "Set your own or another player's gamemode to creative mode")
@CommandParameters(name = "creative", aliases = "gmc,egmc,ecreative,eecreative,creativemode,ecreativemode", description = "Set your own or another player's gamemode to creative mode")
public class CreativeCMD extends PlexCommand
{
@Override
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Expand Up @@ -9,12 +9,10 @@
import dev.plex.punishment.PunishmentType;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.time.LocalDateTime;
import dev.plex.util.TimeUtils;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;

import dev.plex.util.TimeUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand Down
103 changes: 103 additions & 0 deletions server/src/main/java/dev/plex/command/impl/GamemodeCMD.java
@@ -0,0 +1,103 @@
package dev.plex.command.impl;

import dev.plex.api.event.GameModeUpdateEvent;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@CommandParameters(name = "gamemode", usage = "/<command> <creative | survival | adventure | default | spectator> [player]", description = "Change your gamemode", aliases = "gm,egamemode,gmt,egmt")
@CommandPermissions(level = Rank.OP, permission = "plex.gamemode", source = RequiredCommandSource.ANY)
public class GamemodeCMD extends PlexCommand
{
private GameMode gamemode;

@Override
protected Component execute(@NotNull CommandSender sender, @Nullable Player playerSender, String[] args)
{
if (args.length == 0)
{
return usage();
}
switch (args[0].toLowerCase())
{
case "survival", "s", "0" ->
{
gamemode = GameMode.SURVIVAL;
update(sender, playerSender, GameMode.SURVIVAL);
return null;
}
case "creative", "c", "1" ->
{
gamemode = GameMode.CREATIVE;
update(sender, playerSender, GameMode.CREATIVE);
return null;
}
case "adventure", "a", "2" ->
{
gamemode = GameMode.ADVENTURE;
update(sender, playerSender, GameMode.ADVENTURE);
return null;
}
case "default", "d", "5" ->
{
gamemode = plugin.getServer().getDefaultGameMode();
update(sender, playerSender, plugin.getServer().getDefaultGameMode());
return null;
}
case "spectator", "sp", "3", "6" ->
{
gamemode = GameMode.SPECTATOR;
checkRank(sender, Rank.ADMIN, "plex.gamemode.spectator");
update(sender, playerSender, GameMode.SPECTATOR);
return null;
}
}
if (args.length > 1)
{
checkRank(sender, Rank.ADMIN, "plex.gamemode.others");
Player player = getNonNullPlayer(args[1]);
Bukkit.getServer().getPluginManager().callEvent(new GameModeUpdateEvent(sender, player, gamemode));
}
return null;
}

private void update(CommandSender sender, Player playerSender, GameMode gameMode)
{
if (isConsole(sender))
{
throw new CommandFailException(PlexUtils.messageString("consoleMustDefinePlayer"));
}
if (!(playerSender == null))
{
Bukkit.getServer().getPluginManager().callEvent(new GameModeUpdateEvent(sender, playerSender.getPlayer(), gameMode));
}
}

@Override
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException
{
if (args.length == 1)
{
return Arrays.asList("creative", "survival", "adventure", "spectator", "default");
}
if (args.length == 2)
{
return PlexUtils.getPlayerNameList();
}
return Collections.emptyList();
}
}
Expand Up @@ -10,10 +10,10 @@
import dev.plex.punishment.Punishment;
import dev.plex.punishment.PunishmentType;
import dev.plex.rank.enums.Rank;
import dev.plex.util.BungeeUtil;
import dev.plex.util.PlexUtils;
import dev.plex.util.TimeUtils;
import dev.plex.util.WebUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.UUID;
Expand Down Expand Up @@ -66,7 +66,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
plugin.getPunishmentManager().punish(plexPlayer, punishment);
PlexUtils.broadcast(messageComponent("kickedPlayer", sender.getName(), plexPlayer.getName()));
player.kick(componentFromString(reason));
BungeeUtil.kickPlayer(player, Punishment.generateBanMessage(punishment));
return null;
}
}
File renamed without changes.
File renamed without changes.
Expand Up @@ -42,13 +42,17 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
}

punishedPlayer.setLockedUp(!punishedPlayer.isLockedUp());
if (punishedPlayer.isLockedUp())
{
player.openInventory(player.getInventory());
}
PlexUtils.broadcast(messageComponent(punishedPlayer.isLockedUp() ? "lockedUpPlayer" : "unlockedPlayer", sender.getName(), player.getName()));
return null;
}

@Override
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException
{
return args.length == 1 && checkTab(sender, Rank.ADMIN, "plex.mute") ? PlexUtils.getPlayerNameList() : ImmutableList.of();
return args.length == 1 && checkTab(sender, Rank.ADMIN, "plex.lockup") ? PlexUtils.getPlayerNameList() : ImmutableList.of();
}
}
File renamed without changes.
File renamed without changes.
Expand Up @@ -9,12 +9,10 @@
import dev.plex.punishment.PunishmentType;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.time.LocalDateTime;
import dev.plex.util.TimeUtils;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;

import dev.plex.util.TimeUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand All @@ -40,17 +38,10 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
return messageComponent("playerMuted");
}

if (isAdmin(getPlexPlayer(player)))
if (silentCheckRank(player, Rank.ADMIN, "plex.mute"))
{
if (!isConsole(sender))
{
assert playerSender != null;
PlexPlayer plexPlayer1 = getPlexPlayer(playerSender);
if (!plexPlayer1.getRankFromString().isAtLeast(getPlexPlayer(player).getRankFromString()))
{
return messageComponent("higherRankThanYou");
}
}
send(sender, messageComponent("higherRankThanYou"));
return null;
}

Punishment punishment = new Punishment(punishedPlayer.getUuid(), getUUID(sender));
Expand Down
File renamed without changes.
Expand Up @@ -10,7 +10,6 @@
import dev.plex.storage.StorageType;
import dev.plex.util.PlexUtils;
import dev.plex.util.TimeUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
Expand Down Expand Up @@ -38,8 +37,12 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
return usage();
}

Player player = getNonNullPlayer(args[0]);
PlexPlayer plexPlayer = getPlexPlayer(player);
PlexPlayer plexPlayer = DataUtils.getPlayer(args[0]);

if (plexPlayer == null)
{
return messageComponent("playerNotFound");
}

switch (args[1].toLowerCase())
{
Expand All @@ -51,7 +54,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
{
if (notes.size() == 0)
{
send(sender, mmString("<red>This player has no notes!"));
send(sender, messageComponent("noNotes"));
return;
}
readNotes(sender, plexPlayer, notes);
Expand All @@ -62,7 +65,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
List<Note> notes = plexPlayer.getNotes();
if (notes.size() == 0)
{
return mmString("<red>This player has no notes!");
return messageComponent("noNotes");
}
readNotes(sender, plexPlayer, notes);
}
Expand All @@ -87,7 +90,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
{
DataUtils.update(plexPlayer);
}
return Component.text("Note added.").color(NamedTextColor.GREEN);
return messageComponent("noteAdded");
}
}
case "remove":
Expand All @@ -103,7 +106,7 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
}
catch (NumberFormatException ignored)
{
return Component.text("Invalid number: " + args[2]).color(NamedTextColor.RED);
return messageComponent("unableToParseNumber", args[2]);
}
if (plugin.getStorageType() != StorageType.MONGODB)
{
Expand All @@ -115,13 +118,13 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
if (note.getId() == id)
{
plugin.getSqlNotes().deleteNote(id, plexPlayer.getUuid()).whenComplete((notes1, ex1) ->
send(sender, Component.text("Removed note with ID: " + id).color(NamedTextColor.GREEN)));
send(sender, messageComponent("removedNote", id)));
deleted = true;
}
}
if (!deleted)
{
send(sender, mmString("<red>A note with this ID could not be found"));
send(sender, messageComponent("noteNotFound"));
}
plexPlayer.getNotes().removeIf(note -> note.getId() == id);
});
Expand All @@ -130,9 +133,9 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
{
if (plexPlayer.getNotes().removeIf(note -> note.getId() == id))
{
return Component.text("Removed note with ID: " + id).color(NamedTextColor.GREEN);
return messageComponent("removedNote", id);
}
return mmString("<red>A note with this ID could not be found");
return messageComponent("noteNotFound");
}
return null;
}
Expand All @@ -147,15 +150,15 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
plugin.getSqlNotes().deleteNote(note.getId(), plexPlayer.getUuid());
}
plexPlayer.getNotes().clear();
send(sender, Component.text("Cleared " + notes.size() + " note(s).").color(NamedTextColor.GREEN));
send(sender, messageComponent("clearedNotes", notes.size()));
});
}
else
{
int count = plexPlayer.getNotes().size();
plexPlayer.getNotes().clear();
DataUtils.update(plexPlayer);
return Component.text("Cleared " + count + " note(s).").color(NamedTextColor.GREEN);
return messageComponent("clearedNotes", count);
}
return null;
}
Expand Down
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,5 @@
package dev.plex.command.impl;

import dev.plex.Plex;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
Expand All @@ -10,23 +9,22 @@
import dev.plex.module.PlexModuleFile;
import dev.plex.rank.enums.Rank;
import dev.plex.util.BuildInfo;
import dev.plex.util.PlexLog;
import dev.plex.util.PlexUtils;
import dev.plex.util.TimeUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import dev.plex.util.TimeUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@CommandPermissions(level = Rank.OP, permission = "plex.plex", source = RequiredCommandSource.ANY)
@CommandPermissions(level = Rank.IMPOSTOR, source = RequiredCommandSource.ANY)
@CommandParameters(name = "plex", usage = "/<command> [reload | redis | modules [reload]]", description = "Show information about Plex or reload it")
public class PlexCMD extends PlexCommand
{
Expand Down Expand Up @@ -59,13 +57,13 @@ protected Component execute(@NotNull CommandSender sender, @Nullable Player play
plugin.getRankManager().importDefaultRanks();
send(sender, "Imported ranks");
plugin.setSystem(plugin.config.getString("system"));
if (!plugin.setupPermissions() && plugin.getSystem().equalsIgnoreCase("permissions") && !plugin.getServer().getPluginManager().isPluginEnabled("Vault"))
if (plugin.getSystem().equalsIgnoreCase("permissions") && !plugin.getServer().getPluginManager().isPluginEnabled("Vault"))
{
throw new RuntimeException("Vault is required to run on the server if you use permissions!");
}
plugin.getServiceManager().endServices();
plugin.getServiceManager().startServices();
PlexLog.debug("Restarted services");
send(sender, "Restarted services.");
TimeUtils.TIMEZONE = plugin.config.getString("server.timezone");
send(sender, "Set timezone to: " + TimeUtils.TIMEZONE);
send(sender, "Plex successfully reloaded.");
Expand All @@ -92,26 +90,36 @@ else if (args[0].equalsIgnoreCase("modules"))
}
if (args[1].equalsIgnoreCase("reload"))
{
checkRank(sender, Rank.SENIOR_ADMIN, "plex.modules.reload");
plugin.getModuleManager().unloadModules();
plugin.getModuleManager().loadAllModules();
plugin.getModuleManager().loadModules();
plugin.getModuleManager().enableModules();
checkRank(sender, Rank.EXECUTIVE, "plex.modules.reload");
plugin.getModuleManager().reloadModules();
return mmString("<green>All modules reloaded!");
}
else if (args[1].equalsIgnoreCase("update"))
{
if (!hasUpdateAccess(playerSender, sender))
{
return messageComponent("noPermissionRank", "an Owner or Developer");
}
for (PlexModule module : plugin.getModuleManager().getModules())
{
plugin.getUpdateChecker().updateJar(sender, module.getPlexModuleFile().getName(), true);
}
plugin.getModuleManager().reloadModules();
return mmString("<green>All modules updated and reloaded!");
}
}
else if (args[0].equalsIgnoreCase("update"))
{
if (sender instanceof Player player && !PlexUtils.DEVELOPERS.contains(player.getUniqueId().toString()))
if (!hasUpdateAccess(playerSender, sender))
{
return messageComponent("noPermissionRank", "a developer");
return messageComponent("noPermissionRank", "an Owner or Developer");
}
if (!plugin.getUpdateChecker().getUpdateStatusMessage(sender, false, 0))
{
return mmString("<red>Plex is already up to date!");
}
plugin.getUpdateChecker().updateJar(sender);
return null;
plugin.getUpdateChecker().updateJar(sender, "Plex", false);
return mmString("<red>Alert: Restart the server for the new JAR file to be applied.");
}
else
{
Expand All @@ -133,4 +141,27 @@ else if (args[0].equalsIgnoreCase("modules"))
}
return Collections.emptyList();
}

// Owners and developers only have access
private boolean hasUpdateAccess(Player player, CommandSender sender)
{
// Allow CONSOLE, get OfflinePlayer for Telnet
if (isConsole(sender))
{
if (sender.getName().equalsIgnoreCase("CONSOLE"))
{
return true;
}
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(sender.getName());
if (offlinePlayer.hasPlayedBefore())
{
return PlexUtils.DEVELOPERS.contains(offlinePlayer.getUniqueId().toString())
|| plugin.config.getStringList("titles.owners").contains(sender.getName());
}
return false;
}
assert player != null;
return PlexUtils.DEVELOPERS.contains(player.getUniqueId().toString())
|| plugin.config.getStringList("titles.owners").contains(player.getName());
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Expand Up @@ -9,13 +9,11 @@
import dev.plex.punishment.PunishmentType;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.time.LocalDateTime;
import dev.plex.util.TimeUtils;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;

import dev.plex.util.TimeUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.Title;
Expand Down
@@ -1,12 +1,12 @@
package dev.plex.command.impl;

import com.google.common.collect.ImmutableList;
import dev.plex.api.event.GameModeUpdateEvent;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.event.GameModeUpdateEvent;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.util.List;
Expand All @@ -19,7 +19,7 @@
import org.jetbrains.annotations.Nullable;

@CommandPermissions(level = Rank.ADMIN, permission = "plex.gamemode.spectator", source = RequiredCommandSource.ANY)
@CommandParameters(name = "spectator", aliases = "gmsp", description = "Set your own or another player's gamemode to spectator mode")
@CommandParameters(name = "spectator", aliases = "gmsp,egmsp,spec", description = "Set your own or another player's gamemode to spectator mode")
public class SpectatorCMD extends PlexCommand
{
@Override
Expand Down
@@ -1,12 +1,12 @@
package dev.plex.command.impl;

import com.google.common.collect.ImmutableList;
import dev.plex.api.event.GameModeUpdateEvent;
import dev.plex.command.PlexCommand;
import dev.plex.command.annotation.CommandParameters;
import dev.plex.command.annotation.CommandPermissions;
import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.event.GameModeUpdateEvent;
import dev.plex.rank.enums.Rank;
import dev.plex.util.PlexUtils;
import java.util.List;
Expand All @@ -19,7 +19,7 @@
import org.jetbrains.annotations.Nullable;

@CommandPermissions(level = Rank.OP, permission = "plex.gamemode.survival", source = RequiredCommandSource.ANY)
@CommandParameters(name = "survival", aliases = "gms", description = "Set your own or another player's gamemode to survival mode")
@CommandParameters(name = "survival", aliases = "gms,egms,esurvival,survivalmode,esurvivalmode", description = "Set your own or another player's gamemode to survival mode")
public class SurvivalCMD extends PlexCommand
{
@Override
Expand Down