Initial commit
This commit is contained in:
commit
c10b295171
136
.gitignore
vendored
Normal file
136
.gitignore
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Gradle template
|
||||
.gradle
|
||||
/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
# InsaneCore related
|
||||
/output/
|
||||
/**/build/
|
||||
/.idea/
|
||||
.idea/codeStyles/
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/misc.xml
|
||||
.idea/modules/
|
||||
.idea/vcs.xml
|
||||
gradle/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
!/gradle.properties
|
||||
gradle.properties
|
||||
### Gradle template
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.log.lck
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
19
.gitlab-ci.yml
Normal file
19
.gitlab-ci.yml
Normal file
@ -0,0 +1,19 @@
|
||||
image: gradle:jdk11
|
||||
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
build:prod:
|
||||
stage: build
|
||||
only:
|
||||
- master
|
||||
before_script:
|
||||
- gradle wrapper
|
||||
script:
|
||||
- ./gradlew shadowJar
|
||||
- ./gradlew publishMavenPublicationToApiRepository
|
||||
artifacts:
|
||||
paths:
|
||||
- output
|
||||
expire_in: 1 week
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Project Structure
|
||||
- **Commons**: Commons is common libraries and/or utilities that are shared across the network.
|
||||
- **ServerData**: This branch of the project controls the database backend for both Redis and MySQL. All modules that use Redis or MySQL must have this as a dependency.
|
||||
- **ServerController**: This will dynamically start and stop servers on demand.
|
||||
- **Proxy**: The proxy will handle server balancing and player caching.
|
||||
- **API**: This is the frontend of the project if you will. All developers will be given access to this branch of the project where they can access multiple parts of the server.
|
||||
- **Core**: The core is a shared module between all Spigot plugins. Everything used between multiple Spigot servers will be created here.
|
||||
- **Hub**: This is pretty self-explanatory. Any Hub related things will go here.
|
||||
- **Testing**: This part of the project is strictly for testing purposes .
|
||||
|
||||
# Redis
|
||||
Redis is used for a variety of different things. Mainly, Redis is used for the server backend and as a global cache.
|
||||
|
||||
# MySQL
|
||||
MySQL is used for the main account backend. Everything related to accounts will be stored here.
|
||||
|
||||
# Production
|
||||
When something is being released for production, you must set the production parameter in the **MySQLController** class and make sure you are on the **master** branch!
|
||||
|
||||
# Before Starting
|
||||
- Create a **gradle.properties** file which will contain your
|
||||
Nexus username and password.
|
||||
- Stick to Java naming conventions, which can be found [here](https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html)
|
||||
|
||||
# Building From Source
|
||||
- Clone the project
|
||||
- Open the project in your IDE of choice (Intellij is highly recommended)
|
||||
- Run the task **shadowJar** which can be found under **McGamerCore** » **Tasks** » **shadow**
|
||||
|
||||
**If you are confused on how to do things, or you're unsure on where to put something, please contact Braydon on Discord**
|
27
api/build.gradle.kts
Normal file
27
api/build.gradle.kts
Normal file
@ -0,0 +1,27 @@
|
||||
dependencies {
|
||||
api(project(":serverdata"))
|
||||
implementation("com.zaxxer:HikariCP:3.4.5")
|
||||
implementation("mysql:mysql-connector-java:8.0.23")
|
||||
compile("com.sparkjava:spark-core:2.9.3")
|
||||
compile("com.google.guava:guava:30.1-jre")
|
||||
}
|
||||
|
||||
val jar by tasks.getting(Jar::class) {
|
||||
manifest {
|
||||
attributes["Main-Class"] = "zone.themcgamer.api.API"
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val tokens = mapOf("version" to project.version)
|
||||
from(sourceSets["main"].resources.srcDirs) {
|
||||
filter<org.apache.tools.ant.filters.ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName.set("${project.rootProject.name}-${project.name}-v${project.version}.jar")
|
||||
destinationDir = file("$rootDir/output")
|
||||
}
|
||||
}
|
178
api/src/main/java/zone/themcgamer/api/API.java
Normal file
178
api/src/main/java/zone/themcgamer/api/API.java
Normal file
@ -0,0 +1,178 @@
|
||||
package zone.themcgamer.api;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import spark.Spark;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
import zone.themcgamer.api.model.ModelSerializer;
|
||||
import zone.themcgamer.api.model.impl.*;
|
||||
import zone.themcgamer.api.repository.AccountRepository;
|
||||
import zone.themcgamer.api.route.AccountRoute;
|
||||
import zone.themcgamer.api.route.ServersRoute;
|
||||
import zone.themcgamer.api.route.StatusRoute;
|
||||
import zone.themcgamer.data.APIAccessLevel;
|
||||
import zone.themcgamer.data.jedis.JedisController;
|
||||
import zone.themcgamer.data.jedis.data.APIKey;
|
||||
import zone.themcgamer.data.jedis.repository.RedisRepository;
|
||||
import zone.themcgamer.data.jedis.repository.impl.APIKeyRepository;
|
||||
import zone.themcgamer.data.mysql.MySQLController;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class API {
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(IModel.class, new ModelSerializer())
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
private static final List<Class<? extends IModel>> models = new ArrayList<>();
|
||||
private static final Map<Object, List<Method>> routes = new HashMap<>();
|
||||
private static final boolean requiresAuthentication = false;
|
||||
|
||||
// Rate Limiting
|
||||
private static final int rateLimit = 120; // The amount of requests a minute
|
||||
private static final Cache<String, Integer> requests = CacheBuilder.newBuilder() // The logged requests (ip, count)
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Initializing Redis
|
||||
new JedisController().start();
|
||||
|
||||
// Initializing MySQL and Repositories
|
||||
MySQLController mySQLController = new MySQLController(false);
|
||||
AccountRepository accountRepository = new AccountRepository(mySQLController.getDataSource());
|
||||
|
||||
// Adding models
|
||||
addModel(MinecraftServerModel.class);
|
||||
addModel(NodeModel.class);
|
||||
addModel(ServerGroupModel.class);
|
||||
addModel(AccountModel.class);
|
||||
addModel(StatusModel.class);
|
||||
|
||||
// Adding the routes
|
||||
addRoute(new ServersRoute());
|
||||
addRoute(new AccountRoute(accountRepository));
|
||||
addRoute(new StatusRoute());
|
||||
|
||||
// 404 Handling
|
||||
Spark.notFound((request, response) -> {
|
||||
response.type("application/json");
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("success", "false");
|
||||
map.put("error", "Not found");
|
||||
|
||||
System.err.println("Requested url not found: " + request.pathInfo());
|
||||
|
||||
return gson.toJson(map);
|
||||
});
|
||||
|
||||
// Handling the routes
|
||||
APIKeyRepository apiKeyRepository = RedisRepository.getRepository(APIKeyRepository.class).orElse(null);
|
||||
if (apiKeyRepository == null)
|
||||
throw new NullPointerException();
|
||||
for (Map.Entry<Object, List<Method>> entry : routes.entrySet()) {
|
||||
for (Method method : entry.getValue()) {
|
||||
RestPath restPath = method.getAnnotation(RestPath.class);
|
||||
String path = "/" + restPath.version().getName() + restPath.path();
|
||||
System.out.println("Registered Path: " + path + " (accessLevel = " + restPath.accessLevel().name() + ")");
|
||||
Spark.get(path, (request, response) -> {
|
||||
response.type("application/json");
|
||||
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
try {
|
||||
Integer requestCount = requests.getIfPresent(request.ip());
|
||||
if (requestCount == null)
|
||||
requestCount = 0;
|
||||
int remaining = Math.max(rateLimit - requestCount, 0);
|
||||
|
||||
// Display the rate limit and the remaining request count using headers
|
||||
response.header("x-ratelimit-limit", "" + rateLimit);
|
||||
response.header("x-ratelimit-remaining", "" + remaining);
|
||||
|
||||
// If the rate limit has been exceeded, set the status code to 429 (Too Many Requests) and display an error
|
||||
if (++requestCount > rateLimit) {
|
||||
System.out.println("Incoming request from \"" + request.ip() + "\" using path \"" + request.pathInfo() + "\" was rate limited");
|
||||
response.status(429);
|
||||
throw new APIException("Rate limit exceeded");
|
||||
}
|
||||
requests.put(request.ip(), requestCount);
|
||||
|
||||
System.out.println("Handling incoming request from \"" + request.ip() + "\" using path \"" + request.pathInfo() + "\"");
|
||||
|
||||
APIKey key = new APIKey(UUID.randomUUID().toString(), APIAccessLevel.STANDARD);
|
||||
if (requiresAuthentication) {
|
||||
String apiKey = request.headers("key");
|
||||
if (apiKey == null)
|
||||
throw new APIException("Unauthorized");
|
||||
// Check if the provided API key is valid
|
||||
Optional<List<APIKey>> keys = apiKeyRepository.lookup(apiKey);
|
||||
if (keys.isEmpty() || (keys.get().isEmpty()))
|
||||
throw new APIException("Unauthorized");
|
||||
key = keys.get().get(0);
|
||||
if (restPath.accessLevel() == APIAccessLevel.DEV && (restPath.accessLevel() != key.getAccessLevel()))
|
||||
throw new APIException("Unauthorized");
|
||||
}
|
||||
// Checking if the request has the appropriate headers for the get route
|
||||
if (restPath.headers().length > 0) {
|
||||
for (String header : restPath.headers()) {
|
||||
if (!request.headers().contains(header)) {
|
||||
throw new APIException("Inappropriate headers");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (method.getParameterTypes().length != 3)
|
||||
throw new APIException("Invalid route defined");
|
||||
Object object = method.invoke(entry.getKey(), request, response, key);
|
||||
jsonObject.addProperty("success", "true");
|
||||
if (object instanceof IModel) {
|
||||
if (models.contains(object.getClass()))
|
||||
jsonObject.add("value", gson.toJsonTree(((IModel) object).toMap(), HashMap.class));
|
||||
else throw new APIException("Undefined model: " + object.toString());
|
||||
} else if (object instanceof ArrayList)
|
||||
jsonObject.add("value", gson.toJsonTree(object, ArrayList.class));
|
||||
else if (object instanceof HashMap)
|
||||
jsonObject.add("value", gson.toJsonTree(object, HashMap.class));
|
||||
else jsonObject.addProperty("value", object.toString());
|
||||
} catch (Throwable ex) {
|
||||
if (ex instanceof InvocationTargetException)
|
||||
ex = ex.getCause();
|
||||
String message = ex.getLocalizedMessage();
|
||||
if (message == null || (message.trim().isEmpty()))
|
||||
message = ex.getClass().getSimpleName();
|
||||
jsonObject.addProperty("success", "false");
|
||||
jsonObject.addProperty("error", message);
|
||||
if (!(ex instanceof APIException)) {
|
||||
System.err.println("The route \"" + entry.getKey().getClass().getSimpleName() + "\" raised an exception:");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return gson.toJson(jsonObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addModel(Class<? extends IModel> modelClass) {
|
||||
models.add(modelClass);
|
||||
}
|
||||
|
||||
private static void addRoute(Object object) {
|
||||
List<Method> methods = routes.getOrDefault(object, new ArrayList<>());
|
||||
for (Method method : object.getClass().getMethods()) {
|
||||
if (method.isAnnotationPresent(RestPath.class)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
routes.put(object, methods);
|
||||
}
|
||||
}
|
25
api/src/main/java/zone/themcgamer/api/APIException.java
Normal file
25
api/src/main/java/zone/themcgamer/api/APIException.java
Normal file
@ -0,0 +1,25 @@
|
||||
package zone.themcgamer.api;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class APIException extends RuntimeException {
|
||||
/**
|
||||
* Constructs a new runtime exception with {@code null} as its
|
||||
* detail message. The cause is not initialized, and may subsequently be
|
||||
* initialized by a call to {@link #initCause}.
|
||||
*/
|
||||
public APIException() {}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message.
|
||||
* The cause is not initialized, and may subsequently be initialized by a
|
||||
* call to {@link #initCause}.
|
||||
*
|
||||
* @param message the detail message. The detail message is saved for
|
||||
* later retrieval by the {@link #getMessage()} method.
|
||||
*/
|
||||
public APIException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
14
api/src/main/java/zone/themcgamer/api/APIVersion.java
Normal file
14
api/src/main/java/zone/themcgamer/api/APIVersion.java
Normal file
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public enum APIVersion {
|
||||
V1("v1");
|
||||
|
||||
private final String name;
|
||||
}
|
23
api/src/main/java/zone/themcgamer/api/RestPath.java
Normal file
23
api/src/main/java/zone/themcgamer/api/RestPath.java
Normal file
@ -0,0 +1,23 @@
|
||||
package zone.themcgamer.api;
|
||||
|
||||
import zone.themcgamer.data.APIAccessLevel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface RestPath {
|
||||
String path();
|
||||
|
||||
APIVersion version();
|
||||
|
||||
APIAccessLevel accessLevel() default APIAccessLevel.STANDARD;
|
||||
|
||||
String[] headers() default {};
|
||||
}
|
10
api/src/main/java/zone/themcgamer/api/model/IModel.java
Normal file
10
api/src/main/java/zone/themcgamer/api/model/IModel.java
Normal file
@ -0,0 +1,10 @@
|
||||
package zone.themcgamer.api.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public interface IModel {
|
||||
HashMap<String, Object> toMap();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package zone.themcgamer.api.model;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ModelSerializer implements JsonSerializer<IModel> {
|
||||
/**
|
||||
* Gson invokes this call-back method during serialization when it encounters a field of the
|
||||
* specified type.
|
||||
*
|
||||
* <p>In the implementation of this call-back method, you should consider invoking
|
||||
* {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
|
||||
* non-trivial field of the {@code iModel} object. However, you should never invoke it on the
|
||||
* {@code iModel} object itself since that will cause an infinite loop (Gson will call your
|
||||
* call-back method again).</p>
|
||||
*
|
||||
* @param iModel the object that needs to be converted to Json.
|
||||
* @param typeOfSrc the actual type (fully genericized version) of the source object.
|
||||
* @param context
|
||||
* @return a JsonElement corresponding to the specified object.
|
||||
*/
|
||||
@Override
|
||||
public JsonElement serialize(IModel iModel, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject object = new JsonObject();
|
||||
for (Map.Entry<String, Object> entry : iModel.toMap().entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Enum<?>)
|
||||
value = ((Enum<?>) value).name();
|
||||
object.addProperty(entry.getKey(), value.toString());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package zone.themcgamer.api.model.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter @ToString
|
||||
public class AccountModel implements IModel {
|
||||
private final int id;
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final Rank primaryRank;
|
||||
private final Rank[] secondaryRanks;
|
||||
private final double gold, gems;
|
||||
private String encryptedIpAddress;
|
||||
private final long firstLogin, lastLogin;
|
||||
private long timeCached;
|
||||
|
||||
@Override
|
||||
public HashMap<String, Object> toMap() {
|
||||
return new HashMap<>() {{
|
||||
put("id", id);
|
||||
put("uuid", uuid);
|
||||
put("name", name);
|
||||
put("primaryRank", primaryRank.name());
|
||||
put("secondaryRanks", Arrays.stream(secondaryRanks).map(Rank::name).collect(Collectors.joining(", ")));
|
||||
put("gold", gold);
|
||||
put("gems", gems);
|
||||
put("encryptedIpAddress", encryptedIpAddress);
|
||||
put("firstLogin", firstLogin);
|
||||
put("lastLogin", lastLogin);
|
||||
put("timeCached", timeCached);
|
||||
}};
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package zone.themcgamer.api.model.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
import zone.themcgamer.data.jedis.data.server.MinecraftServer;
|
||||
import zone.themcgamer.data.jedis.data.server.ServerState;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter @ToString
|
||||
public class MinecraftServerModel implements IModel {
|
||||
private final String id;
|
||||
private final int numericId;
|
||||
private final String name;
|
||||
private final NodeModel node;
|
||||
private final ServerGroupModel group;
|
||||
private final String address;
|
||||
private final long port;
|
||||
|
||||
private final int usedRam, maxRam;
|
||||
private final ServerState state;
|
||||
private final long lastStateChange;
|
||||
|
||||
private final int online, maxPlayers;
|
||||
private final double tps;
|
||||
private final UUID host;
|
||||
private final String game;
|
||||
|
||||
private final String metaData;
|
||||
private final long created, lastHeartbeat;
|
||||
|
||||
@Override
|
||||
public HashMap<String, Object> toMap() {
|
||||
return new HashMap<>() {{
|
||||
put("id", id);
|
||||
put("numericId", numericId);
|
||||
put("name", name);
|
||||
put("node", node == null ? null : node.toMap());
|
||||
put("group", group);
|
||||
put("address", address);
|
||||
put("port", port);
|
||||
put("usedRam", usedRam);
|
||||
put("maxRam", maxRam);
|
||||
put("state", state.name());
|
||||
put("lastStateChange", lastStateChange);
|
||||
put("online", online);
|
||||
put("maxPlayers", maxPlayers);
|
||||
put("tps", tps);
|
||||
put("host", host);
|
||||
put("game", game);
|
||||
put("metaData", metaData);
|
||||
put("lastHeartbeat", lastHeartbeat);
|
||||
}};
|
||||
}
|
||||
|
||||
public static MinecraftServerModel fromMinecraftServer(MinecraftServer minecraftServer) {
|
||||
if (minecraftServer == null)
|
||||
return null;
|
||||
return new MinecraftServerModel(
|
||||
minecraftServer.getId(),
|
||||
minecraftServer.getNumericId(),
|
||||
minecraftServer.getName(),
|
||||
NodeModel.fromNode(minecraftServer.getNode()),
|
||||
ServerGroupModel.fromServerGroup(minecraftServer.getGroup()),
|
||||
minecraftServer.getAddress(),
|
||||
minecraftServer.getPort(),
|
||||
|
||||
minecraftServer.getUsedRam(),
|
||||
minecraftServer.getMaxRam(),
|
||||
minecraftServer.getState(),
|
||||
minecraftServer.getLastStateChange(),
|
||||
|
||||
minecraftServer.getOnline(),
|
||||
minecraftServer.getMaxPlayers(),
|
||||
minecraftServer.getTps(),
|
||||
minecraftServer.getHost(),
|
||||
minecraftServer.getGame(),
|
||||
minecraftServer.getMetaData(),
|
||||
minecraftServer.getCreated(),
|
||||
minecraftServer.getLastHeartbeat()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package zone.themcgamer.api.model.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
import zone.themcgamer.data.jedis.data.Node;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public class NodeModel implements IModel {
|
||||
private final String name, address, portRange;
|
||||
|
||||
@Override
|
||||
public HashMap<String, Object> toMap() {
|
||||
return new HashMap<>() {{
|
||||
put("name", name);
|
||||
put("address", address);
|
||||
put("portRange", portRange);
|
||||
}};
|
||||
}
|
||||
|
||||
public static NodeModel fromNode(Node node) {
|
||||
if (node == null)
|
||||
return null;
|
||||
return new NodeModel(
|
||||
node.getName(),
|
||||
node.getAddress(),
|
||||
node.getPortRange()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package zone.themcgamer.api.model.impl;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
import zone.themcgamer.data.jedis.data.ServerGroup;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@RequiredArgsConstructor @Setter @Getter @ToString
|
||||
public class ServerGroupModel implements IModel {
|
||||
private final String name;
|
||||
private final long memoryPerServer;
|
||||
private final String serverJar, templatePath, pluginJarName, worldPath, startupScript, privateAddress;
|
||||
private final UUID host;
|
||||
private final String game;
|
||||
private final int minPlayers, maxPlayers, minServers, maxServers;
|
||||
private final boolean kingdom, staticGroup;
|
||||
|
||||
@Override
|
||||
public HashMap<String, Object> toMap() {
|
||||
return new HashMap<>() {{
|
||||
put("name", name);
|
||||
put("memoryPerServer", memoryPerServer);
|
||||
put("serverJar", serverJar);
|
||||
put("templatePath", templatePath);
|
||||
put("pluginJarName", pluginJarName);
|
||||
put("worldPath", worldPath);
|
||||
put("startupScript", startupScript);
|
||||
put("privateAddress", privateAddress);
|
||||
put("host", host);
|
||||
put("game", game);
|
||||
put("minPlayers", minPlayers);
|
||||
put("maxPlayers", maxPlayers);
|
||||
put("minServers", minServers);
|
||||
put("maxServers", maxServers);
|
||||
put("kingdom", kingdom);
|
||||
put("staticGroup", staticGroup);
|
||||
}};
|
||||
}
|
||||
|
||||
public static ServerGroupModel fromServerGroup(ServerGroup serverGroup) {
|
||||
if (serverGroup == null)
|
||||
return null;
|
||||
return new ServerGroupModel(
|
||||
serverGroup.getName(),
|
||||
serverGroup.getMemoryPerServer(),
|
||||
serverGroup.getServerJar(),
|
||||
serverGroup.getTemplatePath(),
|
||||
serverGroup.getPluginJarName(),
|
||||
serverGroup.getWorldPath(),
|
||||
serverGroup.getStartupScript(),
|
||||
serverGroup.getPrivateAddress(),
|
||||
serverGroup.getHost(),
|
||||
serverGroup.getGame(),
|
||||
serverGroup.getMinPlayers(),
|
||||
serverGroup.getMaxPlayers(),
|
||||
serverGroup.getMinServers(),
|
||||
serverGroup.getMaxServers(),
|
||||
serverGroup.isKingdom(),
|
||||
serverGroup.isStaticGroup()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package zone.themcgamer.api.model.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.api.model.IModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter @ToString
|
||||
public class StatusModel implements IModel {
|
||||
private final UUID uuid;
|
||||
private final String playerName;
|
||||
private String server;
|
||||
private final long timeJoined;
|
||||
|
||||
@Override
|
||||
public HashMap<String, Object> toMap() {
|
||||
return new HashMap<>() {{
|
||||
put("uuid", uuid);
|
||||
put("name", playerName);
|
||||
put("server", server);
|
||||
put("timeJoined", timeJoined);
|
||||
}};
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package zone.themcgamer.api.repository;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import zone.themcgamer.api.model.impl.AccountModel;
|
||||
import zone.themcgamer.data.Rank;
|
||||
import zone.themcgamer.data.mysql.data.column.Column;
|
||||
import zone.themcgamer.data.mysql.data.column.impl.StringColumn;
|
||||
import zone.themcgamer.data.mysql.repository.MySQLRepository;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class AccountRepository extends MySQLRepository {
|
||||
private static final String SELECT_ACCOUNT = "SELECT * FROM `accounts` WHERE `uuid` = ? LIMIT 1";
|
||||
|
||||
public AccountRepository(HikariDataSource dataSource) {
|
||||
super(dataSource);
|
||||
}
|
||||
|
||||
public AccountModel getAccount(UUID uuid) {
|
||||
AccountModel[] model = new AccountModel[] { null };
|
||||
executeQuery(SELECT_ACCOUNT, new Column[] {
|
||||
new StringColumn("uuid", uuid.toString())
|
||||
}, resultSet -> {
|
||||
try {
|
||||
if (resultSet.next())
|
||||
model[0] = constructAccount(uuid, resultSet);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
});
|
||||
return model[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link AccountModel} from the given parameters
|
||||
* @param accountId the account id
|
||||
* @param uuid the uuid
|
||||
* @param name the name
|
||||
* @param resultSet the result set
|
||||
* @param ipAddress the ip address
|
||||
* @param encryptedIpAddress the encrypted ip address
|
||||
* @param lastLogin the last login
|
||||
* @return the account
|
||||
*/
|
||||
private AccountModel constructAccount(UUID uuid, ResultSet resultSet) {
|
||||
try {
|
||||
Rank[] secondaryRanks = Arrays.stream(resultSet.getString("secondaryRanks")
|
||||
.split(",")).map(rankName -> Rank.lookup(rankName).orElse(null))
|
||||
.filter(Objects::nonNull).toArray(Rank[]::new);
|
||||
return new AccountModel(
|
||||
resultSet.getInt("id"),
|
||||
uuid,
|
||||
resultSet.getString("name"),
|
||||
Rank.lookup(resultSet.getString("primaryRank")).orElse(Rank.DEFAULT),
|
||||
secondaryRanks,
|
||||
resultSet.getInt("gold"),
|
||||
resultSet.getInt("gems"),
|
||||
resultSet.getString("ipAddress"),
|
||||
resultSet.getLong("firstLogin"),
|
||||
resultSet.getLong("lastLogin"),
|
||||
-1L
|
||||
);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package zone.themcgamer.api.route;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import zone.themcgamer.api.APIException;
|
||||
import zone.themcgamer.api.APIVersion;
|
||||
import zone.themcgamer.api.RestPath;
|
||||
import zone.themcgamer.api.model.impl.AccountModel;
|
||||
import zone.themcgamer.api.repository.AccountRepository;
|
||||
import zone.themcgamer.common.MiscUtils;
|
||||
import zone.themcgamer.data.APIAccessLevel;
|
||||
import zone.themcgamer.data.jedis.data.APIKey;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AccountRoute {
|
||||
// Account model cache for players that were looked up via the account route
|
||||
public static final Cache<UUID, AccountModel> CACHE = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private final AccountRepository accountRepository;
|
||||
|
||||
@RestPath(path = "/account/:uuid", version = APIVersion.V1)
|
||||
public AccountModel get(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
UUID uuid = MiscUtils.getUuid(request.params(":uuid"));
|
||||
if (uuid == null)
|
||||
throw new APIException("Invalid UUID");
|
||||
AccountModel account = CACHE.getIfPresent(uuid);
|
||||
if (account == null) {
|
||||
account = accountRepository.getAccount(uuid);
|
||||
account.setTimeCached(System.currentTimeMillis());
|
||||
CACHE.put(uuid, account);
|
||||
}
|
||||
if (apiKey.getAccessLevel() == APIAccessLevel.STANDARD)
|
||||
account.setEncryptedIpAddress("Unauthorized");
|
||||
return account;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package zone.themcgamer.api.route;
|
||||
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import zone.themcgamer.api.APIException;
|
||||
import zone.themcgamer.api.APIVersion;
|
||||
import zone.themcgamer.api.RestPath;
|
||||
import zone.themcgamer.api.model.impl.MinecraftServerModel;
|
||||
import zone.themcgamer.api.model.impl.ServerGroupModel;
|
||||
import zone.themcgamer.data.APIAccessLevel;
|
||||
import zone.themcgamer.data.jedis.data.APIKey;
|
||||
import zone.themcgamer.data.jedis.data.ServerGroup;
|
||||
import zone.themcgamer.data.jedis.data.server.MinecraftServer;
|
||||
import zone.themcgamer.data.jedis.repository.RedisRepository;
|
||||
import zone.themcgamer.data.jedis.repository.impl.MinecraftServerRepository;
|
||||
import zone.themcgamer.data.jedis.repository.impl.ServerGroupRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ServersRoute {
|
||||
private final ServerGroupRepository serverGroupRepository;
|
||||
private final MinecraftServerRepository minecraftServerRepository;
|
||||
|
||||
public ServersRoute() {
|
||||
serverGroupRepository = RedisRepository.getRepository(ServerGroupRepository.class).orElse(null);
|
||||
minecraftServerRepository = RedisRepository.getRepository(MinecraftServerRepository.class).orElse(null);
|
||||
}
|
||||
|
||||
/*
|
||||
Server Groups
|
||||
*/
|
||||
|
||||
@RestPath(path = "/serverGroups", version = APIVersion.V1, accessLevel = APIAccessLevel.DEV)
|
||||
public List<ServerGroupModel> getGroups(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
List<ServerGroupModel> models = new ArrayList<>();
|
||||
for (ServerGroup serverGroup : serverGroupRepository.getCached())
|
||||
models.add(ServerGroupModel.fromServerGroup(serverGroup));
|
||||
return models;
|
||||
}
|
||||
|
||||
@RestPath(path = "/serverGroup/:name", version = APIVersion.V1, accessLevel = APIAccessLevel.DEV)
|
||||
public ServerGroupModel getServerGroup(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
String name = request.params(":name");
|
||||
if (name == null || (name.trim().isEmpty()))
|
||||
throw new APIException("Invalid Server Group");
|
||||
Optional<ServerGroup> optionalServerGroup = serverGroupRepository.lookup(name);
|
||||
if (optionalServerGroup.isEmpty())
|
||||
throw new APIException("Server group not found");
|
||||
return ServerGroupModel.fromServerGroup(optionalServerGroup.get());
|
||||
}
|
||||
|
||||
/*
|
||||
Minecraft Servers
|
||||
*/
|
||||
|
||||
@RestPath(path = "/minecraftServers", version = APIVersion.V1, accessLevel = APIAccessLevel.DEV)
|
||||
public List<MinecraftServerModel> getMinecraftServers(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
List<MinecraftServerModel> models = new ArrayList<>();
|
||||
for (MinecraftServer minecraftServer : minecraftServerRepository.getCached())
|
||||
models.add(MinecraftServerModel.fromMinecraftServer(minecraftServer));
|
||||
return models;
|
||||
}
|
||||
|
||||
@RestPath(path = "/minecraftServer/:id", version = APIVersion.V1, accessLevel = APIAccessLevel.DEV)
|
||||
public MinecraftServerModel getMinecraftServer(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
String id = request.params(":id");
|
||||
if (id == null || (id.trim().isEmpty()))
|
||||
throw new APIException("Invalid Minecraft Server");
|
||||
Optional<MinecraftServer> optionalMinecraftServer = minecraftServerRepository.lookup(id);
|
||||
if (optionalMinecraftServer.isEmpty())
|
||||
throw new APIException("Minecraft server not found");
|
||||
return MinecraftServerModel.fromMinecraftServer(optionalMinecraftServer.get());
|
||||
}
|
||||
}
|
62
api/src/main/java/zone/themcgamer/api/route/StatusRoute.java
Normal file
62
api/src/main/java/zone/themcgamer/api/route/StatusRoute.java
Normal file
@ -0,0 +1,62 @@
|
||||
package zone.themcgamer.api.route;
|
||||
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import zone.themcgamer.api.APIException;
|
||||
import zone.themcgamer.api.APIVersion;
|
||||
import zone.themcgamer.api.RestPath;
|
||||
import zone.themcgamer.api.model.impl.StatusModel;
|
||||
import zone.themcgamer.data.APIAccessLevel;
|
||||
import zone.themcgamer.data.jedis.cache.CacheRepository;
|
||||
import zone.themcgamer.data.jedis.cache.ICacheItem;
|
||||
import zone.themcgamer.data.jedis.cache.ItemCacheType;
|
||||
import zone.themcgamer.data.jedis.cache.impl.PlayerStatusCache;
|
||||
import zone.themcgamer.data.jedis.data.APIKey;
|
||||
import zone.themcgamer.data.jedis.repository.RedisRepository;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class StatusRoute {
|
||||
private final CacheRepository repository;
|
||||
|
||||
public StatusRoute() {
|
||||
repository = RedisRepository.getRepository(CacheRepository.class).orElse(null);
|
||||
}
|
||||
|
||||
@RestPath(path = "/status", version = APIVersion.V1)
|
||||
public Map<String, Object> getStatuses(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
List<ICacheItem<?>> statuses = repository.filter(cacheItem -> cacheItem.getType() == ItemCacheType.PLAYER_STATUS);
|
||||
StringBuilder namesBuilder = new StringBuilder();
|
||||
for (ICacheItem<?> status : statuses)
|
||||
namesBuilder.append(((PlayerStatusCache) status).getPlayerName()).append(", ");
|
||||
String names = namesBuilder.toString();
|
||||
if (!names.isEmpty())
|
||||
names = names.substring(0, names.length() - 2);
|
||||
String finalNames = names;
|
||||
return new HashMap<>() {{
|
||||
put("total", statuses.size());
|
||||
put("names", apiKey.getAccessLevel() == APIAccessLevel.STANDARD ? "Unauthorized" : finalNames);
|
||||
}};
|
||||
}
|
||||
|
||||
@RestPath(path = "/status/:name", version = APIVersion.V1)
|
||||
public StatusModel getStatus(Request request, Response response, APIKey apiKey) throws APIException {
|
||||
String name = request.params(":name");
|
||||
if (name == null || (name.trim().isEmpty() || name.length() > 16))
|
||||
throw new APIException("Invalid username");
|
||||
PlayerStatusCache statusCache = repository
|
||||
.lookup(PlayerStatusCache.class, playerStatusCache -> playerStatusCache.getPlayerName().equals(name))
|
||||
.stream().findFirst().orElse(null);
|
||||
if (statusCache == null)
|
||||
throw new APIException("Player not found");
|
||||
StatusModel model = new StatusModel(statusCache.getUuid(), statusCache.getPlayerName(), statusCache.getServer(), statusCache.getTimeJoined());
|
||||
if (apiKey.getAccessLevel() == APIAccessLevel.STANDARD)
|
||||
model.setServer("Unauthorized");
|
||||
return model;
|
||||
}
|
||||
}
|
3
api/src/main/resources/META-INF/MANIFEST.MF
Normal file
3
api/src/main/resources/META-INF/MANIFEST.MF
Normal file
@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: zone.themcgamer.api.API
|
||||
|
19
arcade/build.gradle.kts
Normal file
19
arcade/build.gradle.kts
Normal file
@ -0,0 +1,19 @@
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
compileOnly("com.destroystokyo:paperspigot:1.12.2")
|
||||
implementation("com.github.cryptomorin:XSeries:7.8.0")
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val tokens = mapOf("version" to project.version)
|
||||
from(sourceSets["main"].resources.srcDirs) {
|
||||
filter<org.apache.tools.ant.filters.ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName.set("${project.rootProject.name}-${project.name}-v${project.version}.jar")
|
||||
destinationDir = file("$rootDir/output")
|
||||
}
|
||||
}
|
53
arcade/src/main/java/zone/themcgamer/arcade/Arcade.java
Normal file
53
arcade/src/main/java/zone/themcgamer/arcade/Arcade.java
Normal file
@ -0,0 +1,53 @@
|
||||
package zone.themcgamer.arcade;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import zone.themcgamer.arcade.manager.ArcadeManager;
|
||||
import zone.themcgamer.arcade.player.PlayerDataManager;
|
||||
import zone.themcgamer.arcade.scoreboard.ArcadeScoreboard;
|
||||
import zone.themcgamer.core.chat.ChatManager;
|
||||
import zone.themcgamer.core.chat.component.IChatComponent;
|
||||
import zone.themcgamer.core.chat.component.impl.BasicNameComponent;
|
||||
import zone.themcgamer.core.chat.component.impl.BasicRankComponent;
|
||||
import zone.themcgamer.core.common.MathUtils;
|
||||
import zone.themcgamer.core.common.scoreboard.ScoreboardHandler;
|
||||
import zone.themcgamer.core.plugin.MGZPlugin;
|
||||
import zone.themcgamer.core.plugin.Startup;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter
|
||||
public class Arcade extends MGZPlugin {
|
||||
public static Arcade INSTANCE;
|
||||
|
||||
private ArcadeManager arcadeManager;
|
||||
private Location spawn;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
super.onEnable();
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
@Startup
|
||||
public void loadArcade() {
|
||||
new PlayerDataManager(this);
|
||||
arcadeManager = new ArcadeManager(this, traveller);
|
||||
|
||||
new ScoreboardHandler(this, ArcadeScoreboard.class, 3L);
|
||||
|
||||
new ChatManager(this, badSportSystem, new IChatComponent[] {
|
||||
new BasicRankComponent(),
|
||||
new BasicNameComponent()
|
||||
});
|
||||
|
||||
MGZWorld world = MGZWorld.get(Bukkit.getWorlds().get(0));
|
||||
spawn = world.getDataPoint("SPAWN");
|
||||
if (spawn != null)
|
||||
spawn.setYaw(MathUtils.getFacingYaw(spawn, world.getDataPoints("LOOK_AT")));
|
||||
else spawn = new Location(world.getWorld(), 0, 150, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package zone.themcgamer.arcade.commands;
|
||||
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.command.help.HelpCommand;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class GameCommand extends HelpCommand {
|
||||
@Command(name = "game", aliases = {"arcade"}, description = "Game commands", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GameForceMapCommand {
|
||||
final Game game;
|
||||
@Command(name = "game.forcemap", usage = "<mapName>", description = "Force a map.", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class GameHostCommand {
|
||||
@Command(name = "game.host", description = "Manage the game", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
|
||||
//TODO open gui to manage the game
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class GameMaxPlayersCommand {
|
||||
@Command(name = "game.maxplayers", usage = "<amount>", description = "Set the max players.", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class GameMinPlayersCommand {
|
||||
@Command(name = "game.minplayers", usage = "<amount>", description = "Set the min players.", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class GamePlayerTeamCommand {
|
||||
@Command(name = "game.setteam", usage = "<player> <team>", description = "Set a player to team", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.arcade.manager.ArcadeManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GameStartCommand {
|
||||
private final ArcadeManager arcadeManager;
|
||||
|
||||
@Command(name = "game.start", description = "Force start the game", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Style.error("Game", "Please use &6/" + command.getLabel() + " <seconds>"));
|
||||
return;
|
||||
}
|
||||
if (arcadeManager.getState() != GameState.LOBBY)
|
||||
sender.sendMessage(Style.main("Game", "The game is not in lobby state!"));
|
||||
else {
|
||||
try {
|
||||
long time = Long.parseLong(args[0]);
|
||||
arcadeManager.getMapVotingManager().startVoting(TimeUnit.SECONDS.toMillis(time));
|
||||
} catch (NumberFormatException ex) {
|
||||
sender.sendMessage(Style.error("Game", "Invalid amount! Please use /" + command.getLabel() + " <seconds>"));
|
||||
return;
|
||||
}
|
||||
arcadeManager.getMapVotingManager().startVoting();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package zone.themcgamer.arcade.commands.arguments;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GameStopCommand {
|
||||
final Game game;
|
||||
@Command(name = "game.stop", description = "Force stop this game.", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package zone.themcgamer.arcade.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.core.common.WrappedBukkitEvent;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote This event is called when the {@link GameState} is changed
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class GameStateChangeEvent extends WrappedBukkitEvent {
|
||||
private final Game game;
|
||||
private final GameState from, to;
|
||||
}
|
47
arcade/src/main/java/zone/themcgamer/arcade/game/Game.java
Normal file
47
arcade/src/main/java/zone/themcgamer/arcade/game/Game.java
Normal file
@ -0,0 +1,47 @@
|
||||
package zone.themcgamer.arcade.game;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import zone.themcgamer.arcade.player.GamePlayer;
|
||||
import zone.themcgamer.arcade.team.Team;
|
||||
import zone.themcgamer.core.game.MGZGame;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote This class represents a game
|
||||
*/
|
||||
@Setter @Getter
|
||||
public abstract class Game implements Listener {
|
||||
private final MGZGame mgzGame;
|
||||
private final Team[] teams;
|
||||
|
||||
// Game Flags
|
||||
protected boolean joinMessages = true;
|
||||
protected boolean quitMessages = true;
|
||||
protected boolean blockPlace;
|
||||
protected boolean blockBreak;
|
||||
|
||||
// Data
|
||||
private long started;
|
||||
private final Set<GamePlayer> players = new HashSet<>();
|
||||
|
||||
public abstract List<String> getScoreboard(GamePlayer gamePlayer, Player player);
|
||||
|
||||
public Game(MGZGame mgzGame) {
|
||||
this(mgzGame, null);
|
||||
}
|
||||
|
||||
public Game(MGZGame mgzGame, Team[] teams) {
|
||||
this.mgzGame = mgzGame;
|
||||
if (teams == null || (teams.length <= 0))
|
||||
teams = new Team[] { new Team("PLAYERS", ChatColor.WHITE, true) };
|
||||
this.teams = teams;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package zone.themcgamer.arcade.game;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.arcade.game.impl.TheBridgeGame;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Game Manager") @Getter
|
||||
public class GameManager extends Module {
|
||||
private final List<Game> games = Arrays.asList(
|
||||
new TheBridgeGame()
|
||||
);
|
||||
|
||||
public GameManager(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package zone.themcgamer.arcade.game;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote The state of the currently running game
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public enum GameState {
|
||||
LOBBY("§aWaiting..."),
|
||||
STARTING("§6Starting"),
|
||||
PLAYING("§cPlaying"),
|
||||
ENDING("§9Ending");
|
||||
|
||||
private final String displayName;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package zone.themcgamer.arcade.game.impl;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.arcade.player.GamePlayer;
|
||||
import zone.themcgamer.arcade.team.Team;
|
||||
import zone.themcgamer.core.game.MGZGame;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class TheBridgeGame extends Game {
|
||||
public TheBridgeGame() {
|
||||
super(MGZGame.THE_BRIDGE, new Team[] {
|
||||
new Team("Red", ChatColor.RED, false),
|
||||
new Team("Blue", ChatColor.BLUE, false)
|
||||
});
|
||||
blockBreak = true;
|
||||
blockPlace = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getScoreboard(GamePlayer gamePlayer, Player player) {
|
||||
return Arrays.asList(
|
||||
"&b&lGame Duration",
|
||||
"§e2:50",
|
||||
"",
|
||||
"§c§lRED",
|
||||
"§fPlayers: §c0",
|
||||
"§fNexus: §c100",
|
||||
"",
|
||||
"§9§lBLUE",
|
||||
"§fPlayers: §90",
|
||||
"§fNexus: §9100"
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
package zone.themcgamer.arcade.manager;
|
||||
|
||||
import com.cryptomorin.xseries.XSound;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import zone.themcgamer.arcade.Arcade;
|
||||
import zone.themcgamer.arcade.commands.GameCommand;
|
||||
import zone.themcgamer.arcade.commands.arguments.*;
|
||||
import zone.themcgamer.arcade.event.GameStateChangeEvent;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.arcade.game.GameManager;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.arcade.map.GameMap;
|
||||
import zone.themcgamer.arcade.map.MapManager;
|
||||
import zone.themcgamer.arcade.map.MapVotingManager;
|
||||
import zone.themcgamer.arcade.map.event.MapVoteWinEvent;
|
||||
import zone.themcgamer.arcade.player.GamePlayer;
|
||||
import zone.themcgamer.core.account.Account;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.common.PlayerUtils;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.common.scheduler.ScheduleType;
|
||||
import zone.themcgamer.core.common.scheduler.event.SchedulerEvent;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.traveller.ServerTraveller;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Arcade Manager") @Getter
|
||||
public class ArcadeManager extends Module {
|
||||
private static final int DEFAULT_COUNTDOWN = 10;
|
||||
|
||||
private final GameManager gameManager;
|
||||
|
||||
private Game game;
|
||||
private GameState state = GameState.LOBBY;
|
||||
private int countdown = -1;
|
||||
|
||||
private GameMap map;
|
||||
|
||||
private boolean mapsLoaded;
|
||||
private MapVotingManager mapVotingManager;
|
||||
|
||||
public ArcadeManager(JavaPlugin plugin, ServerTraveller traveller) {
|
||||
super(plugin);
|
||||
new LobbyManager(plugin, this, traveller);
|
||||
gameManager = new GameManager(plugin);
|
||||
game = gameManager.getGames().get(0);
|
||||
MapManager mapManager = new MapManager(plugin);
|
||||
mapManager.withConsumer(future -> {
|
||||
future.whenComplete((maps, throwable) -> {
|
||||
mapsLoaded = true;
|
||||
mapVotingManager = new MapVotingManager(plugin, this, mapManager);
|
||||
});
|
||||
});
|
||||
registerCommand(new GameCommand());
|
||||
registerCommand(new GameForceMapCommand(game));
|
||||
registerCommand(new GameHostCommand());
|
||||
registerCommand(new GameMaxPlayersCommand());
|
||||
registerCommand(new GameMinPlayersCommand());
|
||||
registerCommand(new GamePlayerTeamCommand());
|
||||
registerCommand(new GameStartCommand(this));
|
||||
registerCommand(new GameStopCommand(game));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onLogin(PlayerLoginEvent event) {
|
||||
if (!mapsLoaded)
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "§cPlease wait whilst we load the maps...");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void handleCountdown(SchedulerEvent event) {
|
||||
if (countdown == -1)
|
||||
return;
|
||||
ChatColor color = ChatColor.GREEN;
|
||||
switch (countdown) {
|
||||
case 4: {
|
||||
color = ChatColor.YELLOW;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
color = ChatColor.GOLD;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
color = ChatColor.RED;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
color = ChatColor.DARK_RED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (event.getType() == ScheduleType.SECOND) {
|
||||
if (Bukkit.getOnlinePlayers().size() < game.getMgzGame().getMinPlayers()) {
|
||||
countdown = -1;
|
||||
setState(GameState.LOBBY);
|
||||
Bukkit.broadcastMessage(Style.error(game.getMgzGame().getName(), "§cCountdown stopped, there are not enough players!"));
|
||||
return;
|
||||
}
|
||||
if (countdown > 0) {
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
player.sendActionBar("§aGame starting in §f" + color + countdown + "§a...");
|
||||
}
|
||||
if (countdown % 10 == 0 || countdown <= 5) {
|
||||
if (countdown <= 0) {
|
||||
startGame();
|
||||
return;
|
||||
}
|
||||
float pitch = (float) countdown / 2f;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.playSound(player.getEyeLocation(), XSound.ENTITY_PLAYER_LEVELUP.parseSound(), 0.9f, pitch);
|
||||
player.sendMessage(Style.main(game.getMgzGame().getName(), "Game starting in §f" + color + countdown + " second" + (countdown == 1 ? "" : "s") + "§7..."));
|
||||
//player.sendTitle((countdown == 5 ? color + "\u277A" : (countdown == 4 ? color + "\u2779" : (countdown == 3 ? color + "\u2778" : (countdown == 2 ? color + "\u2777" : (countdown == 1 ? color + "\u2776" : ""))))), "",5,5,5);
|
||||
}
|
||||
}
|
||||
countdown--;
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void handleActionBar(SchedulerEvent event) {
|
||||
if (event.getType() == ScheduleType.SECOND) {
|
||||
if (state != GameState.LOBBY)
|
||||
return;
|
||||
String actionBar = "&aWaiting for &b" + Math.abs(game.getMgzGame().getMaxPlayers() - Bukkit.getOnlinePlayers().size()) + " &amore players...";
|
||||
if (mapVotingManager != null && (mapVotingManager.isVoting())) {
|
||||
actionBar = "&a&lPlease vote a map!";
|
||||
//TODO 2 colors animation for the vote map just do something you like
|
||||
}
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendActionBar(Style.color(actionBar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onMapVoteWin(MapVoteWinEvent event) {
|
||||
if (state != GameState.LOBBY)
|
||||
return;
|
||||
setMap(event.getMap());
|
||||
startCountdown(DEFAULT_COUNTDOWN);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
PlayerUtils.reset(player, true, true, GameMode.SURVIVAL);
|
||||
if (state == GameState.LOBBY || state == GameState.STARTING)
|
||||
player.teleport(Arcade.INSTANCE.getSpawn());
|
||||
else {
|
||||
// TODO: 1/27/21 teleport the player to the game map
|
||||
}
|
||||
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (optionalAccount.isPresent() && game.isJoinMessages())
|
||||
event.setJoinMessage("§a§lJoin §8» §7" + optionalAccount.get().getDisplayName() + " §7has joined the game!");
|
||||
else event.setJoinMessage(null);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (optionalAccount.isPresent() && game.isQuitMessages())
|
||||
event.setQuitMessage("§4§lQuit §8» §7" + optionalAccount.get().getDisplayName() + " §7left the game!");
|
||||
else event.setQuitMessage(null);
|
||||
}
|
||||
|
||||
public void setMap(@Nullable MGZWorld mgzWorld) {
|
||||
if (state != GameState.LOBBY)
|
||||
throw new IllegalStateException("The map cannot be updated in this state: " + state.name());
|
||||
if (map != null) {
|
||||
Bukkit.unloadWorld(map.getBukkitWorld(), true);
|
||||
FileUtils.deleteQuietly(map.getBukkitWorld().getWorldFolder());
|
||||
}
|
||||
if (mgzWorld == null)
|
||||
return;
|
||||
try {
|
||||
FileUtils.copyDirectory(mgzWorld.getDataFile().getParentFile(), new File(mgzWorld.getName()));
|
||||
|
||||
WorldCreator creator = new WorldCreator(mgzWorld.getName());
|
||||
creator.environment(World.Environment.NORMAL);
|
||||
creator.type(WorldType.FLAT);
|
||||
if (mgzWorld.getPreset() != null)
|
||||
creator.generatorSettings(mgzWorld.getPreset());
|
||||
creator.generateStructures(false);
|
||||
World world = creator.createWorld();
|
||||
|
||||
long time = 6000L;
|
||||
if (mgzWorld.getName().toLowerCase().contains("christmas"))
|
||||
time = 12000L;
|
||||
else if (mgzWorld.getName().toLowerCase().contains("halloween"))
|
||||
time = 17000L;
|
||||
world.setTime(time);
|
||||
world.setThundering(false);
|
||||
world.setStorm(false);
|
||||
world.setSpawnLocation(0, 150, 0);
|
||||
world.setGameRuleValue("randomTickSpeed", "0");
|
||||
world.setGameRuleValue("doDaylightCycle", "false");
|
||||
world.setGameRuleValue("showDeathMessages", "false");
|
||||
world.setGameRuleValue("doFireTick", "false");
|
||||
world.setGameRuleValue("mobGriefing", "false");
|
||||
world.setGameRuleValue("doMobLoot", "false");
|
||||
world.setGameRuleValue("doMobSpawning", "false");
|
||||
|
||||
map = new GameMap(mgzWorld, world);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current game to the provided {@link Game}
|
||||
* @param game the game to set
|
||||
*/
|
||||
public void setGame(Game game) {
|
||||
if (state != GameState.LOBBY)
|
||||
stopGame();
|
||||
this.game = game;
|
||||
setState(GameState.LOBBY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the game instantly
|
||||
*/
|
||||
public void startCountdown() {
|
||||
startCountdown(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the game with the given countdown
|
||||
* @param countdown the countdown
|
||||
*/
|
||||
public void startCountdown(int countdown) {
|
||||
if (state != GameState.LOBBY)
|
||||
return;
|
||||
setState(GameState.STARTING);
|
||||
this.countdown = countdown;
|
||||
if (countdown <= 0)
|
||||
startGame();
|
||||
}
|
||||
|
||||
public void startGame() {
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
throw new IllegalStateException("The game is already in a running state: " + state.name());
|
||||
setState(GameState.PLAYING);
|
||||
countdown = -1;
|
||||
|
||||
// Setting up the game
|
||||
game.setStarted(System.currentTimeMillis());
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
// TODO: 1/31/21 check if player is a staff member and is vanished
|
||||
|
||||
PlayerUtils.reset(player, true, true, GameMode.SURVIVAL);
|
||||
|
||||
GamePlayer gamePlayer = GamePlayer.getPlayer(player.getUniqueId());
|
||||
|
||||
// TODO: 1/31/21 team calculations
|
||||
|
||||
player.teleport(map.getBukkitWorld().getSpawnLocation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the currently running game
|
||||
*/
|
||||
public void stopGame() {
|
||||
if (state != GameState.PLAYING)
|
||||
return;
|
||||
setState(GameState.LOBBY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the game state to the given {@link GameState}
|
||||
* @param state the state to set
|
||||
*/
|
||||
private void setState(GameState state) {
|
||||
Bukkit.getPluginManager().callEvent(new GameStateChangeEvent(game, this.state, state));
|
||||
this.state = state;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package zone.themcgamer.arcade.manager;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Arcade Kingdom Manager")
|
||||
public class KingdomManager extends Module {
|
||||
public KingdomManager(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
package zone.themcgamer.arcade.manager;
|
||||
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.block.LeavesDecayEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerAttemptPickupItemEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.weather.WeatherChangeEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.arcade.Arcade;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.arcade.map.menu.MapVotingMenu;
|
||||
import zone.themcgamer.arcade.map.menu.TimeVoteMenu;
|
||||
import zone.themcgamer.arcade.team.menu.SelectTeamMenu;
|
||||
import zone.themcgamer.core.account.Account;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.SkullTexture;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.traveller.ServerTraveller;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Lobby Manager")
|
||||
public class LobbyManager extends Module {
|
||||
private static final ItemStack INFORMATION = new ItemBuilder(XMaterial.WRITTEN_BOOK)
|
||||
.setName("§a§lGame §8» §7How to play?")
|
||||
.addLoreLine("&7Click to get a small documentation about this game").toItemStack();
|
||||
private static final ItemStack KITS = new ItemBuilder(XMaterial.FEATHER)
|
||||
.setName("§a§lKits §8» §7Select Kit")
|
||||
.addLoreLine("&7Click to select a kit").toItemStack();
|
||||
|
||||
private static final ItemStack TEAM_SELECTOR = new ItemBuilder(XMaterial.PLAYER_HEAD)
|
||||
.setSkullOwner(SkullTexture.TEAM_UNDYED)
|
||||
.setName("§a§lTeams §8» §7Select Team")
|
||||
.addLoreLine("&7Click to select a team").toItemStack();
|
||||
public static final ItemStack MAP_VOTE = new ItemBuilder(XMaterial.BOOKSHELF)
|
||||
.setName("§a§lVote §8» §7Select Map")
|
||||
.toItemStack();
|
||||
private static final ItemStack TIME_VOTE = new ItemBuilder(XMaterial.CLOCK)
|
||||
.setName("§a§lVote §8» §7Select Time")
|
||||
.addLoreLine("&7Click to select a time").toItemStack();
|
||||
|
||||
private static final ItemStack GO_BACK_LOBBY = new ItemBuilder(XMaterial.ORANGE_BED)
|
||||
.setName("§a§lGame §8» §c§lGo back to lobby")
|
||||
.addLoreLine("&7Click to go back to a lobby server").toItemStack();
|
||||
|
||||
private final ArcadeManager arcadeManager;
|
||||
private final ServerTraveller traveller;
|
||||
|
||||
public LobbyManager(JavaPlugin plugin, ArcadeManager arcadeManager, ServerTraveller traveller) {
|
||||
super(plugin);
|
||||
this.arcadeManager = arcadeManager;
|
||||
this.traveller = traveller;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
player.getInventory().setItem(0, INFORMATION);
|
||||
player.getInventory().setItem(1, KITS);
|
||||
player.getInventory().setItem(3, TEAM_SELECTOR);
|
||||
player.getInventory().setItem(4, MAP_VOTE);
|
||||
player.getInventory().setItem(5, TIME_VOTE);
|
||||
player.getInventory().setItem(8, GO_BACK_LOBBY);
|
||||
player.getInventory().setHeldItemSlot(0);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onInventoryClick(InventoryClickEvent event) {
|
||||
Player player = (Player) event.getWhoClicked();
|
||||
Inventory inventory = event.getClickedInventory();
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (inventory == null)
|
||||
return;
|
||||
if (player.getInventory().equals(inventory) && player.getGameMode() != GameMode.CREATIVE)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onInteract(PlayerInteractEvent event) {
|
||||
if (event.getAction() == Action.PHYSICAL)
|
||||
return;
|
||||
Player player = event.getPlayer();
|
||||
ItemStack item = event.getItem();
|
||||
if (item == null)
|
||||
return;
|
||||
if (item.isSimilar(TEAM_SELECTOR))
|
||||
new SelectTeamMenu(player, arcadeManager.getGame()).open();
|
||||
else if (item.isSimilar(MAP_VOTE)) {
|
||||
if (!arcadeManager.getMapVotingManager().isVoting()) {
|
||||
player.sendMessage(Style.main("Voting", "&cYou can not vote at this moment, waiting for more players..."));
|
||||
return;
|
||||
}
|
||||
new MapVotingMenu(player, arcadeManager.getMapVotingManager()).open();
|
||||
} else if (item.isSimilar(TIME_VOTE)) {
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (optionalAccount.isPresent() && (!optionalAccount.get().hasRank(Rank.GAMER))) {
|
||||
player.sendMessage(Style.rankRequired(Rank.GAMER));
|
||||
return;
|
||||
}
|
||||
new TimeVoteMenu(player).open();
|
||||
} else if (item.isSimilar(GO_BACK_LOBBY))
|
||||
traveller.sendPlayer(player,"Hub");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onDamage(EntityDamageEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getCause() == EntityDamageEvent.DamageCause.VOID && entity instanceof Player)
|
||||
entity.teleport(Arcade.INSTANCE.getSpawn());
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPickupItem(PlayerAttemptPickupItemEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getPlayer().getGameMode() != GameMode.CREATIVE)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onDropItem(PlayerDropItemEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getPlayer().getGameMode() != GameMode.CREATIVE)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onFoodLevelChange(FoodLevelChangeEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBlockPlace(BlockPlaceEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getPlayer().getGameMode() == GameMode.CREATIVE)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBlockBreak(BlockBreakEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getPlayer().getGameMode() == GameMode.CREATIVE)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onWeatherChange(WeatherChangeEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onTnTPrime(ExplosionPrimeEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onLeaveDecay(LeavesDecayEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void entityChangeSoil(PlayerInteractEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getAction() != Action.PHYSICAL)
|
||||
return;
|
||||
if (event.getClickedBlock().getType() == XMaterial.FARMLAND.parseMaterial())
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onHangingBreakByEntity(HangingBreakByEntityEvent event) {
|
||||
GameState state = arcadeManager.getState();
|
||||
if (state == GameState.PLAYING || state == GameState.ENDING)
|
||||
return;
|
||||
if (event.getRemover() instanceof Player)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
15
arcade/src/main/java/zone/themcgamer/arcade/map/GameMap.java
Normal file
15
arcade/src/main/java/zone/themcgamer/arcade/map/GameMap.java
Normal file
@ -0,0 +1,15 @@
|
||||
package zone.themcgamer.arcade.map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.World;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class GameMap {
|
||||
private final MGZWorld mgzWorld;
|
||||
private final World bukkitWorld;
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package zone.themcgamer.arcade.map;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.common.ZipUtils;
|
||||
import zone.themcgamer.core.game.MGZGame;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Map Manager") @Getter
|
||||
public class MapManager extends Module {
|
||||
private final List<MGZWorld> maps = new ArrayList<>();
|
||||
|
||||
public MapManager(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
public void withConsumer(Consumer<CompletableFuture<Void>> consumer) {
|
||||
consumer.accept(CompletableFuture.runAsync(() -> {
|
||||
File mapsDirectory = new File("maps");
|
||||
if (!mapsDirectory.exists())
|
||||
mapsDirectory.mkdirs();
|
||||
for (MGZGame game : MGZGame.values()) {
|
||||
File parsedMapsDirectory = new File(File.separator + "home" + File.separator + "minecraft" + File.separator +
|
||||
"upload" + File.separator + "maps" + File.separator + game.name());
|
||||
if (!parsedMapsDirectory.exists())
|
||||
continue;
|
||||
File[] files = parsedMapsDirectory.listFiles();
|
||||
if (files == null)
|
||||
continue;
|
||||
for (File file : files) {
|
||||
String fileName = file.getName();
|
||||
String[] split = fileName.split("\\.");
|
||||
if (split.length < 1)
|
||||
continue;
|
||||
String lastDottedString = split[split.length - 1];
|
||||
if (!lastDottedString.equals("zip"))
|
||||
continue;
|
||||
File targetDirectory = new File(mapsDirectory, game.name() + File.separator +
|
||||
file.getName().substring(0, fileName.indexOf(lastDottedString) - 1));
|
||||
if (!targetDirectory.exists())
|
||||
targetDirectory.mkdirs();
|
||||
try {
|
||||
ZipUtils.unzip(file, targetDirectory);
|
||||
maps.add(new MGZWorld(targetDirectory));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package zone.themcgamer.arcade.map;
|
||||
|
||||
import com.cryptomorin.xseries.XSound;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.arcade.manager.ArcadeManager;
|
||||
import zone.themcgamer.arcade.manager.LobbyManager;
|
||||
import zone.themcgamer.arcade.map.event.MapVoteWinEvent;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.common.scheduler.ScheduleType;
|
||||
import zone.themcgamer.core.common.scheduler.event.SchedulerEvent;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Map Voting")
|
||||
public class MapVotingManager extends Module {
|
||||
private final ArcadeManager arcadeManager;
|
||||
private final MapManager mapManager;
|
||||
|
||||
@Getter private boolean voting;
|
||||
private long startedVoting, votingTime;
|
||||
@Getter private final Map<MGZWorld, Integer> maps = new HashMap<>();
|
||||
@Getter private final Set<UUID> voted = new HashSet<>();
|
||||
|
||||
public MapVotingManager(JavaPlugin plugin, ArcadeManager arcadeManager, MapManager mapManager) {
|
||||
super(plugin);
|
||||
this.arcadeManager = arcadeManager;
|
||||
this.mapManager = mapManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onSchedule(SchedulerEvent event) {
|
||||
if (event.getType() != ScheduleType.SECOND
|
||||
|| !voting
|
||||
|| startedVoting == -1L
|
||||
|| (System.currentTimeMillis() - startedVoting) < votingTime)
|
||||
return;
|
||||
List<Map.Entry<MGZWorld, Integer>> entries = new ArrayList<>(maps.entrySet());
|
||||
entries.sort((a, b) -> Integer.compare(b.getValue(), a.getValue()));
|
||||
Map.Entry<MGZWorld, Integer> entry = entries.get(0);
|
||||
MGZWorld map = entry.getKey();
|
||||
int votes = entry.getValue();
|
||||
Bukkit.getPluginManager().callEvent(new MapVoteWinEvent(map, votes));
|
||||
|
||||
Bukkit.broadcastMessage(Style.main("Voting", "Map §f" + map.getName() + " §7won with §6" +
|
||||
votes + " §7vote" + (votes == 1 ? "" : "s") + "!"));
|
||||
stopVoting();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
if (voting || arcadeManager.getState() != GameState.LOBBY)
|
||||
return;
|
||||
if (Bukkit.getOnlinePlayers().size() >= arcadeManager.getGame().getMgzGame().getMinPlayers()) {
|
||||
startVoting();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
if (Bukkit.getOnlinePlayers().size() - 1 < arcadeManager.getGame().getMgzGame().getMinPlayers() && voting) {
|
||||
stopVoting();
|
||||
Bukkit.broadcastMessage(Style.main("Voting", "§cMap voting has ended as there are not enough players!"));
|
||||
}
|
||||
}
|
||||
|
||||
public void startVoting() {
|
||||
startVoting(TimeUnit.SECONDS.toMillis(30L));
|
||||
}
|
||||
|
||||
public void startVoting(long time) {
|
||||
List<WorldCategory> worldCategories = Arrays.asList(arcadeManager.getGame().getMgzGame().getWorldCategories());
|
||||
List<MGZWorld> maps = mapManager.getMaps().stream()
|
||||
.filter(mgzWorld -> worldCategories.contains(mgzWorld.getCategory()))
|
||||
.collect(Collectors.toList());
|
||||
if (maps.isEmpty())
|
||||
return;
|
||||
Collections.shuffle(maps);
|
||||
voting = true;
|
||||
startedVoting = System.currentTimeMillis();
|
||||
votingTime = time;
|
||||
for (int i = 0; i < Math.min(maps.size(), 5); i++)
|
||||
this.maps.put(maps.get(i), 0);
|
||||
if (!voting)
|
||||
return;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.playSound(player.getEyeLocation(), XSound.ENTITY_VILLAGER_AMBIENT.parseSound(), 0.9F, 0.1F);
|
||||
player.sendMessage(Style.main("Voting", "Now let's vote for a map you like, &6" + player.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public void stopVoting() {
|
||||
voting = false;
|
||||
startedVoting = votingTime = -1L;
|
||||
maps.clear();
|
||||
voted.clear();
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
player.getInventory().remove(LobbyManager.MAP_VOTE);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package zone.themcgamer.arcade.map.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import zone.themcgamer.core.common.WorldTime;
|
||||
import zone.themcgamer.core.common.WrappedBukkitEvent;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class MapTimeVoteWinEvent extends WrappedBukkitEvent {
|
||||
private final WorldTime worldTime;
|
||||
private final int votes;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package zone.themcgamer.arcade.map.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import zone.themcgamer.core.common.WrappedBukkitEvent;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class MapVoteWinEvent extends WrappedBukkitEvent {
|
||||
private final MGZWorld map;
|
||||
private final int votes;
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package zone.themcgamer.arcade.map.menu;
|
||||
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import com.cryptomorin.xseries.XSound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import zone.themcgamer.arcade.map.MapVotingManager;
|
||||
import zone.themcgamer.common.RandomUtils;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.Menu;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class MapVotingMenu extends Menu {
|
||||
private final MapVotingManager mapVotingManager;
|
||||
|
||||
public MapVotingMenu(Player player, MapVotingManager mapVotingManager) {
|
||||
super(player, "Map Voting", 3, MenuType.CHEST);
|
||||
this.mapVotingManager = mapVotingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpen() {
|
||||
set(1, 1, new Button(new ItemBuilder(XMaterial.BOOKSHELF)
|
||||
.setName("§6§lRandom")
|
||||
.setLore(
|
||||
"",
|
||||
"§7Click to vote for a random map"
|
||||
).toItemStack(), event -> vote(null)));
|
||||
|
||||
int slot = 3;
|
||||
for (Map.Entry<MGZWorld, Integer> entry : mapVotingManager.getMaps().entrySet()) {
|
||||
MGZWorld map = entry.getKey();
|
||||
set(1, slot++, new Button(new ItemBuilder(XMaterial.PAPER)
|
||||
.setName("§6§l" + map.getName())
|
||||
.setLore(
|
||||
"",
|
||||
"§7Votes §f" + entry.getValue(),
|
||||
"§7Made By §f" + map.getAuthor()
|
||||
).toItemStack(), event -> vote(map)));
|
||||
}
|
||||
}
|
||||
|
||||
private void vote(@Nullable MGZWorld map) {
|
||||
close();
|
||||
if (mapVotingManager.getVoted().contains(player.getUniqueId())) {
|
||||
player.sendMessage(Style.error("Voting", "§cYou already voted!"));
|
||||
player.playSound(player.getLocation(), XSound.BLOCK_NOTE_BLOCK_BASS.parseSound(), 10, 2);
|
||||
} else {
|
||||
if (map == null)
|
||||
map = RandomUtils.random(new ArrayList<>(mapVotingManager.getMaps().keySet()));
|
||||
if (map == null)
|
||||
player.sendMessage(Style.error("Voting", "§cInvalid map!"));
|
||||
else {
|
||||
mapVotingManager.getVoted().add(player.getUniqueId());
|
||||
mapVotingManager.getMaps().put(map, mapVotingManager.getMaps().getOrDefault(map, 0) + 1);
|
||||
player.playSound(player.getLocation(), XSound.UI_BUTTON_CLICK.parseSound(),10, 2);
|
||||
player.sendMessage(Style.main("Voting", "You voted for §f" + map.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package zone.themcgamer.arcade.map.menu;
|
||||
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.WorldTime;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.MenuPattern;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.common.menu.UpdatableMenu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TimeVoteMenu extends UpdatableMenu {
|
||||
public TimeVoteMenu(Player player) {
|
||||
super(player, "Time Vote", 3, MenuType.CHEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
fill(new Button(new ItemBuilder(XMaterial.BLACK_STAINED_GLASS_PANE).setName("&7").toItemStack()));
|
||||
List<Integer> slots = MenuPattern.getSlots(
|
||||
"XXXXXXXXX",
|
||||
"XOXOXOXOX",
|
||||
"XXXXXXXXX"
|
||||
);
|
||||
int index = 0;
|
||||
for (WorldTime time : Arrays.asList(WorldTime.SUNRISE, WorldTime.DAY, WorldTime.SUNSET, WorldTime.MIDNIGHT)) {
|
||||
List<String> lore = new ArrayList<>();
|
||||
for (String descriptionLine : time.getDescription())
|
||||
lore.add("§7" + descriptionLine);
|
||||
lore.add("");
|
||||
lore.add("§e▪ &7Current votes: &b0");
|
||||
lore.add("");
|
||||
lore.add("&aClick to vote!");
|
||||
ItemBuilder icon = new ItemBuilder(XMaterial.CLOCK).setName("&e&l" + time.getDisplayName());
|
||||
icon.setLore(lore);
|
||||
set(slots.get(index++), new Button(icon.toItemStack(), event -> {
|
||||
//TODO do the vote shit here
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package zone.themcgamer.arcade.player;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.arcade.team.Team;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Setter @Getter
|
||||
public class GamePlayer {
|
||||
@Getter private static final Map<UUID, GamePlayer> cache = new HashMap<>();
|
||||
|
||||
private final UUID uuid;
|
||||
private Team team;
|
||||
private boolean spectating;
|
||||
private long logoutTime;
|
||||
|
||||
public GamePlayer(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
cache.put(uuid, this);
|
||||
}
|
||||
|
||||
public Player getBukkitPlayer() {
|
||||
return Bukkit.getPlayer(uuid);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
cache.remove(uuid);
|
||||
}
|
||||
|
||||
public static GamePlayer getPlayer(UUID uuid) {
|
||||
return cache.get(uuid);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package zone.themcgamer.arcade.player;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.core.common.scheduler.ScheduleType;
|
||||
import zone.themcgamer.core.common.scheduler.event.SchedulerEvent;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Player Data Manager")
|
||||
public class PlayerDataManager extends Module {
|
||||
// The amount of time a player has to rejoin the game after being disconnected.
|
||||
// If the player does not join within the given time, their stats for this current
|
||||
// game will be cleared
|
||||
private static final long MAX_REJOIN_TIME = TimeUnit.MINUTES.toMillis(3L);
|
||||
|
||||
public PlayerDataManager(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void expirePlayers(SchedulerEvent event) {
|
||||
if (event.getType() != ScheduleType.SECOND)
|
||||
return;
|
||||
GamePlayer.getCache().entrySet().removeIf(entry -> {
|
||||
GamePlayer gamePlayer = entry.getValue();
|
||||
if (gamePlayer.getBukkitPlayer() != null)
|
||||
return false;
|
||||
return (System.currentTimeMillis() - gamePlayer.getLogoutTime()) >= MAX_REJOIN_TIME;
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
UUID uuid = event.getPlayer().getUniqueId();
|
||||
if (GamePlayer.getPlayer(uuid) == null)
|
||||
new GamePlayer(uuid);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
GamePlayer gamePlayer = GamePlayer.getPlayer(event.getPlayer().getUniqueId());
|
||||
if (gamePlayer != null)
|
||||
gamePlayer.setLogoutTime(System.currentTimeMillis());
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package zone.themcgamer.arcade.scoreboard;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.arcade.Arcade;
|
||||
import zone.themcgamer.arcade.game.GameState;
|
||||
import zone.themcgamer.arcade.manager.ArcadeManager;
|
||||
import zone.themcgamer.arcade.map.GameMap;
|
||||
import zone.themcgamer.arcade.player.GamePlayer;
|
||||
import zone.themcgamer.common.DoubleUtils;
|
||||
import zone.themcgamer.core.account.Account;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.animation.impl.WaveAnimation;
|
||||
import zone.themcgamer.core.common.scoreboard.WritableScoreboard;
|
||||
import zone.themcgamer.core.plugin.MGZPlugin;
|
||||
import zone.themcgamer.data.jedis.data.server.MinecraftServer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ArcadeScoreboard extends WritableScoreboard {
|
||||
private final GamePlayer gamePlayer;
|
||||
private WaveAnimation title;
|
||||
|
||||
public ArcadeScoreboard(Player player) {
|
||||
super(player);
|
||||
gamePlayer = GamePlayer.getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
ArcadeManager arcadeManager = Arcade.INSTANCE.getArcadeManager();
|
||||
String title = arcadeManager.getGame().getMgzGame().getName();
|
||||
if (this.title == null || (!this.title.getInput().equals(title))) {
|
||||
this.title = new WaveAnimation(title)
|
||||
.withPrimary(ChatColor.GOLD.toString())
|
||||
.withSecondary(ChatColor.WHITE.toString())
|
||||
.withBold();
|
||||
}
|
||||
return this.title.next();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLines() {
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (!optionalAccount.isPresent()) {
|
||||
writeBlank();
|
||||
return;
|
||||
}
|
||||
Account account = optionalAccount.get();
|
||||
ArcadeManager arcadeManager = Arcade.INSTANCE.getArcadeManager();
|
||||
GameState state = arcadeManager.getState();
|
||||
MinecraftServer minecraftServer = MGZPlugin.getMinecraftServer();
|
||||
|
||||
GameMap map = arcadeManager.getMap();
|
||||
|
||||
writeBlank();
|
||||
switch (state) {
|
||||
case LOBBY:
|
||||
case STARTING: {
|
||||
write("§e▪ §fKit: §bWarrior");
|
||||
write("§e▪ §fGold: §6" + DoubleUtils.format(account.getGold(), true) + " \u26C3");
|
||||
writeBlank();
|
||||
write("§e▪ §fMap: §b" + (arcadeManager.getMapVotingManager().isVoting() ? "Voting..." : (map == null ? "None" : map.getMgzWorld().getName())));
|
||||
write("§e▪ §fState: §b" + state.getDisplayName());
|
||||
write("§e▪ §fPlayers: §a" + Bukkit.getOnlinePlayers().size() + "§7/§c" + arcadeManager.getGame().getMgzGame().getMaxPlayers());
|
||||
writeBlank();
|
||||
write("§e▪ §fGame: §b" + minecraftServer.getId().replace(minecraftServer.getGroup().getName().toLowerCase(), ""));
|
||||
break;
|
||||
}
|
||||
case PLAYING:
|
||||
case ENDING: {
|
||||
for (String line : arcadeManager.getGame().getScoreboard(gamePlayer, player)) {
|
||||
if (line.trim().isEmpty())
|
||||
writeBlank();
|
||||
else write(line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
writeBlank();
|
||||
write("§6themcgamer.zone");
|
||||
}
|
||||
}
|
59
arcade/src/main/java/zone/themcgamer/arcade/team/Team.java
Normal file
59
arcade/src/main/java/zone/themcgamer/arcade/team/Team.java
Normal file
@ -0,0 +1,59 @@
|
||||
package zone.themcgamer.arcade.team;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.ChatColor;
|
||||
import zone.themcgamer.core.common.SkullTexture;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class Team {
|
||||
private final String name;
|
||||
private final ChatColor color;
|
||||
private final boolean friendlyFire;
|
||||
|
||||
public String getSkullTexture() {
|
||||
switch (color) {
|
||||
case DARK_GREEN: {
|
||||
return SkullTexture.TEAM_GREEN;
|
||||
}
|
||||
case DARK_AQUA: {
|
||||
return SkullTexture.TEAM_CYAN;
|
||||
}
|
||||
case DARK_RED:
|
||||
case RED: {
|
||||
return SkullTexture.TEAM_RED;
|
||||
}
|
||||
case DARK_PURPLE: {
|
||||
return SkullTexture.TEAM_PURPLE;
|
||||
}
|
||||
case GOLD: {
|
||||
return SkullTexture.TEAM_ORANGE;
|
||||
}
|
||||
case GRAY: {
|
||||
return SkullTexture.TEAM_LIGHT_GRAY;
|
||||
}
|
||||
case DARK_GRAY: {
|
||||
return SkullTexture.TEAM_GRAY;
|
||||
}
|
||||
case BLUE: {
|
||||
return SkullTexture.TEAM_BLUE;
|
||||
}
|
||||
case GREEN: {
|
||||
return SkullTexture.TEAM_LIME;
|
||||
}
|
||||
case AQUA: {
|
||||
return SkullTexture.TEAM_LIGHT_BLUE;
|
||||
}
|
||||
case LIGHT_PURPLE: {
|
||||
return SkullTexture.TEAM_MAGENTA;
|
||||
}
|
||||
case YELLOW: {
|
||||
return SkullTexture.TEAM_YELLOW;
|
||||
}
|
||||
}
|
||||
return SkullTexture.TEAM_WHITE;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package zone.themcgamer.arcade.team.menu;
|
||||
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.arcade.game.Game;
|
||||
import zone.themcgamer.arcade.player.GamePlayer;
|
||||
import zone.themcgamer.arcade.team.Team;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.MenuPattern;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.common.menu.UpdatableMenu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SelectTeamMenu extends UpdatableMenu {
|
||||
private final GamePlayer gamePlayer;
|
||||
private final Game game;
|
||||
|
||||
public SelectTeamMenu(Player player, Game game) {
|
||||
super(player, null, 3, MenuType.CHEST);
|
||||
this.game = game;
|
||||
gamePlayer = GamePlayer.getPlayer(player.getUniqueId());
|
||||
if (gamePlayer == null)
|
||||
return;
|
||||
setTitle((gamePlayer.getTeam() == null ? "Pick a Team" : "Current ▪ " + gamePlayer.getTeam().getColor() + gamePlayer.getTeam().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
if (gamePlayer == null)
|
||||
return;
|
||||
fillBorders(new Button(new ItemBuilder(XMaterial.BLACK_STAINED_GLASS_PANE)
|
||||
.setName("&7").toItemStack()));
|
||||
List<Integer> slots = MenuPattern.getSlots(
|
||||
"XXXXXXXXX",
|
||||
"XOOOOOOOX",
|
||||
"XXXXXXXXX"
|
||||
);
|
||||
int index = 0;
|
||||
for (Team team : game.getTeams()) {
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("");
|
||||
lore.add("§e▪ &7Players: " + team.getColor() + "0");
|
||||
lore.add("");
|
||||
if (isInTeam(gamePlayer, team))
|
||||
lore.add("§aSelected!");
|
||||
else lore.add("&7Click to join &f" + team.getColor() + team.getName() + " &7team!");
|
||||
set(slots.get(index++), new Button(new ItemBuilder(XMaterial.PLAYER_HEAD)
|
||||
.setGlow(isInTeam(gamePlayer, team))
|
||||
.setSkullOwner(team.getSkullTexture())
|
||||
.setName(team.getColor() + team.getName())
|
||||
.setLore(lore).toItemStack(), event -> {
|
||||
if (isInTeam(gamePlayer, team)) {
|
||||
player.sendMessage(Style.main("Teams", "You're already on this team!"));
|
||||
return;
|
||||
}
|
||||
close();
|
||||
//TODO Check if team is unbalanced
|
||||
|
||||
gamePlayer.setTeam(team);
|
||||
player.getInventory().setItem(3, new ItemBuilder(XMaterial.PLAYER_HEAD)
|
||||
.setSkullOwner(team.getSkullTexture())
|
||||
.setName("§a§lTeams §8» §7Select team")
|
||||
.addLoreLine("&7Click to select a team").toItemStack());
|
||||
player.sendMessage(Style.main("Teams","You've joined the §f" + team.getColor() + team.getName() + " &7team!"));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isInTeam(GamePlayer gamePlayer, Team team) {
|
||||
return (gamePlayer.getTeam() != null && (gamePlayer.getTeam().equals(team)));
|
||||
}
|
||||
}
|
5
arcade/src/main/resources/plugin.yml
Normal file
5
arcade/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,5 @@
|
||||
name: Arcade
|
||||
version: 1.0-SNAPSHOT
|
||||
api-version: 1.13
|
||||
main: zone.themcgamer.arcade.Arcade
|
||||
author: MGZ Development Team
|
137
build.gradle.kts
Normal file
137
build.gradle.kts
Normal file
@ -0,0 +1,137 @@
|
||||
import java.util.Optional as javaOptional
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
kotlin("jvm") version "1.4.30-RC"
|
||||
id("com.github.johnrengelman.shadow") version "6.1.0"
|
||||
id("com.gorylenko.gradle-git-properties") version "2.2.2"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "zone.themcgamer"
|
||||
version = "1.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
||||
apply {
|
||||
plugin("java-library")
|
||||
plugin("kotlin")
|
||||
plugin("com.github.johnrengelman.shadow")
|
||||
plugin("com.gorylenko.gradle-git-properties")
|
||||
plugin("maven-publish")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(kotlin("stdlib-jdk8"))
|
||||
compileOnly("org.jetbrains:annotations:20.1.0")
|
||||
|
||||
// lombok
|
||||
compileOnly("org.projectlombok:lombok:1.18.16")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.16")
|
||||
|
||||
testCompileOnly("org.projectlombok:lombok:1.18.16")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.16")
|
||||
|
||||
compileOnly("org.slf4j:slf4j-simple:1.7.30")
|
||||
|
||||
implementation("com.google.code.gson:gson:2.7")
|
||||
|
||||
implementation("commons-io:commons-io:2.6")
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.10.0-RC1")
|
||||
}
|
||||
|
||||
gitProperties {
|
||||
customProperty("insane_module", name)
|
||||
}
|
||||
|
||||
tasks {
|
||||
compileJava {
|
||||
options.compilerArgs.add("-parameters")
|
||||
options.forkOptions.executable = "javac"
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "11";
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = project.name
|
||||
url = uri("https://mvn.cnetwork.club/repository/${project.name}/")
|
||||
|
||||
credentials {
|
||||
username = System.getenv("NEXUS_USERNAME")
|
||||
password = System.getenv("NEXUS_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getPropertySafe("vcsImcPrivateToken").ifPresent { token ->
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://vcs.cnetwork.club/api/v4/projects/1/packages/maven")
|
||||
credentials(HttpHeaderCredentials::class) {
|
||||
name = "Private-Token"
|
||||
value = token // the variable resides in ~/.gradle/gradle.properties
|
||||
}
|
||||
authentication {
|
||||
create<HttpHeaderAuthentication>("header")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
|
||||
maven {
|
||||
url = uri("https://mvn.cnetwork.club/repository/public/")
|
||||
|
||||
credentials {
|
||||
username = getEnv("NEXUS_USERNAME").orElseGet {
|
||||
getPropertySafe("mavenUsername")
|
||||
.orElseThrow { IllegalArgumentException("Central repo not configured") }
|
||||
}
|
||||
password = getEnv("NEXUS_PASSWORD").orElseGet {
|
||||
getPropertySafe("mavenPassword")
|
||||
.orElseThrow { IllegalArgumentException("Central repo not configured") }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
fun getEnv(env: String): java.util.Optional<String> {
|
||||
return javaOptional.ofNullable(System.getenv(env))
|
||||
}
|
||||
|
||||
fun getPropertySafe(property: String): javaOptional<String> {
|
||||
return if (project.hasProperty(property)) javaOptional.of(
|
||||
project.property(property).toString()
|
||||
) else javaOptional.empty()
|
||||
}
|
19
buildserver/build.gradle.kts
Normal file
19
buildserver/build.gradle.kts
Normal file
@ -0,0 +1,19 @@
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
compileOnly("com.destroystokyo:paperspigot:1.12.2")
|
||||
implementation("com.github.cryptomorin:XSeries:7.8.0")
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val tokens = mapOf("version" to project.version)
|
||||
from(sourceSets["main"].resources.srcDirs) {
|
||||
filter<org.apache.tools.ant.filters.ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName.set("${project.rootProject.name}-${project.name}-v${project.version}.jar")
|
||||
destinationDir = file("$rootDir/output")
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package zone.themcgamer.buildServer;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import zone.themcgamer.buildServer.listener.PlayerListener;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.chat.ChatManager;
|
||||
import zone.themcgamer.core.chat.component.IChatComponent;
|
||||
import zone.themcgamer.core.chat.component.impl.BasicNameComponent;
|
||||
import zone.themcgamer.core.chat.component.impl.BasicRankComponent;
|
||||
import zone.themcgamer.core.plugin.MGZPlugin;
|
||||
import zone.themcgamer.core.plugin.Startup;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter
|
||||
public class Build extends MGZPlugin {
|
||||
public static Build INSTANCE;
|
||||
|
||||
private World mainWorld;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
super.onEnable();
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
@Startup
|
||||
public void loadBuildServer() {
|
||||
mainWorld = Bukkit.getWorlds().get(0);
|
||||
WorldManager worldManager = new WorldManager(this);
|
||||
new PlayerListener(this, worldManager);
|
||||
|
||||
new ChatManager(this, badSportSystem, new IChatComponent[] {
|
||||
new BasicRankComponent(),
|
||||
new BasicNameComponent()
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package zone.themcgamer.buildServer.backup;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.common.ZipUtils;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class BackupTask {
|
||||
private static final int MAX_BACKUP_SIZE = 15;
|
||||
|
||||
public BackupTask(JavaPlugin plugin, MGZWorld world) {
|
||||
if (world.getWorld() != null)
|
||||
throw new IllegalStateException("Cannot backup a loaded world");
|
||||
File backupDirectory = new File("backups" + File.separator + world.getCategory().name() + File.separator + world.getName());
|
||||
if (!backupDirectory.exists())
|
||||
backupDirectory.mkdirs();
|
||||
plugin.getLogger().info("Backing up world \"" + world.getName() + "\" under category \"" + world.getCategory().name() + "\"");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
File[] files = backupDirectory.listFiles();
|
||||
if (files != null && (files.length > MAX_BACKUP_SIZE)) {
|
||||
File firstBackup = null;
|
||||
for (File file : files) {
|
||||
if (firstBackup == null || (file.lastModified() < firstBackup.lastModified()))
|
||||
firstBackup = file;
|
||||
}
|
||||
if (firstBackup != null)
|
||||
FileUtils.deleteQuietly(firstBackup);
|
||||
}
|
||||
File worldDirectory = new File("maps" + File.separator + world.getCategory().name() + File.separator + world.getName());
|
||||
if (!worldDirectory.exists())
|
||||
return;
|
||||
File zipFile = new File(backupDirectory, new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()) + ".zip");
|
||||
ZipUtils.zip(worldDirectory.getPath(), zipFile.getPath());
|
||||
plugin.getLogger().info("Created backup \"" + zipFile.getPath() + "\"");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package zone.themcgamer.buildServer.listener;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.buildServer.Build;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.account.Account;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.common.PlayerUtils;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class PlayerListener implements Listener {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
public PlayerListener(JavaPlugin plugin, WorldManager worldManager) {
|
||||
this.worldManager = worldManager;
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.getGameMode() != GameMode.CREATIVE)
|
||||
player.setGameMode(GameMode.CREATIVE);
|
||||
if (!player.getAllowFlight())
|
||||
player.setAllowFlight(true);
|
||||
}
|
||||
}, 20L, 20L);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
private void onLogin(PlayerLoginEvent event) {
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED)
|
||||
return;
|
||||
Player player = event.getPlayer();
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (player.isOp() || player.isWhitelisted() || (optionalAccount.isPresent() && (optionalAccount.get().hasRank(Rank.BUILDER))))
|
||||
return;
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "§cOnly builders can join this server!");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
PlayerUtils.reset(player, true, false, GameMode.CREATIVE);
|
||||
|
||||
player.sendMessage(Style.color("&8&m" + Strings.repeat("-", 30)));
|
||||
player.sendMessage("");
|
||||
player.sendMessage(Style.color(" &e➢ &6&lBuild"));
|
||||
player.sendMessage("");
|
||||
player.sendMessage(Style.color(" &7For a list of commands, use &f/help"));
|
||||
player.sendMessage(Style.color(" &7Wanna learn how to use the build system? Use &f/tutorial"));
|
||||
player.sendMessage("");
|
||||
player.sendMessage(Style.color("&8&m" + Strings.repeat("-", 30)));
|
||||
|
||||
player.teleport(Build.INSTANCE.getMainWorld().getSpawnLocation());
|
||||
event.setJoinMessage(Style.color("&8[&a+&8] &7" + player.getName()));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Optional<Account> optionalAccount = AccountManager.fromCache(player.getUniqueId());
|
||||
if (!optionalAccount.isPresent()) {
|
||||
player.sendMessage(Style.error("Chat", "§cCannot send chat message"));
|
||||
return;
|
||||
}
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
event.setFormat((mgzWorld == null ? "" : "§7[" + mgzWorld.getName() + "§7] ") + "§f" +
|
||||
optionalAccount.get().getPrimaryRank().getColor() + player.getName() + "§7: §f" + event.getMessage().replaceAll("%", "%%"));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onDamage(EntityDamageEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
if (entity instanceof Player) {
|
||||
event.setCancelled(true);
|
||||
if (event.getCause() == EntityDamageEvent.DamageCause.VOID)
|
||||
entity.teleport(entity.getWorld().getSpawnLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onFoodLevelChange(FoodLevelChangeEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
event.setQuitMessage(Style.color("&8[&c-&8] &7" + event.getPlayer().getName()));
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
package zone.themcgamer.buildServer.parse;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Sign;
|
||||
import zone.themcgamer.common.DoubleUtils;
|
||||
import zone.themcgamer.common.MathUtils;
|
||||
import zone.themcgamer.common.ZipUtils;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ParseTask {
|
||||
@Getter private final MGZWorld mgzWorld;
|
||||
private final int radius;
|
||||
private final List<Integer> ids;
|
||||
|
||||
private World world;
|
||||
private Location center;
|
||||
private long started;
|
||||
|
||||
@Setter private boolean preparing = true;
|
||||
private int x, y, z, parsedBlocks;
|
||||
@Getter private boolean completed;
|
||||
|
||||
private final Map<String, List<Location>> dataPoints = new HashMap<>();
|
||||
|
||||
public ParseTask(MGZWorld mgzWorld, int radius, List<Integer> ids) {
|
||||
this.mgzWorld = mgzWorld;
|
||||
this.radius = radius;
|
||||
this.ids = ids;
|
||||
x = z = -radius;
|
||||
}
|
||||
|
||||
public void start(World world, Location center) {
|
||||
if (!preparing)
|
||||
return;
|
||||
this.world = world;
|
||||
this.center = center;
|
||||
started = System.currentTimeMillis();
|
||||
preparing = false;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (preparing)
|
||||
return;
|
||||
long started = System.currentTimeMillis();
|
||||
for (; x <= radius; x++) {
|
||||
for (; y <= 256; y++) {
|
||||
for (; z <= radius; z++) {
|
||||
if ((System.currentTimeMillis() - started) >= 10L)
|
||||
return;
|
||||
parsedBlocks++;
|
||||
if (parsedBlocks % 15_000_000 == 0) {
|
||||
double complete = (double) parsedBlocks / 1_000_000;
|
||||
double total = (double) ((radius * 2) * 256 * (radius * 2)) / 1_000_000;
|
||||
double percent = MathUtils.round(complete / total * 100, 1);
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Parse of map §b" + mgzWorld.getName() + " §7is §6" + percent + "% §7complete"));
|
||||
}
|
||||
Block block = world.getBlockAt(center.getBlockX() + x, y, center.getBlockZ() + z);
|
||||
Location blockLocation = block.getLocation();
|
||||
blockLocation.setX(blockLocation.getBlockX() + .5);
|
||||
blockLocation.setZ(blockLocation.getBlockZ() + .5);
|
||||
|
||||
if (block.getType() == Material.SPONGE) {
|
||||
Block blockAbove = block.getRelative(BlockFace.UP);
|
||||
if (blockAbove.getType() == Material.SIGN || blockAbove.getType() == Material.SIGN_POST) {
|
||||
Sign sign = (Sign) blockAbove.getState();
|
||||
String[] lines = sign.getLines();
|
||||
if (lines.length < 1)
|
||||
continue;
|
||||
StringBuilder signText = new StringBuilder(lines[0]);
|
||||
if (lines.length >= 2)
|
||||
signText.append(" ").append(lines[1]);
|
||||
if (lines.length >= 3)
|
||||
signText.append(" ").append(lines[2]);
|
||||
if (lines.length >= 4)
|
||||
signText.append(" ").append(lines[3]);
|
||||
String dataPointName = signText.toString().trim();
|
||||
|
||||
List<Location> locations = dataPoints.getOrDefault(dataPointName, new ArrayList<>());
|
||||
locations.add(blockLocation);
|
||||
dataPoints.put(dataPointName, locations);
|
||||
|
||||
block.setType(Material.AIR);
|
||||
blockAbove.setType(Material.AIR);
|
||||
}
|
||||
} else if (ids.contains(block.getTypeId())) {
|
||||
String dataPointName = block.getType().name();
|
||||
List<Location> locations = dataPoints.getOrDefault(dataPointName, new ArrayList<>());
|
||||
locations.add(blockLocation);
|
||||
dataPoints.put(dataPointName, locations);
|
||||
}
|
||||
}
|
||||
z = -radius;
|
||||
}
|
||||
y = 0;
|
||||
}
|
||||
// Unloading the world
|
||||
Bukkit.unloadWorld(world, true);
|
||||
|
||||
File directory = world.getWorldFolder();
|
||||
|
||||
// Removing unnecessary files
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
String fileName = file.getName();
|
||||
if (fileName.equals("level.dat") || fileName.equals("region") || fileName.equals(MGZWorld.FILE_NAME))
|
||||
continue;
|
||||
FileUtils.deleteQuietly(file);
|
||||
}
|
||||
}
|
||||
// Saving properties file
|
||||
mgzWorld.setDataPoints(dataPoints);
|
||||
mgzWorld.save(new File(world.getWorldFolder(), MGZWorld.FILE_NAME));
|
||||
|
||||
// Zipping the parsed world
|
||||
File targetDirectory = new File(File.separator + "home" + File.separator + "minecraft" + File.separator + "upload" + File.separator +
|
||||
"maps" + File.separator + mgzWorld.getCategory().name());
|
||||
if (!targetDirectory.exists())
|
||||
targetDirectory.mkdirs();
|
||||
File targetFile = new File(targetDirectory, mgzWorld.getName().replaceAll(" ", "_") + ".zip");
|
||||
if (targetFile.exists())
|
||||
FileUtils.deleteQuietly(targetFile);
|
||||
ZipUtils.zip(directory.getPath(), targetFile.getPath());
|
||||
|
||||
// Deleting the parsed world
|
||||
FileUtils.deleteQuietly(directory);
|
||||
|
||||
// Marking the parse as complete
|
||||
completed = true;
|
||||
|
||||
// Announcing the parse summary
|
||||
long elapsed = System.currentTimeMillis() - this.started;
|
||||
String time;
|
||||
if (elapsed < 1000)
|
||||
time = elapsed + "ms";
|
||||
else time = (elapsed / 1000) + " seconds";
|
||||
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Parse summary of §b" + mgzWorld.getName() + "§7:"));
|
||||
Bukkit.broadcastMessage(Style.color(" §8- §7Blocks §f" + DoubleUtils.format(parsedBlocks, false)));
|
||||
if (!dataPoints.isEmpty()) {
|
||||
Bukkit.broadcastMessage(" §bData Points");
|
||||
for (Map.Entry<String, List<Location>> entry : dataPoints.entrySet())
|
||||
Bukkit.broadcastMessage(" §6" + entry.getKey() + " §f" + entry.getValue().size());
|
||||
}
|
||||
Bukkit.broadcastMessage(Style.color(" §8- §7Time §f" + time));
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package zone.themcgamer.buildServer.parse.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class ParseCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "parse", description = "Parse a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /parse <radius>"));
|
||||
return;
|
||||
}
|
||||
int radius = 0;
|
||||
try {
|
||||
radius = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
if (radius <= 0) {
|
||||
player.sendMessage(Style.error("Map", "§cCannot parse map with a radius of §b" + radius));
|
||||
return;
|
||||
}
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
if (mgzWorld == null) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot parse this map."));
|
||||
return;
|
||||
}
|
||||
if (!player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cOnly server operators can parse maps."));
|
||||
return;
|
||||
}
|
||||
List<Integer> ids = new ArrayList<>();
|
||||
for (String idString : Arrays.stream(args).skip(1).collect(Collectors.toList())) {
|
||||
int id;
|
||||
try {
|
||||
id = Integer.parseInt(idString);
|
||||
ids.add(id);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
worldManager.parse(mgzWorld, player.getLocation(), radius, ids);
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Map §b" + mgzWorld.getName() + " §7is now being parsed!" +
|
||||
(ids.isEmpty() ? "" : " §7(" + ids.size() + " ids)")));
|
||||
}
|
||||
}
|
@ -0,0 +1,413 @@
|
||||
package zone.themcgamer.buildServer.world;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.*;
|
||||
import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.weather.WeatherChangeEvent;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.buildServer.Build;
|
||||
import zone.themcgamer.buildServer.backup.BackupTask;
|
||||
import zone.themcgamer.buildServer.parse.ParseTask;
|
||||
import zone.themcgamer.buildServer.parse.command.ParseCommand;
|
||||
import zone.themcgamer.buildServer.world.command.*;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
import zone.themcgamer.core.world.WorldGenerator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "World Manager") @Getter
|
||||
public class WorldManager extends Module {
|
||||
private static final String[] permissions = new String[] {
|
||||
"gopaint.use",
|
||||
"gobrush.use",
|
||||
"minecraft.command.difficulty",
|
||||
"fawe.permpack.basic",
|
||||
"fawe.voxelbrush",
|
||||
"fawe.confirm",
|
||||
"worldedit.*",
|
||||
"voxelsniper.*",
|
||||
"builders.util.secretblocks",
|
||||
"builders.util.banner",
|
||||
"builders.util.color",
|
||||
"builders.util.noclip",
|
||||
"builders.util.nightvision",
|
||||
"builders.util.advancedfly",
|
||||
"builders.util.tpgm3",
|
||||
"headdb.open",
|
||||
"headdb.phead",
|
||||
"headdb.free.*",
|
||||
"headdb.category.*",
|
||||
"astools.*",
|
||||
"chars"
|
||||
};
|
||||
|
||||
private final List<MGZWorld> worlds = new ArrayList<>();
|
||||
private final List<ParseTask> parseTasks = new ArrayList<>();
|
||||
private final Map<Player, PermissionAttachment> permissionAttachments = new HashMap<>();
|
||||
|
||||
public WorldManager(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
for (World world : Bukkit.getWorlds())
|
||||
setupWorld(world);
|
||||
// Loading maps from the maps directory
|
||||
for (WorldCategory category : WorldCategory.values()) {
|
||||
File categoryDirectory = new File("maps" + File.separator + category.name());
|
||||
if (!categoryDirectory.exists())
|
||||
continue;
|
||||
File[] files = categoryDirectory.listFiles();
|
||||
if (files == null)
|
||||
continue;
|
||||
for (File directory : files) {
|
||||
if (!directory.isDirectory())
|
||||
continue;
|
||||
try {
|
||||
worlds.add(new MGZWorld(new File(directory, MGZWorld.FILE_NAME)));
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().severe("Failed to add world \"" + directory.getPath() + "\"");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Deleting worlds from the main directory
|
||||
for (MGZWorld world : worlds) {
|
||||
String worldName = world.getCategory().name() + "-" + world.getName();
|
||||
World bukkitWorld = Bukkit.getWorld(worldName);
|
||||
if (bukkitWorld != null)
|
||||
Bukkit.unloadWorld(bukkitWorld, true);
|
||||
File worldDirectory = new File(Bukkit.getWorldContainer(), worldName);
|
||||
if (!worldDirectory.exists())
|
||||
continue;
|
||||
File targetDirectory = new File("maps" + File.separator + world.getCategory().name() + File.separator + world.getName());
|
||||
if (!targetDirectory.exists())
|
||||
targetDirectory.mkdirs();
|
||||
copyWorld(worldDirectory, targetDirectory);
|
||||
FileUtils.deleteQuietly(worldDirectory);
|
||||
}
|
||||
// Creating the parse directory and deleting old parsed worlds
|
||||
File parseDirectory = new File("parse");
|
||||
if (!parseDirectory.exists())
|
||||
parseDirectory.mkdirs();
|
||||
else {
|
||||
File[] files = parseDirectory.listFiles();
|
||||
if (files != null) {
|
||||
for (File directory : files) {
|
||||
if (!directory.isDirectory())
|
||||
continue;
|
||||
FileUtils.deleteQuietly(directory);
|
||||
System.out.println("Deleted old parsed world: " + directory.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
MGZWorld mgzWorld = getWorld(world);
|
||||
if (mgzWorld != null) {
|
||||
if (world.getPlayers().isEmpty()) {
|
||||
setupWorld(world);
|
||||
Bukkit.unloadWorld(world, true);
|
||||
unloadWorld(mgzWorld);
|
||||
mgzWorld.setWorld(null);
|
||||
new BackupTask(plugin, mgzWorld);
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Unloading map §b" + mgzWorld.getName()));
|
||||
}
|
||||
}
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (player.isOp())
|
||||
continue;
|
||||
PermissionAttachment attachment = permissionAttachments.get(player);
|
||||
if (attachment == null)
|
||||
continue;
|
||||
for (String permission : permissions) {
|
||||
if (mgzWorld == null || (!mgzWorld.hasPrivileges(player))) {
|
||||
if (player.hasPermission(permission))
|
||||
attachment.setPermission(permission, false);
|
||||
} else if (mgzWorld.hasPrivileges(player) && !player.hasPermission(permission))
|
||||
attachment.setPermission(permission, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
parseTasks.removeIf(parseTask -> {
|
||||
if (parseTask.isCompleted())
|
||||
return true;
|
||||
parseTask.run();
|
||||
return false;
|
||||
});
|
||||
}, 0L, 1L);
|
||||
registerCommand(new CreateCommand(this));
|
||||
registerCommand(new MapsCommand(this));
|
||||
registerCommand(new MenuCommand());
|
||||
registerCommand(new MapCommand(this));
|
||||
registerCommand(new MapInfoCommand(this));
|
||||
registerCommand(new AdminCommand(this));
|
||||
registerCommand(new AuthorCommand(this));
|
||||
registerCommand(new RenameCommand(this));
|
||||
registerCommand(new CategoryCommand(this));
|
||||
registerCommand(new SaveCommand(this));
|
||||
registerCommand(new DeleteCommand(this));
|
||||
registerCommand(new ReloadWorldEditCommand());
|
||||
|
||||
registerCommand(new ParseCommand(this));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
permissionAttachments.put(player, event.getPlayer().addAttachment(getPlugin()));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlace(BlockPlaceEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
MGZWorld world = getWorld(player.getWorld());
|
||||
if ((world == null && !player.isOp()) || (world != null && (!world.hasPrivileges(player))))
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBreak(BlockBreakEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
MGZWorld world = getWorld(player.getWorld());
|
||||
if ((world == null && !player.isOp()) || (world != null && (!world.hasPrivileges(player))))
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onInteract(PlayerInteractEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
MGZWorld world = getWorld(player.getWorld());
|
||||
if ((world == null && !player.isOp()) || (world != null && (!world.hasPrivileges(player))))
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onEntitySpawn(EntitySpawnEvent event) {
|
||||
if (event.getEntityType() != EntityType.ARMOR_STAND)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onIgnite(BlockIgniteEvent event) {
|
||||
BlockIgniteEvent.IgniteCause cause = event.getCause();
|
||||
if (cause == BlockIgniteEvent.IgniteCause.LAVA || cause == BlockIgniteEvent.IgniteCause.SPREAD)
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBurn(BlockBurnEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onSpread(BlockSpreadEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBlockFade(BlockFadeEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onLeafDecay(LeavesDecayEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onBlockForm(BlockFormEvent event) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onWeatherChange(WeatherChangeEvent event) {
|
||||
if (event.toWeatherState())
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onWorldChange(PlayerChangedWorldEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
MGZWorld toWorld = getWorld(player.getWorld());
|
||||
if (toWorld != null) {
|
||||
player.chat("/mapinfo");
|
||||
if (!toWorld.hasPrivileges(player)) {
|
||||
player.sendMessage(Style.error("Maps", "You do not have privileges to build on this map, " +
|
||||
"please contact the map author to request build privileges!"));
|
||||
}
|
||||
}
|
||||
DeleteCommand.getConfirmDelete().remove(player);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
permissionAttachments.remove(player);
|
||||
DeleteCommand.getConfirmDelete().remove(player);
|
||||
}
|
||||
|
||||
public void loadWorld(MGZWorld world) {
|
||||
if (world == null)
|
||||
return;
|
||||
File target = new File(Bukkit.getWorldContainer(),world.getCategory().name() + "-" + world.getName());
|
||||
copyWorld(new File("maps" + File.separator + world.getCategory().name() + File.separator + world.getName()), target);
|
||||
world.setDataFile(new File(target, MGZWorld.FILE_NAME));
|
||||
}
|
||||
|
||||
public void unloadWorld(MGZWorld world) {
|
||||
if (world == null)
|
||||
return;
|
||||
File target = new File("maps" + File.separator + world.getCategory().name() + File.separator + world.getName());
|
||||
copyWorld(world.getWorld().getWorldFolder(), target);
|
||||
FileUtils.deleteQuietly(world.getWorld().getWorldFolder());
|
||||
world.setDataFile(new File(target, MGZWorld.FILE_NAME));
|
||||
}
|
||||
|
||||
public boolean isIllegalName(String name) {
|
||||
File worldContainer = Bukkit.getWorldContainer();
|
||||
File[] files = worldContainer.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.getName().equalsIgnoreCase(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public World create(String name, String author, WorldCategory category, WorldGenerator generator, String preset) {
|
||||
name = name.replaceAll(" ", "_");
|
||||
MGZWorld mgzWorld = getWorld(name, category);
|
||||
if (mgzWorld != null)
|
||||
throw new IllegalArgumentException("Map with name \"" + name + "\" already exists under category " + category.name());
|
||||
if (preset == null)
|
||||
preset = generator.getPreset();
|
||||
if (preset == null)
|
||||
throw new IllegalArgumentException("Preset is null for generator type " + generator.name());
|
||||
|
||||
// Setting up the vanilla world
|
||||
World world = getWorldCreator(category.name() + "-" + name, preset).createWorld();
|
||||
setupWorld(world);
|
||||
world.save();
|
||||
|
||||
// Creating the MGZWorld
|
||||
File file = new File(world.getWorldFolder(), MGZWorld.FILE_NAME);
|
||||
mgzWorld = new MGZWorld(world, file, name, author, preset, category);
|
||||
mgzWorld.save();
|
||||
worlds.add(mgzWorld);
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public MGZWorld getWorld(World world) {
|
||||
List<MGZWorld> worlds = lookup(mgzWorld -> mgzWorld.getWorld() != null && (mgzWorld.getWorld().getName().equals(world.getName())));
|
||||
if (worlds.isEmpty())
|
||||
return null;
|
||||
return worlds.get(0);
|
||||
}
|
||||
|
||||
public MGZWorld getWorld(String name, WorldCategory category) {
|
||||
List<MGZWorld> worlds = lookup(mgzWorld -> mgzWorld.getName().equalsIgnoreCase(name) && mgzWorld.getCategory() == category);
|
||||
if (worlds.isEmpty())
|
||||
return null;
|
||||
return worlds.get(0);
|
||||
}
|
||||
|
||||
public List<MGZWorld> getWorld(String name) {
|
||||
return lookup(world -> world.getName().equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
public List<MGZWorld> lookup(Predicate<MGZWorld> predicate) {
|
||||
return worlds.stream().filter(predicate).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void parse(MGZWorld mgzWorld, Location center, int radius, List<Integer> ids) {
|
||||
if (beingParsed(mgzWorld))
|
||||
throw new IllegalStateException("World \"" + mgzWorld.getName() + "\" is already being parsed");
|
||||
World world = mgzWorld.getWorld();
|
||||
for (Player worldPlayer : world.getPlayers())
|
||||
worldPlayer.teleport(Build.INSTANCE.getMainWorld().getSpawnLocation());
|
||||
Bukkit.unloadWorld(world, true);
|
||||
unloadWorld(mgzWorld);
|
||||
mgzWorld.setWorld(null);
|
||||
|
||||
ParseTask parseTask = new ParseTask(mgzWorld, radius, ids);
|
||||
parseTasks.add(parseTask);
|
||||
|
||||
try {
|
||||
File parseWorldDirectory = new File("parse" + File.separator + mgzWorld.getCategory().name() + File.separator +
|
||||
mgzWorld.getName().replaceAll(" ", "_"));
|
||||
FileUtils.copyDirectory(new File("maps" + File.separator + mgzWorld.getCategory().name() + File.separator + mgzWorld.getName()), parseWorldDirectory);
|
||||
World parseWorld = getWorldCreator(parseWorldDirectory.getPath(), mgzWorld.getPreset()).createWorld();
|
||||
setupWorld(parseWorld);
|
||||
center.setWorld(parseWorld);
|
||||
parseTask.start(parseWorld, center);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean beingParsed(MGZWorld world) {
|
||||
return parseTasks.stream().anyMatch(parseTask -> parseTask.getMgzWorld().equals(world));
|
||||
}
|
||||
|
||||
public void copyWorld(File oldDirectory, File newDirectory) {
|
||||
try {
|
||||
FileUtils.copyDirectory(oldDirectory, new File(newDirectory.getPath().replaceAll(" ", "_")));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public WorldCreator getWorldCreator(String name, String preset) {
|
||||
WorldCreator creator = new WorldCreator(name);
|
||||
creator.environment(World.Environment.NORMAL);
|
||||
creator.type(WorldType.FLAT);
|
||||
if (preset != null)
|
||||
creator.generatorSettings(preset);
|
||||
creator.generateStructures(false);
|
||||
return creator;
|
||||
}
|
||||
|
||||
public void setupWorld(World world) {
|
||||
long time = 6000L;
|
||||
if (world.getName().toLowerCase().contains("christmas"))
|
||||
time = 12000L;
|
||||
else if (world.getName().toLowerCase().contains("halloween"))
|
||||
time = 17000L;
|
||||
world.setTime(time);
|
||||
world.setThundering(false);
|
||||
world.setStorm(false);
|
||||
world.setSpawnLocation(0, 150, 0);
|
||||
world.setGameRuleValue("randomTickSpeed", "0");
|
||||
world.setGameRuleValue("doDaylightCycle", "false");
|
||||
world.setGameRuleValue("showDeathMessages", "false");
|
||||
world.setGameRuleValue("doFireTick", "false");
|
||||
world.setGameRuleValue("mobGriefing", "false");
|
||||
world.setGameRuleValue("doMobLoot", "false");
|
||||
world.setGameRuleValue("doMobSpawning", "false");
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AdminCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "admin", description = "Add or remove an admin from a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /admin <player>"));
|
||||
return;
|
||||
}
|
||||
MGZWorld world = worldManager.getWorld(player.getWorld());
|
||||
if (world == null) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot modify the admins of this map."));
|
||||
return;
|
||||
}
|
||||
if (!world.getOriginalCreator().equals(player.getName()) && !player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot modify the admins of this map."));
|
||||
return;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase(world.getOriginalCreator())) {
|
||||
player.sendMessage(Style.error("Map", "§cThe original author can't have their admin privileges modified."));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(world)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, changing the admins list has been disabled"));
|
||||
return;
|
||||
}
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (world.getAdmins().remove(args[0].toLowerCase())) {
|
||||
player.sendMessage(Style.main("Map", "§b" + args[0] + " §7is no-longer an admin on §6" + world.getName()));
|
||||
if (target != null && (!player.equals(target)))
|
||||
target.sendMessage(Style.main("Map", "§cYour admin privileges on §b" + world.getName() + " §cwere removed"));
|
||||
} else {
|
||||
world.getAdmins().add(args[0].toLowerCase());
|
||||
player.sendMessage(Style.main("Map", "§b" + args[0] + " §7is now an admin on §6" + world.getName()));
|
||||
if (target != null && (!player.equals(target)))
|
||||
target.sendMessage(Style.main("Map", "§aYou were given admin privileges on §b" + world.getName()));
|
||||
}
|
||||
world.save();
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AuthorCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "author", description = "Set the author of a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /author <author>"));
|
||||
return;
|
||||
}
|
||||
MGZWorld world = worldManager.getWorld(player.getWorld());
|
||||
if (world == null) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot update the author of this map."));
|
||||
return;
|
||||
}
|
||||
if (!world.getOriginalCreator().equals(player.getName()) && !player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot update the author of this map."));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(world)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, changing the author has been disabled"));
|
||||
return;
|
||||
}
|
||||
world.setAuthor(String.join(" ", args));
|
||||
world.save();
|
||||
player.sendMessage(Style.main("Map", "Map author set to §b" + world.getAuthor()));
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.Build;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class CategoryCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "category", description = "Set the category of a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /category <category>"));
|
||||
return;
|
||||
}
|
||||
WorldCategory category = WorldCategory.lookup(String.join(" ", args));
|
||||
if (category == null) {
|
||||
player.sendMessage(Style.error("Map", "§cInvalid category: §f" + Arrays.stream(WorldCategory.values())
|
||||
.map(WorldCategory::getName).collect(Collectors.joining("§7, §f"))));
|
||||
return;
|
||||
}
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
World world;
|
||||
if (mgzWorld == null || ((world = mgzWorld.getWorld()) == null)) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot update the category of this map."));
|
||||
return;
|
||||
}
|
||||
if (!mgzWorld.getOriginalCreator().equals(player.getName()) && !player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot update the category of this map."));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(mgzWorld)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, changing the category has been disabled"));
|
||||
return;
|
||||
}
|
||||
if (worldManager.getWorld(mgzWorld.getName(), category) != null) {
|
||||
player.sendMessage(Style.error("Map", "§cThere is already a map with that category"));
|
||||
return;
|
||||
}
|
||||
for (Player worldPlayer : world.getPlayers()) {
|
||||
worldPlayer.teleport(Build.INSTANCE.getMainWorld().getSpawnLocation());
|
||||
worldPlayer.sendMessage(Style.main("Map", "Map category set to §b" + category.getName()));
|
||||
}
|
||||
try {
|
||||
Bukkit.unloadWorld(world, true);
|
||||
mgzWorld.setWorld(null);
|
||||
|
||||
WorldCategory oldCategory = mgzWorld.getCategory();
|
||||
mgzWorld.setCategory(category);
|
||||
mgzWorld.save();
|
||||
|
||||
File newDirectory = new File("maps" + File.separator + category.name() + File.separator + mgzWorld.getName());
|
||||
FileUtils.moveDirectory(new File(oldCategory.name() + "-" + mgzWorld.getName()), newDirectory);
|
||||
mgzWorld.setDataFile(new File(newDirectory, MGZWorld.FILE_NAME));
|
||||
FileUtils.deleteQuietly(new File("maps" + File.separator + oldCategory.name() + File.separator + mgzWorld.getName()));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
import zone.themcgamer.core.world.WorldGenerator;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class CreateCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "create", description = "Create a new map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /create <name> [generator|-v]"));
|
||||
player.sendMessage(Style.main("Map", "§6generator §7= A custom generator, see here: https://www.minecraft101.net/superflat/"));
|
||||
player.sendMessage(Style.main("Map", "§6-v §7= void"));
|
||||
return;
|
||||
}
|
||||
MGZWorld mgzWorld = worldManager.getWorld(args[0], WorldCategory.OTHER);
|
||||
if (mgzWorld != null) {
|
||||
player.sendMessage(Style.error("Map", "§cThere is already a map with that name"));
|
||||
return;
|
||||
}
|
||||
String name = args[0];
|
||||
if (worldManager.isIllegalName(name)) {
|
||||
player.sendMessage(Style.error("Map", "§cIllegal map name!"));
|
||||
return;
|
||||
}
|
||||
WorldGenerator generator = WorldGenerator.FLAT;
|
||||
String preset = null;
|
||||
if (args.length >= 2) {
|
||||
if (args[1].equalsIgnoreCase("-v"))
|
||||
generator = WorldGenerator.VOID;
|
||||
else {
|
||||
generator = WorldGenerator.CUSTOM;
|
||||
preset = args[1].toLowerCase();
|
||||
}
|
||||
}
|
||||
World world = worldManager.create(name, player.getName(), WorldCategory.OTHER, generator, preset);
|
||||
player.teleport(world.getSpawnLocation());
|
||||
player.sendMessage(Style.main("Map", "Created a new map named §b" + name + "§7!"));
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.Build;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DeleteCommand {
|
||||
@Getter private static final List<Player> confirmDelete = new ArrayList<>();
|
||||
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "delete", description = "Delete a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
World world;
|
||||
if (mgzWorld == null || ((world = mgzWorld.getWorld()) == null)) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot delete this map."));
|
||||
return;
|
||||
}
|
||||
if (!mgzWorld.getOriginalCreator().equals(player.getName()) && !player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot delete this map."));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(mgzWorld)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, deleting has been disabled"));
|
||||
return;
|
||||
}
|
||||
if (confirmDelete.remove(player)) {
|
||||
for (Player worldPlayer : world.getPlayers()) {
|
||||
worldPlayer.teleport(Build.INSTANCE.getMainWorld().getSpawnLocation());
|
||||
worldPlayer.sendMessage(Style.main("Map", "Map §b" + mgzWorld.getName() + " §7was deleted"));
|
||||
}
|
||||
Bukkit.unloadWorld(world, true);
|
||||
FileUtils.deleteQuietly(world.getWorldFolder());
|
||||
mgzWorld.setWorld(null);
|
||||
worldManager.getWorlds().remove(mgzWorld);
|
||||
FileUtils.deleteQuietly(new File("maps" + File.separator + mgzWorld.getCategory().name() + File.separator + mgzWorld.getName()));
|
||||
} else {
|
||||
confirmDelete.add(player);
|
||||
player.sendMessage(Style.main("Map", "Execute this command again to confirm deletion of map §b" + mgzWorld.getName()));
|
||||
player.sendMessage(Style.main("Map", "§c§lNOTE §f- §7This action CANNOT be undone"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.common.EnumUtils;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.command.TabComplete;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MapCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "map", description = "Teleport to a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /map <name> [category]"));
|
||||
return;
|
||||
}
|
||||
List<MGZWorld> worlds = worldManager.getWorld(args[0]);
|
||||
if (worlds.isEmpty()) {
|
||||
player.sendMessage(Style.error("Map", "§cThere is no map with that name"));
|
||||
return;
|
||||
}
|
||||
MGZWorld world;
|
||||
if (worlds.size() == 1)
|
||||
world = worlds.get(0);
|
||||
else {
|
||||
WorldCategory category = null;
|
||||
if (args.length >= 2)
|
||||
category = EnumUtils.fromString(WorldCategory.class, args[1].toUpperCase());
|
||||
if (category == null) {
|
||||
player.sendMessage(Style.error("Map", "§cYou either didn't specify a category, or the given category is incorrect!"));
|
||||
return;
|
||||
}
|
||||
world = worldManager.getWorld(args[0], category);
|
||||
}
|
||||
if (world == null) {
|
||||
player.sendMessage(Style.error("Map", "§cFailed to locate world, please wait a few moments and try again"));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(world)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, teleportation has been disabled"));
|
||||
return;
|
||||
}
|
||||
if (world.getWorld() == null) {
|
||||
worldManager.loadWorld(world);
|
||||
world.setWorld(worldManager.getWorldCreator(world.getCategory().name() + "-" + world.getName(), world.getPreset()).createWorld());
|
||||
worldManager.setupWorld(world.getWorld());
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Loaded world §b" + world.getName()));
|
||||
}
|
||||
player.teleport(world.getWorld().getSpawnLocation());
|
||||
player.sendMessage(Style.main("Map", "Teleported to §b" + world.getName()));
|
||||
}
|
||||
|
||||
@TabComplete(name = "map")
|
||||
public List<String> onTab(CommandProvider command) {
|
||||
return worldManager.getWorlds().stream().map(MGZWorld::getName).collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MapInfoCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "mapinfo", description = "View information about a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
MGZWorld world = worldManager.getWorld(player.getWorld());
|
||||
if (world == null) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot view information for this map."));
|
||||
return;
|
||||
}
|
||||
player.sendMessage(Style.main("Map", "Information for §b" + world.getName() + "§7:"));
|
||||
player.sendMessage(" §8- §7Author §f" + world.getAuthor() + (world.getAuthor().equals(world.getOriginalCreator()) ? "" : " §7(original: " + world.getOriginalCreator() + ")"));
|
||||
player.sendMessage(" §8- §7Preset §f" + world.getPreset());
|
||||
player.sendMessage(" §8- §7Category §f" + world.getCategory().getName());
|
||||
player.sendMessage(" §8- §7Admins §f" + (world.getAdmins().isEmpty() ? "None" : String.join("§7, §f", world.getAdmins())));
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MapsCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "maps", description = "List all maps", ranks = { Rank.BUILDER })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
Map<WorldCategory, List<MGZWorld>> worldsMap = new HashMap<>();
|
||||
for (MGZWorld world : worldManager.getWorlds()) {
|
||||
List<MGZWorld> worlds = worldsMap.getOrDefault(world.getCategory(), new ArrayList<>());
|
||||
worlds.add(world);
|
||||
worldsMap.put(world.getCategory(), worlds);
|
||||
}
|
||||
if (worldsMap.isEmpty()) {
|
||||
sender.sendMessage(Style.main("Map", "There are no maps to view."));
|
||||
return;
|
||||
}
|
||||
int maps = 0;
|
||||
for (List<MGZWorld> list : worldsMap.values())
|
||||
maps+= list.size();
|
||||
sender.sendMessage(Style.main("Maps", "Showing §b" + maps + " §7maps"));
|
||||
for (Map.Entry<WorldCategory, List<MGZWorld>> entry : worldsMap.entrySet()) {
|
||||
sender.sendMessage(Style.color("&6" + entry.getKey().getName() + " &8» &f" +
|
||||
entry.getValue().stream().map(MGZWorld::getName).collect(Collectors.joining("§7, §f"))));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import zone.themcgamer.buildServer.world.menu.BuildManagerMenu;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
public class MenuCommand {
|
||||
@Command(name = "menu", aliases = { "gui", "buildmanager", "bm" } , description = "Open the build management menu.",
|
||||
ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
new BuildManagerMenu(command.getPlayer()).open();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ReloadWorldEditCommand {
|
||||
@Command(name = "reloadworldedit", aliases = { "reloadwe", "rwe" }, description = "Reload WorldEdit", ranks = { Rank.BUILDER })
|
||||
public void onCommand(CommandProvider command) {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit");
|
||||
plugin.onDisable();
|
||||
plugin.onEnable();
|
||||
Bukkit.broadcastMessage(Style.main("Map", "WorldEdit was reloaded"));
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.Build;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class RenameCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "name", description = "Rename a map", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
player.sendMessage(Style.main("Map", "Usage: /name <name>"));
|
||||
return;
|
||||
}
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
World world;
|
||||
if (mgzWorld == null || ((world = mgzWorld.getWorld()) == null)) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot rename this map."));
|
||||
return;
|
||||
}
|
||||
if (!mgzWorld.getOriginalCreator().equals(player.getName()) && !player.isOp()) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot rename this map."));
|
||||
return;
|
||||
}
|
||||
String mapName = args[0];
|
||||
if (worldManager.isIllegalName(mapName)) {
|
||||
player.sendMessage(Style.error("Map", "§cIllegal map name!"));
|
||||
return;
|
||||
}
|
||||
if (worldManager.beingParsed(mgzWorld)) {
|
||||
player.sendMessage(Style.error("Map", "§cThis map is currently being parsed, changing the name has been disabled"));
|
||||
return;
|
||||
}
|
||||
String name = String.join(" ", args).replaceAll(" ", "_");
|
||||
if (worldManager.isIllegalName(name)) {
|
||||
player.sendMessage(Style.error("Map", "§cIllegal map name!"));
|
||||
return;
|
||||
}
|
||||
if (worldManager.getWorld(name, mgzWorld.getCategory()) != null) {
|
||||
player.sendMessage(Style.error("Map", "§cThere is already a map with that name"));
|
||||
return;
|
||||
}
|
||||
for (Player worldPlayer : world.getPlayers()) {
|
||||
worldPlayer.teleport(Build.INSTANCE.getMainWorld().getSpawnLocation());
|
||||
worldPlayer.sendMessage(Style.main("Map", "Map name set to §b" + name));
|
||||
}
|
||||
try {
|
||||
Bukkit.unloadWorld(world, true);
|
||||
mgzWorld.setWorld(null);
|
||||
|
||||
String oldName = mgzWorld.getName();
|
||||
mgzWorld.setName(name);
|
||||
mgzWorld.save();
|
||||
|
||||
File newDirectory = new File("maps" + File.separator + mgzWorld.getCategory().name() + File.separator + name);
|
||||
FileUtils.moveDirectory(new File(mgzWorld.getCategory().name() + "-" + oldName), newDirectory);
|
||||
FileUtils.deleteQuietly(new File("maps" + File.separator + mgzWorld.getCategory().name() + File.separator + oldName));
|
||||
mgzWorld.setDataFile(new File(newDirectory, MGZWorld.FILE_NAME));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package zone.themcgamer.buildServer.world.command;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SaveCommand {
|
||||
private final WorldManager worldManager;
|
||||
|
||||
@Command(name = "save", aliases = { "saveworld", "sw" }, description = "Save your world", ranks = { Rank.BUILDER }, playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
player.getWorld().save();
|
||||
|
||||
MGZWorld mgzWorld = worldManager.getWorld(player.getWorld());
|
||||
if (mgzWorld == null)
|
||||
return;
|
||||
|
||||
if (!mgzWorld.hasPrivileges(player)) {
|
||||
player.sendMessage(Style.error("Map", "§cYou cannot save this map."));
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.broadcastMessage(Style.main("Map", "Saved the map &b" + mgzWorld.getName() + "&7!"));
|
||||
worldManager.copyWorld(player.getWorld().getWorldFolder(), new File("maps" + File.separator + mgzWorld.getCategory().name() + File.separator + mgzWorld.getName()));
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package zone.themcgamer.buildServer.world.menu;
|
||||
|
||||
import com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.Menu;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
|
||||
public class BuildManagerMenu extends Menu {
|
||||
public BuildManagerMenu(Player player) {
|
||||
super(player, "Build Manager", 1, MenuType.CHEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpen() {
|
||||
set(0, new Button(new ItemBuilder(XMaterial.LEAD)
|
||||
.setName("§6My Maps").setLore(
|
||||
"",
|
||||
"§7Click to view the worlds you have access to"
|
||||
).toItemStack(), event -> {
|
||||
new MapsMenu(player, null).open();
|
||||
}));
|
||||
|
||||
set(1, new Button(new ItemBuilder(XMaterial.FILLED_MAP)
|
||||
.setName("§6All Maps").setLore(
|
||||
"",
|
||||
"§7Click to view all maps"
|
||||
).toItemStack(), event -> {
|
||||
new MapsCategoryMenu(player).open();
|
||||
}));
|
||||
|
||||
MGZWorld world;
|
||||
WorldManager worldManager = Module.getModule(WorldManager.class);
|
||||
if (worldManager == null || ((world = worldManager.getWorld(player.getWorld())) == null || !world.hasPrivileges(player)))
|
||||
return;
|
||||
set(8, new Button(new ItemBuilder(world.getCategory().getIcon())
|
||||
.setName("§6" + world.getName()).setLore(
|
||||
"",
|
||||
"§7Click to manage your map!"
|
||||
).toItemStack()));
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package zone.themcgamer.buildServer.world.menu;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.Menu;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
|
||||
public class MapsCategoryMenu extends Menu {
|
||||
public MapsCategoryMenu(Player player) {
|
||||
super(player, "Select Category", 3, MenuType.CHEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpen() {
|
||||
WorldManager worldManager = Module.getModule(WorldManager.class);
|
||||
if (worldManager == null)
|
||||
return;
|
||||
for (WorldCategory worldCategory : WorldCategory.values()) {
|
||||
long worlds = worldManager.getWorlds().stream().filter(world -> world.getCategory() == worldCategory).count();
|
||||
add(new Button(new ItemBuilder(worldCategory.getIcon())
|
||||
.setName("§b§l" + worldCategory.getName())
|
||||
.setLore(
|
||||
"§6" + worlds + " §7map" + (worlds == 1 ? "" : "s") + " in this category!",
|
||||
"",
|
||||
"§aClick to view this category."
|
||||
).toItemStack(), event -> {
|
||||
if (worlds <= 0L)
|
||||
return;
|
||||
new MapsMenu(player, worldCategory).open();
|
||||
}));
|
||||
}
|
||||
set(2, 0, new Button(new ItemBuilder(Material.ARROW)
|
||||
.setName("§c« Go Back").toItemStack(), event -> new BuildManagerMenu(player).open()));
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package zone.themcgamer.buildServer.world.menu;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import zone.themcgamer.buildServer.world.WorldManager;
|
||||
import zone.themcgamer.core.common.ItemBuilder;
|
||||
import zone.themcgamer.core.common.menu.Button;
|
||||
import zone.themcgamer.core.common.menu.Menu;
|
||||
import zone.themcgamer.core.common.menu.MenuType;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.world.MGZWorld;
|
||||
import zone.themcgamer.core.world.WorldCategory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MapsMenu extends Menu {
|
||||
private final WorldCategory category;
|
||||
|
||||
public MapsMenu(Player player, @Nullable WorldCategory category) {
|
||||
super(player, category == null ? "My Maps" : "Maps - " + category.getName(), 6, MenuType.CHEST);
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpen() {
|
||||
WorldManager worldManager = Module.getModule(WorldManager.class);
|
||||
if (worldManager == null)
|
||||
return;
|
||||
List<MGZWorld> worlds = worldManager.getWorlds().stream()
|
||||
.filter(world -> category == null ? world.hasPrivileges(player) : world.getCategory() == category)
|
||||
.sorted((a, b) -> Boolean.compare(a.getOriginalCreator().equals(player.getName()), b.getOriginalCreator().equals(player.getName())))
|
||||
.collect(Collectors.toList());
|
||||
for (MGZWorld world : worlds) {
|
||||
add(new Button(new ItemBuilder(world.getCategory().getIcon())
|
||||
.setName("§a" + world.getName() + (world.hasPrivileges(player) ? " §c(Access)" : "")).setLore(
|
||||
"",
|
||||
" §8- §7Author §f" + world.getAuthor() + (world.getAuthor().equals(world.getOriginalCreator()) ? "" : " §7(original: " + world.getOriginalCreator() + ")"),
|
||||
" §8- §7Preset §f" + world.getPreset(),
|
||||
" §8- §7Category §f" + world.getCategory().getName(),
|
||||
" §8- §7Admins §f" + (world.getAdmins().isEmpty() ? "None" : String.join("§7, §f", world.getAdmins())),
|
||||
"",
|
||||
"§aClick to teleport to this map."
|
||||
).toItemStack(), event -> {
|
||||
player.chat(String.format("/map %s %s", world.getName(), world.getCategory().name()));
|
||||
}));
|
||||
}
|
||||
set(5, 0, new Button(new ItemBuilder(Material.ARROW)
|
||||
.setName("§c« Go Back").toItemStack(), event -> {
|
||||
if (category == null)
|
||||
new BuildManagerMenu(player).open();
|
||||
else new MapsCategoryMenu(player).open();
|
||||
}));
|
||||
}
|
||||
}
|
5
buildserver/src/main/resources/plugin.yml
Normal file
5
buildserver/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,5 @@
|
||||
name: Build
|
||||
version: 1.0-SNAPSHOT
|
||||
api-version: 1.13
|
||||
main: zone.themcgamer.buildServer.Build
|
||||
author: MGZ Development Team
|
1
commons/build.gradle.kts
Normal file
1
commons/build.gradle.kts
Normal file
@ -0,0 +1 @@
|
||||
dependencies {}
|
52
commons/src/main/java/zone/themcgamer/common/BuildData.java
Normal file
52
commons/src/main/java/zone/themcgamer/common/BuildData.java
Normal file
@ -0,0 +1,52 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote This class holds data for the current build
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public class BuildData {
|
||||
@Getter private static BuildData build;
|
||||
|
||||
static {
|
||||
InputStream inputStream = BuildData.class.getClassLoader().getResourceAsStream("git.properties");
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(inputStream);
|
||||
build = new BuildData(
|
||||
properties.getProperty("git.branch"),
|
||||
properties.getProperty("git.build.host"),
|
||||
properties.getProperty("git.build.user.email"),
|
||||
properties.getProperty("git.build.user.name"),
|
||||
properties.getProperty("git.build.version"),
|
||||
properties.getProperty("git.commit.id"),
|
||||
properties.getProperty("git.commit.id.abbrev"),
|
||||
properties.getProperty("git.commit.message.full"),
|
||||
properties.getProperty("git.commit.message.short"),
|
||||
properties.getProperty("git.commit.time"),
|
||||
properties.getProperty("insane_module")
|
||||
);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String branch, host, email, username, version, commitId, commitIdAbbreviation,
|
||||
commitMessageFull, commitMessageShort, time, module;
|
||||
}
|
158
commons/src/main/java/zone/themcgamer/common/CpuMonitor.java
Normal file
158
commons/src/main/java/zone/themcgamer/common/CpuMonitor.java
Normal file
@ -0,0 +1,158 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import javax.management.JMX;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Lucko
|
||||
* credits = https://github.com/lucko/spark
|
||||
*/
|
||||
public enum CpuMonitor {
|
||||
;
|
||||
|
||||
/** The object name of the com.sun.management.OperatingSystemMXBean */
|
||||
private static final String OPERATING_SYSTEM_BEAN = "java.lang:type=OperatingSystem";
|
||||
/** The OperatingSystemMXBean instance */
|
||||
private static final OperatingSystemMXBean BEAN;
|
||||
/** The executor used to monitor & calculate rolling averages. */
|
||||
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(r);
|
||||
thread.setName("core-cpu-monitor");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
// Rolling averages for system/process data
|
||||
private static final RollingAverage SYSTEM_AVERAGE_10_SEC = new RollingAverage(10);
|
||||
private static final RollingAverage SYSTEM_AVERAGE_1_MIN = new RollingAverage(60);
|
||||
private static final RollingAverage SYSTEM_AVERAGE_15_MIN = new RollingAverage(60 * 15);
|
||||
private static final RollingAverage PROCESS_AVERAGE_10_SEC = new RollingAverage(10);
|
||||
private static final RollingAverage PROCESS_AVERAGE_1_MIN = new RollingAverage(60);
|
||||
private static final RollingAverage PROCESS_AVERAGE_15_MIN = new RollingAverage(60 * 15);
|
||||
|
||||
static {
|
||||
try {
|
||||
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
ObjectName diagnosticBeanName = ObjectName.getInstance(OPERATING_SYSTEM_BEAN);
|
||||
BEAN = JMX.newMXBeanProxy(beanServer, diagnosticBeanName, OperatingSystemMXBean.class);
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("OperatingSystemMXBean is not supported by the system", e);
|
||||
}
|
||||
|
||||
// schedule rolling average calculations.
|
||||
EXECUTOR.scheduleAtFixedRate(new RollingAverageCollectionTask(), 1, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the static initializer has been called.
|
||||
*/
|
||||
@SuppressWarnings("EmptyMethod")
|
||||
public static void ensureMonitoring() {
|
||||
// intentionally empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "recent cpu usage" for the whole system. This value is a
|
||||
* double in the [0.0,1.0] interval. A value of 0.0 means that all CPUs
|
||||
* were idle during the recent period of time observed, while a value
|
||||
* of 1.0 means that all CPUs were actively running 100% of the time
|
||||
* during the recent period being observed. All values betweens 0.0 and
|
||||
* 1.0 are possible depending of the activities going on in the system.
|
||||
* If the system recent cpu usage is not available, the method returns a
|
||||
* negative value.
|
||||
*
|
||||
* @return the "recent cpu usage" for the whole system; a negative
|
||||
* value if not available.
|
||||
*/
|
||||
public static double systemLoad() {
|
||||
return BEAN.getSystemCpuLoad();
|
||||
}
|
||||
|
||||
public static double systemLoad10SecAvg() {
|
||||
return SYSTEM_AVERAGE_10_SEC.getAverage();
|
||||
}
|
||||
|
||||
public static double systemLoad1MinAvg() {
|
||||
return SYSTEM_AVERAGE_1_MIN.getAverage();
|
||||
}
|
||||
|
||||
public static double systemLoad15MinAvg() {
|
||||
return SYSTEM_AVERAGE_15_MIN.getAverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "recent cpu usage" for the Java Virtual Machine process.
|
||||
* This value is a double in the [0.0,1.0] interval. A value of 0.0 means
|
||||
* that none of the CPUs were running threads from the JVM process during
|
||||
* the recent period of time observed, while a value of 1.0 means that all
|
||||
* CPUs were actively running threads from the JVM 100% of the time
|
||||
* during the recent period being observed. Threads from the JVM include
|
||||
* the application threads as well as the JVM internal threads. All values
|
||||
* betweens 0.0 and 1.0 are possible depending of the activities going on
|
||||
* in the JVM process and the whole system. If the Java Virtual Machine
|
||||
* recent CPU usage is not available, the method returns a negative value.
|
||||
*
|
||||
* @return the "recent cpu usage" for the Java Virtual Machine process;
|
||||
* a negative value if not available.
|
||||
*/
|
||||
public static double processLoad() {
|
||||
return BEAN.getProcessCpuLoad();
|
||||
}
|
||||
|
||||
public static double processLoad10SecAvg() {
|
||||
return PROCESS_AVERAGE_10_SEC.getAverage();
|
||||
}
|
||||
|
||||
public static double processLoad1MinAvg() {
|
||||
return PROCESS_AVERAGE_1_MIN.getAverage();
|
||||
}
|
||||
|
||||
public static double processLoad15MinAvg() {
|
||||
return PROCESS_AVERAGE_15_MIN.getAverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task to poll CPU loads and add to the rolling averages in the enclosing class.
|
||||
*/
|
||||
private static final class RollingAverageCollectionTask implements Runnable {
|
||||
private final RollingAverage[] systemAverages = new RollingAverage[]{
|
||||
SYSTEM_AVERAGE_10_SEC,
|
||||
SYSTEM_AVERAGE_1_MIN,
|
||||
SYSTEM_AVERAGE_15_MIN
|
||||
};
|
||||
private final RollingAverage[] processAverages = new RollingAverage[]{
|
||||
PROCESS_AVERAGE_10_SEC,
|
||||
PROCESS_AVERAGE_1_MIN,
|
||||
PROCESS_AVERAGE_15_MIN
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
BigDecimal systemCpuLoad = new BigDecimal(systemLoad());
|
||||
BigDecimal processCpuLoad = new BigDecimal(processLoad());
|
||||
|
||||
if (systemCpuLoad.signum() != -1) { // if value is not negative
|
||||
for (RollingAverage average : this.systemAverages) {
|
||||
average.add(systemCpuLoad);
|
||||
}
|
||||
}
|
||||
|
||||
if (processCpuLoad.signum() != -1) { // if value is not negative
|
||||
for (RollingAverage average : this.processAverages) {
|
||||
average.add(processCpuLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OperatingSystemMXBean {
|
||||
double getSystemCpuLoad();
|
||||
double getProcessCpuLoad();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class DoubleUtils {
|
||||
/**
|
||||
* Format the given value into a readable format
|
||||
* @param amount the value to format
|
||||
* @param shortSuffixes whether or not to have shorrt suffixes
|
||||
* @return the formatted value
|
||||
* @author Ell (modified by Braydon)
|
||||
*/
|
||||
public static String format(double amount, boolean shortSuffixes) {
|
||||
if (amount <= 0.0D)
|
||||
return "0";
|
||||
List<String> suffixes;
|
||||
if (shortSuffixes)
|
||||
suffixes = Arrays.asList("", "k", "m", "b", "t", "Qa", "Qu", "Se", "Sp", "o", "n", "d");
|
||||
else
|
||||
suffixes = Arrays.asList("", " Thousand", " Million", " Billion", " Trillion", " Quadrillion",
|
||||
" Quintillion", " Sextillion", " Septillion", " Octillion", " Nonillion", " Decillion");
|
||||
double chunks = Math.floor(Math.floor(Math.log10(amount) / 3));
|
||||
amount/= Math.pow(10D, chunks * 3 - 1);
|
||||
amount/= 10D;
|
||||
String suffix = suffixes.get((int) chunks);
|
||||
String format = MathUtils.formatString(amount, 1);
|
||||
if (format.replace(".", "").length() > 5)
|
||||
format = format.substring(0, 5);
|
||||
return format + suffix;
|
||||
}
|
||||
}
|
26
commons/src/main/java/zone/themcgamer/common/EnumUtils.java
Normal file
26
commons/src/main/java/zone/themcgamer/common/EnumUtils.java
Normal file
@ -0,0 +1,26 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public class EnumUtils {
|
||||
/**
|
||||
* Get the enum value from the given class with the given name
|
||||
* @param clazz - The enum class
|
||||
* @param name - The name
|
||||
* @return the enum value
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends Enum<T>> T fromString(Class<T> clazz, String name) {
|
||||
for (T value : clazz.getEnumConstants()) {
|
||||
if (value.name().equalsIgnoreCase(name)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
29
commons/src/main/java/zone/themcgamer/common/HashUtils.java
Normal file
29
commons/src/main/java/zone/themcgamer/common/HashUtils.java
Normal file
@ -0,0 +1,29 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class HashUtils {
|
||||
/**
|
||||
* Encrypt the given {@link String} as SHA-256
|
||||
* @param s the string to encrypt
|
||||
* @return the encrypted string
|
||||
*/
|
||||
public static String encryptSha256(String s) {
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
messageDigest.update(s.getBytes());
|
||||
byte digest[] = messageDigest.digest();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (byte b : digest)
|
||||
buffer.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
||||
return buffer.toString();
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
27
commons/src/main/java/zone/themcgamer/common/MathUtils.java
Normal file
27
commons/src/main/java/zone/themcgamer/common/MathUtils.java
Normal file
@ -0,0 +1,27 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class MathUtils {
|
||||
public static double round(double value, int places) {
|
||||
if (places < 0)
|
||||
throw new IllegalArgumentException();
|
||||
return new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
public static double format(double number, int additional) {
|
||||
return Double.parseDouble(formatString(number, additional));
|
||||
}
|
||||
|
||||
public static String formatString(double number, int additional) {
|
||||
return new DecimalFormat("#.#" + "#".repeat(Math.max(0, additional - 1)),
|
||||
new DecimalFormatSymbols(Locale.CANADA)).format(number);
|
||||
}
|
||||
}
|
45
commons/src/main/java/zone/themcgamer/common/MiscUtils.java
Normal file
45
commons/src/main/java/zone/themcgamer/common/MiscUtils.java
Normal file
@ -0,0 +1,45 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class MiscUtils {
|
||||
/**
|
||||
* Get a {@link String} based on the provided string array
|
||||
* @param array - The string array
|
||||
* @return the string
|
||||
*/
|
||||
public static String arrayToString(String... array) {
|
||||
return arrayToString(Arrays.asList(array));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link String} based on the provided {@link List<String>}
|
||||
* @param list - The string list
|
||||
* @return the string
|
||||
*/
|
||||
public static String arrayToString(List<String> list) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String message : list)
|
||||
builder.append(message).append("\n");
|
||||
return builder.substring(0, builder.toString().length() - 1);
|
||||
}
|
||||
|
||||
public static UUID getUuid(String s) {
|
||||
if (s == null || (s.trim().isEmpty()))
|
||||
return null;
|
||||
try {
|
||||
return UUID.fromString(s);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String percent(double value, double max) {
|
||||
double percent = (value * 100d) / max;
|
||||
return (int) percent + "%";
|
||||
}
|
||||
}
|
118
commons/src/main/java/zone/themcgamer/common/RandomUtils.java
Normal file
118
commons/src/main/java/zone/themcgamer/common/RandomUtils.java
Normal file
@ -0,0 +1,118 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public class RandomUtils {
|
||||
/**
|
||||
* Return whether or not the {@param chance} has been met
|
||||
* @param chance - The chance
|
||||
* @param max - The maximum number
|
||||
* @return whether or not the {@param chance} has been met
|
||||
*/
|
||||
public static boolean chance(int chance, int max) {
|
||||
return randomInt(max) + 1 <= chance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the {@param chance} has been met
|
||||
* @param chance - The chance
|
||||
* @param max - The maximum number
|
||||
* @return whether or not the {@param chance} has been met
|
||||
*/
|
||||
public static boolean chance(double chance, double max) {
|
||||
return randomDouble(max) + 1 <= chance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random int between 0 and the maximum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static int randomInt(int max) {
|
||||
return randomInt(0, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random int between the minimum and maximum values
|
||||
* @param min - The minimum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static int randomInt(int min, int max) {
|
||||
return ThreadLocalRandom.current().nextInt(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random long between 0 and the maximum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static long randomLong(long max) {
|
||||
return randomLong(0L, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random long between the minimum and maximum values
|
||||
* @param min - The minimum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static long randomLong(long min, long max) {
|
||||
return ThreadLocalRandom.current().nextLong(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random double between 0 and the maximum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static double randomDouble(double max) {
|
||||
return randomDouble(0D, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random double between the minimum and maximum values
|
||||
* @param min - The minimum value
|
||||
* @param max - The maximum value
|
||||
* @return the random number
|
||||
*/
|
||||
public static double randomDouble(double min, double max) {
|
||||
return ThreadLocalRandom.current().nextDouble(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a random {@link Enum<T>} value from the given
|
||||
* {@link Enum<T>} class
|
||||
* @param enumClass - The enum class
|
||||
* @return the random enum value
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends Enum<T>> T random(Class<T> enumClass) {
|
||||
if (!enumClass.isEnum())
|
||||
throw new IllegalArgumentException("Class '" + enumClass.getSimpleName() + "' must be an enum");
|
||||
return random(Arrays.asList(enumClass.getEnumConstants()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a random {@link Object} from the provided {@link java.util.ArrayList}
|
||||
* @param list - The list to get the object from
|
||||
* @return the random object
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T random(List<T> list) {
|
||||
if (list.isEmpty())
|
||||
return null;
|
||||
if (list.size() == 1)
|
||||
return list.get(0);
|
||||
return list.get(randomInt(0, list.size() - 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* @author Lucko
|
||||
* credits = https://github.com/lucko/spark
|
||||
*/
|
||||
public class RollingAverage {
|
||||
|
||||
private final Queue<BigDecimal> samples;
|
||||
private final int size;
|
||||
private BigDecimal total = BigDecimal.ZERO;
|
||||
|
||||
public RollingAverage(int size) {
|
||||
this.size = size;
|
||||
this.samples = new ArrayDeque<>(this.size);
|
||||
}
|
||||
|
||||
public void add(BigDecimal num) {
|
||||
synchronized (this) {
|
||||
this.total = this.total.add(num);
|
||||
this.samples.add(num);
|
||||
if (this.samples.size() > this.size) {
|
||||
this.total = this.total.subtract(this.samples.remove());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double getAverage() {
|
||||
synchronized (this) {
|
||||
if (this.samples.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return this.total.divide(BigDecimal.valueOf(this.samples.size()), 30, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
public double getMax() {
|
||||
synchronized (this) {
|
||||
BigDecimal max = BigDecimal.ZERO;
|
||||
for (BigDecimal sample : this.samples) {
|
||||
if (sample.compareTo(max) > 0) {
|
||||
max = sample;
|
||||
}
|
||||
}
|
||||
return max.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
public double getMin() {
|
||||
synchronized (this) {
|
||||
BigDecimal min = BigDecimal.ZERO;
|
||||
for (BigDecimal sample : this.samples) {
|
||||
if (min == BigDecimal.ZERO || sample.compareTo(min) < 0) {
|
||||
min = sample;
|
||||
}
|
||||
}
|
||||
return min.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
public double getMedian() {
|
||||
return getPercentile(50);
|
||||
}
|
||||
|
||||
public double getPercentile(int percentile) {
|
||||
if (percentile < 0 || percentile > 100) {
|
||||
throw new IllegalArgumentException("Invalid percentage " + percentile);
|
||||
}
|
||||
|
||||
List<BigDecimal> sortedSamples;
|
||||
synchronized (this) {
|
||||
if (this.samples.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
sortedSamples = new ArrayList<>(this.samples);
|
||||
}
|
||||
sortedSamples.sort(null);
|
||||
|
||||
int rank = (int) Math.ceil((percentile / 100d) * (sortedSamples.size() - 1));
|
||||
return sortedSamples.get(rank).doubleValue();
|
||||
}
|
||||
|
||||
}
|
95
commons/src/main/java/zone/themcgamer/common/TimeUtils.java
Normal file
95
commons/src/main/java/zone/themcgamer/common/TimeUtils.java
Normal file
@ -0,0 +1,95 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class TimeUtils {
|
||||
/**
|
||||
* Format the given time as a date and time {@link String}
|
||||
* @param time the time to format
|
||||
* @return the formatted time
|
||||
*/
|
||||
public static String when(long time) {
|
||||
return new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(time);
|
||||
}
|
||||
|
||||
public static String formatIntoDetailedString(long time, boolean shortTime) {
|
||||
int secs = (int) (time / 1000L);
|
||||
if (secs == 0)
|
||||
return "0 " + (shortTime ? "s" : "seconds");
|
||||
int remainder = secs % 86400;
|
||||
int days = secs / 86400;
|
||||
int hours = remainder / 3600;
|
||||
int minutes = remainder / 60 - hours * 60;
|
||||
int seconds = remainder % 3600 - minutes * 60;
|
||||
String fDays = (days > 0) ? (" " + days + (shortTime ? "d" : " day") + ((days > 1) ? shortTime ? "" : "s" : "")) : "";
|
||||
String fHours = (hours > 0) ? (" " + hours + (shortTime ? "h" : " hour") + ((hours > 1) ? shortTime ? "" : "s" : "")) : "";
|
||||
String fMinutes = (minutes > 0) ? (" " + minutes + (shortTime ? "m" : " minute") + ((minutes > 1) ? shortTime ? "" : "s" : "")) : "";
|
||||
String fSeconds = (seconds > 0) ? (" " + seconds + (shortTime ? "s" : " second") + ((seconds > 1) ? shortTime ? "" : "s" : "")) : "";
|
||||
return (fDays + fHours + fMinutes + fSeconds).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the provided unix time into readable time such as "1.0 Minute"
|
||||
* @param time - The unix time to convert
|
||||
* @return the formatted time
|
||||
*/
|
||||
public static String convertString(long time) {
|
||||
return convertString(time, true, TimeUnit.FIT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the provided unix time into readable time such as "1.0 Minute"
|
||||
* @param time - The unix time to convert
|
||||
* @param includeDecimals - Whether or not to format the time with decimals
|
||||
* @param type - The type to format the time as. Use {@code TimeUnit.FIT} for
|
||||
* the time to format based on the given unix time
|
||||
* @param shortString - Whether or not the time string is shortened
|
||||
* @return the formatted time
|
||||
*/
|
||||
public static String convertString(long time, boolean includeDecimals, TimeUnit type, boolean shortString) {
|
||||
if (time == -1L)
|
||||
return "Perm" + (shortString ? "" : "anent");
|
||||
else if (time <= 0L)
|
||||
return "0.0" + (shortString ? "ms" : " Millisecond");
|
||||
if (type == TimeUnit.FIT) {
|
||||
if (time < java.util.concurrent.TimeUnit.MINUTES.toMillis(1L))
|
||||
type = TimeUnit.SECONDS;
|
||||
else if (time < java.util.concurrent.TimeUnit.HOURS.toMillis(1L))
|
||||
type = TimeUnit.MINUTES;
|
||||
else if (time < java.util.concurrent.TimeUnit.DAYS.toMillis(1L))
|
||||
type = TimeUnit.HOURS;
|
||||
else type = TimeUnit.DAYS;
|
||||
}
|
||||
double num;
|
||||
String text;
|
||||
if (type == TimeUnit.DAYS) {
|
||||
num = MathUtils.format(time / 8.64E7, 1);
|
||||
text = shortString ? "d" : " Day";
|
||||
} else if (type == TimeUnit.HOURS) {
|
||||
num = MathUtils.format(time / 3600000.0, 1);
|
||||
text = shortString ? "h" : " Hour";
|
||||
} else if (type == TimeUnit.MINUTES) {
|
||||
num = MathUtils.format(time / 60000.0, 1);
|
||||
text = shortString ? "m" : " Minute";
|
||||
} else if (type == TimeUnit.SECONDS) {
|
||||
num = MathUtils.format(time / 1000.0, 1);
|
||||
text = shortString ? "s" : " Second";
|
||||
} else {
|
||||
num = MathUtils.format(time, 1);
|
||||
text = shortString ? "ms" : " Millisecond";
|
||||
}
|
||||
if (includeDecimals)
|
||||
text = num + text;
|
||||
else text = ((int) num) + text;
|
||||
if (num != 1.0 && !shortString)
|
||||
text+= "s";
|
||||
return text;
|
||||
}
|
||||
|
||||
public enum TimeUnit {
|
||||
FIT, DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS
|
||||
}
|
||||
}
|
15
commons/src/main/java/zone/themcgamer/common/TriTuple.java
Normal file
15
commons/src/main/java/zone/themcgamer/common/TriTuple.java
Normal file
@ -0,0 +1,15 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter
|
||||
public class TriTuple<A, B, C> {
|
||||
private A left;
|
||||
private B middle;
|
||||
private C right;
|
||||
}
|
24
commons/src/main/java/zone/themcgamer/common/Tuple.java
Normal file
24
commons/src/main/java/zone/themcgamer/common/Tuple.java
Normal file
@ -0,0 +1,24 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter
|
||||
public class Tuple<L, R> implements Cloneable {
|
||||
private L left;
|
||||
private R right;
|
||||
|
||||
@Override
|
||||
public Tuple<L, R> clone() {
|
||||
try {
|
||||
return (Tuple<L, R>) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
70
commons/src/main/java/zone/themcgamer/common/ZipUtils.java
Normal file
70
commons/src/main/java/zone/themcgamer/common/ZipUtils.java
Normal file
@ -0,0 +1,70 @@
|
||||
package zone.themcgamer.common;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class ZipUtils {
|
||||
/**
|
||||
* Zip the given directory
|
||||
* @param sourceDirectoryPath the path of the directory to zip
|
||||
* @param zipDirectoryPath the path of the output file
|
||||
*/
|
||||
public static void zip(String sourceDirectoryPath, String zipDirectoryPath) {
|
||||
try {
|
||||
Path zipPath = Files.createFile(Paths.get(zipDirectoryPath));
|
||||
try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipPath))) {
|
||||
Path sourcePath = Paths.get(sourceDirectoryPath);
|
||||
for (Path path : Files.walk(sourcePath).filter(path -> !Files.isDirectory(path)).collect(Collectors.toList())) {
|
||||
ZipEntry zipEntry = new ZipEntry(sourcePath.relativize(path).toString());
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
Files.copy(path, zipOutputStream);
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void unzip(File source, File output) throws IOException {
|
||||
long started = System.currentTimeMillis();
|
||||
|
||||
FileInputStream fileInputStream = new FileInputStream(source);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
|
||||
ZipEntry entry = zipInputStream.getNextEntry();
|
||||
while (entry != null) {
|
||||
File file = new File(output, entry.getName());
|
||||
if (entry.isDirectory())
|
||||
file.mkdirs();
|
||||
else {
|
||||
File parent = file.getParentFile();
|
||||
if (!parent.exists())
|
||||
parent.mkdirs();
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
|
||||
byte[] buffer = new byte[1024];
|
||||
int location;
|
||||
while ((location = zipInputStream.read(buffer)) != -1)
|
||||
bufferedOutputStream.write(buffer, 0, location);
|
||||
bufferedOutputStream.close();
|
||||
fileOutputStream.close();
|
||||
}
|
||||
entry = zipInputStream.getNextEntry();
|
||||
}
|
||||
fileInputStream.close();
|
||||
|
||||
zipInputStream.closeEntry();
|
||||
zipInputStream.close();
|
||||
|
||||
System.out.println("Finished unzip process for \"" + source.getPath() + "\" in " + (System.currentTimeMillis() - started) + "ms");
|
||||
}
|
||||
}
|
6
core/build.gradle.kts
Normal file
6
core/build.gradle.kts
Normal file
@ -0,0 +1,6 @@
|
||||
dependencies {
|
||||
api(project(":serverdata"))
|
||||
implementation("com.zaxxer:HikariCP:3.4.5")
|
||||
compileOnly("com.destroystokyo:paperspigot:1.12.2")
|
||||
implementation("com.github.cryptomorin:XSeries:7.8.0")
|
||||
}
|
80
core/src/main/java/zone/themcgamer/core/account/Account.java
Normal file
80
core/src/main/java/zone/themcgamer/core/account/Account.java
Normal file
@ -0,0 +1,80 @@
|
||||
package zone.themcgamer.core.account;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote The account implemention for {@link Account}
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter @ToString
|
||||
public class Account {
|
||||
private final int id;
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private Rank primaryRank;
|
||||
private Rank[] secondaryRanks;
|
||||
private final double gold, gems;
|
||||
private final String lastEncryptedIpAddress, ipAddress, encryptedIpAddress;
|
||||
private final long firstLogin, lastLogin;
|
||||
|
||||
public String getPrimaryRankName() {
|
||||
return primaryRank.getDisplayName();
|
||||
}
|
||||
|
||||
public String[] getSecondaryRanksNames() {
|
||||
String[] rankNames = new String[secondaryRanks.length];
|
||||
for (int i = 0; i < secondaryRanks.length; i++)
|
||||
rankNames[i] = secondaryRanks[i].getDisplayName();
|
||||
return rankNames;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return primaryRank.getColor() + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not the player has the provided {@link Rank}
|
||||
* @param rank the rank to check
|
||||
* @return if the player has the rank
|
||||
*/
|
||||
public boolean hasRank(Rank rank) {
|
||||
if (rank.ordinal() < primaryRank.ordinal()) // If the rank provided is above the player's rank, we return false
|
||||
return false;
|
||||
boolean checkSecondary = false;
|
||||
// If the player's primary rank is a staff rank, and the rank to check is a donator rank, we skip over
|
||||
// the primary rank checking and move onto the secondary rank checking
|
||||
if ((primaryRank.getCategory() == Rank.RankCategory.STAFF && rank.getCategory() == Rank.RankCategory.DONATOR)
|
||||
|| rank.getCategory() == Rank.RankCategory.SUB) {
|
||||
checkSecondary = true;
|
||||
}
|
||||
// If we aren't checking the secondary ranks, we check if the rank being checked is higher than the player's
|
||||
// rank and return the value
|
||||
if (!checkSecondary)
|
||||
return rank.ordinal() >= primaryRank.ordinal();
|
||||
List<Rank> secondaryRanks = Arrays.asList(this.secondaryRanks);
|
||||
if (rank.getCategory() == Rank.RankCategory.DONATOR) {
|
||||
int index = rank.ordinal();
|
||||
if (index > 0) { // If the rank index is above 0, we're able to fetch the previous rank in the list
|
||||
Rank previousRank = Rank.values()[index - 1];
|
||||
// If the rank before the rank being checked is a donator rank, then we check if the player has
|
||||
// the previous rank. If the player doesn't have the previous rank, but they have the rank being
|
||||
// checked, we return true as they have the rank, otherwise return the hasPrevious value
|
||||
if (previousRank.getCategory() == Rank.RankCategory.DONATOR) {
|
||||
boolean hasPrevious = hasRank(previousRank);
|
||||
if (!hasPrevious && secondaryRanks.contains(rank))
|
||||
return true;
|
||||
return hasPrevious;
|
||||
}
|
||||
}
|
||||
}
|
||||
return secondaryRanks.contains(rank);
|
||||
}
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
package zone.themcgamer.core.account;
|
||||
|
||||
import com.cryptomorin.xseries.XSound;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.common.EnumUtils;
|
||||
import zone.themcgamer.core.account.command.GemsCommand;
|
||||
import zone.themcgamer.core.account.command.GoldCommand;
|
||||
import zone.themcgamer.core.account.command.PlayerInfoCommand;
|
||||
import zone.themcgamer.core.account.command.rank.RankCommand;
|
||||
import zone.themcgamer.core.account.command.rank.arguments.ClearArgument;
|
||||
import zone.themcgamer.core.account.command.rank.arguments.InfoArgument;
|
||||
import zone.themcgamer.core.account.command.rank.arguments.ListArgument;
|
||||
import zone.themcgamer.core.account.command.rank.arguments.SetArgument;
|
||||
import zone.themcgamer.core.account.event.AccountLoadEvent;
|
||||
import zone.themcgamer.core.account.event.AccountUnloadEvent;
|
||||
import zone.themcgamer.core.command.impl.social.MessageCommand;
|
||||
import zone.themcgamer.core.command.impl.social.ReplyCommand;
|
||||
import zone.themcgamer.core.common.MojangUtils;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
import zone.themcgamer.core.module.ModuleInfo;
|
||||
import zone.themcgamer.core.nametag.NametagManager;
|
||||
import zone.themcgamer.data.Rank;
|
||||
import zone.themcgamer.data.jedis.cache.CacheRepository;
|
||||
import zone.themcgamer.data.jedis.cache.impl.PlayerCache;
|
||||
import zone.themcgamer.data.jedis.command.JedisCommandHandler;
|
||||
import zone.themcgamer.data.jedis.command.impl.RankMessageCommand;
|
||||
import zone.themcgamer.data.jedis.command.impl.StaffChatCommand;
|
||||
import zone.themcgamer.data.jedis.command.impl.account.AccountRankClearCommand;
|
||||
import zone.themcgamer.data.jedis.command.impl.account.AccountRankSetCommand;
|
||||
import zone.themcgamer.data.jedis.command.impl.player.PlayerDirectMessageEvent;
|
||||
import zone.themcgamer.data.jedis.repository.RedisRepository;
|
||||
import zone.themcgamer.data.mysql.MySQLController;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ModuleInfo(name = "Account Manager")
|
||||
public class AccountManager extends Module {
|
||||
public static final List<MiniAccount<?>> MINI_ACCOUNTS = new ArrayList<>();
|
||||
public static final Map<UUID, Account> CACHE = new HashMap<>(); // Account cache for online players
|
||||
public static final Cache<UUID, Account> LOOKUP_CACHE = CacheBuilder.newBuilder() // Account cache for players that were looked up via the lookup method
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.removalListener(removalNotification -> {
|
||||
Object key = removalNotification.getKey();
|
||||
if (key instanceof UUID) {
|
||||
if (CACHE.containsKey(key))
|
||||
return;
|
||||
for (MiniAccount<?> miniAccount : MINI_ACCOUNTS)
|
||||
miniAccount.getAccounts().remove(key);
|
||||
}
|
||||
}).build();
|
||||
|
||||
private static final String KICK_MESSAGE = "Failed to fetch your account data, please try again in a few moments";
|
||||
|
||||
private final AccountRepository repository;
|
||||
private final CacheRepository cacheRepository;
|
||||
private final NametagManager nametagManager;
|
||||
|
||||
private final AtomicInteger playersConnecting = new AtomicInteger(); // The amount of players connecting to the server
|
||||
private final AtomicInteger accountsLoading = new AtomicInteger(); // The amount of players connecting to the server
|
||||
private final List<UUID> loggingIn = Collections.synchronizedList(new ArrayList<>()); // The list of uuids logging in
|
||||
|
||||
public AccountManager(JavaPlugin plugin, MySQLController mySQLController, NametagManager nametagManager) {
|
||||
super(plugin);
|
||||
repository = new AccountRepository(mySQLController.getDataSource());
|
||||
cacheRepository = RedisRepository.getRepository(CacheRepository.class).orElse(null);
|
||||
this.nametagManager = nametagManager;
|
||||
|
||||
// In-case somebody decides to do a no no and reloads the server, we wanna kick all
|
||||
// online players so they can rejoin to load their account
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
Bukkit.getScheduler().runTask(plugin, () -> player.kickPlayer("Please re-join"));
|
||||
|
||||
registerCommand(new RankCommand());
|
||||
registerCommand(new InfoArgument(this));
|
||||
registerCommand(new SetArgument(this));
|
||||
registerCommand(new ClearArgument(this));
|
||||
registerCommand(new ListArgument());
|
||||
|
||||
registerCommand(new GoldCommand(this));
|
||||
registerCommand(new GemsCommand(this));
|
||||
registerCommand(new MessageCommand(this, cacheRepository));
|
||||
registerCommand(new ReplyCommand(this, cacheRepository));
|
||||
registerCommand(new PlayerInfoCommand(this, cacheRepository));
|
||||
registerCommand(new zone.themcgamer.core.command.impl.staff.StaffChatCommand());
|
||||
|
||||
JedisCommandHandler.getInstance().addListener(jedisCommand -> {
|
||||
if (jedisCommand instanceof AccountRankSetCommand) {
|
||||
AccountRankSetCommand accountRankSetCommand = (AccountRankSetCommand) jedisCommand;
|
||||
Player player = Bukkit.getPlayer(accountRankSetCommand.getUuid());
|
||||
if (player != null) {
|
||||
Rank rank = EnumUtils.fromString(Rank.class, accountRankSetCommand.getConstantName());
|
||||
if (rank == null)
|
||||
rank = Rank.DEFAULT;
|
||||
Rank finalRank = rank;
|
||||
fromCache(player.getUniqueId()).ifPresent(account -> account.setPrimaryRank(finalRank));
|
||||
nametagManager.setNametag(player, rank.getNametag(), null, rank.ordinal() + 1);
|
||||
player.sendMessage(Style.main("Rank", "Your rank was updated to §f" + accountRankSetCommand.getRankDisplayName()));
|
||||
}
|
||||
} else if (jedisCommand instanceof AccountRankClearCommand) {
|
||||
AccountRankClearCommand accountRankClearCommand = (AccountRankClearCommand) jedisCommand;
|
||||
Player player = Bukkit.getPlayer(accountRankClearCommand.getUuid());
|
||||
if (player != null) {
|
||||
fromCache(player.getUniqueId()).ifPresent(account -> {
|
||||
account.setPrimaryRank(Rank.DEFAULT);
|
||||
account.setSecondaryRanks(new Rank[0]);
|
||||
|
||||
Rank rank = account.getPrimaryRank();
|
||||
nametagManager.setNametag(player, rank.getNametag(), null, rank.ordinal() + 1);
|
||||
});
|
||||
player.sendMessage(Style.main("Rank", "Your ranks were cleared"));
|
||||
}
|
||||
} else if (jedisCommand instanceof RankMessageCommand) {
|
||||
RankMessageCommand rankMessageCommand = (RankMessageCommand) jedisCommand;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Optional<Account> optionalAccount = fromCache(player.getUniqueId());
|
||||
if (optionalAccount.isEmpty())
|
||||
continue;
|
||||
if (optionalAccount.get().hasRank(rankMessageCommand.getRank()))
|
||||
player.sendMessage(rankMessageCommand.getMessage());
|
||||
}
|
||||
} else if (jedisCommand instanceof StaffChatCommand) {
|
||||
StaffChatCommand staffChatCommand = (StaffChatCommand) jedisCommand;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Optional<Account> account = fromCache(player.getUniqueId());
|
||||
if (account.isEmpty() || (!account.get().hasRank(Rank.HELPER)))
|
||||
continue;
|
||||
String format = staffChatCommand.getPrefix() +
|
||||
" &7" + staffChatCommand.getUsername() + " &8» &f" + staffChatCommand.getMessage();
|
||||
player.sendMessage(Style.main(staffChatCommand.getServer(), format));
|
||||
}
|
||||
} else if (jedisCommand instanceof PlayerDirectMessageEvent) {
|
||||
PlayerDirectMessageEvent playerDirectMessageEvent = (PlayerDirectMessageEvent) jedisCommand;
|
||||
UUID uuid = playerDirectMessageEvent.getReceiver();
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player == null || !player.isOnline())
|
||||
return;
|
||||
|
||||
player.sendMessage(Style.color("&b\u2709 &7(from " + playerDirectMessageEvent.getSenderDisplayName() + "&7) &8\u00BB&f " + playerDirectMessageEvent.getMessage()));
|
||||
player.playSound(player.getLocation(), XSound.ENTITY_CHICKEN_EGG.parseSound(), 10, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
try {
|
||||
UUID uuid = event.getUniqueId();
|
||||
|
||||
// Incrementing the players connecting
|
||||
playersConnecting.incrementAndGet();
|
||||
|
||||
// If the amount of accounts loading is 3 or more, we wanna sleep the thread (delay the login) for
|
||||
// 25 milliseconds. This will cause less strain on the MySQL server with multiple queries at once.
|
||||
while (accountsLoading.get() >= 3) {
|
||||
try {
|
||||
Thread.sleep(25L);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// We store the time the player started connecting and add them to the logging in list
|
||||
long started = System.currentTimeMillis();
|
||||
accountsLoading.incrementAndGet();
|
||||
loggingIn.add(uuid);
|
||||
Account[] accountArray = new Account[] { null };
|
||||
AtomicBoolean repositoryException = new AtomicBoolean();
|
||||
|
||||
// Loading the players account from MySQL and removing them from the logging in list
|
||||
Bukkit.getScheduler().runTaskAsynchronously(getPlugin(), () -> {
|
||||
try {
|
||||
accountArray[0] = repository.login(uuid, event.getName(), event.getAddress().getHostAddress());
|
||||
} catch (SQLException ex) {
|
||||
repositoryException.set(true);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
loggingIn.remove(uuid);
|
||||
});
|
||||
// We sleep the thread for however long it takes the player's account to be fetched from
|
||||
// the database (with a maximum time of 20 seconds) to give the MySQL server time to fetch
|
||||
// the account
|
||||
long timeSlept = 0L;
|
||||
while (loggingIn.contains(uuid) && System.currentTimeMillis() - started < TimeUnit.SECONDS.toMillis(20L)) {
|
||||
if (repositoryException.get()) {
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, KICK_MESSAGE + " (repository)");
|
||||
break;
|
||||
}
|
||||
timeSlept+= 1L;
|
||||
Thread.sleep(1L);
|
||||
}
|
||||
log(event.getName() + " has taken " + (System.currentTimeMillis() - started) + "ms to login (" + timeSlept + "ms was spent sleeping)");
|
||||
|
||||
Account account = accountArray[0];
|
||||
// If the player is still in the logging in list, or the player's account is null, we wanna disallow login
|
||||
// and show the player an error
|
||||
if (loggingIn.remove(uuid) || account == null || (account.getId() <= 0))
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, KICK_MESSAGE + " (" + (account == null ? "account" : "timeout") + ")");
|
||||
else {
|
||||
// If the login was successful, we call the AccountLoadEvent and locally cache the player's account
|
||||
// so it can be used in the future
|
||||
Bukkit.getScheduler().runTask(getPlugin(), () -> Bukkit.getPluginManager().callEvent(new AccountLoadEvent(account)));
|
||||
CACHE.put(uuid, account);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, KICK_MESSAGE + " (exception)");
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
accountsLoading.decrementAndGet();
|
||||
}
|
||||
} finally {
|
||||
playersConnecting.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onLogin(PlayerLoginEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Account account;
|
||||
// If the player does not have an account (this should NEVER happen), disallow login
|
||||
if ((account = CACHE.get(player.getUniqueId())) == null)
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, KICK_MESSAGE + " (cache)");
|
||||
else {
|
||||
// Automatic opping for player's with the rank Jr.Dev or above
|
||||
Rank opRank = Rank.JR_DEVELOPER;
|
||||
if (account.hasRank(opRank) && !player.isOp())
|
||||
player.setOp(true);
|
||||
else if (!account.hasRank(opRank) && player.isOp())
|
||||
player.setOp(false);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Account account = CACHE.get(player.getUniqueId());
|
||||
if (account == null)
|
||||
return;
|
||||
Rank rank = account.getPrimaryRank();
|
||||
nametagManager.setNametag(player, rank.getNametag(), null, rank.ordinal() + 1);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
private void onQuit(PlayerQuitEvent event) {
|
||||
UUID uuid = event.getPlayer().getUniqueId();
|
||||
// Loop through all of the mini accounts and remove the player's account
|
||||
for (MiniAccount<?> miniAccount : MINI_ACCOUNTS)
|
||||
miniAccount.getAccounts().remove(uuid);
|
||||
// Call the unload event and remove the main account
|
||||
Bukkit.getPluginManager().callEvent(new AccountUnloadEvent(CACHE.remove(uuid)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a {@link Account} with the given username
|
||||
* @param name the name of the account to lookup
|
||||
* @param consumer the account consumer
|
||||
*/
|
||||
public void lookup(String name, Consumer<Account> consumer) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
Player player = Bukkit.getPlayerExact(name);
|
||||
UUID uuid = player == null ? null : player.getUniqueId();
|
||||
if (uuid == null) {
|
||||
Optional<PlayerCache> optionalPlayerCache = cacheRepository
|
||||
.lookup(PlayerCache.class, playerCache -> playerCache.getName().equalsIgnoreCase(name));
|
||||
if (optionalPlayerCache.isPresent())
|
||||
uuid = optionalPlayerCache.get().getUuid();
|
||||
}
|
||||
if (uuid == null)
|
||||
MojangUtils.getUUIDAsync(name, fetchedUUID -> lookup(fetchedUUID, name, consumer));
|
||||
else lookup(uuid, name, consumer);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a {@link Account} with the given {@link UUID}
|
||||
* @param uuid the uuid of the account to lookup
|
||||
* @param name the name of the account to lookup
|
||||
* @param consumer the account consumer
|
||||
*/
|
||||
public void lookup(UUID uuid, String name, Consumer<Account> consumer) {
|
||||
if (uuid == null) {
|
||||
consumer.accept(null);
|
||||
return;
|
||||
}
|
||||
AtomicReference<Account> reference = new AtomicReference<>(CACHE.get(uuid));
|
||||
if (reference.get() == null)
|
||||
reference.set(LOOKUP_CACHE.getIfPresent(uuid));
|
||||
if (reference.get() != null)
|
||||
consumer.accept(reference.get());
|
||||
else {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
reference.set(repository.login(uuid, name, ""));
|
||||
if (reference.get() != null)
|
||||
LOOKUP_CACHE.put(uuid, reference.get());
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
consumer.accept(reference.get());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setRank(Account account, Rank rank) {
|
||||
account.setPrimaryRank(rank);
|
||||
repository.setRank(account.getId(), rank);
|
||||
JedisCommandHandler.getInstance().send(new AccountRankSetCommand(account.getUuid(), rank.name(), rank.getColor() + rank.getDisplayName()));
|
||||
}
|
||||
|
||||
public void clearRanks(Account account) {
|
||||
account.setPrimaryRank(Rank.DEFAULT);
|
||||
account.setSecondaryRanks(new Rank[0]);
|
||||
repository.clearRanks(account.getId());
|
||||
JedisCommandHandler.getInstance().send(new AccountRankClearCommand(account.getUuid()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given {@link MiniAccount}
|
||||
* @param miniAccount the mini account to add
|
||||
*/
|
||||
public static void addMiniAccount(MiniAccount<?> miniAccount) {
|
||||
MINI_ACCOUNTS.add(miniAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Account} from the provided uuid
|
||||
* @param uuid the uuid to get the account for
|
||||
* @return the optional account
|
||||
*/
|
||||
public static Optional<Account> fromCache(UUID uuid) {
|
||||
return Optional.ofNullable(CACHE.get(uuid));
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
package zone.themcgamer.core.account;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.bukkit.Bukkit;
|
||||
import zone.themcgamer.common.HashUtils;
|
||||
import zone.themcgamer.core.account.event.AccountPreLoadEvent;
|
||||
import zone.themcgamer.data.Rank;
|
||||
import zone.themcgamer.data.jedis.cache.CacheRepository;
|
||||
import zone.themcgamer.data.jedis.cache.impl.PlayerCache;
|
||||
import zone.themcgamer.data.jedis.repository.RedisRepository;
|
||||
import zone.themcgamer.data.mysql.data.column.Column;
|
||||
import zone.themcgamer.data.mysql.data.column.impl.IntegerColumn;
|
||||
import zone.themcgamer.data.mysql.data.column.impl.LongColumn;
|
||||
import zone.themcgamer.data.mysql.data.column.impl.StringColumn;
|
||||
import zone.themcgamer.data.mysql.repository.MySQLRepository;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class AccountRepository extends MySQLRepository {
|
||||
private static final String SELECT_ACCOUNT = "SELECT * FROM `accounts` WHERE `uuid` = ? LIMIT 1";
|
||||
private static final String INSERT_ACCOUNT = "INSERT INTO `accounts` " +
|
||||
"(`id`, `uuid`, `name`, `primaryRank`, `secondaryRanks`, `gold`, `gems`, `ipAddress`, `firstLogin`, `lastLogin`) VALUES " +
|
||||
"(NULL, ?, ?, '" + Rank.DEFAULT.name() + "', '', '0', '0', ?, ?, ?);";
|
||||
private static final String UPDATE_RANK = "UPDATE `accounts` SET `primaryRank` = ? WHERE `id` = ?;";
|
||||
|
||||
public AccountRepository(HikariDataSource dataSource) {
|
||||
super(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt a login with the given uuid, name, and ip address
|
||||
* @param uuid the uuid
|
||||
* @param name the username
|
||||
* @param ipAddress the ip address
|
||||
* @return the fetched account
|
||||
*/
|
||||
public Account login(UUID uuid, String name, String ipAddress) throws SQLException {
|
||||
if (uuid == null || (name == null || name.trim().isEmpty()))
|
||||
return null;
|
||||
boolean offlineLookup = ipAddress.trim().isEmpty();
|
||||
String encryptedIpAddress = offlineLookup ? "" : HashUtils.encryptSha256(ipAddress);
|
||||
int accountId = -1;
|
||||
boolean loadedFromCache = false;
|
||||
CacheRepository cacheRepository = RedisRepository.getRepository(CacheRepository.class).orElse(null);
|
||||
if (cacheRepository != null) {
|
||||
PlayerCache cache = cacheRepository.lookup(PlayerCache.class, uuid).orElse(null);
|
||||
if (cache != null) {
|
||||
if (!cache.getName().equals(name)) {
|
||||
cache.setName(name);
|
||||
cacheRepository.post(cache);
|
||||
}
|
||||
accountId = cache.getAccountId();
|
||||
loadedFromCache = true;
|
||||
}
|
||||
}
|
||||
Account account = null;
|
||||
long now = System.currentTimeMillis();
|
||||
try (
|
||||
Connection connection = dataSource.getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(SELECT_ACCOUNT);
|
||||
) {
|
||||
// Attempt to select the existing account from the database
|
||||
statement.setString(1, uuid.toString());
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
|
||||
String query = "";
|
||||
|
||||
if (resultSet.next()) { // If the account exists in the database, we wanna load its values
|
||||
if (accountId <= 0) // If the account id has not been loaded from the cache, we wanna fetch it from the database
|
||||
accountId = resultSet.getInt(1);
|
||||
account = constructAccount(accountId, uuid, name, resultSet, ipAddress, encryptedIpAddress, offlineLookup ? -1L : now);
|
||||
|
||||
// If the account exists in the database and we're not doing an offline account lookup, we wanna update
|
||||
// some key values in the database for the user, like their name, ip address, and last login time
|
||||
if (!offlineLookup)
|
||||
query = "UPDATE `accounts` SET `name`='" + name + "', `ipAddress`='" + encryptedIpAddress + "', `lastLogin`='" + System.currentTimeMillis() + "' WHERE `id` = '" + accountId + "';";
|
||||
} else {
|
||||
// Inserting the new account into the database
|
||||
int[] idArray = new int[1];
|
||||
executeInsert(connection, INSERT_ACCOUNT, new Column[] {
|
||||
new StringColumn("uuid", uuid.toString()),
|
||||
new StringColumn("name", name),
|
||||
new StringColumn("ipAddress", encryptedIpAddress),
|
||||
new LongColumn("firstLogin", now),
|
||||
new LongColumn("lastLogin", now),
|
||||
}, insertResultSet -> {
|
||||
try {
|
||||
while (insertResultSet.next()) {
|
||||
// After we insert the account, we wanna get the account id that was generated and
|
||||
// store it in the account object
|
||||
idArray[0] = insertResultSet.getInt(1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
});
|
||||
accountId = idArray[0];
|
||||
}
|
||||
Bukkit.getPluginManager().callEvent(new AccountPreLoadEvent(uuid, name, ipAddress));
|
||||
int finalAccountId = accountId;
|
||||
query+= AccountManager.MINI_ACCOUNTS.parallelStream().map(miniAccount -> miniAccount.getQuery(finalAccountId, uuid, name, ipAddress, encryptedIpAddress)).collect(Collectors.joining());
|
||||
if (!query.trim().isEmpty()) {
|
||||
statement.execute(query);
|
||||
statement.getUpdateCount();
|
||||
statement.getMoreResults();
|
||||
for (MiniAccount<?> miniAccount : AccountManager.MINI_ACCOUNTS) {
|
||||
Object miniAccountObject = miniAccount.getAccount(accountId, uuid, name, ipAddress, encryptedIpAddress);
|
||||
if (miniAccountObject != null)
|
||||
miniAccount.addAccount(uuid, miniAccountObject);
|
||||
try {
|
||||
ResultSet miniAccountResultSet = statement.getResultSet();
|
||||
if (miniAccountResultSet == null)
|
||||
continue;
|
||||
miniAccount.loadAccount(accountId, uuid, name, ipAddress, encryptedIpAddress, miniAccountResultSet);
|
||||
} finally {
|
||||
statement.getMoreResults();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (account == null) {
|
||||
account = new Account(
|
||||
accountId,
|
||||
uuid,
|
||||
name,
|
||||
Rank.DEFAULT,
|
||||
new Rank[0],
|
||||
0D,
|
||||
0D,
|
||||
encryptedIpAddress,
|
||||
ipAddress,
|
||||
encryptedIpAddress,
|
||||
now,
|
||||
now
|
||||
);
|
||||
}
|
||||
if (!loadedFromCache && cacheRepository != null)
|
||||
cacheRepository.post(new PlayerCache(uuid, name, accountId));
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setRank(int accountId, Rank rank) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
executeInsert(UPDATE_RANK, new Column[] {
|
||||
new StringColumn("primaryRank", rank.name()),
|
||||
new IntegerColumn("id", accountId)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void clearRanks(int accountId) {
|
||||
String query = UPDATE_RANK
|
||||
.replaceFirst("\\?", "'" + Rank.DEFAULT.name() + "'")
|
||||
.replaceFirst("\\?", "'" + accountId + "'");
|
||||
query+= "UPDATE `accounts` SET `secondaryRanks` = '' WHERE `id` = " + accountId + ";";
|
||||
String finalQuery = query;
|
||||
CompletableFuture.runAsync(() -> {
|
||||
executeInsert(finalQuery, new Column[0]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link Account} from the given parameters
|
||||
* @param accountId the account id
|
||||
* @param uuid the uuid
|
||||
* @param name the name
|
||||
* @param resultSet the result set
|
||||
* @param ipAddress the ip address
|
||||
* @param encryptedIpAddress the encrypted ip address
|
||||
* @param lastLogin the last login
|
||||
* @return the account
|
||||
*/
|
||||
private Account constructAccount(int accountId, UUID uuid, String name, ResultSet resultSet, String ipAddress, String encryptedIpAddress, long lastLogin) {
|
||||
try {
|
||||
Rank[] secondaryRanks = Arrays.stream(resultSet.getString("secondaryRanks")
|
||||
.split(",")).map(rankName -> Rank.lookup(rankName).orElse(null))
|
||||
.filter(Objects::nonNull).toArray(Rank[]::new);
|
||||
return new Account(
|
||||
accountId,
|
||||
uuid,
|
||||
resultSet.getString("name"),
|
||||
Rank.lookup(resultSet.getString("primaryRank")).orElse(Rank.DEFAULT),
|
||||
secondaryRanks,
|
||||
resultSet.getInt("gold"),
|
||||
resultSet.getInt("gems"),
|
||||
resultSet.getString("ipAddress"),
|
||||
ipAddress,
|
||||
encryptedIpAddress,
|
||||
resultSet.getLong("firstLogin"),
|
||||
lastLogin == -1L ? resultSet.getLong("lastLogin") : lastLogin
|
||||
);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package zone.themcgamer.core.account;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import zone.themcgamer.core.module.Module;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @implNote A mini account makes it easier to organize accounts into different sections.
|
||||
* For instance, important information regarding a player is stored in
|
||||
* {@link Account}, and other things will be stored in a mini account.
|
||||
*/
|
||||
@Getter
|
||||
public abstract class MiniAccount<T> extends Module {
|
||||
private final Map<UUID, T> accounts = new HashMap<>();
|
||||
|
||||
public MiniAccount(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default account using the given account id, uuid, or name
|
||||
* @param accountId the account id
|
||||
* @param uuid the uuid
|
||||
* @param name the name
|
||||
* @return the default account
|
||||
*/
|
||||
public abstract T getAccount(int accountId, UUID uuid, String name, String ip, String encryptedIp);
|
||||
|
||||
/**
|
||||
* Get the query to fetch the account using the given account id, uuid, or name
|
||||
* @param accountId the account id
|
||||
* @param uuid the uuid
|
||||
* @param name the name
|
||||
* @return the query
|
||||
*/
|
||||
public abstract String getQuery(int accountId, UUID uuid, String name, String ip, String encryptedIp);
|
||||
|
||||
/**
|
||||
* Called when an account is fetched from MySQL
|
||||
* @param accountId the account id
|
||||
* @param uuid the uuid
|
||||
* @param name the name
|
||||
* @param resultSet the result set that was fetched
|
||||
* @throws SQLException exception
|
||||
*/
|
||||
public abstract void loadAccount(int accountId, UUID uuid, String name, String ip, String encryptedIp, ResultSet resultSet) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add the provided account identified by the given {@link UUID}
|
||||
* @param uuid the uuid to identify the account
|
||||
* @param object the account
|
||||
*/
|
||||
public void addAccount(UUID uuid, Object object) {
|
||||
accounts.put(uuid, (T) object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link T} account for the given {@link UUID}
|
||||
* @param uuid the uuid of the account
|
||||
* @return the optional account
|
||||
*/
|
||||
public Optional<T> lookup(UUID uuid) {
|
||||
return Optional.ofNullable(accounts.get(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link T} account that tests against the {@link Predicate}
|
||||
* @param predicate the predicate to test against
|
||||
* @return the optional account
|
||||
*/
|
||||
public Optional<T> lookup(Predicate<T> predicate) {
|
||||
return accounts.values().stream().filter(predicate).findFirst();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package zone.themcgamer.core.account.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.common.DoubleUtils;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GemsCommand {
|
||||
private final AccountManager accountManager;
|
||||
|
||||
@Command(name = "gems", description = "View your gems", playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
String target = player.getName();
|
||||
if (args.length > 0)
|
||||
target = args[0];
|
||||
String finalTarget = target;
|
||||
accountManager.lookup(target, account -> {
|
||||
if (account == null) {
|
||||
player.sendMessage(Style.invalidAccount("Account", finalTarget));
|
||||
return;
|
||||
}
|
||||
String gems = DoubleUtils.format(account.getGems(), false);
|
||||
if (player.getName().equals(account.getName()))
|
||||
player.sendMessage(Style.main("Account", String.format("You have &2%s Gem" + (account.getGems() == 1 ? "" : "s") + "&7!", gems)));
|
||||
else player.sendMessage(Style.main("Account", String.format("&b%s &7has &2%s Gem" + (account.getGems() == 1 ? "" : "s") + "&7!", account.getDisplayName(), gems)));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package zone.themcgamer.core.account.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import zone.themcgamer.common.DoubleUtils;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GoldCommand {
|
||||
private final AccountManager accountManager;
|
||||
|
||||
@Command(name = "gold", description = "View your gold", playersOnly = true)
|
||||
public void onCommand(CommandProvider command) {
|
||||
Player player = command.getPlayer();
|
||||
String[] args = command.getArgs();
|
||||
String target = player.getName();
|
||||
if (args.length > 0)
|
||||
target = args[0];
|
||||
String finalTarget = target;
|
||||
accountManager.lookup(target, account -> {
|
||||
if (account == null) {
|
||||
player.sendMessage(Style.invalidAccount("Account", finalTarget));
|
||||
return;
|
||||
}
|
||||
String gold = DoubleUtils.format(account.getGold(), false);
|
||||
if (player.getName().equals(account.getName()))
|
||||
player.sendMessage(Style.main("Account", String.format("You have &6%s Gold&7!", gold)));
|
||||
else player.sendMessage(Style.main("Account", String.format("&b%s &7has &6%s Gold&7!", account.getDisplayName(), gold)));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package zone.themcgamer.core.account.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.common.TimeUtils;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.data.Rank;
|
||||
import zone.themcgamer.data.jedis.cache.CacheRepository;
|
||||
import zone.themcgamer.data.jedis.cache.impl.PlayerStatusCache;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class PlayerInfoCommand {
|
||||
private final AccountManager accountManager;
|
||||
private final CacheRepository cacheRepository;
|
||||
|
||||
@Command(name = "playerinfo", aliases = { "pinfo" }, description = "Get information about a player", ranks = { Rank.HELPER })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Style.main("Account", "Usage: /" + command.getLabel() + " <player>"));
|
||||
return;
|
||||
}
|
||||
accountManager.lookup(args[0], account -> {
|
||||
if (account == null) {
|
||||
sender.sendMessage(Style.invalidAccount("Account", args[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<PlayerStatusCache> playerStatusCacheOptional = cacheRepository.lookup(PlayerStatusCache.class, account.getUuid());
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage(Style.color("&a&lPlayer Information"));
|
||||
sender.sendMessage(Style.color("&7Id: &c" + account.getId()));
|
||||
sender.sendMessage(Style.color("&7Player: &b" + account.getName()));
|
||||
sender.sendMessage(Style.color("&7Status: " + (playerStatusCacheOptional.isEmpty() ? "&cOffline" : "&aOnline")));
|
||||
sender.sendMessage(Style.color("&7Server: &b" + (playerStatusCacheOptional.isEmpty() ? "&cN/A" : playerStatusCacheOptional.get().getServer())));
|
||||
sender.sendMessage(Style.color("&7Registered At: &b" + TimeUtils.when(account.getFirstLogin())));
|
||||
sender.sendMessage(Style.color("&7Last Seen: &b" + TimeUtils.when(account.getLastLogin())));
|
||||
sender.sendMessage(Style.color("&7Rank: &b" + account.getPrimaryRank().getColor() + account.getPrimaryRank().getDisplayName()));
|
||||
sender.sendMessage(Style.color("&7Sub Ranks: &b" + (account.getSecondaryRanksNames().length == 0 ? "None" : String.join("§7, §f", account.getSecondaryRanksNames()))));
|
||||
sender.sendMessage(Style.color("&7Gold: &b" + account.getGold()));
|
||||
sender.sendMessage(Style.color("&7Gems: &b" + account.getGems()));
|
||||
sender.sendMessage("");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package zone.themcgamer.core.account.command.rank;
|
||||
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.command.help.HelpCommand;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public class RankCommand extends HelpCommand {
|
||||
@Command(name = "rank", description = "Rank management", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package zone.themcgamer.core.account.command.rank.arguments;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class ClearArgument {
|
||||
private final AccountManager accountManager;
|
||||
|
||||
@Command(name = "rank.clear", usage = "<player>", description = "Clear the ranks for a player", ranks = { Rank.ADMIN })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Style.main("Rank", "Usage: /rank clear <player>"));
|
||||
return;
|
||||
}
|
||||
accountManager.lookup(args[0], account -> {
|
||||
if (account == null) {
|
||||
sender.sendMessage(Style.invalidAccount("Rank", args[0]));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Style.main("Rank", account.getDisplayName() + " §7had their ranks cleared"));
|
||||
accountManager.clearRanks(account);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package zone.themcgamer.core.account.command.rank.arguments;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import zone.themcgamer.core.account.AccountManager;
|
||||
import zone.themcgamer.core.command.Command;
|
||||
import zone.themcgamer.core.command.CommandProvider;
|
||||
import zone.themcgamer.core.common.Style;
|
||||
import zone.themcgamer.data.Rank;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public class InfoArgument {
|
||||
private final AccountManager accountManager;
|
||||
|
||||
@Command(name = "rank.info", usage = "<player>", description = "View rank info for a player", ranks = { Rank.MODERATOR })
|
||||
public void onCommand(CommandProvider command) {
|
||||
CommandSender sender = command.getSender();
|
||||
String[] args = command.getArgs();
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Style.main("Rank", "Usage: /rank info <player>"));
|
||||
return;
|
||||
}
|
||||
accountManager.lookup(args[0], account -> {
|
||||
if (account == null) {
|
||||
sender.sendMessage(Style.invalidAccount("Rank", args[0]));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Style.main("Rank", "Rank information for " + account.getDisplayName() + "§7:"));
|
||||
sender.sendMessage(" §8- §7Primary Rank §f" + account.getPrimaryRank().getDisplayName());
|
||||
sender.sendMessage(" §8- §7Sub Ranks §f" + (account.getSecondaryRanks().length < 1 ? "None" :
|
||||
Arrays.stream(account.getSecondaryRanks()).map(Rank::getDisplayName).collect(Collectors.joining("§7, §f"))));
|
||||
});
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user