更换至 Gradle
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class AlreadyStartException extends RuntimeException {
|
||||
}
|
||||
19
src/main/scala/com/ilummc/eagletdl/CompleteEvent.java
Normal file
19
src/main/scala/com/ilummc/eagletdl/CompleteEvent.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class CompleteEvent {
|
||||
private EagletTask task;
|
||||
private boolean success;
|
||||
|
||||
CompleteEvent(EagletTask task, boolean success) {
|
||||
this.task = task;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
27
src/main/scala/com/ilummc/eagletdl/ConnectedEvent.java
Normal file
27
src/main/scala/com/ilummc/eagletdl/ConnectedEvent.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class ConnectedEvent {
|
||||
|
||||
private long contentLength;
|
||||
private EagletTask task;
|
||||
|
||||
public ConnectedEvent(long length, EagletTask task) {
|
||||
this.contentLength = length;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the download task.
|
||||
* <p>
|
||||
* If the length is -1, this task cannot be downloaded in multiple threads.
|
||||
*
|
||||
* @return length
|
||||
*/
|
||||
public long getContentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class DoNotSupportMultipleThreadException extends RuntimeException {
|
||||
}
|
||||
8
src/main/scala/com/ilummc/eagletdl/EagletHandler.java
Normal file
8
src/main/scala/com/ilummc/eagletdl/EagletHandler.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EagletHandler<T> {
|
||||
|
||||
void handle(T event) ;
|
||||
|
||||
}
|
||||
477
src/main/scala/com/ilummc/eagletdl/EagletTask.java
Normal file
477
src/main/scala/com/ilummc/eagletdl/EagletTask.java
Normal file
@@ -0,0 +1,477 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class EagletTask {
|
||||
|
||||
private ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
Map<String, String> httpHeader = new ConcurrentHashMap<>();
|
||||
|
||||
private URL url;
|
||||
|
||||
EagletHandler<ErrorEvent> onError = event -> event.getException().printStackTrace();
|
||||
|
||||
private EagletHandler<StartEvent> onStart;
|
||||
|
||||
private EagletHandler<CompleteEvent> onComplete;
|
||||
|
||||
private EagletHandler<ConnectedEvent> onConnected;
|
||||
|
||||
private EagletHandler<ProgressEvent> onProgress;
|
||||
|
||||
private Proxy proxy;
|
||||
|
||||
private String md5, sha1, sha256;
|
||||
|
||||
String requestMethod = "GET";
|
||||
|
||||
private int threadAmount = 1;
|
||||
|
||||
int connectionTimeout = 7000;
|
||||
int readTimeout = 7000;
|
||||
int maxRetry = 5;
|
||||
|
||||
private File dest;
|
||||
|
||||
private transient boolean running = false;
|
||||
private transient long contentLength, maxBlockingTime = 7000;
|
||||
private transient ExecutorService executorService;
|
||||
private transient Thread monitor;
|
||||
|
||||
public EagletTask() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop this task forcefully, and the target file will not be removed.
|
||||
*/
|
||||
public void stop() {
|
||||
executorService.shutdownNow();
|
||||
monitor.interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the download file
|
||||
* <p>
|
||||
* 开始下载文件
|
||||
*/
|
||||
public EagletTask start() {
|
||||
// create thread pool for download
|
||||
executorService = Executors.newFixedThreadPool(threadAmount);
|
||||
// check if is already running
|
||||
if (running) {
|
||||
throw new AlreadyStartException();
|
||||
}
|
||||
// start the monitor thread
|
||||
monitor = new Thread(() -> {
|
||||
lock.lock();
|
||||
// fire a new start event
|
||||
if (onStart != null) {
|
||||
onStart.handle(new StartEvent(this));
|
||||
}
|
||||
try {
|
||||
// create the target file
|
||||
if (!dest.exists()) {
|
||||
dest.createNewFile();
|
||||
}
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
// set the connection properties
|
||||
httpHeader.forEach(connection::addRequestProperty);
|
||||
connection.setRequestMethod(requestMethod);
|
||||
connection.setConnectTimeout(30000);
|
||||
connection.setReadTimeout(30000);
|
||||
connection.connect();
|
||||
contentLength = connection.getContentLengthLong();
|
||||
// fire a new connected event
|
||||
// contains connection properties
|
||||
if (onConnected != null) {
|
||||
onConnected.handle(new ConnectedEvent(contentLength, this));
|
||||
}
|
||||
// if this is an unknown length task
|
||||
if (contentLength == -1 || threadAmount == 1) {
|
||||
// pass the connection instance to this new thread
|
||||
SingleThreadDownload download = new SingleThreadDownload(connection, dest, this);
|
||||
executorService.execute(download);
|
||||
long last = 0;
|
||||
do {
|
||||
Thread.sleep(500);
|
||||
// check the progress
|
||||
long progress = download.getCurrentProgress();
|
||||
// fire a new progress event
|
||||
if (onProgress != null) {
|
||||
onProgress.handle(new ProgressEvent(progress - last < 0 ? 0 : progress - last, this, ((double) progress) / Math.max((double) contentLength, 0D)));
|
||||
}
|
||||
last = progress;
|
||||
// check complete
|
||||
} while (last != contentLength && !download.isComplete());
|
||||
// close the thread pool, DoNotSupportMultipleThreadExceptionrelease resources
|
||||
executorService.shutdown();
|
||||
// change the running flag to false
|
||||
running = false;
|
||||
} else {
|
||||
List<SplitDownload> splitDownloads = new ArrayList<>();
|
||||
// Assign download task length
|
||||
long blockSize = contentLength / threadAmount;
|
||||
for (int threadId = 0; threadId < threadAmount; threadId++) {
|
||||
long startIndex = threadId * blockSize;
|
||||
long endIndex = (threadId + 1) * blockSize - 1;
|
||||
if (threadId == (threadAmount - 1)) {
|
||||
endIndex = contentLength - 1;
|
||||
}
|
||||
SplitDownload download = new SplitDownload(url, startIndex, endIndex, dest, this);
|
||||
// Start downloading
|
||||
executorService.execute(download);
|
||||
splitDownloads.add(download);
|
||||
}
|
||||
long last = 0;
|
||||
do {
|
||||
Thread.sleep(500);
|
||||
long progress = 0;
|
||||
// Collect download progress
|
||||
for (SplitDownload splitDownload : splitDownloads) {
|
||||
progress += splitDownload.getCurrentIndex() - splitDownload.startIndex;
|
||||
// blocked then restart from current index
|
||||
if (!splitDownload.isComplete() && System.currentTimeMillis() - splitDownload.getLastUpdateTime() > maxBlockingTime) {
|
||||
splitDownload.setStartIndex(splitDownload.getCurrentIndex());
|
||||
if (splitDownload.getRetry() <= maxRetry) {
|
||||
executorService.execute(splitDownload);
|
||||
} else {
|
||||
throw new RetryFailedException(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fire a progress event
|
||||
if (onProgress != null) {
|
||||
onProgress.handle(new ProgressEvent(progress - last, this,
|
||||
((double) progress) / ((double) contentLength)));
|
||||
}
|
||||
last = progress;
|
||||
// check complete
|
||||
} while (last < contentLength);
|
||||
// close the thread pool, release resources
|
||||
executorService.shutdown();
|
||||
// change the running flag to false
|
||||
running = false;
|
||||
}
|
||||
// check hash
|
||||
if (md5 != null && !md5.equalsIgnoreCase(HashUtil.md5(dest))) {
|
||||
throw new HashNotMatchException();
|
||||
}
|
||||
if (sha1 != null && !sha1.equalsIgnoreCase(HashUtil.sha1(dest))) {
|
||||
throw new HashNotMatchException();
|
||||
}
|
||||
if (sha256 != null && !sha256.equalsIgnoreCase(HashUtil.sha256(dest))) {
|
||||
throw new HashNotMatchException();
|
||||
}
|
||||
if (onComplete != null) {
|
||||
onComplete.handle(new CompleteEvent(this, true));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
onError.handle(new ErrorEvent(e, this));
|
||||
executorService.shutdown();
|
||||
if (onComplete != null) {
|
||||
onComplete.handle(new CompleteEvent(this, false));
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}, "EagletTaskMonitor");
|
||||
monitor.start();
|
||||
return this;
|
||||
}
|
||||
|
||||
public EagletTask waitUntil() {
|
||||
while (lock.tryLock()) {
|
||||
lock.unlock();
|
||||
}
|
||||
lock.lock();
|
||||
lock.unlock();
|
||||
return this;
|
||||
}
|
||||
|
||||
public EagletTask waitFor(long timeout, TimeUnit unit) {
|
||||
while (lock.tryLock()) {
|
||||
lock.unlock();
|
||||
}
|
||||
try {
|
||||
lock.tryLock(timeout, unit);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public EagletTask maxRetry(int maxRetry) {
|
||||
this.maxRetry = maxRetry;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sha256 hash of the download file. Case is not sensitive.
|
||||
* <p>
|
||||
* If the hash check failed, an error event will be fired.
|
||||
*
|
||||
* @param sha256 file sha1
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask sha256(String sha256) {
|
||||
this.sha256 = sha256;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sha1 hash of the download file. Case is not sensitive.
|
||||
* <p>
|
||||
* If the hash check failed, an error event will be fired.
|
||||
*
|
||||
* @param sha1 file sha1
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask sha1(String sha1) {
|
||||
this.sha1 = sha1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the md5 hash of the download file. Case is not sensitive.
|
||||
* <p>
|
||||
* If the hash check failed, an error event will be fired.
|
||||
*
|
||||
* @param md5 file md5
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask md5(String md5) {
|
||||
this.md5 = md5;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max blocked time per download thread.
|
||||
* <p>
|
||||
* If the thread blocks exceeded the provided time, this thread will re-start the task.
|
||||
*
|
||||
* @param maxBlockingTime time
|
||||
* @return task instance
|
||||
*/
|
||||
private EagletTask maxBlocking(long maxBlockingTime) {
|
||||
this.maxBlockingTime = maxBlockingTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the progress handler
|
||||
* <p>
|
||||
* This handler will be called every 1000 milli seconds.
|
||||
* <p>
|
||||
* 设置处理进度的时间监听器。该监听器的 handle 方法每秒调用一次。
|
||||
*
|
||||
* @param onProgress handler
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setOnProgress(EagletHandler<ProgressEvent> onProgress) {
|
||||
this.onProgress = onProgress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the download file
|
||||
*
|
||||
* @param file the file's absolute path
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask file(String file) {
|
||||
this.dest = new File(file);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the download file
|
||||
*
|
||||
* @param file the file
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask file(File file) {
|
||||
this.dest = file;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connected handler
|
||||
* <p>
|
||||
* This will be called when the connection is established
|
||||
* <p>
|
||||
* Async call
|
||||
*
|
||||
* @param onConnected onConnected event handler
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setOnConnected(EagletHandler<ConnectedEvent> onConnected) {
|
||||
this.onConnected = onConnected;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the read timeout, default is 7000
|
||||
*
|
||||
* @param timeout timeout
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask readTimeout(int timeout) {
|
||||
this.readTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection timeout, default is 7000
|
||||
*
|
||||
* @param timeout timeout
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask connectionTimeout(int timeout) {
|
||||
this.connectionTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request method, default is <code>GET</code>
|
||||
*
|
||||
* @param requestMethod the request method
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask requestMethod(String requestMethod) {
|
||||
this.requestMethod = requestMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the complete event handler
|
||||
* <p>
|
||||
* This handler will be called when everything is complete, and the downloaded file is available
|
||||
* <p>
|
||||
* Async call
|
||||
*
|
||||
* @param onComplete the handler
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setOnComplete(EagletHandler<CompleteEvent> onComplete) {
|
||||
this.onComplete = onComplete;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start handler
|
||||
* <p>
|
||||
* This handler will be called when the <code>start</code> method is called
|
||||
* <p>
|
||||
* Async call
|
||||
*
|
||||
* @param onStart the handler
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setOnStart(EagletHandler<StartEvent> onStart) {
|
||||
this.onStart = onStart;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the network proxy
|
||||
*
|
||||
* @param proxy the proxy
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask proxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error handler, default is to print the stack trace
|
||||
* <p>
|
||||
* This handler will be called when an exception is thrown
|
||||
* <p>
|
||||
* Async call
|
||||
*
|
||||
* @param onError the handler
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setOnError(EagletHandler<ErrorEvent> onError) {
|
||||
this.onError = onError;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set how much thread should be used to download, default is 1
|
||||
*
|
||||
* @param i thread amount
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask setThreads(int i) {
|
||||
if (i < 1) {
|
||||
throw new RuntimeException("Thread amount cannot be zero or negative!");
|
||||
}
|
||||
threadAmount = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the download source
|
||||
*
|
||||
* @param url the url
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask url(URL url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the download source
|
||||
*
|
||||
* @param url the url
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask url(String url) {
|
||||
try {
|
||||
this.url = new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
onError.handle(new ErrorEvent(e, this));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the http header field
|
||||
*
|
||||
* @return task instance
|
||||
*/
|
||||
public EagletTask clearHeaders() {
|
||||
httpHeader.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header field of the http request
|
||||
*
|
||||
* @param key header key
|
||||
* @param value header value
|
||||
* @return builder instance
|
||||
*/
|
||||
public EagletTask header(String key, String value) {
|
||||
httpHeader.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/scala/com/ilummc/eagletdl/ErrorEvent.java
Normal file
20
src/main/scala/com/ilummc/eagletdl/ErrorEvent.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class ErrorEvent {
|
||||
|
||||
private Throwable e;
|
||||
private EagletTask task;
|
||||
|
||||
public ErrorEvent(Throwable e, EagletTask task) {
|
||||
this.e = e;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class HashNotMatchException extends RuntimeException {
|
||||
}
|
||||
63
src/main/scala/com/ilummc/eagletdl/HashUtil.java
Normal file
63
src/main/scala/com/ilummc/eagletdl/HashUtil.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class HashUtil {
|
||||
|
||||
public static String sha256(File file) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA256");
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = -1;
|
||||
while ((length = fis.read(buffer, 0, 1024)) != -1) {
|
||||
md.update(buffer, 0, length);
|
||||
}
|
||||
BigInteger bigInt = new BigInteger(1, md.digest());
|
||||
return bigInt.toString(16);
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String sha1(File file) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = -1;
|
||||
while ((length = fis.read(buffer, 0, 1024)) != -1) {
|
||||
md.update(buffer, 0, length);
|
||||
}
|
||||
BigInteger bigInt = new BigInteger(1, md.digest());
|
||||
return bigInt.toString(16);
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String md5(File file) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = -1;
|
||||
while ((length = fis.read(buffer, 0, 1024)) != -1) {
|
||||
md.update(buffer, 0, length);
|
||||
}
|
||||
BigInteger bigInt = new BigInteger(1, md.digest());
|
||||
return bigInt.toString(16);
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
53
src/main/scala/com/ilummc/eagletdl/ProgressEvent.java
Normal file
53
src/main/scala/com/ilummc/eagletdl/ProgressEvent.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class ProgressEvent {
|
||||
|
||||
private long speed;
|
||||
private EagletTask task;
|
||||
private double percentage;
|
||||
|
||||
ProgressEvent(long speed, EagletTask task, double percentage) {
|
||||
this.speed = speed;
|
||||
this.task = task;
|
||||
this.percentage = percentage;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public long getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public double getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public String getPercentageFormatted() {
|
||||
return formatDouble(percentage * 100D) + " %";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the speed with format like <code>X.00 MiB</code>, <code>Y.50 GiB</code>, etc.
|
||||
*
|
||||
* @return formatted speed string
|
||||
*/
|
||||
public String getSpeedFormatted() {
|
||||
return format(getSpeed());
|
||||
}
|
||||
|
||||
private static String formatDouble(double d) {
|
||||
return new DecimalFormat("0.00").format(d);
|
||||
}
|
||||
|
||||
public static String format(long l) {
|
||||
if (l < 1024) return l + " B";
|
||||
if (l < 1024 * 1024) return formatDouble((double) l / 1024D) + " KiB";
|
||||
if (l < 1024 * 1024 * 1024) return formatDouble((double) l / (1024D * 1024D)) + " MiB";
|
||||
if (l < 1024 * 1024 * 1024 * 1024L) return formatDouble((double) l / (1024D * 1024D * 1024)) + " GiB";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
14
src/main/scala/com/ilummc/eagletdl/RetryFailedException.java
Normal file
14
src/main/scala/com/ilummc/eagletdl/RetryFailedException.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class RetryFailedException extends RuntimeException {
|
||||
|
||||
private EagletTask task;
|
||||
|
||||
RetryFailedException(EagletTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
49
src/main/scala/com/ilummc/eagletdl/SingleThreadDownload.java
Normal file
49
src/main/scala/com/ilummc/eagletdl/SingleThreadDownload.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
class SingleThreadDownload implements Runnable {
|
||||
|
||||
private HttpURLConnection connection;
|
||||
private File target;
|
||||
private EagletTask task;
|
||||
|
||||
private transient long currentProgress = 0, lastUpdateTime = System.currentTimeMillis();
|
||||
|
||||
private transient boolean complete = false;
|
||||
|
||||
SingleThreadDownload(HttpURLConnection connection, File target, EagletTask task) {
|
||||
this.connection = connection;
|
||||
this.target = target;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
long getLastUpdateTime() {
|
||||
return lastUpdateTime;
|
||||
}
|
||||
|
||||
long getCurrentProgress() {
|
||||
return currentProgress;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] buf = new byte[1024];
|
||||
int len = 0;
|
||||
try (BufferedInputStream stream = new BufferedInputStream(connection.getInputStream()); BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(target))) {
|
||||
while ((len = stream.read(buf)) > 0) {
|
||||
outputStream.write(buf, 0, len);
|
||||
currentProgress += len;
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
task.onError.handle(new ErrorEvent(e, task));
|
||||
}
|
||||
complete = true;
|
||||
}
|
||||
}
|
||||
92
src/main/scala/com/ilummc/eagletdl/SplitDownload.java
Normal file
92
src/main/scala/com/ilummc/eagletdl/SplitDownload.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
class SplitDownload implements Runnable {
|
||||
|
||||
private URL url;
|
||||
long startIndex, endIndex;
|
||||
private File target;
|
||||
private EagletTask task;
|
||||
|
||||
private transient long currentIndex, lastUpdateTime = System.currentTimeMillis(), tmpStart;
|
||||
private transient int retry = 0;
|
||||
private transient boolean complete;
|
||||
|
||||
SplitDownload(URL url, long startIndex, long endIndex, File dest, EagletTask task) {
|
||||
this.url = url;
|
||||
tmpStart = this.startIndex = this.currentIndex = startIndex;
|
||||
this.endIndex = endIndex;
|
||||
target = dest;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
void setStartIndex(long index) {
|
||||
this.tmpStart = index;
|
||||
}
|
||||
|
||||
long getLastUpdateTime() {
|
||||
return lastUpdateTime;
|
||||
}
|
||||
|
||||
long getCurrentIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
int getRetry() {
|
||||
return retry;
|
||||
}
|
||||
|
||||
boolean isComplete() {
|
||||
return complete || currentIndex == endIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
complete = false;
|
||||
currentIndex = tmpStart;
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
// set the connection properties
|
||||
task.httpHeader.forEach(connection::addRequestProperty);
|
||||
connection.setRequestMethod(task.requestMethod);
|
||||
connection.setConnectTimeout(task.connectionTimeout);
|
||||
connection.setReadTimeout(task.readTimeout);
|
||||
// set the download range
|
||||
connection.setRequestProperty("Range", "bytes=" + tmpStart + "-" + endIndex);
|
||||
connection.connect();
|
||||
// if response code not equals 206, it means that the server do not support multi thread downloading
|
||||
if (connection.getResponseCode() == 206) {
|
||||
RandomAccessFile file = new RandomAccessFile(target, "rwd");
|
||||
file.seek(tmpStart);
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
try (BufferedInputStream stream = new BufferedInputStream(connection.getInputStream())) {
|
||||
while ((len = stream.read(buf)) > 0) {
|
||||
file.write(buf, 0, len);
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
currentIndex += len;
|
||||
// some mysterious error occurred while downloading
|
||||
if (currentIndex >= endIndex + 2) {
|
||||
currentIndex = tmpStart;
|
||||
lastUpdateTime = 0;
|
||||
retry++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
complete = true;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
throw new DoNotSupportMultipleThreadException();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
task.onError.handle(new ErrorEvent(e, task));
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/main/scala/com/ilummc/eagletdl/StartEvent.java
Normal file
14
src/main/scala/com/ilummc/eagletdl/StartEvent.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.eagletdl;
|
||||
|
||||
public class StartEvent {
|
||||
|
||||
private EagletTask task;
|
||||
|
||||
StartEvent(EagletTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public EagletTask getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
114
src/main/scala/com/ilummc/tlib/ExampleMain.java
Normal file
114
src/main/scala/com/ilummc/tlib/ExampleMain.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.ilummc.tlib;
|
||||
|
||||
import com.ilummc.tlib.annotations.TConfig;
|
||||
import com.ilummc.tlib.bean.Property;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@TConfig(name = "cfg.yml", charset = "GBK")
|
||||
public class ExampleMain extends JavaPlugin {
|
||||
|
||||
private Property<Boolean> update = Property.of(false);
|
||||
|
||||
public static void main(String[] args) {
|
||||
MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
|
||||
System.out.println(bean.getHeapMemoryUsage().toString());
|
||||
System.out.println(bean.getNonHeapMemoryUsage().toString());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (GarbageCollectorMXBean mxBean : ManagementFactory.getGarbageCollectorMXBeans()) {
|
||||
System.out.println(mxBean.getName());
|
||||
System.out.println(mxBean.getCollectionCount());
|
||||
System.out.println(mxBean.getCollectionTime());
|
||||
for (String s : mxBean.getMemoryPoolNames()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
System.out.println(mxBean.getObjectName().toString());
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
for (String s : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
update.addListener(((oldVal, newVal) -> {
|
||||
Bukkit.getLogger().info("配置项 enableUpdate 的值由 " + oldVal + " 变为了 " + newVal);
|
||||
if (newVal) {
|
||||
Updater.start();
|
||||
} else {
|
||||
Updater.stop();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static class Updater {
|
||||
public static void start() {
|
||||
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class CD {
|
||||
|
||||
final long start, period;
|
||||
final TimeUnit unit;
|
||||
final Runnable onStart, onFinish, onTimer;
|
||||
|
||||
CD(long start, long period, TimeUnit unit, Runnable onStart, Runnable onFinish, Runnable onTimer) {
|
||||
this.start = start;
|
||||
this.period = period;
|
||||
this.unit = unit;
|
||||
this.onStart = onStart;
|
||||
this.onFinish = onFinish;
|
||||
this.onTimer = onTimer;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CD.builder().setOnStart(() -> {
|
||||
}).setOnFinish(() -> {
|
||||
}).setOnTimer(1000, TimeUnit.MILLISECONDS, () -> {
|
||||
}).build();
|
||||
}
|
||||
|
||||
public static CdBuilder builder() {
|
||||
return new CdBuilder();
|
||||
}
|
||||
|
||||
private static class CdBuilder {
|
||||
private long start, period;
|
||||
private TimeUnit unit;
|
||||
private Runnable onStart, onFinish, onTimer;
|
||||
|
||||
public CdBuilder setOnStart(Runnable runnable) {
|
||||
this.onStart = runnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CdBuilder setOnFinish(Runnable runnable) {
|
||||
this.onFinish = runnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CdBuilder setOnTimer(long period, TimeUnit timeUnit, Runnable runnable) {
|
||||
this.period = period;
|
||||
this.unit = timeUnit;
|
||||
this.onTimer = runnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CD build() {
|
||||
return new CD(start, period, unit, onStart, onFinish, onTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
137
src/main/scala/com/ilummc/tlib/TLib.java
Normal file
137
src/main/scala/com/ilummc/tlib/TLib.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package com.ilummc.tlib;
|
||||
|
||||
import com.ilummc.tlib.annotations.Dependency;
|
||||
import com.ilummc.tlib.compat.PlaceholderHook;
|
||||
import com.ilummc.tlib.config.TLibConfig;
|
||||
import com.ilummc.tlib.db.Pool;
|
||||
import com.ilummc.tlib.inject.TConfigWatcher;
|
||||
import com.ilummc.tlib.inject.TDependencyInjector;
|
||||
import com.ilummc.tlib.inject.TPluginManager;
|
||||
import com.ilummc.tlib.logger.TLogger;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleLoader;
|
||||
import com.ilummc.tlib.util.IO;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import me.skymc.taboolib.fileutils.FileUtils;
|
||||
import me.skymc.taboolib.plugin.PluginUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "com.zaxxer:HikariCP:3.1.0")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.slf4j:slf4j-api:1.7.25")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:activejdbc:2.0")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:javalite-common:2.0")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:app-config:2.0")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.codehaus.jackson:jackson-mapper-asl:1.9.13")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.codehaus.jackson:jackson-core-asl:1.9.13")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "jaxen:jaxen:1.1.6")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "dom4j:dom4j:1.6.1")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "xml-apis:xml-apis:1.0.b2")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.ehcache:ehcache:3.5.2")
|
||||
@Dependency(type = Dependency.Type.LIBRARY, maven = "com.h2database:h2:1.4.197")
|
||||
public class TLib {
|
||||
|
||||
private static TLib tLib;
|
||||
private static YamlConfiguration internalLanguage;
|
||||
private TLogger logger = new TLogger("§8[§3§lTabooLib§8][§r{1}§8] §f{2}", Main.getInst(), TLogger.FINE);
|
||||
private TLibConfig config;
|
||||
private TConfigWatcher configWatcher = new TConfigWatcher();
|
||||
private File libsFolder;
|
||||
|
||||
private TLib() {
|
||||
libsFolder = new File(Main.getInst().getDataFolder(), "/libs");
|
||||
if (!libsFolder.exists()) {
|
||||
libsFolder.mkdirs();
|
||||
}
|
||||
try {
|
||||
String yamlText = new String(IO.readFully(FileUtils.getResource("lang/internal.yml")), Charset.forName("utf-8"));
|
||||
internalLanguage = new YamlConfiguration();
|
||||
internalLanguage.loadFromString(yamlText);
|
||||
} catch (IOException | InvalidConfigurationException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
tLib = new TLib();
|
||||
|
||||
TLocaleLoader.init();
|
||||
PlaceholderHook.init();
|
||||
TLocaleLoader.load(Main.getInst(), false);
|
||||
}
|
||||
|
||||
public static void initPost() {
|
||||
TDependencyInjector.inject(Main.getInst(), TLib.getTLib());
|
||||
try {
|
||||
Pool.init();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void unload() {
|
||||
try {
|
||||
Pool.unload();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
tLib.getConfigWatcher().unregisterAll();
|
||||
TDependencyInjector.eject(Main.getInst(), tLib);
|
||||
|
||||
}
|
||||
|
||||
public static void injectPluginManager() {
|
||||
if (!tLib.isInjectEnabled() || tLib.isBlackListPluginExists()) {
|
||||
TLocale.Logger.fatal("TLIB.INJECTION-DISABLED");
|
||||
Arrays.stream(Bukkit.getPluginManager().getPlugins()).filter(plugin -> plugin != Main.getInst()).forEach(plugin -> TDependencyInjector.inject(plugin, plugin));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Field field = Bukkit.getServer().getClass().getDeclaredField("pluginManager");
|
||||
field.setAccessible(true);
|
||||
field.set(Bukkit.getServer(), new TPluginManager());
|
||||
TLocale.Logger.info("TLIB.INJECTION-SUCCESS");
|
||||
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException ignored) {
|
||||
TLocale.Logger.error("TLIB.INJECTION-FAILED");
|
||||
Arrays.stream(Bukkit.getPluginManager().getPlugins()).filter(plugin -> !TabooLib.isTabooLib(plugin)).forEach(plugin -> TDependencyInjector.inject(plugin, plugin));
|
||||
}
|
||||
}
|
||||
|
||||
public static TLib getTLib() {
|
||||
return tLib;
|
||||
}
|
||||
|
||||
public static YamlConfiguration getInternalLanguage() {
|
||||
return internalLanguage;
|
||||
}
|
||||
|
||||
public TLogger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public TLibConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public TConfigWatcher getConfigWatcher() {
|
||||
return configWatcher;
|
||||
}
|
||||
|
||||
public File getLibsFolder() {
|
||||
return libsFolder;
|
||||
}
|
||||
|
||||
public boolean isInjectEnabled() {
|
||||
return Main.getInst().getConfig().getBoolean("PLUGIN-INJECTOR.ENABLE", true);
|
||||
}
|
||||
|
||||
public boolean isBlackListPluginExists() {
|
||||
return Main.getInst().getConfig().getStringList("PLUGIN-INJECTOR.DISABLE-ON-PLUGIN-EXISTS").stream().anyMatch(PluginUtils::isPluginExists);
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/scala/com/ilummc/tlib/annotations/Dependencies.java
Normal file
14
src/main/scala/com/ilummc/tlib/annotations/Dependencies.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Dependencies {
|
||||
|
||||
Dependency[] value();
|
||||
|
||||
}
|
||||
24
src/main/scala/com/ilummc/tlib/annotations/Dependency.java
Normal file
24
src/main/scala/com/ilummc/tlib/annotations/Dependency.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import com.ilummc.tlib.dependency.TDependency;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(Dependencies.class)
|
||||
public @interface Dependency {
|
||||
|
||||
enum Type {PLUGIN, LIBRARY}
|
||||
|
||||
Type type();
|
||||
|
||||
String plugin() default "";
|
||||
|
||||
String maven() default "";
|
||||
|
||||
String mavenRepo() default TDependency.MAVEN_REPO;
|
||||
|
||||
String url() default "";
|
||||
|
||||
}
|
||||
16
src/main/scala/com/ilummc/tlib/annotations/Logger.java
Normal file
16
src/main/scala/com/ilummc/tlib/annotations/Logger.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Logger {
|
||||
|
||||
String value() default "[{0}|{1}§f] {2}";
|
||||
|
||||
int level() default com.ilummc.tlib.logger.TLogger.INFO;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PluginInstance {
|
||||
|
||||
String value();
|
||||
}
|
||||
29
src/main/scala/com/ilummc/tlib/annotations/TConfig.java
Normal file
29
src/main/scala/com/ilummc/tlib/annotations/TConfig.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import com.ilummc.tlib.util.Ref;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TConfig {
|
||||
|
||||
String name() default "config.yml";
|
||||
|
||||
boolean fromJar() default false;
|
||||
|
||||
boolean saveOnExit() default false;
|
||||
|
||||
boolean readOnly() default true;
|
||||
|
||||
String charset() default "UTF-8";
|
||||
|
||||
boolean listenChanges() default false;
|
||||
|
||||
int excludeModifiers() default Modifier.STATIC | Modifier.TRANSIENT | Ref.ACC_SYNTHETIC | Ref.ACC_BRIDGE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ilummc.tlib.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TLocalePlugin {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ilummc.tlib.annotations.clr;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(CommandHandlers.class)
|
||||
public @interface CommandHandler {
|
||||
|
||||
/**
|
||||
* Name of the command
|
||||
*
|
||||
* @return Name of the command
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.tlib.annotations.clr;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CommandHandlers {
|
||||
|
||||
CommandHandler[] value();
|
||||
|
||||
}
|
||||
14
src/main/scala/com/ilummc/tlib/annotations/clr/Sub.java
Normal file
14
src/main/scala/com/ilummc/tlib/annotations/clr/Sub.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.tlib.annotations.clr;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Sub {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
11
src/main/scala/com/ilummc/tlib/annotations/db/Database.java
Normal file
11
src/main/scala/com/ilummc/tlib/annotations/db/Database.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
public @interface Database {
|
||||
|
||||
boolean sharedPool() default true;
|
||||
|
||||
int poolSize() default 8;
|
||||
|
||||
Class<?> configClass();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
public @interface DatabasePassword {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
public @interface DatabaseType {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
public @interface DatabaseUrl {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
public @interface DatabaseUser {
|
||||
}
|
||||
14
src/main/scala/com/ilummc/tlib/annotations/db/SQLTable.java
Normal file
14
src/main/scala/com/ilummc/tlib/annotations/db/SQLTable.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.ilummc.tlib.annotations.db;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SQLTable {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
43
src/main/scala/com/ilummc/tlib/bean/Property.java
Normal file
43
src/main/scala/com/ilummc/tlib/bean/Property.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.ilummc.tlib.bean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class Property<T> {
|
||||
|
||||
private Property(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private List<BiConsumer<T, T>> consumers;
|
||||
|
||||
private T value;
|
||||
|
||||
public void set(T value) {
|
||||
if (value != this.value) {
|
||||
if (consumers != null) {
|
||||
for (BiConsumer<T, T> consumer : consumers) {
|
||||
consumer.accept(this.value, value);
|
||||
}
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void addListener(BiConsumer<T, T> consumer) {
|
||||
if (consumers == null) {
|
||||
consumers = new ArrayList<>();
|
||||
}
|
||||
consumers.add(consumer);
|
||||
}
|
||||
|
||||
public static <T> Property<T> of(T value) {
|
||||
return new Property<>(value);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/main/scala/com/ilummc/tlib/bean/PropertyTypeAdaptor.java
Normal file
16
src/main/scala/com/ilummc/tlib/bean/PropertyTypeAdaptor.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.ilummc.tlib.bean;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class PropertyTypeAdaptor implements JsonDeserializer<Property> {
|
||||
|
||||
@Override
|
||||
public Property deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
180
src/main/scala/com/ilummc/tlib/bungee/api/ChatColor.java
Normal file
180
src/main/scala/com/ilummc/tlib/bungee/api/ChatColor.java
Normal file
@@ -0,0 +1,180 @@
|
||||
package com.ilummc.tlib.bungee.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Simplistic enumeration of all supported color values for chat.
|
||||
* @author md_5
|
||||
*/
|
||||
public enum ChatColor {
|
||||
|
||||
/**
|
||||
* Represents black.
|
||||
*/
|
||||
BLACK('0', "black"),
|
||||
/**
|
||||
* Represents dark blue.
|
||||
*/
|
||||
DARK_BLUE('1', "dark_blue"),
|
||||
/**
|
||||
* Represents dark green.
|
||||
*/
|
||||
DARK_GREEN('2', "dark_green"),
|
||||
/**
|
||||
* Represents dark blue (aqua).
|
||||
*/
|
||||
DARK_AQUA('3', "dark_aqua"),
|
||||
/**
|
||||
* Represents dark red.
|
||||
*/
|
||||
DARK_RED('4', "dark_red"),
|
||||
/**
|
||||
* Represents dark purple.
|
||||
*/
|
||||
DARK_PURPLE('5', "dark_purple"),
|
||||
/**
|
||||
* Represents gold.
|
||||
*/
|
||||
GOLD('6', "gold"),
|
||||
/**
|
||||
* Represents gray.
|
||||
*/
|
||||
GRAY('7', "gray"),
|
||||
/**
|
||||
* Represents dark gray.
|
||||
*/
|
||||
DARK_GRAY('8', "dark_gray"),
|
||||
/**
|
||||
* Represents blue.
|
||||
*/
|
||||
BLUE('9', "blue"),
|
||||
/**
|
||||
* Represents green.
|
||||
*/
|
||||
GREEN('a', "green"),
|
||||
/**
|
||||
* Represents aqua.
|
||||
*/
|
||||
AQUA('b', "aqua"),
|
||||
/**
|
||||
* Represents red.
|
||||
*/
|
||||
RED('c', "red"),
|
||||
/**
|
||||
* Represents light purple.
|
||||
*/
|
||||
LIGHT_PURPLE('d', "light_purple"),
|
||||
/**
|
||||
* Represents yellow.
|
||||
*/
|
||||
YELLOW('e', "yellow"),
|
||||
/**
|
||||
* Represents white.
|
||||
*/
|
||||
WHITE('f', "white"),
|
||||
/**
|
||||
* Represents magical characters that change around randomly.
|
||||
*/
|
||||
MAGIC('k', "obfuscated"),
|
||||
/**
|
||||
* Makes the text bold.
|
||||
*/
|
||||
BOLD('l', "bold"),
|
||||
/**
|
||||
* Makes a line appear through the text.
|
||||
*/
|
||||
STRIKETHROUGH('m', "strikethrough"),
|
||||
/**
|
||||
* Makes the text appear underlined.
|
||||
*/
|
||||
UNDERLINE('n', "underline"),
|
||||
/**
|
||||
* Makes the text italic.
|
||||
*/
|
||||
ITALIC('o', "italic"),
|
||||
/**
|
||||
* Resets all previous chat colors or formats.
|
||||
*/
|
||||
RESET('r', "reset");
|
||||
/**
|
||||
* The special character which prefixes all chat colour codes. Use this if
|
||||
* you need to dynamically convert colour codes from your custom format.
|
||||
*/
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRr";
|
||||
/**
|
||||
* Pattern to remove all colour codes.
|
||||
*/
|
||||
public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]");
|
||||
/**
|
||||
* Colour instances keyed by their active character.
|
||||
*/
|
||||
private static final Map<Character, ChatColor> BY_CHAR = new HashMap<Character, ChatColor>();
|
||||
/**
|
||||
* The code appended to {@link #COLOR_CHAR} to make usable colour.
|
||||
*/
|
||||
private final char code;
|
||||
/**
|
||||
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
|
||||
*/
|
||||
private final String toString;
|
||||
|
||||
private final String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
static {
|
||||
for (ChatColor colour : values()) {
|
||||
BY_CHAR.put(colour.code, colour);
|
||||
}
|
||||
}
|
||||
|
||||
ChatColor(char code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.toString = new String(new char[]{COLOR_CHAR, code});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the given message of all color codes
|
||||
*
|
||||
* @param input String to strip of color
|
||||
* @return A copy of the input string, without any coloring
|
||||
*/
|
||||
public static String stripColor(final String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
for (int i = 0; i < b.length - 1; i++) {
|
||||
if (b[i] == altColorChar && ALL_CODES.indexOf(b[i + 1]) > -1) {
|
||||
b[i] = ChatColor.COLOR_CHAR;
|
||||
b[i + 1] = Character.toLowerCase(b[i + 1]);
|
||||
}
|
||||
}
|
||||
return new String(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the colour represented by the specified code.
|
||||
*
|
||||
* @param code the code to search for
|
||||
* @return the mapped colour, or null if non exists
|
||||
*/
|
||||
public static ChatColor getByChar(char code) {
|
||||
return BY_CHAR.get(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ilummc.tlib.bungee.api;
|
||||
|
||||
/**
|
||||
* Represents the position on the screen where a message will appear.
|
||||
* @author md_5
|
||||
*/
|
||||
public enum ChatMessageType {
|
||||
|
||||
CHAT,
|
||||
SYSTEM,
|
||||
ACTION_BAR
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
import com.ilummc.tlib.bungee.api.chat.ComponentBuilder.FormatRetention;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public abstract class BaseComponent {
|
||||
|
||||
BaseComponent parent;
|
||||
|
||||
/**
|
||||
* The color of this component and any child components (unless overridden)
|
||||
*/
|
||||
private ChatColor color;
|
||||
/**
|
||||
* Whether this component and any child components (unless overridden) is
|
||||
* bold
|
||||
*/
|
||||
private Boolean bold;
|
||||
/**
|
||||
* Whether this component and any child components (unless overridden) is
|
||||
* italic
|
||||
*/
|
||||
private Boolean italic;
|
||||
/**
|
||||
* Whether this component and any child components (unless overridden) is
|
||||
* underlined
|
||||
*/
|
||||
private Boolean underlined;
|
||||
/**
|
||||
* Whether this component and any child components (unless overridden) is
|
||||
* strikethrough
|
||||
*/
|
||||
private Boolean strikethrough;
|
||||
/**
|
||||
* Whether this component and any child components (unless overridden) is
|
||||
* obfuscated
|
||||
*/
|
||||
private Boolean obfuscated;
|
||||
/**
|
||||
* The text to insert into the chat when this component (and child
|
||||
* components) are clicked while pressing the shift key
|
||||
*/
|
||||
private String insertion;
|
||||
|
||||
/**
|
||||
* Appended components that inherit this component's formatting and events
|
||||
*/
|
||||
private List<BaseComponent> extra;
|
||||
|
||||
/**
|
||||
* The action to perform when this component (and child components) are
|
||||
* clicked
|
||||
*/
|
||||
private ClickEvent clickEvent;
|
||||
/**
|
||||
* The action to perform when this component (and child components) are
|
||||
* hovered over
|
||||
*/
|
||||
private HoverEvent hoverEvent;
|
||||
|
||||
public String getInsertion() {
|
||||
return insertion;
|
||||
}
|
||||
|
||||
public List<BaseComponent> getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public ClickEvent getClickEvent() {
|
||||
return clickEvent;
|
||||
}
|
||||
|
||||
public HoverEvent getHoverEvent() {
|
||||
return hoverEvent;
|
||||
}
|
||||
|
||||
public void setParent(BaseComponent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setColor(ChatColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
|
||||
public void setUnderlined(Boolean underlined) {
|
||||
this.underlined = underlined;
|
||||
}
|
||||
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
|
||||
public void setObfuscated(Boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
public void setInsertion(String insertion) {
|
||||
this.insertion = insertion;
|
||||
}
|
||||
|
||||
public void setClickEvent(ClickEvent clickEvent) {
|
||||
this.clickEvent = clickEvent;
|
||||
}
|
||||
|
||||
public void setHoverEvent(HoverEvent hoverEvent) {
|
||||
this.hoverEvent = hoverEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "parent=" + "BaseComponent{" + parent + ", color=" + color + ", bold=" + bold + ", italic=" + italic + ", underlined=" + underlined + ", strikethrough=" + strikethrough + ", obfuscated=" + obfuscated + ", insertion='" + insertion + '\'' + ", extra=" + extra + ", clickEvent=" + clickEvent + ", hoverEvent=" + hoverEvent + '}';
|
||||
}
|
||||
|
||||
BaseComponent() {
|
||||
}
|
||||
|
||||
BaseComponent(BaseComponent old) {
|
||||
copyFormatting(old, FormatRetention.ALL, true);
|
||||
|
||||
if (old.getExtra() != null) {
|
||||
for (BaseComponent extra : old.getExtra()) {
|
||||
addExtra(extra.duplicate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the events and formatting of a BaseComponent. Already set
|
||||
* formatting will be replaced.
|
||||
*
|
||||
* @param component the component to copy from
|
||||
*/
|
||||
public void copyFormatting(BaseComponent component) {
|
||||
copyFormatting(component, FormatRetention.ALL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the events and formatting of a BaseComponent.
|
||||
*
|
||||
* @param component the component to copy from
|
||||
* @param replace if already set formatting should be replaced by the new
|
||||
* component
|
||||
*/
|
||||
public void copyFormatting(BaseComponent component, boolean replace) {
|
||||
copyFormatting(component, FormatRetention.ALL, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified formatting of a BaseComponent.
|
||||
*
|
||||
* @param component the component to copy from
|
||||
* @param retention the formatting to copy
|
||||
* @param replace if already set formatting should be replaced by the new
|
||||
* component
|
||||
*/
|
||||
public void copyFormatting(BaseComponent component, FormatRetention retention, boolean replace) {
|
||||
if (retention == FormatRetention.EVENTS || retention == FormatRetention.ALL) {
|
||||
if (replace || clickEvent == null) {
|
||||
setClickEvent(component.getClickEvent());
|
||||
}
|
||||
if (replace || hoverEvent == null) {
|
||||
setHoverEvent(component.getHoverEvent());
|
||||
}
|
||||
}
|
||||
if (retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL) {
|
||||
if (replace || color == null) {
|
||||
setColor(component.getColorRaw());
|
||||
}
|
||||
if (replace || bold == null) {
|
||||
setBold(component.isBoldRaw());
|
||||
}
|
||||
if (replace || italic == null) {
|
||||
setItalic(component.isItalicRaw());
|
||||
}
|
||||
if (replace || underlined == null) {
|
||||
setUnderlined(component.isUnderlinedRaw());
|
||||
}
|
||||
if (replace || strikethrough == null) {
|
||||
setStrikethrough(component.isStrikethroughRaw());
|
||||
}
|
||||
if (replace || obfuscated == null) {
|
||||
setObfuscated(component.isObfuscatedRaw());
|
||||
}
|
||||
if (replace || insertion == null) {
|
||||
setInsertion(component.getInsertion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retains only the specified formatting.
|
||||
*
|
||||
* @param retention the formatting to retain
|
||||
*/
|
||||
public void retain(FormatRetention retention) {
|
||||
if (retention == FormatRetention.FORMATTING || retention == FormatRetention.NONE) {
|
||||
setClickEvent(null);
|
||||
setHoverEvent(null);
|
||||
}
|
||||
if (retention == FormatRetention.EVENTS || retention == FormatRetention.NONE) {
|
||||
setColor(null);
|
||||
setBold(null);
|
||||
setItalic(null);
|
||||
setUnderlined(null);
|
||||
setStrikethrough(null);
|
||||
setObfuscated(null);
|
||||
setInsertion(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the BaseComponent and returns the clone.
|
||||
*
|
||||
* @return The duplicate of this BaseComponent
|
||||
*/
|
||||
public abstract BaseComponent duplicate();
|
||||
|
||||
/**
|
||||
* Clones the BaseComponent without formatting and returns the clone.
|
||||
*
|
||||
* @return The duplicate of this BaseComponent
|
||||
* @deprecated API use discouraged, use traditional duplicate
|
||||
*/
|
||||
@Deprecated
|
||||
public BaseComponent duplicateWithoutFormatting() {
|
||||
BaseComponent component = duplicate();
|
||||
component.retain(FormatRetention.NONE);
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the components to a string that uses the old formatting codes
|
||||
* ({@link ChatColor#COLOR_CHAR}
|
||||
*
|
||||
* @param components the components to convert
|
||||
* @return the string in the old format
|
||||
*/
|
||||
public static String toLegacyText(BaseComponent... components) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (BaseComponent msg : components) {
|
||||
builder.append(msg.toLegacyText());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the components into a string without any formatting
|
||||
*
|
||||
* @param components the components to convert
|
||||
* @return the string as plain text
|
||||
*/
|
||||
public static String toPlainText(BaseComponent... components) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (BaseComponent msg : components) {
|
||||
builder.append(msg.toPlainText());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color of this component. This uses the parent's color if this
|
||||
* component doesn't have one. {@link ChatColor#WHITE}
|
||||
* is returned if no color is found.
|
||||
*
|
||||
* @return the color of this component
|
||||
*/
|
||||
public ChatColor getColor() {
|
||||
if (color == null) {
|
||||
if (parent == null) {
|
||||
return ChatColor.WHITE;
|
||||
}
|
||||
return parent.getColor();
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color of this component without checking the parents color.
|
||||
* May return null
|
||||
*
|
||||
* @return the color of this component
|
||||
*/
|
||||
public ChatColor getColorRaw() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is bold. This uses the parent's setting if
|
||||
* this component hasn't been set. false is returned if none of the parent
|
||||
* chain has been set.
|
||||
*
|
||||
* @return whether the component is bold
|
||||
*/
|
||||
public boolean isBold() {
|
||||
if (bold == null) {
|
||||
return parent != null && parent.isBold();
|
||||
}
|
||||
return bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is bold without checking the parents
|
||||
* setting. May return null
|
||||
*
|
||||
* @return whether the component is bold
|
||||
*/
|
||||
public Boolean isBoldRaw() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is italic. This uses the parent's setting
|
||||
* if this component hasn't been set. false is returned if none of the
|
||||
* parent chain has been set.
|
||||
*
|
||||
* @return whether the component is italic
|
||||
*/
|
||||
public boolean isItalic() {
|
||||
if (italic == null) {
|
||||
return parent != null && parent.isItalic();
|
||||
}
|
||||
return italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is italic without checking the parents
|
||||
* setting. May return null
|
||||
*
|
||||
* @return whether the component is italic
|
||||
*/
|
||||
public Boolean isItalicRaw() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is underlined. This uses the parent's
|
||||
* setting if this component hasn't been set. false is returned if none of
|
||||
* the parent chain has been set.
|
||||
*
|
||||
* @return whether the component is underlined
|
||||
*/
|
||||
public boolean isUnderlined() {
|
||||
if (underlined == null) {
|
||||
return parent != null && parent.isUnderlined();
|
||||
}
|
||||
return underlined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is underlined without checking the parents
|
||||
* setting. May return null
|
||||
*
|
||||
* @return whether the component is underlined
|
||||
*/
|
||||
public Boolean isUnderlinedRaw() {
|
||||
return underlined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is strikethrough. This uses the parent's
|
||||
* setting if this component hasn't been set. false is returned if none of
|
||||
* the parent chain has been set.
|
||||
*
|
||||
* @return whether the component is strikethrough
|
||||
*/
|
||||
public boolean isStrikethrough() {
|
||||
if (strikethrough == null) {
|
||||
return parent != null && parent.isStrikethrough();
|
||||
}
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is strikethrough without checking the
|
||||
* parents setting. May return null
|
||||
*
|
||||
* @return whether the component is strikethrough
|
||||
*/
|
||||
public Boolean isStrikethroughRaw() {
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is obfuscated. This uses the parent's
|
||||
* setting if this component hasn't been set. false is returned if none of
|
||||
* the parent chain has been set.
|
||||
*
|
||||
* @return whether the component is obfuscated
|
||||
*/
|
||||
public boolean isObfuscated() {
|
||||
if (obfuscated == null) {
|
||||
return parent != null && parent.isObfuscated();
|
||||
}
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this component is obfuscated without checking the parents
|
||||
* setting. May return null
|
||||
*
|
||||
* @return whether the component is obfuscated
|
||||
*/
|
||||
public Boolean isObfuscatedRaw() {
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
public void setExtra(List<BaseComponent> components) {
|
||||
components.forEach(component -> component.parent = this);
|
||||
extra = components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a text element to the component. The text will inherit this
|
||||
* component's formatting
|
||||
*
|
||||
* @param text the text to append
|
||||
*/
|
||||
public void addExtra(String text) {
|
||||
addExtra(new TextComponent(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a component to the component. The text will inherit this
|
||||
* component's formatting
|
||||
*
|
||||
* @param component the component to append
|
||||
*/
|
||||
public void addExtra(BaseComponent component) {
|
||||
if (extra == null) {
|
||||
extra = new ArrayList<>();
|
||||
}
|
||||
component.parent = this;
|
||||
extra.add(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the component has any formatting or events applied to it
|
||||
*
|
||||
* @return Whether any formatting or events are applied
|
||||
*/
|
||||
public boolean hasFormatting() {
|
||||
return color != null || italic != null || bold != null || underlined != null || strikethrough != null || obfuscated != null || insertion != null || hoverEvent != null || clickEvent != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the component into a string without any formatting
|
||||
*
|
||||
* @return the string as plain text
|
||||
*/
|
||||
public String toPlainText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
toPlainText(builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
void toPlainText(StringBuilder builder) {
|
||||
if (extra != null) {
|
||||
extra.forEach(e -> e.toPlainText(builder));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the component to a string that uses the old formatting codes
|
||||
* ({@link ChatColor#COLOR_CHAR}
|
||||
*
|
||||
* @return the string in the old format
|
||||
*/
|
||||
public String toLegacyText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
toLegacyText(builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
void toLegacyText(StringBuilder builder) {
|
||||
if (extra != null) {
|
||||
extra.forEach(e -> e.toLegacyText(builder));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public final class ClickEvent {
|
||||
|
||||
/**
|
||||
* The type of action to perform on click
|
||||
*/
|
||||
private final Action action;
|
||||
/**
|
||||
* Depends on action
|
||||
*
|
||||
* @see Action
|
||||
*/
|
||||
private final String value;
|
||||
|
||||
public ClickEvent(Action action, String value) {
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
|
||||
/**
|
||||
* Open a url at the path given by
|
||||
* {@link ClickEvent#value}
|
||||
*/
|
||||
OPEN_URL,
|
||||
/**
|
||||
* Open a file at the path given by
|
||||
* {@link ClickEvent#value}
|
||||
*/
|
||||
OPEN_FILE,
|
||||
/**
|
||||
* Run the command given by
|
||||
* {@link ClickEvent#value}
|
||||
*/
|
||||
RUN_COMMAND,
|
||||
/**
|
||||
* Inserts the string given by
|
||||
* {@link ClickEvent#value} into the players
|
||||
* text box
|
||||
*/
|
||||
SUGGEST_COMMAND,
|
||||
/**
|
||||
* Change to the page number given by
|
||||
* {@link ClickEvent#value} in a book
|
||||
*/
|
||||
CHANGE_PAGE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* ComponentBuilder simplifies creating basic messages by allowing the use of a
|
||||
* chainable builder.
|
||||
* </p>
|
||||
* <pre>
|
||||
* new ComponentBuilder("Hello ").color(ChatColor.RED).
|
||||
* append("World").color(ChatColor.BLUE). append("!").bold(true).create();
|
||||
* </pre>
|
||||
* <p>
|
||||
* All methods (excluding {@link #append(String)} and {@link #create()} work on
|
||||
* the last part appended to the builder, so in the example above "Hello " would
|
||||
* be {@link ChatColor#RED} and "World" would be
|
||||
* {@link ChatColor#BLUE} but "!" would be bold and
|
||||
* {@link ChatColor#BLUE} because append copies the previous
|
||||
* part's formatting
|
||||
* </p>
|
||||
*
|
||||
* @author md_5
|
||||
*/
|
||||
public final class ComponentBuilder {
|
||||
|
||||
private BaseComponent current;
|
||||
private final List<BaseComponent> parts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a ComponentBuilder from the other given ComponentBuilder to clone
|
||||
* it.
|
||||
*
|
||||
* @param original the original for the new ComponentBuilder.
|
||||
*/
|
||||
public ComponentBuilder(ComponentBuilder original) {
|
||||
current = original.current.duplicate();
|
||||
original.parts.stream().map(BaseComponent::duplicate).forEach(parts::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ComponentBuilder with the given text as the first part.
|
||||
*
|
||||
* @param text the first text element
|
||||
*/
|
||||
public ComponentBuilder(String text) {
|
||||
current = new TextComponent(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ComponentBuilder with the given component as the first part.
|
||||
*
|
||||
* @param component the first component element
|
||||
*/
|
||||
public ComponentBuilder(BaseComponent component) {
|
||||
current = component.duplicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a component to the builder and makes it the current target for
|
||||
* formatting. The component will have all the formatting from previous
|
||||
* part.
|
||||
*
|
||||
* @param component the component to append
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(BaseComponent component) {
|
||||
return append(component, FormatRetention.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a component to the builder and makes it the current target for
|
||||
* formatting. You can specify the amount of formatting retained from
|
||||
* previous part.
|
||||
*
|
||||
* @param component the component to append
|
||||
* @param retention the formatting to retain
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(BaseComponent component, FormatRetention retention) {
|
||||
parts.add(current);
|
||||
|
||||
BaseComponent previous = current;
|
||||
current = component.duplicate();
|
||||
current.copyFormatting(previous, retention, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the components to the builder and makes the last element the
|
||||
* current target for formatting. The components will have all the
|
||||
* formatting from previous part.
|
||||
*
|
||||
* @param components the components to append
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(BaseComponent[] components) {
|
||||
return append(components, FormatRetention.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the components to the builder and makes the last element the
|
||||
* current target for formatting. You can specify the amount of formatting
|
||||
* retained from previous part.
|
||||
*
|
||||
* @param components the components to append
|
||||
* @param retention the formatting to retain
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(BaseComponent[] components, FormatRetention retention) {
|
||||
Preconditions.checkArgument(components.length != 0, "No components to append");
|
||||
|
||||
BaseComponent previous = current;
|
||||
for (BaseComponent component : components) {
|
||||
parts.add(current);
|
||||
|
||||
current = component.duplicate();
|
||||
current.copyFormatting(previous, retention, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the text to the builder and makes it the current target for
|
||||
* formatting. The text will have all the formatting from previous part.
|
||||
*
|
||||
* @param text the text to append
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(String text) {
|
||||
return append(text, FormatRetention.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the text to the builder and makes it the current target for
|
||||
* formatting. You can specify the amount of formatting retained from
|
||||
* previous part.
|
||||
*
|
||||
* @param text the text to append
|
||||
* @param retention the formatting to retain
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder append(String text, FormatRetention retention) {
|
||||
parts.add(current);
|
||||
|
||||
BaseComponent old = current;
|
||||
current = new TextComponent(text);
|
||||
current.copyFormatting(old, retention, false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the current part.
|
||||
*
|
||||
* @param color the new color
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder color(ChatColor color) {
|
||||
current.setColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the current part is bold.
|
||||
*
|
||||
* @param bold whether this part is bold
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder bold(boolean bold) {
|
||||
current.setBold(bold);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the current part is italic.
|
||||
*
|
||||
* @param italic whether this part is italic
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder italic(boolean italic) {
|
||||
current.setItalic(italic);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the current part is underlined.
|
||||
*
|
||||
* @param underlined whether this part is underlined
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder underlined(boolean underlined) {
|
||||
current.setUnderlined(underlined);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the current part is strikethrough.
|
||||
*
|
||||
* @param strikethrough whether this part is strikethrough
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder strikethrough(boolean strikethrough) {
|
||||
current.setStrikethrough(strikethrough);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the current part is obfuscated.
|
||||
*
|
||||
* @param obfuscated whether this part is obfuscated
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder obfuscated(boolean obfuscated) {
|
||||
current.setObfuscated(obfuscated);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the insertion text for the current part.
|
||||
*
|
||||
* @param insertion the insertion text
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder insertion(String insertion) {
|
||||
current.setInsertion(insertion);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the click event for the current part.
|
||||
*
|
||||
* @param clickEvent the click event
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder event(ClickEvent clickEvent) {
|
||||
current.setClickEvent(clickEvent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hover event for the current part.
|
||||
*
|
||||
* @param hoverEvent the hover event
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder event(HoverEvent hoverEvent) {
|
||||
current.setHoverEvent(hoverEvent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current part back to normal settings. Only text is kept.
|
||||
*
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder reset() {
|
||||
return retain(FormatRetention.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retains only the specified formatting. Text is not modified.
|
||||
*
|
||||
* @param retention the formatting to retain
|
||||
* @return this ComponentBuilder for chaining
|
||||
*/
|
||||
public ComponentBuilder retain(FormatRetention retention) {
|
||||
current.retain(retention);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components needed to display the message created by this
|
||||
* builder.
|
||||
*
|
||||
* @return the created components
|
||||
*/
|
||||
public BaseComponent[] create() {
|
||||
BaseComponent[] result = parts.toArray(new BaseComponent[parts.size() + 1]);
|
||||
result[parts.size()] = current;
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum FormatRetention {
|
||||
|
||||
/**
|
||||
* Specify that we do not want to retain anything from the previous
|
||||
* component.
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* Specify that we want the formatting retained from the previous
|
||||
* component.
|
||||
*/
|
||||
FORMATTING,
|
||||
/**
|
||||
* Specify that we want the events retained from the previous component.
|
||||
*/
|
||||
EVENTS,
|
||||
/**
|
||||
* Specify that we want to retain everything from the previous
|
||||
* component.
|
||||
*/
|
||||
ALL
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public final class HoverEvent {
|
||||
|
||||
private final Action action;
|
||||
private final BaseComponent[] value;
|
||||
|
||||
public HoverEvent(Action action, BaseComponent[] value) {
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public BaseComponent[] getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "action=" + "HoverEvent{" + action + ", value=" + Arrays.toString(value) + '}';
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
|
||||
SHOW_TEXT,
|
||||
SHOW_ACHIEVEMENT,
|
||||
SHOW_ITEM,
|
||||
SHOW_ENTITY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by Fernflower decompiler)
|
||||
//
|
||||
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public class TextComponent extends BaseComponent {
|
||||
|
||||
private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$");
|
||||
private String text;
|
||||
|
||||
public static BaseComponent[] fromLegacyText(String message) {
|
||||
ArrayList<BaseComponent> components = new ArrayList();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
TextComponent component = new TextComponent();
|
||||
Matcher matcher = url.matcher(message);
|
||||
|
||||
for(int i = 0; i < message.length(); ++i) {
|
||||
char c = message.charAt(i);
|
||||
TextComponent old;
|
||||
if (c == 167) {
|
||||
++i;
|
||||
c = message.charAt(i);
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
c = (char)(c + 32);
|
||||
}
|
||||
|
||||
ChatColor format = ChatColor.getByChar(c);
|
||||
if (format != null) {
|
||||
if (builder.length() > 0) {
|
||||
old = component;
|
||||
component = new TextComponent(component);
|
||||
old.setText(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
components.add(old);
|
||||
}
|
||||
|
||||
switch(format) {
|
||||
case BOLD:
|
||||
component.setBold(true);
|
||||
break;
|
||||
case ITALIC:
|
||||
component.setItalic(true);
|
||||
break;
|
||||
case UNDERLINE:
|
||||
component.setUnderlined(true);
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
component.setStrikethrough(true);
|
||||
break;
|
||||
case MAGIC:
|
||||
component.setObfuscated(true);
|
||||
break;
|
||||
case RESET:
|
||||
format = ChatColor.WHITE;
|
||||
default:
|
||||
component = new TextComponent();
|
||||
component.setColor(format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int pos = message.indexOf(32, i);
|
||||
if (pos == -1) {
|
||||
pos = message.length();
|
||||
}
|
||||
|
||||
if (matcher.region(i, pos).find()) {
|
||||
if (builder.length() > 0) {
|
||||
old = component;
|
||||
component = new TextComponent(component);
|
||||
old.setText(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
components.add(old);
|
||||
}
|
||||
|
||||
old = component;
|
||||
component = new TextComponent(component);
|
||||
String urlString = message.substring(i, pos);
|
||||
component.setText(urlString);
|
||||
component.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, urlString.startsWith("http") ? urlString : "http://" + urlString));
|
||||
components.add(component);
|
||||
i += pos - i - 1;
|
||||
component = old;
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.length() > 0) {
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
}
|
||||
|
||||
if (components.isEmpty()) {
|
||||
components.add(new TextComponent(""));
|
||||
}
|
||||
|
||||
return components.toArray(new BaseComponent[0]);
|
||||
}
|
||||
|
||||
public TextComponent() {
|
||||
this.text = "";
|
||||
}
|
||||
|
||||
public TextComponent(TextComponent textComponent) {
|
||||
super(textComponent);
|
||||
this.setText(textComponent.getText());
|
||||
}
|
||||
|
||||
public TextComponent(BaseComponent... extras) {
|
||||
this.setText("");
|
||||
this.setExtra(new ArrayList(Arrays.asList(extras)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent duplicate() {
|
||||
return new TextComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder) {
|
||||
builder.append(this.text);
|
||||
super.toPlainText(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder) {
|
||||
builder.append(this.getColor());
|
||||
if (this.isBold()) {
|
||||
builder.append(ChatColor.BOLD);
|
||||
}
|
||||
|
||||
if (this.isItalic()) {
|
||||
builder.append(ChatColor.ITALIC);
|
||||
}
|
||||
|
||||
if (this.isUnderlined()) {
|
||||
builder.append(ChatColor.UNDERLINE);
|
||||
}
|
||||
|
||||
if (this.isStrikethrough()) {
|
||||
builder.append(ChatColor.STRIKETHROUGH);
|
||||
}
|
||||
|
||||
if (this.isObfuscated()) {
|
||||
builder.append(ChatColor.MAGIC);
|
||||
}
|
||||
|
||||
builder.append(this.text);
|
||||
super.toLegacyText(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TextComponent{text=%s, %s}", this.text, super.toString());
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@ConstructorProperties({"text"})
|
||||
public TextComponent(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.ilummc.tlib.bungee.api.chat;
|
||||
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public final class TranslatableComponent extends BaseComponent {
|
||||
|
||||
private final ResourceBundle locales = ResourceBundle.getBundle("mojang-translations/en_US");
|
||||
private final Pattern format = Pattern.compile("%(?:(\\d+)\\$)?([A-Za-z%]|$)");
|
||||
|
||||
/**
|
||||
* The key into the Minecraft locale files to use for the translation. The
|
||||
* text depends on the client's locale setting. The console is always en_US
|
||||
*/
|
||||
private String translate;
|
||||
/**
|
||||
* The components to substitute into the translation
|
||||
*/
|
||||
private List<BaseComponent> with;
|
||||
|
||||
public ResourceBundle getLocales() {
|
||||
return locales;
|
||||
}
|
||||
|
||||
public Pattern getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getTranslate() {
|
||||
return translate;
|
||||
}
|
||||
|
||||
public void setTranslate(String translate) {
|
||||
this.translate = translate;
|
||||
}
|
||||
|
||||
public List<BaseComponent> getWith() {
|
||||
return with;
|
||||
}
|
||||
|
||||
public TranslatableComponent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translatable component from the original to clone it.
|
||||
*
|
||||
* @param original the original for the new translatable component.
|
||||
*/
|
||||
public TranslatableComponent(TranslatableComponent original) {
|
||||
super(original);
|
||||
setTranslate(original.getTranslate());
|
||||
|
||||
if (original.getWith() != null) {
|
||||
setWith(original.getWith().stream().map(BaseComponent::duplicate).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translatable component with the passed substitutions
|
||||
*
|
||||
* @param translate the translation key
|
||||
* @param with the {@link java.lang.String}s and
|
||||
* {@link BaseComponent}s to use into the
|
||||
* translation
|
||||
* @see #translate
|
||||
* @see #setWith(java.util.List)
|
||||
*/
|
||||
public TranslatableComponent(String translate, Object... with) {
|
||||
setTranslate(translate);
|
||||
if (with != null && with.length != 0) {
|
||||
List<BaseComponent> temp = new ArrayList<>();
|
||||
for (Object w : with) {
|
||||
if (w instanceof String) {
|
||||
temp.add(new TextComponent((String) w));
|
||||
} else {
|
||||
temp.add((BaseComponent) w);
|
||||
}
|
||||
}
|
||||
setWith(temp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a duplicate of this TranslatableComponent.
|
||||
*
|
||||
* @return the duplicate of this TranslatableComponent.
|
||||
*/
|
||||
@Override
|
||||
public BaseComponent duplicate() {
|
||||
return new TranslatableComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "locales=" + "TranslatableComponent{" + locales + ", format=" + format + ", translate='" + translate + '\'' + ", with=" + with + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the translation substitutions to be used in this component. Removes
|
||||
* any previously set substitutions
|
||||
*
|
||||
* @param components the components to substitute
|
||||
*/
|
||||
public void setWith(List<BaseComponent> components) {
|
||||
components.forEach(component -> component.parent = this);
|
||||
with = components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a text substitution to the component. The text will inherit this
|
||||
* component's formatting
|
||||
*
|
||||
* @param text the text to substitute
|
||||
*/
|
||||
public void addWith(String text) {
|
||||
addWith(new TextComponent(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a component substitution to the component. The text will inherit
|
||||
* this component's formatting
|
||||
*
|
||||
* @param component the component to substitute
|
||||
*/
|
||||
public void addWith(BaseComponent component) {
|
||||
if (with == null) {
|
||||
with = new ArrayList<>();
|
||||
}
|
||||
component.parent = this;
|
||||
with.add(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder) {
|
||||
String trans;
|
||||
try {
|
||||
trans = locales.getString(translate);
|
||||
} catch (MissingResourceException ex) {
|
||||
trans = translate;
|
||||
}
|
||||
|
||||
Matcher matcher = format.matcher(trans);
|
||||
int position = 0;
|
||||
int i = 0;
|
||||
while (matcher.find(position)) {
|
||||
int pos = matcher.start();
|
||||
if (pos != position) {
|
||||
builder.append(trans, position, pos);
|
||||
}
|
||||
position = matcher.end();
|
||||
|
||||
String formatCode = matcher.group(2);
|
||||
switch (formatCode.charAt(0)) {
|
||||
case 's':
|
||||
case 'd':
|
||||
String withIndex = matcher.group(1);
|
||||
with.get(withIndex != null ? Integer.parseInt(withIndex) - 1 : i++).toPlainText(builder);
|
||||
break;
|
||||
case '%':
|
||||
builder.append('%');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (trans.length() != position) {
|
||||
builder.append(trans, position, trans.length());
|
||||
}
|
||||
super.toPlainText(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder) {
|
||||
String trans;
|
||||
try {
|
||||
trans = locales.getString(translate);
|
||||
} catch (MissingResourceException e) {
|
||||
trans = translate;
|
||||
}
|
||||
|
||||
Matcher matcher = format.matcher(trans);
|
||||
int position = 0;
|
||||
int i = 0;
|
||||
while (matcher.find(position)) {
|
||||
int pos = matcher.start();
|
||||
if (pos != position) {
|
||||
addFormat(builder);
|
||||
builder.append(trans, position, pos);
|
||||
}
|
||||
position = matcher.end();
|
||||
|
||||
String formatCode = matcher.group(2);
|
||||
switch (formatCode.charAt(0)) {
|
||||
case 's':
|
||||
case 'd':
|
||||
String withIndex = matcher.group(1);
|
||||
with.get(withIndex != null ? Integer.parseInt(withIndex) - 1 : i++).toLegacyText(builder);
|
||||
break;
|
||||
case '%':
|
||||
addFormat(builder);
|
||||
builder.append('%');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (trans.length() != position) {
|
||||
addFormat(builder);
|
||||
builder.append(trans, position, trans.length());
|
||||
}
|
||||
super.toLegacyText(builder);
|
||||
}
|
||||
|
||||
private void addFormat(StringBuilder builder) {
|
||||
builder.append(getColor());
|
||||
if (isBold()) {
|
||||
builder.append(ChatColor.BOLD);
|
||||
}
|
||||
if (isItalic()) {
|
||||
builder.append(ChatColor.ITALIC);
|
||||
}
|
||||
if (isUnderlined()) {
|
||||
builder.append(ChatColor.UNDERLINE);
|
||||
}
|
||||
if (isStrikethrough()) {
|
||||
builder.append(ChatColor.STRIKETHROUGH);
|
||||
}
|
||||
if (isObfuscated()) {
|
||||
builder.append(ChatColor.MAGIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.ilummc.tlib.bungee.chat;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
import com.ilummc.tlib.bungee.api.chat.BaseComponent;
|
||||
import com.ilummc.tlib.bungee.api.chat.ClickEvent;
|
||||
import com.ilummc.tlib.bungee.api.chat.HoverEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public class BaseComponentSerializer {
|
||||
|
||||
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) {
|
||||
if (object.has("color")) {
|
||||
component.setColor(ChatColor.valueOf(object.get("color").getAsString().toUpperCase(Locale.ROOT)));
|
||||
}
|
||||
if (object.has("bold")) {
|
||||
component.setBold(object.get("bold").getAsBoolean());
|
||||
}
|
||||
if (object.has("italic")) {
|
||||
component.setItalic(object.get("italic").getAsBoolean());
|
||||
}
|
||||
if (object.has("underlined")) {
|
||||
component.setUnderlined(object.get("underlined").getAsBoolean());
|
||||
}
|
||||
if (object.has("strikethrough")) {
|
||||
component.setStrikethrough(object.get("strikethrough").getAsBoolean());
|
||||
}
|
||||
if (object.has("obfuscated")) {
|
||||
component.setObfuscated(object.get("obfuscated").getAsBoolean());
|
||||
}
|
||||
if (object.has("insertion")) {
|
||||
component.setInsertion(object.get("insertion").getAsString());
|
||||
}
|
||||
if (object.has("extra")) {
|
||||
component.setExtra(Arrays.asList(context.<BaseComponent[]>deserialize(object.get("extra"), BaseComponent[].class)));
|
||||
}
|
||||
|
||||
//Events
|
||||
if (object.has("clickEvent")) {
|
||||
JsonObject event = object.getAsJsonObject("clickEvent");
|
||||
component.setClickEvent(new ClickEvent(
|
||||
ClickEvent.Action.valueOf(event.get("action").getAsString().toUpperCase(Locale.ROOT)),
|
||||
event.get("value").getAsString()));
|
||||
}
|
||||
if (object.has("hoverEvent")) {
|
||||
JsonObject event = object.getAsJsonObject("hoverEvent");
|
||||
BaseComponent[] res;
|
||||
if (event.get("value").isJsonArray()) {
|
||||
res = context.deserialize(event.get("value"), BaseComponent[].class);
|
||||
} else {
|
||||
res = new BaseComponent[]{context.deserialize(event.get("value"), BaseComponent.class)};
|
||||
}
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.valueOf(event.get("action").getAsString().toUpperCase(Locale.ROOT)), res));
|
||||
}
|
||||
}
|
||||
|
||||
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) {
|
||||
boolean first = false;
|
||||
if (ComponentSerializer.serializedComponents.get() == null) {
|
||||
first = true;
|
||||
ComponentSerializer.serializedComponents.set(new HashSet<>());
|
||||
}
|
||||
try {
|
||||
Preconditions.checkArgument(!ComponentSerializer.serializedComponents.get().contains(component), "Component loop");
|
||||
ComponentSerializer.serializedComponents.get().add(component);
|
||||
if (component.getColorRaw() != null) {
|
||||
object.addProperty("color", component.getColorRaw().getName());
|
||||
}
|
||||
if (component.isBoldRaw() != null) {
|
||||
object.addProperty("bold", component.isBoldRaw());
|
||||
}
|
||||
if (component.isItalicRaw() != null) {
|
||||
object.addProperty("italic", component.isItalicRaw());
|
||||
}
|
||||
if (component.isUnderlinedRaw() != null) {
|
||||
object.addProperty("underlined", component.isUnderlinedRaw());
|
||||
}
|
||||
if (component.isStrikethroughRaw() != null) {
|
||||
object.addProperty("strikethrough", component.isStrikethroughRaw());
|
||||
}
|
||||
if (component.isObfuscatedRaw() != null) {
|
||||
object.addProperty("obfuscated", component.isObfuscatedRaw());
|
||||
}
|
||||
if (component.getInsertion() != null) {
|
||||
object.addProperty("insertion", component.getInsertion());
|
||||
}
|
||||
|
||||
if (component.getExtra() != null) {
|
||||
object.add("extra", context.serialize(component.getExtra()));
|
||||
}
|
||||
|
||||
//Events
|
||||
if (component.getClickEvent() != null) {
|
||||
JsonObject clickEvent = new JsonObject();
|
||||
clickEvent.addProperty("action", component.getClickEvent().getAction().toString().toLowerCase(Locale.ROOT));
|
||||
clickEvent.addProperty("value", component.getClickEvent().getValue());
|
||||
object.add("clickEvent", clickEvent);
|
||||
}
|
||||
if (component.getHoverEvent() != null) {
|
||||
JsonObject hoverEvent = new JsonObject();
|
||||
hoverEvent.addProperty("action", component.getHoverEvent().getAction().toString().toLowerCase(Locale.ROOT));
|
||||
hoverEvent.add("value", context.serialize(component.getHoverEvent().getValue()));
|
||||
object.add("hoverEvent", hoverEvent);
|
||||
}
|
||||
} finally {
|
||||
ComponentSerializer.serializedComponents.get().remove(component);
|
||||
if (first) {
|
||||
ComponentSerializer.serializedComponents.set(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.ilummc.tlib.bungee.chat;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.ilummc.tlib.bungee.api.chat.BaseComponent;
|
||||
import com.ilummc.tlib.bungee.api.chat.TextComponent;
|
||||
import com.ilummc.tlib.bungee.api.chat.TranslatableComponent;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public class ComponentSerializer implements JsonDeserializer<BaseComponent> {
|
||||
|
||||
private final static JsonParser JSON_PARSER = new JsonParser();
|
||||
private final static Gson gson = new GsonBuilder().
|
||||
registerTypeAdapter(BaseComponent.class, new ComponentSerializer()).
|
||||
registerTypeAdapter(TextComponent.class, new TextComponentSerializer()).
|
||||
registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()).
|
||||
create();
|
||||
|
||||
public final static ThreadLocal<HashSet<BaseComponent>> serializedComponents = new ThreadLocal<>();
|
||||
|
||||
public static BaseComponent[] parse(String json) {
|
||||
JsonElement jsonElement = JSON_PARSER.parse(json);
|
||||
|
||||
if (jsonElement.isJsonArray()) {
|
||||
return gson.fromJson(jsonElement, BaseComponent[].class);
|
||||
} else {
|
||||
return new BaseComponent[]{gson.fromJson(jsonElement, BaseComponent.class)};
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(BaseComponent component) {
|
||||
return gson.toJson(component);
|
||||
}
|
||||
|
||||
public static String toString(BaseComponent... components) {
|
||||
if (components.length == 1) {
|
||||
return gson.toJson(components[0]);
|
||||
} else {
|
||||
return gson.toJson(new TextComponent(components));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json.isJsonPrimitive()) {
|
||||
return new TextComponent(json.getAsString());
|
||||
}
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
if (object.has("translate")) {
|
||||
return context.deserialize(json, TranslatableComponent.class);
|
||||
}
|
||||
return context.deserialize(json, TextComponent.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by Fernflower decompiler)
|
||||
//
|
||||
|
||||
package com.ilummc.tlib.bungee.chat;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.ilummc.tlib.bungee.api.chat.BaseComponent;
|
||||
import com.ilummc.tlib.bungee.api.chat.TextComponent;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent> {
|
||||
|
||||
public TextComponentSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
TextComponent component = new TextComponent();
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
this.deserialize(object, component, context);
|
||||
component.setText(object.get("text").getAsString());
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
List<BaseComponent> extra = src.getExtra();
|
||||
JsonObject object = new JsonObject();
|
||||
if (src.hasFormatting() || extra != null && !extra.isEmpty()) {
|
||||
this.serialize(object, src, context);
|
||||
}
|
||||
object.addProperty("text", src.getText());
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by Fernflower decompiler)
|
||||
//
|
||||
|
||||
package com.ilummc.tlib.bungee.chat;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.ilummc.tlib.bungee.api.chat.BaseComponent;
|
||||
import com.ilummc.tlib.bungee.api.chat.TranslatableComponent;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author md_5
|
||||
*/
|
||||
public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent> {
|
||||
|
||||
public TranslatableComponentSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
TranslatableComponent component = new TranslatableComponent();
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
this.deserialize(object, component, context);
|
||||
component.setTranslate(object.get("translate").getAsString());
|
||||
if (object.has("with")) {
|
||||
component.setWith(Arrays.asList((BaseComponent[]) context.deserialize(object.get("with"), BaseComponent[].class)));
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject object = new JsonObject();
|
||||
this.serialize(object, src, context);
|
||||
object.addProperty("translate", src.getTranslate());
|
||||
if (src.getWith() != null) {
|
||||
object.add("with", context.serialize(src.getWith()));
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ilummc.tlib.clr;
|
||||
|
||||
public class CommandLineResolver {
|
||||
|
||||
|
||||
|
||||
}
|
||||
42
src/main/scala/com/ilummc/tlib/compat/PlaceholderHook.java
Normal file
42
src/main/scala/com/ilummc/tlib/compat/PlaceholderHook.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.ilummc.tlib.compat;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class PlaceholderHook {
|
||||
|
||||
private static PlaceholderHook impl;
|
||||
|
||||
public static void init() {
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
impl = new PlaceholderImpl();
|
||||
} else {
|
||||
impl = new AbstractImpl();
|
||||
}
|
||||
}
|
||||
|
||||
public static String replace(CommandSender sender, String text) {
|
||||
return sender instanceof Player ? impl.replace(((Player) sender), text) : text;
|
||||
}
|
||||
|
||||
abstract String replace(Player player, String text);
|
||||
|
||||
private static class PlaceholderImpl extends PlaceholderHook {
|
||||
|
||||
@Override
|
||||
String replace(Player player, String text) {
|
||||
return PlaceholderAPI.setPlaceholders(player, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AbstractImpl extends PlaceholderHook {
|
||||
|
||||
@Override
|
||||
String replace(Player player, String text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/scala/com/ilummc/tlib/config/TLibConfig.java
Normal file
59
src/main/scala/com/ilummc/tlib/config/TLibConfig.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.ilummc.tlib.config;
|
||||
|
||||
import com.ilummc.tlib.annotations.TConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author sky
|
||||
* @since 2018-04-22 14:31:11
|
||||
*/
|
||||
@TConfig(name = "tlib.yml")
|
||||
public class TLibConfig {
|
||||
|
||||
private String dataSourceClassName;
|
||||
|
||||
private String jdbcUrl = "jdbc:h2:file:~/plugins/TabooLib/h2";
|
||||
|
||||
private String driverClassName;
|
||||
|
||||
private String username = "";
|
||||
|
||||
private String password = "";
|
||||
|
||||
private int maximumPoolSize = 4;
|
||||
|
||||
private Map<String, Object> settings = new HashMap<String, Object>() {{
|
||||
put("cachePrepStmts", true);
|
||||
put("useServerPrepStmts", true);
|
||||
}};
|
||||
|
||||
public String getDataSourceClassName() {
|
||||
return dataSourceClassName;
|
||||
}
|
||||
|
||||
public String getJdbcUrl() {
|
||||
return jdbcUrl;
|
||||
}
|
||||
|
||||
public String getDriverClassName() {
|
||||
return driverClassName;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getMaximumPoolSize() {
|
||||
return maximumPoolSize;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSettings() {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
63
src/main/scala/com/ilummc/tlib/db/Pool.java
Normal file
63
src/main/scala/com/ilummc/tlib/db/Pool.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.ilummc.tlib.db;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import org.javalite.activejdbc.Base;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class Pool extends ThreadPoolExecutor {
|
||||
|
||||
private static final AtomicInteger number = new AtomicInteger(1);
|
||||
|
||||
private static final Pool singleton = new Pool();
|
||||
|
||||
private final TLibDataSource dataSource;
|
||||
|
||||
private Pool() {
|
||||
super(TLib.getTLib().getConfig().getMaximumPoolSize(),
|
||||
TLib.getTLib().getConfig().getMaximumPoolSize(),
|
||||
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
|
||||
try {
|
||||
dataSource = new TLibDataSource();
|
||||
this.setThreadFactory(r -> new Thread(() -> {
|
||||
Base.open(dataSource.getDataSource());
|
||||
r.run();
|
||||
}, "TabooLib-DbPool-" + number.getAndIncrement()));
|
||||
prestartAllCoreThreads();
|
||||
TLocale.sendToConsole("DATABASE.CONNECTION-ESTABLISHED", dataSource.getDataSource().getConnection().getMetaData().getDatabaseProductName(),
|
||||
String.valueOf(TLib.getTLib().getConfig().getMaximumPoolSize()));
|
||||
} catch (Exception e) {
|
||||
TLocale.sendToConsole("DATABASE.CONNECTION-ERROR", e.toString());
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void run(Runnable runnable) {
|
||||
instance().execute(runnable);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
||||
}
|
||||
|
||||
public static void unload() {
|
||||
instance().dataSource.disconnect();
|
||||
instance().shutdown();
|
||||
}
|
||||
|
||||
public static Pool instance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
if (t != null) {
|
||||
Base.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
35
src/main/scala/com/ilummc/tlib/db/TLibDataSource.java
Normal file
35
src/main/scala/com/ilummc/tlib/db/TLibDataSource.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.ilummc.tlib.db;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.javalite.activejdbc.Base;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Properties;
|
||||
|
||||
public class TLibDataSource {
|
||||
|
||||
private final HikariDataSource dataSource;
|
||||
|
||||
TLibDataSource() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("jdbcUrl", TLib.getTLib().getConfig().getJdbcUrl());
|
||||
properties.put("username", TLib.getTLib().getConfig().getUsername());
|
||||
properties.put("password", TLib.getTLib().getConfig().getPassword());
|
||||
properties.put("dataSourceClassName", TLib.getTLib().getConfig().getDataSourceClassName());
|
||||
properties.put("driverClassName", TLib.getTLib().getConfig().getDriverClassName());
|
||||
TLib.getTLib().getConfig().getSettings().forEach((k, v) -> properties.put("dataSource." + k, v));
|
||||
dataSource = new HikariDataSource(new HikariConfig(properties));
|
||||
Base.open(dataSource);
|
||||
}
|
||||
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
Base.close();
|
||||
}
|
||||
|
||||
}
|
||||
80
src/main/scala/com/ilummc/tlib/dependency/TDependency.java
Normal file
80
src/main/scala/com/ilummc/tlib/dependency/TDependency.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package com.ilummc.tlib.dependency;
|
||||
|
||||
import com.ilummc.eagletdl.EagletTask;
|
||||
import com.ilummc.eagletdl.ProgressEvent;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.Main;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class TDependency {
|
||||
|
||||
// 阿里 http://maven.aliyun.com/nexus/content/groups/public
|
||||
// Maven Central
|
||||
public static final String MAVEN_REPO = "http://repo1.maven.org/maven2";
|
||||
|
||||
/**
|
||||
* 请求一个插件作为依赖,这个插件将会在所有已经添加的 Jenkins 仓库、Maven 仓库寻找
|
||||
* <p>
|
||||
* 阻塞线程进行下载/加载
|
||||
*
|
||||
* @param args 插件名称,下载地址(可选)
|
||||
* @return 是否成功加载了依赖
|
||||
*/
|
||||
public static boolean requestPlugin(String... args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求一个库作为依赖,这个库将会在 Maven Central、oss.sonatype 以及自定义的 Maven 仓库寻找
|
||||
* <p>
|
||||
* 阻塞线程进行下载/加载
|
||||
*
|
||||
* @param type 依赖名,格式为 groupId:artifactId:version
|
||||
* @return 是否成功加载库,如果加载成功,插件将可以任意调用使用的类
|
||||
*/
|
||||
public static boolean requestLib(String type, String repo, String url) {
|
||||
if (type.matches(".*:.*:.*")) {
|
||||
String[] arr = type.split(":");
|
||||
File file = new File(Main.getInst().getDataFolder(), "/libs/" + String.join("-", arr) + ".jar");
|
||||
if (file.exists()) {
|
||||
TDependencyLoader.addToPath(Main.getInst(), file);
|
||||
return true;
|
||||
} else {
|
||||
if (downloadMaven(repo, arr[0], arr[1], arr[2], file, url)) {
|
||||
TDependencyLoader.addToPath(Main.getInst(), file);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean downloadMaven(String url, String groupId, String artifactId, String version, File target, String dl) {
|
||||
if (Main.isOfflineVersion()) {
|
||||
TLocale.Logger.warn("DEPENDENCY.DOWNLOAD-OFFLINE");
|
||||
return false;
|
||||
}
|
||||
AtomicBoolean failed = new AtomicBoolean(false);
|
||||
String link = dl.length() == 0 ? url + "/" + groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".jar" : dl;
|
||||
new EagletTask()
|
||||
.url(link)
|
||||
.file(target)
|
||||
.setThreads(1)
|
||||
.setOnError(event -> event.getException().printStackTrace())
|
||||
.setOnConnected(event -> TLocale.Logger.info("DEPENDENCY.DOWNLOAD-CONNECTED", String.join(":", new String[] {groupId, artifactId, version}), ProgressEvent.format(event.getContentLength())))
|
||||
.setOnProgress(event -> TLocale.Logger.info("DEPENDENCY.DOWNLOAD-PROGRESS", event.getSpeedFormatted(), event.getPercentageFormatted()))
|
||||
.setOnComplete(event -> {
|
||||
if (event.isSuccess()) {
|
||||
TLocale.Logger.info("DEPENDENCY.DOWNLOAD-SUCCESS", String.join(":", new String[] {groupId, artifactId, version}));
|
||||
} else {
|
||||
failed.set(true);
|
||||
TLocale.Logger.error("DEPENDENCY.DOWNLOAD-FAILED", String.join(":", new String[] {groupId, artifactId, version}), link, target.getName());
|
||||
}
|
||||
}).start().waitUntil();
|
||||
return !failed.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ilummc.tlib.dependency;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class TDependencyLoader {
|
||||
|
||||
public static synchronized void addToPath(Plugin plugin, URL url) {
|
||||
try {
|
||||
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(plugin.getClass().getClassLoader(), url);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void addToPath(Plugin plugin, File file) {
|
||||
try {
|
||||
addToPath(plugin, file.toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
102
src/main/scala/com/ilummc/tlib/filter/TLoggerFilter.java
Normal file
102
src/main/scala/com/ilummc/tlib/filter/TLoggerFilter.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package com.ilummc.tlib.filter;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.filter.impl.FilterConfiguration;
|
||||
import com.ilummc.tlib.filter.impl.FilterInvalidPluginLoader;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Filter;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Bkm016
|
||||
* @since 2018-04-22
|
||||
*/
|
||||
public class TLoggerFilter implements Filter {
|
||||
|
||||
private Filter filter;
|
||||
private Logger logger;
|
||||
private static List<TLoggerFilterHandler> handlers = Lists.newLinkedList();
|
||||
private static Map<String, TLoggerFilter> pluginFilter = Maps.newHashMap();
|
||||
private static TLoggerFilter globalFilter;
|
||||
private static String playerConnectionName;
|
||||
|
||||
static {
|
||||
handlers.add(new FilterConfiguration());
|
||||
handlers.add(new FilterInvalidPluginLoader());
|
||||
// handlers.add(new FilterExceptionMirror());
|
||||
}
|
||||
|
||||
public static void preInit() {
|
||||
inject(new TLoggerFilter(), Bukkit.getLogger());
|
||||
inject(new TLoggerFilter(), TabooLib.instance().getLogger());
|
||||
try {
|
||||
playerConnectionName = Class.forName("net.minecraft.server." + TabooLib.getVersion() + ".PlayerConnection").getName();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void postInit() {
|
||||
Arrays.stream(Bukkit.getPluginManager().getPlugins()).filter(TabooLib::isDependTabooLib).forEach(plugin -> inject(new TLoggerFilter(), plugin.getLogger()));
|
||||
}
|
||||
|
||||
public static void inject0() {
|
||||
inject(new TLoggerFilter(), Logger.getLogger(playerConnectionName));
|
||||
}
|
||||
|
||||
public static void inject(TLoggerFilter filter, Logger logger) {
|
||||
if (!(logger.getFilter() instanceof TLoggerFilter)) {
|
||||
try {
|
||||
filter.filter = logger.getFilter();
|
||||
filter.logger = logger;
|
||||
logger.setFilter(filter);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void eject(Plugin plugin) {
|
||||
try {
|
||||
if (plugin.getLogger().getFilter() instanceof TLoggerFilter) {
|
||||
((TLoggerFilter) plugin.getLogger().getFilter()).filter = null;
|
||||
((TLoggerFilter) plugin.getLogger().getFilter()).logger = null;
|
||||
plugin.getLogger().setFilter(null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static TLoggerFilter getGlobalFilter() {
|
||||
return globalFilter;
|
||||
}
|
||||
|
||||
public static Map<String, TLoggerFilter> getPluginFilter() {
|
||||
return pluginFilter;
|
||||
}
|
||||
|
||||
public static List<TLoggerFilterHandler> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord e) {
|
||||
return handlers.stream().allMatch(filter -> filter.isLoggable(e)) && (filter == null || filter.isLoggable(e));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ilummc.tlib.filter;
|
||||
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* @Author 坏黑
|
||||
* @Since 2018-11-29 11:42
|
||||
*/
|
||||
public abstract class TLoggerFilterHandler {
|
||||
|
||||
abstract public boolean isLoggable(LogRecord e);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ilummc.tlib.filter.impl;
|
||||
|
||||
import com.ilummc.tlib.filter.TLoggerFilterHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* @Author 坏黑
|
||||
* @Since 2018-11-29 11:47
|
||||
*/
|
||||
public class FilterConfiguration extends TLoggerFilterHandler {
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord e) {
|
||||
if (String.valueOf(e.getMessage()).contains("Cannot load configuration from stream")) {
|
||||
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement element : elements) {
|
||||
if (element.getClassName().contains("ConfigUtils")) {
|
||||
// Bukkit 拦截异常?我再扔一个
|
||||
System.out.println(Arrays.asList(e.getParameters()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.ilummc.tlib.filter.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.ilummc.tlib.filter.TLoggerFilterHandler;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.bukkit.command.CommandException;
|
||||
import org.bukkit.event.EventException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @Author 坏黑
|
||||
* @Since 2018-11-29 11:42
|
||||
*/
|
||||
public class FilterExceptionMirror extends TLoggerFilterHandler {
|
||||
|
||||
interface ArgumentsCallback {
|
||||
|
||||
String[] run();
|
||||
}
|
||||
|
||||
private static Pattern patternEvent = Pattern.compile("Could not pass event (.+?) to (.+?)");
|
||||
private static Pattern patternCommand = Pattern.compile("Unhandled exception executing command '(.+?)' in plugin (.+?)");
|
||||
|
||||
/**
|
||||
* 判断是否为调度器异常
|
||||
*/
|
||||
public boolean isScheduleException(LogRecord log) {
|
||||
return String.valueOf(log.getMessage()).contains("generated an exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为可捕捉异常
|
||||
*/
|
||||
public boolean isValidException(Throwable throwable) {
|
||||
return throwable.getCause() != null && throwable.getCause().getStackTrace() != null && throwable.getCause().getStackTrace().length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向控制台打印捕捉到的异常
|
||||
*
|
||||
* @param stackTraceElements 堆栈
|
||||
* @param message 信息类型
|
||||
* @param args 信息参数
|
||||
* @return 是否成功捕捉并打印
|
||||
*/
|
||||
public boolean printException(AtomicReference<Plugin> plugin, StackTraceElement[] stackTraceElements, String message, ArgumentsCallback args) {
|
||||
Set<Plugin> plugins = Sets.newHashSet();
|
||||
List<StackTraceElement> stackTraces = Lists.newLinkedList();
|
||||
for (StackTraceElement stack : stackTraceElements) {
|
||||
try {
|
||||
plugins.add(JavaPlugin.getProvidingPlugin(Class.forName(stack.getClassName())));
|
||||
stackTraces.add(stack);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (!plugins.isEmpty() && plugins.stream().allMatch(p -> TabooLib.isTabooLib(p) || TabooLib.isDependTabooLib(p))) {
|
||||
plugin.set(plugins.iterator().next());
|
||||
TLocale.Logger.error("TFILTER.EXCEPTION-MIRROR." + message + ".HEAD", args.run());
|
||||
for (int i = 0; i < stackTraces.size(); i++) {
|
||||
StackTraceElement stack = stackTraces.get(i);
|
||||
TLocale.Logger.error("TFILTER.EXCEPTION-MIRROR." + message + ".STACK-TRACE", String.valueOf(i), stack.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord e) {
|
||||
if (!Main.getInst().getConfig().getBoolean("EXCEPTION-MIRROR", true) || e.getThrown() == null) {
|
||||
return true;
|
||||
}
|
||||
// 是否为调度器异常
|
||||
if (isScheduleException(e)) {
|
||||
long time = System.currentTimeMillis();
|
||||
AtomicReference<Plugin> plugin = new AtomicReference<>();
|
||||
return !printException(plugin, e.getThrown().getStackTrace(), "SCHEDULE", () -> new String[] {plugin.get().getName(), String.valueOf(System.currentTimeMillis() - time), e.getThrown().getClass().getSimpleName(), String.valueOf(e.getThrown().getMessage())});
|
||||
}
|
||||
// 是否为其他可捕捉异常
|
||||
else if (isValidException(e.getThrown())) {
|
||||
// 事件异常
|
||||
if (e.getThrown() instanceof EventException) {
|
||||
Matcher matcher = patternEvent.matcher(e.getMessage());
|
||||
if (matcher.find()) {
|
||||
long time = System.currentTimeMillis();
|
||||
AtomicReference<Plugin> plugin = new AtomicReference<>();
|
||||
return !printException(plugin, e.getThrown().getCause().getStackTrace(), "EVENT", () -> new String[] {plugin.get().getName(), String.valueOf(System.currentTimeMillis() - time), matcher.group(1), e.getThrown().getCause().getClass().getSimpleName(), String.valueOf(e.getThrown().getCause().getMessage())});
|
||||
}
|
||||
}
|
||||
// 命令异常
|
||||
else if (e.getThrown() instanceof CommandException) {
|
||||
Matcher matcher = patternCommand.matcher(e.getThrown().getMessage());
|
||||
if (matcher.find()) {
|
||||
long time = System.currentTimeMillis();
|
||||
AtomicReference<Plugin> plugin = new AtomicReference<>();
|
||||
return !printException(plugin, e.getThrown().getCause().getStackTrace(), "COMMAND", () -> new String[] {plugin.get().getName(), String.valueOf(System.currentTimeMillis() - time), matcher.group(1), e.getThrown().getCause().getClass().getSimpleName(), String.valueOf(e.getThrown().getCause().getMessage())});
|
||||
}
|
||||
}
|
||||
// 其他异常
|
||||
else {
|
||||
long time = System.currentTimeMillis();
|
||||
AtomicReference<Plugin> plugin = new AtomicReference<>();
|
||||
return !printException(plugin, e.getThrown().getCause().getStackTrace(), "OTHER", () -> new String[] {plugin.get().getName(), String.valueOf(System.currentTimeMillis() - time), e.getThrown().getCause().getClass().getSimpleName(), String.valueOf(e.getThrown().getCause().getMessage())});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ilummc.tlib.filter.impl;
|
||||
|
||||
import com.ilummc.tlib.filter.TLoggerFilterHandler;
|
||||
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* @Author 坏黑
|
||||
* @Since 2018-11-29 11:47
|
||||
*/
|
||||
public class FilterInvalidPluginLoader extends TLoggerFilterHandler {
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord e) {
|
||||
// 屏蔽插件加载器注入导致的警告信息
|
||||
return !String.valueOf(e.getMessage()).contains("Enabled plugin with unregistered PluginClassLoader");
|
||||
}
|
||||
}
|
||||
143
src/main/scala/com/ilummc/tlib/inject/TConfigInjector.java
Normal file
143
src/main/scala/com/ilummc/tlib/inject/TConfigInjector.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package com.ilummc.tlib.inject;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.ilummc.tlib.annotations.TConfig;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.fileutils.ConfigUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TConfigInjector {
|
||||
|
||||
public static void fixUnicode(YamlConfiguration configuration) {
|
||||
try {
|
||||
Field field = YamlConfiguration.class.getDeclaredField("yamlOptions");
|
||||
field.setAccessible(true);
|
||||
field.set(configuration, NoUnicodeDumperOption.INSTANCE);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoUnicodeDumperOption extends DumperOptions {
|
||||
|
||||
private static final NoUnicodeDumperOption INSTANCE = new NoUnicodeDumperOption();
|
||||
|
||||
@Override
|
||||
public void setAllowUnicode(boolean allowUnicode) {
|
||||
super.setAllowUnicode(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowUnicode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLineBreak(LineBreak lineBreak) {
|
||||
super.setLineBreak(LineBreak.getPlatformLineBreak());
|
||||
}
|
||||
}
|
||||
|
||||
public static Object loadConfig(Plugin plugin, Class<?> clazz) {
|
||||
try {
|
||||
TConfig config = clazz.getAnnotation(TConfig.class);
|
||||
Validate.notNull(config);
|
||||
File file = new File(plugin.getDataFolder(), config.name());
|
||||
if (!file.exists()) {
|
||||
if (config.fromJar()) {
|
||||
plugin.saveResource(config.name(), true);
|
||||
} else {
|
||||
saveConfig(plugin, clazz.newInstance());
|
||||
}
|
||||
}
|
||||
Object obj = unserialize(plugin, clazz);
|
||||
if (config.readOnly()) {
|
||||
saveConfig(plugin, obj);
|
||||
}
|
||||
return obj;
|
||||
} catch (NullPointerException e) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL-NO-ANNOTATION", plugin.toString(), clazz.getSimpleName());
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL", plugin.toString(), clazz.getSimpleName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void reloadConfig(Plugin plugin, Object object) {
|
||||
try {
|
||||
TConfig config = object.getClass().getAnnotation(TConfig.class);
|
||||
Validate.notNull(config);
|
||||
File file = new File(plugin.getDataFolder(), config.name());
|
||||
Map<String, Object> map = ConfigUtils.confToMap(ConfigUtils.loadYaml(plugin, file));
|
||||
Object obj = ConfigUtils.mapToObj(map, object);
|
||||
if (config.readOnly()) {
|
||||
saveConfig(plugin, obj);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL-NO-ANNOTATION", plugin.toString(), object.getClass().getSimpleName());
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL", plugin.toString(), object.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
public static Object unserialize(Plugin plugin, Class<?> clazz) {
|
||||
try {
|
||||
TConfig config = clazz.getAnnotation(TConfig.class);
|
||||
Validate.notNull(config);
|
||||
return ConfigUtils.confToObj(
|
||||
ConfigUtils.mapToConf(
|
||||
ConfigUtils.yamlToMap(
|
||||
Files.toString(new File(plugin.getDataFolder(), config.name()), Charset.forName(config.charset())))), clazz);
|
||||
} catch (NullPointerException e) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL-NO-FILE", plugin.toString(), clazz.getSimpleName());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
return clazz.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e1) {
|
||||
TLocale.Logger.warn("CONFIG.LOAD-FAIL", plugin.toString(), clazz.getSimpleName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> serialize(Plugin plugin, Object object) {
|
||||
try {
|
||||
TConfig config = object.getClass().getAnnotation(TConfig.class);
|
||||
Validate.notNull(config);
|
||||
return ConfigUtils.objToMap(ConfigUtils.objToConf(object).getValues(false), config.excludeModifiers());
|
||||
} catch (NullPointerException e) {
|
||||
TLocale.Logger.warn("CONFIG.SAVE-FAIL-NO-ANNOTATION", plugin.toString(), object.getClass().getSimpleName());
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.warn("CONFIG.SAVE-FAIL", plugin.toString(), object.getClass().getSimpleName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void saveConfig(Plugin plugin, Object object) throws IOException, NullPointerException {
|
||||
TConfig config = object.getClass().getAnnotation(TConfig.class);
|
||||
Validate.notNull(config);
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
Map map = gson.fromJson(gson.toJson(object), HashMap.class);
|
||||
YamlConfiguration configuration = (YamlConfiguration) ConfigUtils.mapToConf(map);
|
||||
File target = new File(plugin.getDataFolder(), config.name());
|
||||
if (!target.exists()) {
|
||||
target.createNewFile();
|
||||
}
|
||||
byte[] arr = configuration.saveToString().getBytes(config.charset());
|
||||
Files.write(arr, target);
|
||||
}
|
||||
|
||||
}
|
||||
87
src/main/scala/com/ilummc/tlib/inject/TConfigWatcher.java
Normal file
87
src/main/scala/com/ilummc/tlib/inject/TConfigWatcher.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.ilummc.tlib.inject;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author lzzelAliz
|
||||
*/
|
||||
public class TConfigWatcher {
|
||||
|
||||
private final ScheduledExecutorService service = Executors.newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("TConfigWatcherService-%d").build());
|
||||
private final Map<WatchService, Triple<File, Object, Consumer<Object>>> map = new HashMap<>();
|
||||
|
||||
public TConfigWatcher() {
|
||||
service.scheduleAtFixedRate(() -> {
|
||||
synchronized (map) {
|
||||
map.forEach((service, triple) -> {
|
||||
WatchKey key;
|
||||
while ((key = service.poll()) != null) {
|
||||
for (WatchEvent<?> watchEvent : key.pollEvents()) {
|
||||
if (triple.getLeft().getName().equals(Objects.toString(watchEvent.context()))) {
|
||||
triple.getRight().accept(triple.getMiddle());
|
||||
}
|
||||
}
|
||||
key.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void addSimpleListener(File file, Runnable runnable) {
|
||||
addListener(file, null, obj -> runnable.run());
|
||||
}
|
||||
|
||||
public void addOnListen(File file, Object obj, Consumer<Object> consumer) {
|
||||
try {
|
||||
WatchService service = FileSystems.getDefault().newWatchService();
|
||||
file.getParentFile().toPath().register(service, StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
map.putIfAbsent(service, Triple.of(file, obj, consumer));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> void addListener(File file, T obj, Consumer<T> consumer) {
|
||||
addOnListen(file, obj, (Consumer<Object>) consumer);
|
||||
}
|
||||
|
||||
public void removeListener(File file) {
|
||||
synchronized (map) {
|
||||
map.entrySet().removeIf(entry -> {
|
||||
if (entry.getValue().getLeft().equals(file)) {
|
||||
try {
|
||||
entry.getKey().close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterAll() {
|
||||
service.shutdown();
|
||||
map.forEach((service, pair) -> {
|
||||
try {
|
||||
service.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
188
src/main/scala/com/ilummc/tlib/inject/TDependencyInjector.java
Normal file
188
src/main/scala/com/ilummc/tlib/inject/TDependencyInjector.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package com.ilummc.tlib.inject;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.annotations.*;
|
||||
import com.ilummc.tlib.dependency.TDependency;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleLoader;
|
||||
import com.ilummc.tlib.util.Ref;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Izzel_Aliz
|
||||
*/
|
||||
public class TDependencyInjector {
|
||||
|
||||
private static List<String> injected = new ArrayList<>();
|
||||
|
||||
static void injectOnEnable(Plugin plugin) {
|
||||
inject(plugin, plugin);
|
||||
}
|
||||
|
||||
static void ejectOnDisable(Plugin plugin) {
|
||||
eject(plugin, plugin);
|
||||
}
|
||||
|
||||
public static boolean injected(Plugin plugin) {
|
||||
return injected.contains(plugin.getName());
|
||||
}
|
||||
|
||||
public static void inject(Plugin plugin, Object o) {
|
||||
if (!injected.contains(plugin.getName())) {
|
||||
injected.add(plugin.getName());
|
||||
TLocaleLoader.load(plugin, true);
|
||||
injectDependencies(plugin, o);
|
||||
injectLogger(plugin, o);
|
||||
injectConfig(plugin, o);
|
||||
injectPluginInstance(plugin, o);
|
||||
}
|
||||
}
|
||||
|
||||
public static void eject(Plugin plugin, Object o) {
|
||||
try {
|
||||
injected.remove(plugin.getName());
|
||||
ejectConfig(plugin, o);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static Dependency[] getDependencies(Object o) {
|
||||
Dependency[] dependencies = new Dependency[0];
|
||||
Dependencies d = o.getClass().getAnnotation(Dependencies.class);
|
||||
if (d != null) {
|
||||
dependencies = d.value();
|
||||
}
|
||||
Dependency d2 = o.getClass().getAnnotation(Dependency.class);
|
||||
if (d2 != null) {
|
||||
dependencies = new Dependency[] {d2};
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private static void ejectConfig(Plugin plugin, Object o) {
|
||||
for (Field field : Ref.getDeclaredFields(o.getClass())) {
|
||||
TConfig config;
|
||||
if ((config = field.getType().getAnnotation(TConfig.class)) != null && config.saveOnExit()) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
TConfigInjector.saveConfig(plugin, field.get(o));
|
||||
TLocale.Logger.info("CONFIG.SAVE-SUCCESS", plugin.toString(), config.name());
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.warn("CONFIG.SAVE-FAIL", plugin.toString(), config.name());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void injectConfig(Plugin plugin, Object o) {
|
||||
for (Field field : Ref.getDeclaredFields(o.getClass())) {
|
||||
try {
|
||||
TConfig config;
|
||||
if ((config = field.getType().getAnnotation(TConfig.class)) != null) {
|
||||
field.setAccessible(true);
|
||||
Object obj = TConfigInjector.loadConfig(plugin, field.getType());
|
||||
if (obj != null) {
|
||||
TLocale.Logger.info("CONFIG.LOAD-SUCCESS", plugin.toString(), config.name());
|
||||
field.set(o, obj);
|
||||
if (config.listenChanges()) {
|
||||
TLocale.Logger.info("CONFIG.LISTEN-START", plugin.toString(), config.name());
|
||||
TLib.getTLib().getConfigWatcher().addOnListen(
|
||||
new File(plugin.getDataFolder(), config.name()),
|
||||
obj,
|
||||
object -> {
|
||||
try {
|
||||
TConfigInjector.reloadConfig(plugin, object);
|
||||
TLocale.Logger.info("CONFIG.RELOAD-SUCCESS", plugin.toString(), config.name());
|
||||
} catch (Exception ignored) {
|
||||
TLocale.Logger.warn("CONFIG.RELOAD-FAIL", plugin.toString(), config.name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void injectLogger(Plugin plugin, Object o) {
|
||||
for (Field field : Ref.getDeclaredFields(o.getClass())) {
|
||||
try {
|
||||
Logger logger;
|
||||
if ((logger = field.getAnnotation(Logger.class)) != null) {
|
||||
field.getType().asSubclass(com.ilummc.tlib.logger.TLogger.class);
|
||||
com.ilummc.tlib.logger.TLogger tLogger = new com.ilummc.tlib.logger.TLogger(logger.value(), plugin, logger.level());
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
field.set(o, tLogger);
|
||||
TLoggerManager.setDefaultLogger(plugin, tLogger);
|
||||
}
|
||||
} catch (Exception ignored2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void injectPluginInstance(Plugin plugin, Object o) {
|
||||
for (Field field : Ref.getDeclaredFields(o.getClass())) {
|
||||
try {
|
||||
PluginInstance instance;
|
||||
if ((instance = field.getAnnotation(PluginInstance.class)) != null) {
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
field.getType().asSubclass(JavaPlugin.class);
|
||||
Plugin pl;
|
||||
if ((pl = Bukkit.getPluginManager().getPlugin(instance.value())) == null) {
|
||||
if (!TDependency.requestPlugin(instance.value())) {
|
||||
TLocale.Logger.warn("PLUGIN-AUTOLOAD-FAIL", plugin.getName(), instance.value());
|
||||
return;
|
||||
} else {
|
||||
pl = Bukkit.getPluginManager().getPlugin(instance.value());
|
||||
}
|
||||
}
|
||||
if (pl != null) {
|
||||
field.set(o, pl);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void injectDependencies(Plugin plugin, Object o) {
|
||||
Dependency[] dependencies = getDependencies(o);
|
||||
if (dependencies.length != 0) {
|
||||
TLocale.Logger.info("DEPENDENCY.LOADING-START", plugin.getName());
|
||||
for (Dependency dependency : dependencies) {
|
||||
if (dependency.type() == Dependency.Type.PLUGIN) {
|
||||
if (TDependency.requestPlugin(dependency.plugin())) {
|
||||
TabooLib.debug(" Loaded " + dependency.plugin() + " (" + plugin.getName() + ")");
|
||||
// TLocale.Logger.info("DEPENDENCY.PLUGIN-LOAD-SUCCESS", plugin.getName(), dependency.plugin());
|
||||
} else {
|
||||
TLocale.Logger.warn("DEPENDENCY.PLUGIN-LOAD-FAIL", plugin.getName(), dependency.plugin());
|
||||
}
|
||||
}
|
||||
if (dependency.type() == Dependency.Type.LIBRARY) {
|
||||
if (TDependency.requestLib(dependency.maven(), dependency.mavenRepo(), dependency.url())) {
|
||||
TabooLib.debug(" Loaded " + String.join(":", dependency.maven()) + " (" + plugin.getName() + ")");
|
||||
// TLocale.Logger.info("DEPENDENCY.LIBRARY-LOAD-SUCCESS", plugin.getName(), String.join(":", dependency.maven()));
|
||||
} else {
|
||||
TLocale.Logger.warn("DEPENDENCY.LIBRARY-LOAD-FAIL", plugin.getName(), String.join(":", dependency.maven()));
|
||||
}
|
||||
}
|
||||
}
|
||||
TLocale.Logger.info("DEPENDENCY.LOAD-COMPLETE");
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/scala/com/ilummc/tlib/inject/TLoggerManager.java
Normal file
25
src/main/scala/com/ilummc/tlib/inject/TLoggerManager.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.ilummc.tlib.inject;
|
||||
|
||||
import com.ilummc.tlib.logger.TLogger;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TLoggerManager {
|
||||
|
||||
private static final Map<Plugin, TLogger> map = new HashMap<>();
|
||||
|
||||
public static void setDefaultLogger(Plugin plugin, TLogger logger) {
|
||||
map.put(plugin, logger);
|
||||
}
|
||||
|
||||
public static TLogger getLogger(Plugin plugin) {
|
||||
TLogger logger = map.get(plugin);
|
||||
if (logger == null) {
|
||||
logger = TLogger.getUnformatted(plugin);
|
||||
map.put(plugin, logger);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
231
src/main/scala/com/ilummc/tlib/inject/TPluginManager.java
Normal file
231
src/main/scala/com/ilummc/tlib/inject/TPluginManager.java
Normal file
@@ -0,0 +1,231 @@
|
||||
package com.ilummc.tlib.inject;
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class TPluginManager implements PluginManager {
|
||||
|
||||
private static TPluginManager singleton;
|
||||
private final PluginManager instance;
|
||||
private final Main main = (Main) Main.getInst();
|
||||
private static File updateDirectory = null;
|
||||
private Server server;
|
||||
private Map<Pattern, PluginLoader> fileAssociations = new HashMap<>();
|
||||
private List<Plugin> plugins = new ArrayList<>();
|
||||
private Map<String, Plugin> lookupNames = new HashMap<>();
|
||||
private SimpleCommandMap commandMap;
|
||||
private Map<String, Permission> permissions = new HashMap<>();
|
||||
private Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<>();
|
||||
private Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<>();
|
||||
private Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<>();
|
||||
private boolean useTimings = false;
|
||||
private List<Plugin> delayedDisable = new ArrayList<>();
|
||||
|
||||
public static void delayDisable(Plugin plugin) {
|
||||
if (!singleton.delayedDisable.contains(plugin)) {
|
||||
singleton.delayedDisable.add(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
public TPluginManager() {
|
||||
instance = Bukkit.getPluginManager();
|
||||
// clone all Field in SimplePluginManager
|
||||
cloneField("updateDirectory");
|
||||
cloneField("server");
|
||||
cloneField("fileAssociations");
|
||||
cloneField("plugins");
|
||||
cloneField("lookupNames");
|
||||
cloneField("commandMap");
|
||||
cloneField("permissions");
|
||||
cloneField("defaultPerms");
|
||||
cloneField("permSubs");
|
||||
cloneField("defSubs");
|
||||
cloneField("useTimings");
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
private void cloneField(String bukkitName) {
|
||||
try {
|
||||
Field bukkitField = instance.getClass().getDeclaredField(bukkitName);
|
||||
Field thisFiled = this.getClass().getDeclaredField(bukkitName);
|
||||
if (bukkitField == null || thisFiled == null) {
|
||||
TLocale.Logger.warn("MISC.FIELD-COPY-FAILED", bukkitName);
|
||||
return;
|
||||
}
|
||||
bukkitField.setAccessible(true);
|
||||
thisFiled.setAccessible(true);
|
||||
thisFiled.set(this, bukkitField.get(instance));
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.error("MISC.FIELD-COPY-ERROR", bukkitName, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInterface(Class<? extends PluginLoader> aClass) throws IllegalArgumentException {
|
||||
instance.registerInterface(aClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin(String s) {
|
||||
return instance.getPlugin(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin[] getPlugins() {
|
||||
return instance.getPlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(String s) {
|
||||
return instance.isPluginEnabled(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(Plugin plugin) {
|
||||
return instance.isPluginEnabled(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin loadPlugin(File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException {
|
||||
return instance.loadPlugin(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin[] loadPlugins(File file) {
|
||||
return instance.loadPlugins(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugins() {
|
||||
for (Plugin plugin : getPlugins()) {
|
||||
if (plugin != main && !delayedDisable.contains(plugin)) {
|
||||
disablePlugin(plugin);
|
||||
}
|
||||
}
|
||||
Collections.reverse(delayedDisable);
|
||||
delayedDisable.forEach(singleton::disablePlugin);
|
||||
disablePlugin(main);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPlugins() {
|
||||
instance.clearPlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callEvent(Event event) throws IllegalStateException {
|
||||
instance.callEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvents(Listener listener, Plugin plugin) {
|
||||
instance.registerEvents(listener, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvent(Class<? extends Event> aClass, Listener listener, EventPriority eventPriority, EventExecutor eventExecutor, Plugin plugin) {
|
||||
instance.registerEvent(aClass, listener, eventPriority, eventExecutor, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvent(Class<? extends Event> aClass, Listener listener, EventPriority eventPriority, EventExecutor eventExecutor, Plugin plugin, boolean b) {
|
||||
instance.registerEvent(aClass, listener, eventPriority, eventExecutor, plugin, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enablePlugin(Plugin plugin) {
|
||||
TDependencyInjector.injectOnEnable(plugin);
|
||||
instance.enablePlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin(Plugin plugin) {
|
||||
TDependencyInjector.ejectOnDisable(plugin);
|
||||
instance.disablePlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission getPermission(String s) {
|
||||
return instance.getPermission(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermission(Permission permission) {
|
||||
instance.addPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(Permission permission) {
|
||||
instance.removePermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(String s) {
|
||||
instance.removePermission(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Permission> getDefaultPermissions(boolean b) {
|
||||
return instance.getDefaultPermissions(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculatePermissionDefaults(Permission permission) {
|
||||
instance.recalculatePermissionDefaults(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeToPermission(String s, Permissible permissible) {
|
||||
instance.subscribeToPermission(s, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromPermission(String s, Permissible permissible) {
|
||||
instance.unsubscribeFromPermission(s, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Permissible> getPermissionSubscriptions(String s) {
|
||||
return instance.getPermissionSubscriptions(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeToDefaultPerms(boolean b, Permissible permissible) {
|
||||
instance.subscribeToDefaultPerms(b, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeFromDefaultPerms(boolean b, Permissible permissible) {
|
||||
instance.unsubscribeFromDefaultPerms(b, permissible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Permissible> getDefaultPermSubscriptions(boolean b) {
|
||||
return instance.getDefaultPermSubscriptions(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Permission> getPermissions() {
|
||||
return instance.getPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTimings() {
|
||||
return instance.useTimings();
|
||||
}
|
||||
}
|
||||
130
src/main/scala/com/ilummc/tlib/logger/TLogger.java
Normal file
130
src/main/scala/com/ilummc/tlib/logger/TLogger.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.ilummc.tlib.logger;
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class TLogger {
|
||||
|
||||
public static final int VERBOSE = 0, FINEST = 1, FINE = 2, INFO = 3, WARN = 4, ERROR = 5, FATAL = 6;
|
||||
|
||||
private static TLogger globalLogger = new TLogger("§8[§3§lTabooLib§8][§r{1}§8] §f{2}", Main.getInst(), TLogger.FINE);
|
||||
private final String pattern;
|
||||
private String name;
|
||||
private int level;
|
||||
|
||||
public static TLogger getGlobalLogger() {
|
||||
return globalLogger;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public TLogger(String pattern, Plugin plugin, int level) {
|
||||
this.pattern = pattern;
|
||||
this.name = plugin.getName();
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public TLogger(String pattern, String name, int level) {
|
||||
this.pattern = pattern;
|
||||
this.name = name;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public void verbose(String msg) {
|
||||
if (level <= VERBOSE) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§f全部", TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§f全部", TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void finest(String msg) {
|
||||
if (level <= FINEST) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§e良好", TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§e良好", TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fine(String msg) {
|
||||
if (level <= FINE) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§a正常", TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§a正常", TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
if (level <= INFO) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§b信息", TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§b信息", TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void warn(String msg) {
|
||||
if (level <= WARN) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§6警告", "§6" + TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§6警告", "§6" + TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String msg) {
|
||||
if (level <= ERROR) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§c错误", "§c" + TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§c错误", "§c" + TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fatal(String msg) {
|
||||
if (level <= FATAL) {
|
||||
if (TabooLib.isSpigot()) {
|
||||
Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(pattern, name, "§4致命错误", "§4" + TLocale.Translate.setColored(msg)));
|
||||
} else {
|
||||
BungeeCord.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(Strings.replaceWithOrder(pattern, name, "§4致命错误", "§4" + TLocale.Translate.setColored(msg))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TLogger getUnformatted(Plugin plugin) {
|
||||
return new TLogger("§8[§3§l{0}§8][§r{1}§8] §f{2}", plugin, TLogger.FINE);
|
||||
}
|
||||
|
||||
public static TLogger getUnformatted(String name) {
|
||||
return new TLogger("§8[§3§l{0}§8][§r{1}§8] §f{2}", name, TLogger.FINE);
|
||||
}
|
||||
}
|
||||
47
src/main/scala/com/ilummc/tlib/nms/ActionBar.java
Normal file
47
src/main/scala/com/ilummc/tlib/nms/ActionBar.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.ilummc.tlib.nms;
|
||||
|
||||
import com.ilummc.tlib.util.asm.AsmClassTransformer;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class ActionBar {
|
||||
|
||||
private static ActionBar instance;
|
||||
|
||||
static {
|
||||
if (TabooLib.getVerint() > 11100) {
|
||||
instance = (ActionBar) AsmClassTransformer.builder().from(Impl_1_12.class).fromVersion("v1_12_R1")
|
||||
.toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]).build().transform();
|
||||
} else {
|
||||
instance = (ActionBar) AsmClassTransformer.builder().from(Impl_1_8.class).fromVersion("v1_8_R3")
|
||||
.toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]).build().transform();
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendActionBar(Player player, String text) {
|
||||
instance.send(player, text);
|
||||
}
|
||||
|
||||
public abstract void send(Player player, String text);
|
||||
|
||||
public static class Impl_1_8 extends ActionBar {
|
||||
|
||||
@Override
|
||||
public void send(Player player, String text) {
|
||||
net.minecraft.server.v1_8_R3.ChatComponentText component = new net.minecraft.server.v1_8_R3.ChatComponentText(text);
|
||||
net.minecraft.server.v1_8_R3.PacketPlayOutChat packet = new net.minecraft.server.v1_8_R3.PacketPlayOutChat(component, (byte) 2);
|
||||
((org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Impl_1_12 extends ActionBar {
|
||||
|
||||
@Override
|
||||
public void send(Player player, String text) {
|
||||
net.minecraft.server.v1_12_R1.ChatComponentText component = new net.minecraft.server.v1_12_R1.ChatComponentText(text);
|
||||
net.minecraft.server.v1_12_R1.PacketPlayOutChat packet = new net.minecraft.server.v1_12_R1.PacketPlayOutChat(component, net.minecraft.server.v1_12_R1.ChatMessageType.a((byte) 2));
|
||||
((org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
src/main/scala/com/ilummc/tlib/resources/TLocale.java
Normal file
161
src/main/scala/com/ilummc/tlib/resources/TLocale.java
Normal file
@@ -0,0 +1,161 @@
|
||||
package com.ilummc.tlib.resources;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.bungee.api.ChatColor;
|
||||
import com.ilummc.tlib.bungee.api.chat.TextComponent;
|
||||
import com.ilummc.tlib.bungee.chat.ComponentSerializer;
|
||||
import com.ilummc.tlib.inject.TLoggerManager;
|
||||
import com.ilummc.tlib.util.Ref;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.common.nms.NMSHandler;
|
||||
import me.skymc.taboolib.json.tellraw.TellrawCreator;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
*/
|
||||
public class TLocale {
|
||||
|
||||
private TLocale() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
static String asString(String path, Class<?> callerClass, String... args) {
|
||||
return TLocaleLoader.asString(Ref.getCallerPlugin(callerClass), path, args);
|
||||
}
|
||||
|
||||
static List<String> asStringList(String path, Class<?> callerClass, String... args) {
|
||||
return TLocaleLoader.asStringList(Ref.getCallerPlugin(callerClass), path, args);
|
||||
}
|
||||
|
||||
private static void sendTo(String path, CommandSender sender, String[] args, Class<?> callerClass) {
|
||||
TLocaleLoader.sendTo(Ref.getCallerPlugin(callerClass), path, sender, args);
|
||||
}
|
||||
|
||||
public static void sendToConsole(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> sendTo(path, Bukkit.getConsoleSender(), args, clazz));
|
||||
}
|
||||
|
||||
public static void sendTo(CommandSender sender, String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> sendTo(path, sender, args, clazz));
|
||||
}
|
||||
|
||||
public static void sendTo(String path, CommandSender sender, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> sendTo(path, sender, args, clazz));
|
||||
}
|
||||
|
||||
public static String asString(String path, String... args) {
|
||||
try {
|
||||
return asString(path, Ref.getCallerClass(3).orElse(Main.class), args);
|
||||
} catch (Exception e) {
|
||||
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("FETCH-LOCALE-ERROR"), path));
|
||||
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("LOCALE-ERROR-REASON"), e.getMessage()));
|
||||
return "§4<" + path + "§4>";
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> asStringList(String path, String... args) {
|
||||
try {
|
||||
return asStringList(path, Ref.getCallerClass(3).orElse(Main.class), args);
|
||||
} catch (Exception e) {
|
||||
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("LOCALE-ERROR-REASON"), e.getMessage()));
|
||||
return Collections.singletonList("§4<" + path + "§4>");
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload() {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> TLocaleLoader.load(Ref.getCallerPlugin(clazz), false));
|
||||
}
|
||||
|
||||
public static final class Tellraw extends TLocale {
|
||||
|
||||
public static void send(CommandSender sender, String rawMessage) {
|
||||
if (sender instanceof Player) {
|
||||
TellrawCreator.getAbstractTellraw().sendRawMessage((Player) sender, rawMessage);
|
||||
} else {
|
||||
sender.sendMessage(TextComponent.toLegacyText(ComponentSerializer.parse(rawMessage)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Display extends TLocale {
|
||||
|
||||
public static void sendTitle(Player player, String title, String subTitle) {
|
||||
sendTitle(player, title, subTitle, 10, 20, 10);
|
||||
}
|
||||
|
||||
public static void sendTitle(Player player, String title, String subTitle, int fadein, int stay, int fadeout) {
|
||||
NMSHandler.getHandler().sendTitle(player, title, fadein, stay, fadeout, subTitle, fadein, stay, fadeout);
|
||||
}
|
||||
|
||||
public static void sendActionBar(Player player, String text) {
|
||||
NMSHandler.getHandler().sendActionBar(player, text);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Translate extends TLocale {
|
||||
|
||||
public static boolean isPlaceholderUseDefault() {
|
||||
return Main.getInst().getConfig().getBoolean("LOCALE.USE_PAPI", false);
|
||||
}
|
||||
|
||||
public static boolean isPlaceholderPluginEnabled() {
|
||||
return Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null && Bukkit.getPluginManager().getPlugin("PlaceholderAPI").isEnabled();
|
||||
}
|
||||
|
||||
public static String setColored(String args) {
|
||||
return ChatColor.translateAlternateColorCodes('&', args);
|
||||
}
|
||||
|
||||
public static List<String> setColored(List<String> args) {
|
||||
return args.stream().map(var -> ChatColor.translateAlternateColorCodes('&', var)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static String setPlaceholders(CommandSender sender, String args) {
|
||||
return isPlaceholderPluginEnabled() ? sender instanceof Player ? PlaceholderAPI.setPlaceholders((Player) sender, args) : args : args;
|
||||
}
|
||||
|
||||
public static List<String> setPlaceholders(CommandSender sender, List<String> args) {
|
||||
return isPlaceholderPluginEnabled() ? sender instanceof Player ? PlaceholderAPI.setPlaceholders((Player) sender, args) : args : args;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Logger extends TLocale {
|
||||
|
||||
public static void info(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).info(locale)));
|
||||
}
|
||||
|
||||
public static void warn(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).warn(locale)));
|
||||
}
|
||||
|
||||
public static void error(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).error(locale)));
|
||||
}
|
||||
|
||||
public static void fatal(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).fatal(locale)));
|
||||
}
|
||||
|
||||
public static void fine(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).fine(locale)));
|
||||
}
|
||||
|
||||
public static void finest(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).finest(locale)));
|
||||
}
|
||||
|
||||
public static void verbose(String path, String... args) {
|
||||
Ref.getCallerClass(3).ifPresent(clazz -> asStringList(path, clazz, args).forEach(locale -> TLoggerManager.getLogger(Ref.getCallerPlugin(clazz)).verbose(locale)));
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/main/scala/com/ilummc/tlib/resources/TLocaleInstance.java
Normal file
115
src/main/scala/com/ilummc/tlib/resources/TLocaleInstance.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package com.ilummc.tlib.resources;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.resources.type.TLocaleText;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ThreadSafe
|
||||
@SuppressWarnings("rawtypes")
|
||||
class TLocaleInstance {
|
||||
|
||||
private final Map<String, List<TLocaleSerialize>> map = new HashMap<>();
|
||||
private final Plugin plugin;
|
||||
private final AtomicInteger latestUpdateNodes = new AtomicInteger();
|
||||
|
||||
TLocaleInstance(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public Map<String, List<TLocaleSerialize>> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public AtomicInteger getLatestUpdateNodes() {
|
||||
return latestUpdateNodes;
|
||||
}
|
||||
|
||||
public void sendTo(String path, CommandSender sender, String... args) {
|
||||
try {
|
||||
map.getOrDefault(path, ImmutableList.of(TLocaleSerialize.getEmpty(path))).forEach(tSender -> {
|
||||
if (Bukkit.isPrimaryThread() || "true".equals(System.getProperty("tlib.forceAsync"))) {
|
||||
tSender.sendTo(sender, args);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> tSender.sendTo(sender, args));
|
||||
}
|
||||
});
|
||||
} catch (Exception | Error e) {
|
||||
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("SEND-LOCALE-ERROR"), path));
|
||||
TLib.getTLib().getLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("LOCALE-ERROR-REASON"), e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public String asString(String path, String... args) {
|
||||
return map.getOrDefault(path, ImmutableList.of(TLocaleSerialize.getEmpty(path))).get(0).asString(args);
|
||||
}
|
||||
|
||||
public List<String> asStringList(String path, String... args) {
|
||||
return map.getOrDefault(path, ImmutableList.of(TLocaleSerialize.getEmpty(path))).get(0).asStringList(args);
|
||||
}
|
||||
|
||||
private static boolean isListString(List list) {
|
||||
for (Object o : list) {
|
||||
if (!(o instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void load(YamlConfiguration configuration) {
|
||||
load(configuration, false);
|
||||
}
|
||||
|
||||
public void load(YamlConfiguration configuration, boolean cleanup) {
|
||||
int originNodes = map.size();
|
||||
int updateNodes = 0;
|
||||
if (cleanup) {
|
||||
map.clear();
|
||||
}
|
||||
for (String s : configuration.getKeys(true)) {
|
||||
boolean updated = false;
|
||||
Object value = configuration.get(s);
|
||||
if (value instanceof TLocaleSerialize) {
|
||||
updated = map.put(s, Collections.singletonList((TLocaleSerialize) value)) != null;
|
||||
} else if (value instanceof List && !((List) value).isEmpty()) {
|
||||
if (isListString((List) value)) {
|
||||
updated = map.put(s, Collections.singletonList(TLocaleText.of(value))) != null;
|
||||
} else {
|
||||
updated = map.put(s, ((List<?>) value).stream().map(o -> o instanceof TLocaleSerialize ? (TLocaleSerialize) o : TLocaleText.of(String.valueOf(o))).collect(Collectors.toList())) != null;
|
||||
}
|
||||
} else if (!(value instanceof ConfigurationSection)) {
|
||||
String str = String.valueOf(value);
|
||||
updated = map.put(s, Collections.singletonList(str.length() == 0 ? TLocaleSerialize.getEmpty() : TLocaleText.of(str))) != null;
|
||||
}
|
||||
if (updated) {
|
||||
updateNodes++;
|
||||
}
|
||||
}
|
||||
latestUpdateNodes.set(originNodes - updateNodes);
|
||||
}
|
||||
}
|
||||
169
src/main/scala/com/ilummc/tlib/resources/TLocaleLoader.java
Normal file
169
src/main/scala/com/ilummc/tlib/resources/TLocaleLoader.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package com.ilummc.tlib.resources;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.annotations.TLocalePlugin;
|
||||
import com.ilummc.tlib.logger.TLogger;
|
||||
import com.ilummc.tlib.resources.type.*;
|
||||
import com.ilummc.tlib.util.IO;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboocode.TabooCodeLang;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import me.skymc.taboolib.fileutils.ConfigUtils;
|
||||
import me.skymc.taboolib.fileutils.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TLocaleLoader {
|
||||
|
||||
private static final Map<String, TLocaleInstance> map = new ConcurrentHashMap<>();
|
||||
|
||||
public static void init() {
|
||||
ConfigurationSerialization.registerClass(TLocaleText.class, "TEXT");
|
||||
ConfigurationSerialization.registerClass(TLocaleJson.class, "JSON");
|
||||
ConfigurationSerialization.registerClass(TLocaleBook.class, "BOOK");
|
||||
ConfigurationSerialization.registerClass(TLocaleSound.class, "SOUND");
|
||||
ConfigurationSerialization.registerClass(TLocaleTitle.class, "TITLE");
|
||||
ConfigurationSerialization.registerClass(TLocaleBossBar.class, "BAR");
|
||||
ConfigurationSerialization.registerClass(TLocaleActionBar.class, "ACTION");
|
||||
}
|
||||
|
||||
public static void sendTo(Plugin plugin, String path, CommandSender sender, String... args) {
|
||||
TabooLib.debug(plugin, "TLocaleLoader.sendTo: " + plugin + ", path: " + path + ", sender: " + sender + ", args: " + Arrays.asList(args));
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
Optional.ofNullable(map.get(plugin.getName())).ifPresent(localeInstance -> localeInstance.sendTo(path, sender, args));
|
||||
} else {
|
||||
synchronized (TLocaleLoader.class) {
|
||||
Optional.ofNullable(map.get(plugin.getName())).ifPresent(localeInstance -> localeInstance.sendTo(path, sender, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String asString(Plugin plugin, String path, String... args) {
|
||||
TabooLib.debug(plugin, "TLocaleLoader.asString: " + plugin.getName() + ", path: " + path + ", args: " + Arrays.asList(args));
|
||||
TLocaleInstance tLocaleInstance = map.get(plugin.getName());
|
||||
if (tLocaleInstance != null) {
|
||||
return tLocaleInstance.asString(path, args);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> asStringList(Plugin plugin, String path, String... args) {
|
||||
TabooLib.debug(plugin, "TLocaleLoader.asStringList: " + plugin + ", path: " + path + ", args: " + Arrays.asList(args));
|
||||
TLocaleInstance tLocaleInstance = map.get(plugin.getName());
|
||||
if (tLocaleInstance != null) {
|
||||
return tLocaleInstance.asStringList(path, args);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 载入语言文件
|
||||
*
|
||||
* @param plugin 载入插件
|
||||
* @param isCover 是否覆盖
|
||||
*/
|
||||
public static void load(Plugin plugin, boolean isCover) {
|
||||
try {
|
||||
if (isLoadLocale(plugin, isCover)) {
|
||||
// 获取文件
|
||||
File localeFile = getLocaleFile(plugin);
|
||||
if (localeFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载文件
|
||||
infoLogger("TRY-LOADING-LANG", plugin.getName(), localeFile.getName());
|
||||
YamlConfiguration localeConfiguration = ConfigUtils.loadYaml(plugin, localeFile);
|
||||
YamlConfiguration localeConfigurationAtStream = getLocaleAtStream(plugin, localeFile);
|
||||
|
||||
// 载入配置
|
||||
loadPluginLocale(plugin, localeFile, localeConfiguration, localeConfigurationAtStream);
|
||||
|
||||
// 注册监听
|
||||
TLib.getTLib().getConfigWatcher().removeListener(localeFile);
|
||||
TLib.getTLib().getConfigWatcher().addListener(localeFile, null, obj -> {
|
||||
infoLogger("RELOADING-LANG", plugin.getName());
|
||||
loadPluginLocale(plugin, localeFile, ConfigUtils.loadYaml(plugin, localeFile), getLocaleAtStream(plugin, localeFile));
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errorLogger("ERROR-LOADING-LANG", plugin.getName(), e.toString() + "\n" + e.getStackTrace()[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLocaleLoaded(Plugin plugin) {
|
||||
return map.containsKey(plugin.getName());
|
||||
}
|
||||
|
||||
public static boolean isDependWithTabooLib(Plugin plugin) {
|
||||
return plugin.getClass().getAnnotation(TLocalePlugin.class) != null || plugin.getDescription().getDepend().contains(Main.getInst().getName()) || plugin.getDescription().getSoftDepend().contains(Main.getInst().getName());
|
||||
}
|
||||
|
||||
public static List<String> getLocalePriority() {
|
||||
return Main.getInst().getConfig().contains("LOCALE.PRIORITY") ? Main.getInst().getConfig().getStringList("LOCALE.PRIORITY") : Collections.singletonList("zh_CN");
|
||||
}
|
||||
|
||||
private static boolean isLoadLocale(Plugin plugin, boolean isCover) {
|
||||
return (isCover || !isLocaleLoaded(plugin)) && (plugin.equals(Main.getInst()) || isDependWithTabooLib(plugin));
|
||||
}
|
||||
|
||||
private static void infoLogger(String path, String... args) {
|
||||
TLogger.getGlobalLogger().info(Strings.replaceWithOrder(TLib.getInternalLanguage().getString(path), args));
|
||||
}
|
||||
|
||||
private static void errorLogger(String path, String... args) {
|
||||
TLogger.getGlobalLogger().error(Strings.replaceWithOrder(TLib.getInternalLanguage().getString(path), args));
|
||||
}
|
||||
|
||||
private static File getLocaleFile(Plugin plugin) {
|
||||
releaseLocales(plugin);
|
||||
return getLocalePriority().stream().map(localeName -> new File(plugin.getDataFolder(), "lang/" + localeName + ".yml")).filter(File::exists).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private static void releaseLocales(Plugin plugin) {
|
||||
getLocalePriority().stream().filter(localeName -> !new File(plugin.getDataFolder(), "lang/" + localeName + ".yml").exists() && plugin.getResource("lang/" + localeName + ".yml") != null).forEach(localeName -> plugin.saveResource("lang/" + localeName + ".yml", true));
|
||||
}
|
||||
|
||||
private static TLocaleInstance getLocaleInstance(Plugin plugin) {
|
||||
TLocaleInstance instance = new TLocaleInstance(plugin);
|
||||
map.put(plugin.getName(), instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static YamlConfiguration getLocaleAtStream(Plugin plugin, File localeFile) {
|
||||
InputStream localeInputSteam = FileUtils.getResource(plugin, "lang/" + localeFile.getName());
|
||||
try {
|
||||
String yamlText = new String(IO.readFully(localeInputSteam), Charset.forName("utf-8"));
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
yaml.loadFromString(yamlText);
|
||||
return yaml;
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadPluginLocale(Plugin plugin, File localeFile, YamlConfiguration localeConfiguration, YamlConfiguration localeConfigurationAtStream) {
|
||||
TLocaleInstance localeInstance = getLocaleInstance(plugin);
|
||||
if (localeConfigurationAtStream != null) {
|
||||
localeInstance.load(localeConfigurationAtStream);
|
||||
}
|
||||
localeInstance.load(localeConfiguration);
|
||||
if (localeInstance.getLatestUpdateNodes().get() <= 0) {
|
||||
infoLogger("SUCCESS-LOADING-LANG-NORMAL", plugin.getName(), localeFile.getName().split("\\.")[0], String.valueOf(localeInstance.size()));
|
||||
} else {
|
||||
infoLogger("SUCCESS-LOADING-LANG-UPDATE", plugin.getName(), localeFile.getName().split("\\.")[0], String.valueOf(localeInstance.size()), String.valueOf(localeInstance.getLatestUpdateNodes().get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/main/scala/com/ilummc/tlib/resources/TLocaleSender.java
Normal file
37
src/main/scala/com/ilummc/tlib/resources/TLocaleSender.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.ilummc.tlib.resources;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-05-12 13:58
|
||||
*/
|
||||
public interface TLocaleSender {
|
||||
|
||||
/**
|
||||
* 发送信息
|
||||
*
|
||||
* @param sender 发送目标
|
||||
* @param args 参数
|
||||
*/
|
||||
void sendTo(CommandSender sender, String... args);
|
||||
|
||||
/**
|
||||
* 获取文本
|
||||
*
|
||||
* @param args 参数
|
||||
* @return 文本
|
||||
*/
|
||||
String asString(String... args);
|
||||
|
||||
/**
|
||||
* 获取文本集合
|
||||
*
|
||||
* @param args 参数
|
||||
* @return 文本集合
|
||||
*/
|
||||
List<String> asStringList(String... args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.ilummc.tlib.resources;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-05-12 14:01
|
||||
*/
|
||||
public abstract class TLocaleSerialize implements TLocaleSender, ConfigurationSerializable {
|
||||
|
||||
public static boolean isPlaceholderEnabled(Map<String, Object> map) {
|
||||
Object placeholderObject = map.getOrDefault("papi", TLocale.Translate.isPlaceholderUseDefault());
|
||||
return placeholderObject instanceof Boolean ? (boolean) placeholderObject : placeholderObject instanceof String && "true".equals(placeholderObject);
|
||||
}
|
||||
|
||||
public static String getStringOrDefault(Map<String, Object> map, String path, String def) {
|
||||
Object var = map.getOrDefault(path, def);
|
||||
return var instanceof String ? (String) var : def;
|
||||
}
|
||||
|
||||
public static Integer getIntegerOrDefault(Map<String, Object> map, String path, Integer def) {
|
||||
Object var = map.getOrDefault(path, def);
|
||||
return var instanceof Integer ? (Integer) var : def;
|
||||
}
|
||||
|
||||
public static Double getDoubleOrDefault(Map<String, Object> map, String path, Double def) {
|
||||
Object var = map.getOrDefault(path, def);
|
||||
return var instanceof Double ? (Double) var : def;
|
||||
}
|
||||
|
||||
static TLocaleSerialize getEmpty() {
|
||||
return new TLocaleSerialize() {
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static TLocaleSerialize getEmpty(String path) {
|
||||
return new TLocaleSerialize() {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
sender.sendMessage("§8<" + path + "§8>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return "§8<" + path + "§8>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> asStringList(String... args) {
|
||||
return Collections.singletonList("§4<" + path + "§4>");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> asStringList(String... args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.compat.PlaceholderHook;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.display.ActionUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import java.util.Map;
|
||||
|
||||
@Immutable
|
||||
@SerializableAs("ACTION")
|
||||
public class TLocaleActionBar extends TLocaleSerialize {
|
||||
|
||||
private final String text;
|
||||
private final boolean papi;
|
||||
|
||||
private TLocaleActionBar(String text, boolean papi) {
|
||||
this.text = text;
|
||||
this.papi = papi;
|
||||
}
|
||||
|
||||
public static TLocaleActionBar valueOf(Map<String, Object> map) {
|
||||
String text = ChatColor.translateAlternateColorCodes('&', String.valueOf(map.getOrDefault("text", "Empty Action bar message.")));
|
||||
return new TLocaleActionBar(text, isPlaceholderEnabled(map));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (sender instanceof Player) {
|
||||
TLocale.Display.sendActionBar(((Player) sender), replace(sender, text, args));
|
||||
}
|
||||
}
|
||||
|
||||
private String replace(CommandSender sender, String text, String[] args) {
|
||||
String s = Strings.replaceWithOrder(text, args);
|
||||
return papi ? PlaceholderHook.replace(sender, s) : s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return "ActionBar: [" + text + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = Maps.newHashMap();
|
||||
map.put("text", text);
|
||||
if (papi) {
|
||||
map.put("papi", true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.bungee.chat.ComponentSerializer;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.Main;
|
||||
import me.skymc.taboolib.bookformatter.BookFormatter;
|
||||
import me.skymc.taboolib.bookformatter.builder.BookBuilder;
|
||||
import me.skymc.taboolib.json.tellraw.TellrawJson;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-05-27 0:05
|
||||
*/
|
||||
@ThreadSafe
|
||||
@SerializableAs("BOOK")
|
||||
public class TLocaleBook extends TLocaleSerialize {
|
||||
|
||||
/*
|
||||
BookTest:
|
||||
- ==: BOOK
|
||||
pages:
|
||||
0:
|
||||
- '第一页内容'
|
||||
- '[ <变量1@page-1> ]'
|
||||
1:
|
||||
- '第二页内容'
|
||||
- '[ <变量2@page-2> ]'
|
||||
args:
|
||||
page-1:
|
||||
hover: '展示内容1'
|
||||
command: '/say %player_name% NB1'
|
||||
page-2:
|
||||
hover: '展示内容2'
|
||||
suggest: '/say %player_name% NB2'
|
||||
*/
|
||||
|
||||
private final List<TellrawJson> pages;
|
||||
private final Map<String, Object> map;
|
||||
private final boolean papi;
|
||||
|
||||
public TLocaleBook(List<TellrawJson> pages, Map<String, Object> map, boolean papi) {
|
||||
this.pages = pages;
|
||||
this.map = map;
|
||||
this.papi = papi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return Maps.newHashMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BookBuilder bookBuilder = BookFormatter.writtenBook();
|
||||
pages.stream().map(jsonPage -> format(jsonPage, sender, args)).map(ComponentSerializer::parse).forEach(bookBuilder::addPages);
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BookFormatter.forceOpen((Player) sender, bookBuilder.build());
|
||||
}
|
||||
}.runTask(Main.getInst());
|
||||
}
|
||||
}.runTaskAsynchronously(Main.getInst());
|
||||
}
|
||||
|
||||
private String format(TellrawJson jsonPage, CommandSender sender, String[] args) {
|
||||
return papi ? TLocale.Translate.setPlaceholders(sender, Strings.replaceWithOrder(jsonPage.toRawMessage(), args)) : Strings.replaceWithOrder(jsonPage.toRawMessage(), args);
|
||||
}
|
||||
|
||||
public static TLocaleBook valueOf(Map<String, Object> map) {
|
||||
Map<String, Object> pages = map.containsKey("pages") ? (Map<String, Object>) map.get("pages") : new HashMap<>();
|
||||
Map<String, Object> section = map.containsKey("args") ? (Map<String, Object>) map.get("args") : new HashMap<>();
|
||||
List<TellrawJson> pageJsonList = pages.values().stream().map(page -> TLocaleJson.formatJson(section, page, TellrawJson.create())).collect(Collectors.toList());
|
||||
return new TLocaleBook(pageJsonList, map, isPlaceholderEnabled(map));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.other.NumberUtils;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.bossbar.BossBar;
|
||||
import org.inventivetalent.bossbar.BossBarAPI;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-05-27 18:52
|
||||
*/
|
||||
@ThreadSafe
|
||||
@SerializableAs("BAR")
|
||||
public class TLocaleBossBar extends TLocaleSerialize {
|
||||
|
||||
/*
|
||||
BossBar:
|
||||
- ==: BAR
|
||||
text: 'BossBar 血条公告'
|
||||
color: BLUE
|
||||
style: NOTCHED_20
|
||||
progress: 1.0
|
||||
timeout: 20
|
||||
timeout-interval: 2
|
||||
*/
|
||||
|
||||
private final String text;
|
||||
private final BossBarAPI.Color color;
|
||||
private final BossBarAPI.Style style;
|
||||
private final float progress;
|
||||
private final int timeout;
|
||||
private final int timeoutInterval;
|
||||
private final boolean papi;
|
||||
|
||||
public TLocaleBossBar(String text, BossBarAPI.Color color, BossBarAPI.Style style, float progress, int timeout, int timeoutInterval, boolean papi) {
|
||||
this.text = text;
|
||||
this.color = color;
|
||||
this.style = style;
|
||||
this.progress = progress;
|
||||
this.timeout = timeout;
|
||||
this.timeoutInterval = timeoutInterval;
|
||||
this.papi = papi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (Bukkit.getPluginManager().getPlugin("BossBarAPI") == null) {
|
||||
TLocale.Logger.error("LOCALE.BAR-PLUGIN-NOT-FOUND");
|
||||
return;
|
||||
}
|
||||
if (sender instanceof Player) {
|
||||
TextComponent textComponent = new TextComponent(papi ? TLocale.Translate.setPlaceholders(sender, Strings.replaceWithOrder(text, args)) : TLocale.Translate.setColored(Strings.replaceWithOrder(text, args)));
|
||||
BossBar bossBar = BossBarAPI.addBar((Player) sender, textComponent, color, style, progress, timeout, timeoutInterval);
|
||||
} else {
|
||||
sender.sendMessage(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TLocaleBossBar valueOf(Map<String, Object> map) {
|
||||
return new TLocaleBossBar(map.getOrDefault("text", "§4* Invalid Text*").toString(), getColor(String.valueOf(map.get("color"))), getStyle(String.valueOf(map.get("style"))), (float) NumberUtils.getDouble(String.valueOf(map.getOrDefault("progress", 1))), NumberUtils.getInteger(String.valueOf(map.getOrDefault("timeout", 20))), NumberUtils.getInteger(String.valueOf(map.getOrDefault("timeout-interval", 2))), isPlaceholderEnabled(map));
|
||||
}
|
||||
|
||||
private static BossBarAPI.Color getColor(String color) {
|
||||
try {
|
||||
return BossBarAPI.Color.valueOf(color);
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.error("LOCALE.BAR-STYLE-IDENTIFICATION-FAILED", e.toString());
|
||||
return BossBarAPI.Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
private static BossBarAPI.Style getStyle(String style) {
|
||||
try {
|
||||
return BossBarAPI.Style.valueOf(style);
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.error("LOCALE.BAR-COLOR-IDENTIFICATION-FAILED", e.toString());
|
||||
return BossBarAPI.Style.NOTCHED_20;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
src/main/scala/com/ilummc/tlib/resources/type/TLocaleJson.java
Normal file
227
src/main/scala/com/ilummc/tlib/resources/type/TLocaleJson.java
Normal file
@@ -0,0 +1,227 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.bungee.api.chat.*;
|
||||
import com.ilummc.tlib.bungee.chat.ComponentSerializer;
|
||||
import com.ilummc.tlib.compat.PlaceholderHook;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.inventory.ItemUtils;
|
||||
import me.skymc.taboolib.json.tellraw.TellrawJson;
|
||||
import me.skymc.taboolib.other.NumberUtils;
|
||||
import me.skymc.taboolib.string.VariableFormatter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@ThreadSafe
|
||||
@SerializableAs("JSON")
|
||||
public class TLocaleJson extends TLocaleSerialize {
|
||||
|
||||
private static final Pattern pattern = Pattern.compile("<([^<>]*)?@([^<>]*)>");
|
||||
private final List<BaseComponent[]> components;
|
||||
private final boolean papi;
|
||||
private final Map<String, Object> map;
|
||||
|
||||
private TLocaleJson(List<BaseComponent[]> components, boolean papi, Map<String, Object> map) {
|
||||
this.components = ImmutableList.copyOf(components);
|
||||
this.papi = papi;
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public static TLocaleJson valueOf(Map<String, Object> map) {
|
||||
List<String> textList = getTextList(map.getOrDefault("text", "Empty Node"));
|
||||
|
||||
// 分析 args 并替换
|
||||
Object argsObj = map.get("args");
|
||||
if (argsObj instanceof Map) {
|
||||
Map<String, Object> section = new HashMap<>(((Map<?, ?>) argsObj).size());
|
||||
|
||||
// valueOf(k) 是因为这个键可能加载为一个 Integer 导致 contains(String) 返回 false
|
||||
((Map<?, ?>) argsObj).forEach((k, v) -> section.put(String.valueOf(k), v));
|
||||
List<BaseComponent[]> collect = textList.stream().map(s -> {
|
||||
int index = 0;
|
||||
String[] template = pattern.split(s);
|
||||
Matcher matcher = pattern.matcher(s);
|
||||
// 有可能开头和结尾是替换文本,所以做个特判
|
||||
List<BaseComponent> builder = template.length > index ? new ArrayList<>(Arrays.asList(TextComponent.fromLegacyText(template[index++]))) : new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
String replace = matcher.group();
|
||||
// 假的 <@>
|
||||
if (replace.length() <= 2) {
|
||||
continue;
|
||||
}
|
||||
// 真的 <@xxx>
|
||||
replace = replace.substring(1, replace.length() - 1);
|
||||
String[] split = replace.split("@");
|
||||
// @ 前面的字符串
|
||||
String text = split.length > 1 ? split[0] : "";
|
||||
// @ 后面的节点名
|
||||
String node = split.length > 1 ? split[1] : split[0];
|
||||
// 如果 args 有这个 xxx
|
||||
if (section.containsKey(node)) {
|
||||
Map<String, Object> arg = (Map<String, Object>) section.get(node);
|
||||
text = TLocale.Translate.setColored(String.valueOf(arg.getOrDefault("text", text)));
|
||||
// 可能有很多个 BaseComponent,于是为每个 component 单独设置各种事件
|
||||
BaseComponent[] component = TextComponent.fromLegacyText(text);
|
||||
arg.forEach((key, value) -> {
|
||||
if (key.equalsIgnoreCase("suggest")) {
|
||||
Arrays.stream(component).forEach(baseComponent -> baseComponent.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, String.valueOf(value))));
|
||||
} else if (key.equalsIgnoreCase("command") || "commands".equalsIgnoreCase(key)) {
|
||||
Arrays.stream(component).forEach(baseComponent -> baseComponent.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.valueOf(value))));
|
||||
} else if (key.equalsIgnoreCase("hover")) {
|
||||
Arrays.stream(component).forEach(baseComponent -> baseComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(TLocale.Translate.setColored(String.valueOf(value))).create())));
|
||||
}
|
||||
});
|
||||
// 添加到原来的 list 里面
|
||||
builder.addAll(Arrays.asList(component));
|
||||
} else {
|
||||
// 这个参数节点并没有找到,于是随便放点字符串进去
|
||||
builder.addAll(Arrays.asList(TextComponent.fromLegacyText(text)));
|
||||
TLib.getTLib().getLogger().warn(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("MISSING-ARGUMENT"), node));
|
||||
}
|
||||
// 有可能一开头就是 <@xxx>,然后 split 出来就少了一些,于是直接加上
|
||||
if (index < template.length) {
|
||||
builder.addAll(Arrays.asList(TextComponent.fromLegacyText(template[index++])));
|
||||
}
|
||||
}
|
||||
return builder.toArray(new BaseComponent[0]);
|
||||
}).collect(Collectors.toList());
|
||||
return new TLocaleJson(collect, isPlaceholderEnabled(map), map);
|
||||
}
|
||||
return new TLocaleJson(textList.stream().map(TextComponent::fromLegacyText).collect(Collectors.toList()), isPlaceholderEnabled(map), map);
|
||||
}
|
||||
|
||||
private static List<String> getTextList(Object textObj) {
|
||||
if (textObj instanceof List) {
|
||||
return ((List<?>) textObj).stream().map(Object::toString).map(TLocale.Translate::setColored).collect(Collectors.toList());
|
||||
} else if (textObj instanceof String) {
|
||||
return Lists.newArrayList(TLocale.Translate.setColored((String) textObj));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
components.forEach(comp -> sendRawMessage(sender, replace(comp, sender, args)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return components.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return Maps.newHashMap(map);
|
||||
}
|
||||
|
||||
private void sendRawMessage(CommandSender sender, BaseComponent[] components) {
|
||||
TLocale.Tellraw.send(sender, ComponentSerializer.toString(components));
|
||||
}
|
||||
|
||||
private List<BaseComponent> replace(List<BaseComponent> component, CommandSender sender, String... args) {
|
||||
return component.stream().map(c -> replace(c, sender, args)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private BaseComponent[] replace(BaseComponent[] component, CommandSender sender, String... args) {
|
||||
BaseComponent[] components = new BaseComponent[component.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
components[i] = replace(component[i].duplicate(), sender, args);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
private BaseComponent replace(BaseComponent component, CommandSender sender, String... args) {
|
||||
if (component.getClickEvent() != null) {
|
||||
ClickEvent clickEvent = new ClickEvent(component.getClickEvent().getAction(), replace(sender, component.getClickEvent().getValue(), args));
|
||||
component.setClickEvent(clickEvent);
|
||||
}
|
||||
if (component.getHoverEvent() != null) {
|
||||
HoverEvent hoverEvent = new HoverEvent(component.getHoverEvent().getAction(), replace(component.getHoverEvent().getValue(), sender, args));
|
||||
component.setHoverEvent(hoverEvent);
|
||||
}
|
||||
if (component.getExtra() != null) {
|
||||
component.setExtra(replace(component.getExtra(), sender, args));
|
||||
}
|
||||
if (component instanceof TextComponent) {
|
||||
((TextComponent) component).setText(replace(sender, ((TextComponent) component).getText(), args));
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
private String replace(CommandSender sender, String text, String[] args) {
|
||||
String s = Strings.replaceWithOrder(text, args);
|
||||
return papi ? PlaceholderHook.replace(sender, s) : s;
|
||||
}
|
||||
|
||||
public static TellrawJson formatJson(Map<String, Object> section, Object textObject, TellrawJson pageJson) {
|
||||
List<String> textList = textObject instanceof List ? (List<String>) textObject : Collections.singletonList(String.valueOf(textObject));
|
||||
// 遍历本页文本
|
||||
for (int i = 0; i < textList.size(); i++) {
|
||||
// 捕捉变量
|
||||
for (VariableFormatter.Variable variable : new VariableFormatter(TLocale.Translate.setColored(textList.get(i)), pattern).find().getVariableList()) {
|
||||
// 如果是变量
|
||||
if (variable.isVariable()) {
|
||||
String[] split = variable.getText().split("@");
|
||||
// @ 前面的字符串
|
||||
String text = split.length > 1 ? split[0] : "§4* Invalid Text *";
|
||||
// @ 后面的节点名
|
||||
String node = split.length > 1 ? split[1] : null;
|
||||
// 处理变量
|
||||
formatNode(section, pageJson, text, node);
|
||||
} else {
|
||||
pageJson.append(variable.getText());
|
||||
}
|
||||
}
|
||||
if (i + 1 < textList.size()) {
|
||||
pageJson.newLine();
|
||||
}
|
||||
}
|
||||
return pageJson;
|
||||
}
|
||||
|
||||
private static void formatNode(Map<String, Object> section, TellrawJson pageJson, String text, String node) {
|
||||
if (section.containsKey(node)) {
|
||||
try {
|
||||
Map<String, Object> args = (Map<String, Object>) section.get(node);
|
||||
// 文本
|
||||
pageJson.append(args.getOrDefault("text", text).toString());
|
||||
// 功能
|
||||
if (args.containsKey("item")) {
|
||||
pageJson.hoverItem(ItemUtils.getCacheItem(args.get("item").toString()));
|
||||
}
|
||||
if (args.containsKey("hover")) {
|
||||
pageJson.hoverText(args.get("hover").toString());
|
||||
}
|
||||
if (args.containsKey("suggest")) {
|
||||
pageJson.clickSuggest(args.get("suggest").toString());
|
||||
}
|
||||
if (args.containsKey("command")) {
|
||||
pageJson.clickCommand(args.get("command").toString());
|
||||
}
|
||||
if (args.containsKey("page")) {
|
||||
pageJson.clickChangePage(NumberUtils.getInteger(args.get("page").toString()));
|
||||
}
|
||||
if (args.containsKey("url")) {
|
||||
pageJson.clickOpenURL(args.get("url").toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TLocale.Logger.error("LOCALE.BOOK-ARGUMENTS-IDENTIFICATION-FAILED", e.toString());
|
||||
}
|
||||
} else {
|
||||
pageJson.append("§4* Invalid Argument *");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import me.skymc.taboolib.sound.SoundPack;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-05-06 14:35
|
||||
*/
|
||||
@Immutable
|
||||
@SerializableAs("ACTION")
|
||||
public class TLocaleSound extends TLocaleSerialize {
|
||||
|
||||
private final List<SoundPack> soundPacks;
|
||||
|
||||
public TLocaleSound(List<SoundPack> soundPacks) {
|
||||
this.soundPacks = soundPacks;
|
||||
}
|
||||
|
||||
public static TLocaleSound valueOf(Map<String, Object> map) {
|
||||
List<SoundPack> soundPacks = new ArrayList<>();
|
||||
Object sounds = map.containsKey("sounds") ? map.get("sounds") : map.getOrDefault("sound", "");
|
||||
if (sounds instanceof List) {
|
||||
soundPacks = ((List<String>) sounds).stream().map(SoundPack::new).collect(Collectors.toList());
|
||||
} else {
|
||||
soundPacks.add(new SoundPack(sounds.toString()));
|
||||
}
|
||||
return new TLocaleSound(soundPacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (sender instanceof Player) {
|
||||
soundPacks.forEach(x -> x.play((Player) sender));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "soundPacks=" + "TLocaleSound{" + soundPacks + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = Maps.newHashMap();
|
||||
if (soundPacks.size() == 1) {
|
||||
map.put("sounds", soundPacks.get(0).toString());
|
||||
} else if (soundPacks.size() > 1) {
|
||||
map.put("sounds", soundPacks.stream().map(SoundPack::toString).collect(Collectors.toList()));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
107
src/main/scala/com/ilummc/tlib/resources/type/TLocaleText.java
Normal file
107
src/main/scala/com/ilummc/tlib/resources/type/TLocaleText.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.compat.PlaceholderHook;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.Main;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Immutable
|
||||
@SerializableAs("TEXT")
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class TLocaleText extends TLocaleSerialize {
|
||||
|
||||
private final Object text;
|
||||
private final boolean usePlaceholder;
|
||||
|
||||
private TLocaleText(Object text, boolean usePlaceholder) {
|
||||
this.usePlaceholder = usePlaceholder;
|
||||
if (text instanceof String) {
|
||||
this.text = text;
|
||||
} else if (text instanceof List) {
|
||||
this.text = ImmutableList.copyOf(((List) text));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Param 'text' can only be an instance of String or List<String>");
|
||||
}
|
||||
}
|
||||
|
||||
public static TLocaleText of(String s) {
|
||||
return new TLocaleText(TLocale.Translate.setColored(s), TLocale.Translate.isPlaceholderUseDefault());
|
||||
}
|
||||
|
||||
public static TLocaleText of(Object o) {
|
||||
return o instanceof String ? of(((String) o)) : new TLocaleText(o, false);
|
||||
}
|
||||
|
||||
public static TLocaleText valueOf(Map<String, Object> map) {
|
||||
if (map.containsKey("text")) {
|
||||
Object object = map.get("text");
|
||||
if (object instanceof String[]) {
|
||||
return new TLocaleText(Arrays.stream(((String[]) object)).collect(Collectors.toList()), isPlaceholderEnabled(map));
|
||||
} else {
|
||||
return new TLocaleText(Objects.toString(object), isPlaceholderEnabled(map));
|
||||
}
|
||||
}
|
||||
return new TLocaleText("§cError chat message loaded.", TLocale.Translate.isPlaceholderUseDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (text instanceof String) {
|
||||
sender.sendMessage(replaceText(sender, Strings.replaceWithOrder((String) text, args)));
|
||||
} else if (text instanceof List) {
|
||||
((List) text).forEach(s -> sender.sendMessage(replaceText(sender, Strings.replaceWithOrder(String.valueOf(s), args))));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return Strings.replaceWithOrder(TLocale.Translate.setColored(objectToString(text)), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> asStringList(String... args) {
|
||||
if (text instanceof List) {
|
||||
return ((List<String>) text).stream().map(x -> Strings.replaceWithOrder(TLocale.Translate.setColored(x), args)).collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.singletonList(asString(args));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (text instanceof String[]) {
|
||||
return Arrays.toString((String[]) text);
|
||||
} else {
|
||||
return text.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
return usePlaceholder ? Maps.newHashMap(ImmutableMap.of("text", text, "papi", true)) : Maps.newHashMap(ImmutableMap.of("text", text));
|
||||
}
|
||||
|
||||
private String replaceText(CommandSender sender, String args) {
|
||||
return usePlaceholder ? TLocale.Translate.setPlaceholders(sender, args) : TLocale.Translate.setColored(args);
|
||||
}
|
||||
|
||||
private String objectToString(Object text) {
|
||||
if (text instanceof String) {
|
||||
return ((String) text);
|
||||
} else {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
((List<String>) text).forEach(joiner::add);
|
||||
return joiner.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.ilummc.tlib.resources.type;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.resources.TLocaleSerialize;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.display.TitleUtils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Bkm016
|
||||
* @since 2018-04-22
|
||||
*/
|
||||
|
||||
@Immutable
|
||||
@SerializableAs("TITLE")
|
||||
public class TLocaleTitle extends TLocaleSerialize {
|
||||
|
||||
private final String title;
|
||||
private final String subtitle;
|
||||
private final int fadein;
|
||||
private final int fadeout;
|
||||
private final int stay;
|
||||
|
||||
private boolean usePlaceholder;
|
||||
|
||||
private TLocaleTitle(String title, String subString, int fadein, int fadeout, int stay, boolean usePlaceholder) {
|
||||
this.title = title;
|
||||
this.subtitle = subString;
|
||||
this.fadein = fadein;
|
||||
this.fadeout = fadeout;
|
||||
this.stay = stay;
|
||||
this.usePlaceholder = usePlaceholder;
|
||||
}
|
||||
|
||||
public static TLocaleTitle valueOf(Map<String, Object> map) {
|
||||
TLocaleTitle title;
|
||||
try {
|
||||
title = new TLocaleTitle(getStringOrDefault(map, "title", ""), getStringOrDefault(map, "subtitle", ""), getIntegerOrDefault(map, "fadein", 10), getIntegerOrDefault(map, "fadeout", 10), getIntegerOrDefault(map, "stay", 10), isPlaceholderEnabled(map));
|
||||
} catch (Exception e) {
|
||||
title = new TLocaleTitle("Empty Title message.", e.getMessage(), 10, 20, 10, false);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTo(CommandSender sender, String... args) {
|
||||
if (sender instanceof Player) {
|
||||
TLocale.Display.sendTitle((Player) sender, replaceText(sender, Strings.replaceWithOrder(title, args)), replaceText(sender, Strings.replaceWithOrder(subtitle, args)), fadein, stay, fadeout);
|
||||
} else {
|
||||
TLocale.Logger.error("LOCALE.TITLE-SEND-TO-NON-PLAYER", asString(args));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString(String... args) {
|
||||
return Strings.replaceWithOrder(Strings.replaceWithOrder("TITLE: [title: ''{0}'', subtitle: ''{1}'', fadeIn: {2}, fadeOut: {3}]", title, subtitle, fadein, fadeout), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
HashMap<String, Object> map = Maps.newHashMap();
|
||||
map.put("papi", usePlaceholder);
|
||||
map.put("title", title);
|
||||
map.put("subtitle", subtitle);
|
||||
map.put("fadein", fadein);
|
||||
map.put("fadeout", fadeout);
|
||||
map.put("stay", stay);
|
||||
return map;
|
||||
}
|
||||
|
||||
private String replaceText(CommandSender sender, String text, String... args) {
|
||||
return usePlaceholder ? TLocale.Translate.setPlaceholders(sender, text) : TLocale.Translate.setColored(text);
|
||||
}
|
||||
}
|
||||
18
src/main/scala/com/ilummc/tlib/util/IO.java
Normal file
18
src/main/scala/com/ilummc/tlib/util/IO.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.ilummc.tlib.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class IO {
|
||||
|
||||
public static byte[] readFully(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[1024];
|
||||
int len = 0;
|
||||
while ((len = inputStream.read(buf)) > 0) {
|
||||
stream.write(buf, 0, len);
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
}
|
||||
157
src/main/scala/com/ilummc/tlib/util/Ref.java
Normal file
157
src/main/scala/com/ilummc/tlib/util/Ref.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package com.ilummc.tlib.util;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.util.asm.AsmAnalyser;
|
||||
import me.skymc.taboolib.Main;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import sun.reflect.Reflection;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ThreadSafe
|
||||
public class Ref {
|
||||
|
||||
private static final Map<String, List<Field>> cachedFields = new ConcurrentHashMap<>();
|
||||
|
||||
public static final int ACC_BRIDGE = 0x0040;
|
||||
public static final int ACC_SYNTHETIC = 0x1000;
|
||||
|
||||
public static List<Field> getDeclaredFields(Class<?> clazz) {
|
||||
return getDeclaredFields(clazz, 0, true);
|
||||
}
|
||||
|
||||
public static List<Field> getDeclaredFields(String clazz, int excludeModifiers, boolean cache) {
|
||||
try {
|
||||
return getDeclaredFields(Class.forName(clazz), excludeModifiers, cache);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Field> getDeclaredFields(Class<?> clazz, int excludeModifiers, boolean cache) {
|
||||
try {
|
||||
|
||||
// 特殊判断
|
||||
if (clazz == TLib.class) {
|
||||
return Arrays.asList(clazz.getDeclaredFields());
|
||||
}
|
||||
|
||||
List<Field> fields;
|
||||
if ((fields = cachedFields.get(clazz.getName())) != null) {
|
||||
return fields;
|
||||
}
|
||||
ClassReader classReader = new ClassReader(clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class"));
|
||||
AsmAnalyser analyser = new AsmAnalyser(new ClassWriter(ClassWriter.COMPUTE_MAXS), excludeModifiers);
|
||||
classReader.accept(analyser, ClassReader.SKIP_DEBUG);
|
||||
fields = analyser.getFields().stream().map(name -> {
|
||||
try {
|
||||
return clazz.getDeclaredField(name);
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
if (cache) {
|
||||
cachedFields.putIfAbsent(clazz.getName(), fields);
|
||||
}
|
||||
return fields;
|
||||
} catch (Exception | Error e) {
|
||||
try {
|
||||
List<Field> list = Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(field -> (field.getModifiers() & excludeModifiers) == 0).collect(Collectors.toList());
|
||||
cachedFields.putIfAbsent(clazz.getName(), list);
|
||||
return list;
|
||||
} catch (Error err) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> getCallerClass(int depth) {
|
||||
return Optional.ofNullable(CallerClass.impl.getCallerClass(depth + 1));
|
||||
}
|
||||
|
||||
public static Class<?> getCallerClassNotOptional(int depth) {
|
||||
return CallerClass.impl.getCallerClass(depth);
|
||||
}
|
||||
|
||||
public static String getSerializedName(Field field) {
|
||||
return field.isAnnotationPresent(SerializedName.class) ? field.getAnnotation(SerializedName.class).value() : field.getName();
|
||||
}
|
||||
|
||||
public static Optional<Field> getFieldBySerializedName(Class<?> clazz, String name) {
|
||||
for (Field field : Ref.getDeclaredFields(clazz, 0, false)) {
|
||||
if (field.isAnnotationPresent(SerializedName.class)) {
|
||||
if (field.getAnnotation(SerializedName.class).value().equals(name)) {
|
||||
return Optional.of(field);
|
||||
} else if (field.getName().equals(name)) {
|
||||
return Optional.of(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Plugin getCallerPlugin(Class<?> callerClass) {
|
||||
try {
|
||||
return JavaPlugin.getProvidingPlugin(callerClass);
|
||||
} catch (Exception ignored) {
|
||||
try {
|
||||
ClassLoader loader = callerClass.getClassLoader();
|
||||
Field pluginF = loader.getClass().getDeclaredField("plugin");
|
||||
pluginF.setAccessible(true);
|
||||
Object o = pluginF.get(loader);
|
||||
return (JavaPlugin) o;
|
||||
} catch (Exception e) {
|
||||
return Main.getInst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class CallerClass {
|
||||
|
||||
private static CallerClass impl;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("sun.reflect.Reflection");
|
||||
impl = new ReflectionImpl();
|
||||
} catch (ClassNotFoundException e) {
|
||||
impl = new StackTraceImpl();
|
||||
}
|
||||
}
|
||||
|
||||
abstract Class<?> getCallerClass(int i);
|
||||
|
||||
private static class ReflectionImpl extends CallerClass {
|
||||
|
||||
@SuppressWarnings({"deprecation", "restriction"})
|
||||
@Override
|
||||
Class<?> getCallerClass(int i) {
|
||||
return Reflection.getCallerClass(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StackTraceImpl extends CallerClass {
|
||||
|
||||
@Override
|
||||
Class<?> getCallerClass(int i) {
|
||||
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
||||
try {
|
||||
return Class.forName(elements[i].getClassName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
48
src/main/scala/com/ilummc/tlib/util/Strings.java
Normal file
48
src/main/scala/com/ilummc/tlib/util/Strings.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.ilummc.tlib.util;
|
||||
|
||||
public class Strings {
|
||||
|
||||
public static boolean isBlank(String var) {
|
||||
return var == null || var.trim().isEmpty();
|
||||
}
|
||||
|
||||
public static boolean isEmpty(CharSequence var) {
|
||||
return var == null || var.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化过的 String#replace,比默认快了大概 5 倍
|
||||
*
|
||||
* @param template 模板替换文件
|
||||
* @param args 替换的参数
|
||||
* @return 替换好的字符串
|
||||
*/
|
||||
public static String replaceWithOrder(String template, Object... args) {
|
||||
if (args.length == 0 || template.length() == 0) {
|
||||
return template;
|
||||
}
|
||||
char[] arr = template.toCharArray();
|
||||
StringBuilder stringBuilder = new StringBuilder(template.length());
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] == '{' && Character.isDigit(arr[Math.min(i + 1, arr.length - 1)])
|
||||
&& arr[Math.min(i + 1, arr.length - 1)] - '0' < args.length
|
||||
&& arr[Math.min(i + 2, arr.length - 1)] == '}') {
|
||||
stringBuilder.append(args[arr[i + 1] - '0']);
|
||||
i += 2;
|
||||
} else {
|
||||
stringBuilder.append(arr[i]);
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
// *********************************
|
||||
//
|
||||
// Deprecated
|
||||
//
|
||||
// *********************************
|
||||
|
||||
public static String replaceWithOrder(String template, String... args) {
|
||||
return replaceWithOrder(template, (Object[]) args);
|
||||
}
|
||||
}
|
||||
32
src/main/scala/com/ilummc/tlib/util/asm/AsmAnalyser.java
Normal file
32
src/main/scala/com/ilummc/tlib/util/asm/AsmAnalyser.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.ilummc.tlib.util.asm;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AsmAnalyser extends ClassVisitor implements Opcodes {
|
||||
|
||||
private final List<String> fields = new ArrayList<>();
|
||||
|
||||
private final int excludeModifier;
|
||||
|
||||
public AsmAnalyser(ClassVisitor classVisitor, int excludeModifiers) {
|
||||
super(Opcodes.ASM6, classVisitor);
|
||||
this.excludeModifier = excludeModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
if ((access & excludeModifier) == 0) {
|
||||
fields.add(name);
|
||||
}
|
||||
return super.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
|
||||
public List<String> getFields() {
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
21
src/main/scala/com/ilummc/tlib/util/asm/AsmClassLoader.java
Normal file
21
src/main/scala/com/ilummc/tlib/util/asm/AsmClassLoader.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.ilummc.tlib.util.asm;
|
||||
|
||||
public class AsmClassLoader extends ClassLoader {
|
||||
|
||||
private static final class AsmClassLoaderHolder {
|
||||
private static AsmClassLoader instance = new AsmClassLoader();
|
||||
}
|
||||
|
||||
public static AsmClassLoader getInstance() {
|
||||
return new AsmClassLoader();
|
||||
}
|
||||
|
||||
private AsmClassLoader() {
|
||||
super(AsmClassLoader.class.getClassLoader());
|
||||
}
|
||||
|
||||
public static Class<?> createNewClass(String name, byte[] arr) {
|
||||
return getInstance().defineClass(name, arr, 0, arr.length, AsmClassLoader.class.getProtectionDomain());
|
||||
}
|
||||
|
||||
}
|
||||
162
src/main/scala/com/ilummc/tlib/util/asm/AsmClassTransformer.java
Normal file
162
src/main/scala/com/ilummc/tlib/util/asm/AsmClassTransformer.java
Normal file
@@ -0,0 +1,162 @@
|
||||
package com.ilummc.tlib.util.asm;
|
||||
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
|
||||
public class AsmClassTransformer extends ClassVisitor implements Opcodes {
|
||||
|
||||
private final Class<?> from;
|
||||
private final String fromVer;
|
||||
private final String toVer;
|
||||
private final ClassWriter writer;
|
||||
private String newClassName;
|
||||
private String prevName;
|
||||
|
||||
private AsmClassTransformer(Class<?> from, String fromVer, String toVer, ClassWriter classWriter) {
|
||||
super(Opcodes.ASM5, classWriter);
|
||||
this.writer = classWriter;
|
||||
this.from = from;
|
||||
this.fromVer = fromVer;
|
||||
this.toVer = toVer;
|
||||
}
|
||||
|
||||
public Object transform() {
|
||||
try {
|
||||
ClassReader classReader = new ClassReader(from.getResourceAsStream("/" + from.getName().replace('.', '/') + ".class"));
|
||||
newClassName = from.getName() + "_TabooLibRemap_" + this.hashCode() + "_" + toVer;
|
||||
prevName = from.getName().replace('.', '/');
|
||||
classReader.accept(this, ClassReader.SKIP_DEBUG);
|
||||
Class<?> clazz = AsmClassLoader.createNewClass(newClassName, writer.toByteArray());
|
||||
Field field = from.getClassLoader().getClass().getDeclaredField("classes");
|
||||
field.setAccessible(true);
|
||||
((Map<String, Class<?>>) field.get(from.getClassLoader())).put(newClassName, clazz);
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance();
|
||||
} catch (IOException | NoSuchFieldException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder().toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]);
|
||||
}
|
||||
|
||||
public static Builder builder(String ver) {
|
||||
return new Builder().toVersion(ver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
MethodVisitor visitor = super.visitMethod(access, name, replace(descriptor), replace(signature), replace(exceptions));
|
||||
return new AsmMethodTransformer(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
return super.visitField(access, name, replace(descriptor), replace(signature), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
super.visitInnerClass(replace(name), outerName, replace(name).substring(outerName.length() + 1), access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
super.visit(version, access, newClassName.replace('.', '/'), replace(signature), replace(superName), replace(interfaces));
|
||||
}
|
||||
|
||||
private String replace(String text) {
|
||||
if (text != null) {
|
||||
return text
|
||||
.replace("net/minecraft/server/" + fromVer, "net/minecraft/server/" + toVer)
|
||||
.replace("org/bukkit/craftbukkit/" + fromVer, "org/bukkit/craftbukkit/" + toVer)
|
||||
.replace(prevName, newClassName.replace('.', '/'));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] replace(String[] text) {
|
||||
if (text != null) {
|
||||
for (int i = 0; i < text.length; i++) {
|
||||
text[i] = replace(text[i]);
|
||||
}
|
||||
return text;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class AsmMethodTransformer extends MethodVisitor {
|
||||
|
||||
AsmMethodTransformer(MethodVisitor visitor) {
|
||||
super(Opcodes.ASM5, visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
super.visitMethodInsn(opcode, replace(owner), name, replace(descriptor), isInterface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (value instanceof String) {
|
||||
super.visitLdcInsn(replace((String) value));
|
||||
} else {
|
||||
super.visitLdcInsn(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
super.visitTypeInsn(opcode, replace(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
|
||||
super.visitFieldInsn(opcode, replace(owner), name, replace(descriptor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
|
||||
super.visitLocalVariable(name, replace(descriptor), replace(signature), start, end, index);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Class<?> from;
|
||||
|
||||
private String fromVersion, toVersion;
|
||||
|
||||
public Builder from(Class<?> clazz) {
|
||||
this.from = clazz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fromVersion(String ver) {
|
||||
fromVersion = ver;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder toVersion(String ver) {
|
||||
toVersion = ver;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsmClassTransformer build() {
|
||||
return new AsmClassTransformer(from, fromVersion, toVersion, new ClassWriter(ClassWriter.COMPUTE_MAXS));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import com.ilummc.tlib.scala.Prelude._
|
||||
import Prelude._
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.{EventHandler, Listener}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import com.ilummc.tlib.scala.runtime.{RichLocation, RichOfflinePlayer, RichPlayer, RichVector}
|
||||
import com.ilummc.tlibscala.runtime.{RichLocation, RichOfflinePlayer, RichPlayer, RichVector}
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.util.Vector
|
||||
import org.bukkit.{Location, OfflinePlayer, World, util}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale.Logger
|
||||
import org.bukkit.Bukkit
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import org.bukkit.scheduler.BukkitRunnable
|
||||
|
||||
private[scala] class ScalaTaskExecutor(task: => Any) extends BukkitRunnable {
|
||||
private[tlibscala] class ScalaTaskExecutor(task: => Any) extends BukkitRunnable {
|
||||
|
||||
override def run(): Unit = {
|
||||
try task catch {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala
|
||||
package com.ilummc.tlibscala
|
||||
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala.runtime
|
||||
package com.ilummc.tlibscala.runtime
|
||||
|
||||
import org.bukkit.Location
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala.runtime
|
||||
package com.ilummc.tlibscala.runtime
|
||||
|
||||
import me.skymc.taboolib.Main
|
||||
import me.skymc.taboolib.economy.EcoUtils
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala.runtime
|
||||
package com.ilummc.tlibscala.runtime
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale
|
||||
import me.skymc.taboolib.anvil.AnvilContainerAPI
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.ilummc.tlib.scala.runtime
|
||||
package com.ilummc.tlibscala.runtime
|
||||
|
||||
import org.bukkit.util.Vector
|
||||
|
||||
283
src/main/scala/me/skymc/taboolib/Main.java
Normal file
283
src/main/scala/me/skymc/taboolib/Main.java
Normal file
@@ -0,0 +1,283 @@
|
||||
package me.skymc.taboolib;
|
||||
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.filter.TLoggerFilter;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import com.ilummc.tlib.util.IO;
|
||||
import com.ilummc.tlib.util.Strings;
|
||||
import me.skymc.taboolib.database.GlobalDataManager;
|
||||
import me.skymc.taboolib.database.PlayerDataManager;
|
||||
import me.skymc.taboolib.fileutils.ConfigUtils;
|
||||
import me.skymc.taboolib.fileutils.FileUtils;
|
||||
import me.skymc.taboolib.listener.TListenerHandler;
|
||||
import me.skymc.taboolib.mysql.hikari.HikariHandler;
|
||||
import me.skymc.taboolib.mysql.protect.MySQLConnection;
|
||||
import me.skymc.taboolib.other.NumberUtils;
|
||||
import me.skymc.taboolib.playerdata.DataUtils;
|
||||
import me.skymc.taboolib.socket.TabooLibClient;
|
||||
import me.skymc.taboolib.socket.TabooLibServer;
|
||||
import me.skymc.taboolib.string.language2.Language2;
|
||||
import me.skymc.taboolib.update.UpdateTask;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author sky
|
||||
*/
|
||||
public class Main extends JavaPlugin {
|
||||
|
||||
public Main() {
|
||||
inst = this;
|
||||
}
|
||||
|
||||
public enum StorageType {
|
||||
LOCAL, SQL
|
||||
}
|
||||
|
||||
private static Plugin inst;
|
||||
private static Economy economy;
|
||||
private static File playerDataFolder;
|
||||
private static File serverDataFolder;
|
||||
private static StorageType storageType = StorageType.LOCAL;
|
||||
private static Language2 exampleLanguage2;
|
||||
private static boolean disable = false;
|
||||
private static boolean started = false;
|
||||
private static boolean isInternetOnline = false;
|
||||
private FileConfiguration config = null;
|
||||
|
||||
@Override
|
||||
public FileConfiguration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDefaultConfig() {
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
File file = new File(getDataFolder(), "config.yml");
|
||||
if (!file.exists()) {
|
||||
saveResource("config.yml", true);
|
||||
}
|
||||
config = ConfigUtils.load(this, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
disable = false;
|
||||
// 载入配置文件
|
||||
saveDefaultConfig();
|
||||
// 载入日志过滤
|
||||
TLoggerFilter.preInit();
|
||||
// 载入扩展
|
||||
TabooLibLoader.setupAddons();
|
||||
// 载入牛逼东西
|
||||
TLib.init();
|
||||
TLib.injectPluginManager();
|
||||
// 载入插件设置
|
||||
TabooLibLoader.setup();
|
||||
// 载入大饼
|
||||
TLib.initPost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// 载入日志过滤
|
||||
TLoggerFilter.postInit();
|
||||
// 注册插件配置
|
||||
TabooLibLoader.register();
|
||||
// 启动数据库储存方法
|
||||
if (getStorageType() == StorageType.SQL) {
|
||||
GlobalDataManager.SQLMethod.startSQLMethod();
|
||||
}
|
||||
// 载入完成
|
||||
TLocale.Logger.info("NOTIFY.SUCCESS-LOADED", getDescription().getAuthors().toString(), getDescription().getVersion(), String.valueOf(TabooLib.getVersion()));
|
||||
// 文件保存
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(this, DataUtils::saveAllCaches, 20, 20 * 120);
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> PlayerDataManager.saveAllCaches(true, false), 20, 20 * 60);
|
||||
// 文件监控
|
||||
TLib.getTLib().getConfigWatcher().addSimpleListener(new File(getDataFolder(), "config.yml"), () -> {
|
||||
reloadConfig();
|
||||
TLocale.Logger.info("CONFIG.RELOAD-SUCCESS", inst.getName(), "config.yml");
|
||||
});
|
||||
// 插件联动
|
||||
new BukkitRunnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// 面子工程
|
||||
if (!TabooLib.isSilent()) {
|
||||
InputStream inputStream = FileUtils.getResource("motd.txt");
|
||||
try {
|
||||
String text = new String(IO.readFully(inputStream), Charset.forName("utf-8"));
|
||||
if (text != null) {
|
||||
Arrays.stream(text.split("\n")).forEach(line -> Bukkit.getConsoleSender().sendMessage(Strings.replaceWithOrder(line, getDescription().getVersion())));
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// 本地通讯网络终端
|
||||
if (getConfig().getBoolean("SERVER")) {
|
||||
TabooLibServer.main(new String[0]);
|
||||
}
|
||||
// 本地通讯网络
|
||||
TabooLibClient.init();
|
||||
}
|
||||
}.runTask(this);
|
||||
// 启动
|
||||
started = true;
|
||||
// 载入语言文件
|
||||
exampleLanguage2 = new Language2("Language2", this);
|
||||
// 更新检测
|
||||
if (!TabooLib.isSilent()) {
|
||||
new UpdateTask();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
disable = true;
|
||||
// 如果插件尚未启动完成
|
||||
if (!started) {
|
||||
TLocale.Logger.error("NOTIFY.FAIL-DISABLE");
|
||||
return;
|
||||
}
|
||||
// 注销插件
|
||||
TabooLibLoader.unregister();
|
||||
// 保存数据
|
||||
Bukkit.getOnlinePlayers().forEach(x -> DataUtils.saveOnline(x.getName()));
|
||||
// 结束线程
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
// 保存插件数据
|
||||
DataUtils.saveAllCaches();
|
||||
// 保存玩家数据
|
||||
PlayerDataManager.saveAllPlayers(false, true);
|
||||
// 注销连接池
|
||||
HikariHandler.closeDataSourceForce();
|
||||
// 注销监听器
|
||||
TListenerHandler.cancelListeners();
|
||||
// 结束数据库储存方法
|
||||
if (getStorageType() == StorageType.SQL) {
|
||||
GlobalDataManager.SQLMethod.cancelSQLMethod();
|
||||
}
|
||||
// 清理数据
|
||||
if (getStorageType() == StorageType.LOCAL && getConfig().getBoolean("DELETE-DATA")) {
|
||||
getPlayerDataFolder().delete();
|
||||
}
|
||||
// 清理数据
|
||||
if (getStorageType() == StorageType.SQL && getConfig().getBoolean("DELETE-VARIABLE")) {
|
||||
GlobalDataManager.clearInvalidVariables();
|
||||
}
|
||||
// 提示信息
|
||||
TLocale.Logger.info("NOTIFY.SUCCESS-DISABLE");
|
||||
// 卸载牛逼玩意儿
|
||||
TLib.unload();
|
||||
// 关闭服务器
|
||||
Bukkit.shutdown();
|
||||
}
|
||||
|
||||
// *********************************
|
||||
//
|
||||
// Getter and Setter
|
||||
//
|
||||
// *********************************
|
||||
|
||||
public static Plugin getInst() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
public static String getPrefix() {
|
||||
return "§8[§3§lTabooLib§8] §7";
|
||||
}
|
||||
|
||||
public static net.milkbowl.vault.economy.Economy getEconomy() {
|
||||
return economy;
|
||||
}
|
||||
|
||||
public static void setEconomy(Economy economy) {
|
||||
Main.economy = economy;
|
||||
}
|
||||
|
||||
public static File getPlayerDataFolder() {
|
||||
return playerDataFolder;
|
||||
}
|
||||
|
||||
public static File getServerDataFolder() {
|
||||
return serverDataFolder;
|
||||
}
|
||||
|
||||
public static StorageType getStorageType() {
|
||||
return storageType;
|
||||
}
|
||||
|
||||
public static boolean isDisable() {
|
||||
return disable;
|
||||
}
|
||||
|
||||
public static MySQLConnection getConnection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Language2 getExampleLanguage2() {
|
||||
return exampleLanguage2;
|
||||
}
|
||||
|
||||
public static boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
public static Random getRandom() {
|
||||
return NumberUtils.getRandom();
|
||||
}
|
||||
|
||||
public static String getTablePrefix() {
|
||||
return inst.getConfig().getString("MYSQL.PREFIX");
|
||||
}
|
||||
|
||||
public static boolean isInternetOnline() {
|
||||
return isInternetOnline;
|
||||
}
|
||||
|
||||
public static boolean isOfflineVersion() {
|
||||
return inst.getResource("libs") != null;
|
||||
}
|
||||
|
||||
public static boolean isLibrariesExists() {
|
||||
return TLib.getTLib().getLibsFolder().listFiles().length > 0;
|
||||
}
|
||||
|
||||
// *********************************
|
||||
//
|
||||
// Private Setter
|
||||
//
|
||||
// *********************************
|
||||
|
||||
static void setIsInternetOnline(boolean isInternetOnline) {
|
||||
Main.isInternetOnline = isInternetOnline;
|
||||
}
|
||||
|
||||
static void setPlayerDataFolder(File playerDataFolder) {
|
||||
Main.playerDataFolder = playerDataFolder;
|
||||
}
|
||||
|
||||
static void setServerDataFolder(File serverDataFolder) {
|
||||
Main.serverDataFolder = serverDataFolder;
|
||||
}
|
||||
|
||||
static void setStorageType(StorageType storageType) {
|
||||
Main.storageType = storageType;
|
||||
}
|
||||
}
|
||||
167
src/main/scala/me/skymc/taboolib/TabooLib.java
Normal file
167
src/main/scala/me/skymc/taboolib/TabooLib.java
Normal file
@@ -0,0 +1,167 @@
|
||||
package me.skymc.taboolib;
|
||||
|
||||
import me.skymc.taboolib.common.nms.NMSHandler;
|
||||
import me.skymc.taboolib.other.NumberUtils;
|
||||
import me.skymc.taboolib.playerdata.DataUtils;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author sky
|
||||
*/
|
||||
public class TabooLib {
|
||||
|
||||
private static boolean spigot = false;
|
||||
private static boolean silent = false;
|
||||
|
||||
static {
|
||||
try {
|
||||
// 判断是否为独立客户端运行,不是判断 Bukkit 与 Spigot
|
||||
Class.forName("org.bukkit.Bukkit");
|
||||
spigot = true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主类对象,因 Main 名称容易造成混淆所以转移至此
|
||||
*/
|
||||
public static Main instance() {
|
||||
return (Main) Main.getInst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件是否为 TabooLib(沙雕方法)
|
||||
*/
|
||||
public static boolean isTabooLib(Plugin plugin) {
|
||||
return plugin.equals(instance()) || plugin.getName().equals("TabooLib");
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件是否依赖于 TabooLib(依赖或软兼容)
|
||||
*/
|
||||
public static boolean isDependTabooLib(Plugin plugin) {
|
||||
return plugin.getDescription().getDepend().contains("TabooLib") || plugin.getDescription().getSoftDepend().contains("TabooLib");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 Spigot 核心,因 TabooLib 可在 BungeeCord 上运行所以添加此方法
|
||||
*/
|
||||
public static boolean isSpigot() {
|
||||
return spigot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为静默模式
|
||||
*/
|
||||
public static boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置静默模式,启用后将关闭部分提示
|
||||
*/
|
||||
public static void setSilent(boolean silent) {
|
||||
TabooLib.silent = silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TabooLib 插件版本
|
||||
*/
|
||||
public static double getPluginVersion() {
|
||||
return NumberUtils.getDouble(Main.getInst().getDescription().getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端版本
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return Bukkit.getServer().getClass().getName().split("\\.")[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端版本数字
|
||||
*/
|
||||
public static int getVersionNumber() {
|
||||
return getVerint();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置服务器序列号
|
||||
*/
|
||||
public static void resetServerUID() {
|
||||
DataUtils.getPluginData("TabooLibrary", null).set("serverUID", UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为调试模式
|
||||
*/
|
||||
public static boolean isDebug() {
|
||||
return DataUtils.getPluginData("TabooLibrary", instance()).getBoolean("debug");
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换调试模式
|
||||
*/
|
||||
public static void setDebug(boolean debug) {
|
||||
DataUtils.getPluginData("TabooLibrary", instance()).set("debug", debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送调试信息
|
||||
*/
|
||||
public static void debug(String... args) {
|
||||
debug(instance(), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送调试信息
|
||||
*/
|
||||
public static void debug(Plugin plugin, String... args) {
|
||||
if (TabooLib.isDebug()) {
|
||||
Arrays.stream(args).forEach(var -> Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[TabooLib - DEBUG][" + plugin.getName() + "] " + ChatColor.RED + var));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器序列号
|
||||
*/
|
||||
public static String getServerUID() {
|
||||
if (!DataUtils.getPluginData("TabooLibrary", null).contains("serverUID")) {
|
||||
DataUtils.getPluginData("TabooLibrary", null).set("serverUID", UUID.randomUUID().toString());
|
||||
}
|
||||
return DataUtils.getPluginData("TabooLibrary", null).getString("serverUID");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器 TPS
|
||||
*/
|
||||
public static double[] getTPS() {
|
||||
return NMSHandler.getHandler().getTPS();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static int getVerint() {
|
||||
String version = getVersion();
|
||||
if (version.startsWith("v1_7")) {
|
||||
return 10700;
|
||||
} else if (version.startsWith("v1_8")) {
|
||||
return 10800;
|
||||
} else if (version.startsWith("v1_9")) {
|
||||
return 10900;
|
||||
} else if (version.startsWith("v1_10")) {
|
||||
return 11000;
|
||||
} else if (version.startsWith("v1_11")) {
|
||||
return 11100;
|
||||
} else if (version.startsWith("v1_12")) {
|
||||
return 11200;
|
||||
} else if (version.startsWith("v1_13")) {
|
||||
return 11300;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
128
src/main/scala/me/skymc/taboolib/TabooLibDatabase.java
Normal file
128
src/main/scala/me/skymc/taboolib/TabooLibDatabase.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package me.skymc.taboolib;
|
||||
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.mysql.builder.SQLColumn;
|
||||
import me.skymc.taboolib.mysql.builder.SQLColumnType;
|
||||
import me.skymc.taboolib.mysql.builder.SQLHost;
|
||||
import me.skymc.taboolib.mysql.builder.SQLTable;
|
||||
import me.skymc.taboolib.mysql.hikari.HikariHandler;
|
||||
import me.skymc.taboolib.string.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-08-23 17:15
|
||||
*/
|
||||
public class TabooLibDatabase {
|
||||
|
||||
private static SQLHost host;
|
||||
private static DataSource dataSource;
|
||||
private static HashMap<String, SQLTable> tables = new HashMap<>();
|
||||
|
||||
static void init() {
|
||||
if (Main.getStorageType() != Main.StorageType.SQL) {
|
||||
return;
|
||||
}
|
||||
// 数据库地址
|
||||
host = new SQLHost(
|
||||
// 地址
|
||||
Main.getInst().getConfig().getString("MYSQL.HOST"),
|
||||
// 用户
|
||||
Main.getInst().getConfig().getString("MYSQL.USER"),
|
||||
// 端口
|
||||
Main.getInst().getConfig().getString("MYSQL.POST"),
|
||||
// 密码
|
||||
Main.getInst().getConfig().getString("MYSQL.PASSWORD"),
|
||||
// 数据库
|
||||
Main.getInst().getConfig().getString("MYSQL.DATABASE"), TabooLib.instance());
|
||||
// 连接数据库
|
||||
try {
|
||||
dataSource = HikariHandler.createDataSource(host);
|
||||
} catch (Exception ignored) {
|
||||
TLocale.Logger.error("NOTIFY.ERROR-CONNECTION-FAIL");
|
||||
return;
|
||||
}
|
||||
// 创建各项数据表
|
||||
createTableWithPlayerData();
|
||||
createTableWithPluginData();
|
||||
createTableWithServerUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建玩家数据表
|
||||
*/
|
||||
static void createTableWithPlayerData() {
|
||||
SQLTable table = new SQLTable(Main.getTablePrefix() + "_playerdata", SQLColumn.PRIMARY_KEY_ID, new SQLColumn(SQLColumnType.TEXT, "username"), new SQLColumn(SQLColumnType.TEXT, "configuration"));
|
||||
table.executeUpdate(table.createQuery()).dataSource(dataSource).run();
|
||||
tables.put("playerdata", table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建插件数据表
|
||||
*/
|
||||
static void createTableWithPluginData() {
|
||||
SQLTable table = new SQLTable(Main.getTablePrefix() + "_plugindata", SQLColumn.PRIMARY_KEY_ID, new SQLColumn(SQLColumnType.TEXT, "name"), new SQLColumn(SQLColumnType.TEXT, "variable"), new SQLColumn(SQLColumnType.TEXT, "upgrade"));
|
||||
table.executeUpdate(table.createQuery()).dataSource(dataSource).run();
|
||||
tables.put("plugindata", table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建服务器数据表
|
||||
*/
|
||||
static void createTableWithServerUUID() {
|
||||
SQLTable table = new SQLTable(Main.getTablePrefix() + "_serveruuid", SQLColumn.PRIMARY_KEY_ID, new SQLColumn(SQLColumnType.TEXT, "uuid"), new SQLColumn(SQLColumnType.TEXT, "hash"));
|
||||
table.executeUpdate(table.createQuery()).dataSource(dataSource).run();
|
||||
tables.put("serveruuid", table);
|
||||
// 获取当前服务器信息
|
||||
String hash = getServerHash(TabooLib.getServerUID());
|
||||
if (hash == null) {
|
||||
// 写入序列号
|
||||
table.executeUpdate("insert into " + table.getTableName() + " values(null, ?, ?)")
|
||||
.dataSource(dataSource)
|
||||
.statement(s -> {
|
||||
s.setString(1, TabooLib.getServerUID());
|
||||
s.setString(2, StringUtils.hashKeyForDisk(Main.getInst().getDataFolder().getPath()));
|
||||
}).run();
|
||||
} else if (!hash.equals(StringUtils.hashKeyForDisk(Main.getInst().getDataFolder().getPath()))) {
|
||||
TLocale.Logger.error("NOTIFY.ERROR-SERVER-KEY");
|
||||
TabooLib.resetServerUID();
|
||||
Bukkit.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器序列号对应的目录哈希值
|
||||
*
|
||||
* @param uuid 服务器序列号
|
||||
* @return 目录哈希值
|
||||
*/
|
||||
public static String getServerHash(String uuid) {
|
||||
SQLTable table = tables.get("serveruuid");
|
||||
return table.executeQuery("select * from " + table.getTableName() + " where uuid = ?")
|
||||
.dataSource(dataSource)
|
||||
.statement(s -> s.setString(1, uuid))
|
||||
.resultNext(r -> r.getString("hash"))
|
||||
.run(null, "");
|
||||
}
|
||||
|
||||
// *********************************
|
||||
//
|
||||
// Getter and Setter
|
||||
//
|
||||
// *********************************
|
||||
|
||||
public static SQLHost getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public static HashMap<String, SQLTable> getTables() {
|
||||
return tables;
|
||||
}
|
||||
|
||||
public static DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
300
src/main/scala/me/skymc/taboolib/TabooLibLoader.java
Normal file
300
src/main/scala/me/skymc/taboolib/TabooLibLoader.java
Normal file
@@ -0,0 +1,300 @@
|
||||
package me.skymc.taboolib;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.ilummc.tlib.TLib;
|
||||
import com.ilummc.tlib.annotations.Dependency;
|
||||
import com.ilummc.tlib.dependency.TDependencyLoader;
|
||||
import com.ilummc.tlib.inject.TDependencyInjector;
|
||||
import com.ilummc.tlib.resources.TLocale;
|
||||
import me.skymc.taboolib.bstats.Metrics;
|
||||
import me.skymc.taboolib.deprecated.TabooLibDeprecated;
|
||||
import me.skymc.taboolib.events.TPluginEnableEvent;
|
||||
import me.skymc.taboolib.fileutils.FileUtils;
|
||||
import me.skymc.taboolib.listener.TListener;
|
||||
import me.skymc.taboolib.listener.TListenerHandler;
|
||||
import me.skymc.taboolib.methods.ReflectionUtils;
|
||||
import me.skymc.taboolib.playerdata.DataUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.net.InetAddress;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-08-23 17:04
|
||||
*/
|
||||
@TListener
|
||||
public class TabooLibLoader implements Listener {
|
||||
|
||||
/*
|
||||
关于 TabooLib 各项自动化接口的执行顺序
|
||||
|
||||
[ENABLING]
|
||||
第一阶段:运行 @TInject(Instance Inject)
|
||||
第二阶段(先后):实例化 @TListener -> 实例化 @Instantiable
|
||||
第三阶段(并行):运行 @TFunction & 运行 @TInject
|
||||
|
||||
[ENABLED]
|
||||
第三阶段:注册 @TListener
|
||||
*/
|
||||
|
||||
static TabooLibDeprecated tabooLibDeprecated;
|
||||
static Map<String, List<Class>> pluginClasses = Maps.newHashMap();
|
||||
static List<Loader> loaders = Lists.newArrayList();
|
||||
static List<Runnable> tasks = Lists.newArrayList();
|
||||
static boolean started;
|
||||
|
||||
static void setup() {
|
||||
testInternet();
|
||||
setupDataFolder();
|
||||
setupDatabase();
|
||||
setupLibraries();
|
||||
}
|
||||
|
||||
static void register() {
|
||||
setupClasses();
|
||||
preLoadClasses();
|
||||
registerListener();
|
||||
registerMetrics();
|
||||
postLoadClasses();
|
||||
try {
|
||||
tabooLibDeprecated = new TabooLibDeprecated();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Bukkit.getScheduler().runTask(TabooLib.instance(), () -> {
|
||||
for (Runnable task : tasks) {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void unregister() {
|
||||
unloadClasses();
|
||||
try {
|
||||
tabooLibDeprecated.unregister();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static TabooLibDeprecated getTabooLibDeprecated() {
|
||||
return tabooLibDeprecated;
|
||||
}
|
||||
|
||||
public static Optional<List<Class>> getPluginClasses(Plugin plugin) {
|
||||
return Optional.ofNullable(pluginClasses.get(plugin.getName()));
|
||||
}
|
||||
|
||||
public static List<Class> getPluginClassSafely(Plugin plugin) {
|
||||
List<Class> classes = pluginClasses.get(plugin.getName());
|
||||
return classes == null ? new ArrayList<>() : new ArrayList<>(classes);
|
||||
}
|
||||
|
||||
public static void runTaskOnEnabled(Runnable runnable) {
|
||||
if (Main.isStarted()) {
|
||||
Bukkit.getScheduler().runTask(TabooLib.instance(), () -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tasks.add(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isLoader(Class pluginClass) {
|
||||
return !Loader.class.equals(pluginClass) && Loader.class.isAssignableFrom(pluginClass);
|
||||
}
|
||||
|
||||
static void preLoadClasses() {
|
||||
pluginClasses.forEach((key, classes) -> classes.forEach(pluginClass -> preLoadClass(Bukkit.getPluginManager().getPlugin(key), pluginClass)));
|
||||
}
|
||||
|
||||
static void postLoadClasses() {
|
||||
pluginClasses.forEach((key, classes) -> classes.forEach(pluginClass -> postLoadClass(Bukkit.getPluginManager().getPlugin(key), pluginClass)));
|
||||
}
|
||||
|
||||
static void unloadClasses() {
|
||||
pluginClasses.forEach((key, classes) -> classes.forEach(pluginClass -> unloadClass(Bukkit.getPluginManager().getPlugin(key), pluginClass)));
|
||||
}
|
||||
|
||||
static void registerListener() {
|
||||
TListenerHandler.setupListeners();
|
||||
Bukkit.getScheduler().runTask(TabooLib.instance(), TListenerHandler::registerListeners);
|
||||
}
|
||||
|
||||
static void registerMetrics() {
|
||||
Metrics metrics = new Metrics(TabooLib.instance());
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("plugins_using_taboolib", () -> Math.toIntExact(Arrays.stream(Bukkit.getPluginManager().getPlugins()).filter(plugin -> plugin.getDescription().getDepend().contains("TabooLib")).count())));
|
||||
}
|
||||
|
||||
static void setupDataFolder() {
|
||||
Main.setPlayerDataFolder(FileUtils.folder(Main.getInst().getConfig().getString("DATAURL.PLAYER-DATA")));
|
||||
Main.setServerDataFolder(FileUtils.folder(Main.getInst().getConfig().getString("DATAURL.SERVER-DATA")));
|
||||
}
|
||||
|
||||
static void setupDatabase() {
|
||||
DataUtils.addPluginData("TabooLibrary", null);
|
||||
Main.setStorageType(Main.getInst().getConfig().getBoolean("MYSQL.ENABLE") ? Main.StorageType.SQL : Main.StorageType.LOCAL);
|
||||
TabooLibDatabase.init();
|
||||
}
|
||||
|
||||
static void setupAddons() {
|
||||
TabooLib.instance().saveResource("Addons/TabooLibDeprecated", true);
|
||||
// 傻逼 Gradle 的 shadow 插件会将所有 jar 排除
|
||||
// https://github.com/johnrengelman/shadow/issues/276
|
||||
new File(TabooLib.instance().getDataFolder(), "Addons/TabooLibDeprecated").renameTo(new File(TabooLib.instance().getDataFolder(), "Addons/TabooLibDeprecated.jar"));
|
||||
File file = new File(TabooLib.instance().getDataFolder(), "Addons");
|
||||
if (file.exists()) {
|
||||
Arrays.stream(file.listFiles()).forEach(listFile -> TDependencyLoader.addToPath(TabooLib.instance(), listFile));
|
||||
}
|
||||
}
|
||||
|
||||
static void setupLibraries() {
|
||||
if (Main.isOfflineVersion()) {
|
||||
Arrays.stream(TDependencyInjector.getDependencies(TLib.getTLib())).filter(dependency -> dependency.type() == Dependency.Type.LIBRARY && dependency.maven().matches(".*:.*:.*")).map(dependency -> String.join("-", dependency.maven().split(":")) + ".jar").forEach(fileName -> {
|
||||
File targetFile = FileUtils.file(TLib.getTLib().getLibsFolder(), fileName);
|
||||
InputStream inputStream = FileUtils.getResource("libs/" + fileName);
|
||||
if (!targetFile.exists() && inputStream != null) {
|
||||
FileUtils.inputStreamToFile(inputStream, FileUtils.file(TLib.getTLib().getLibsFolder(), fileName));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void testInternet() {
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(Main.getInst().getConfig().getString("TEST-URL", "aliyun.com"));
|
||||
Main.setIsInternetOnline(inetAddress.isReachable(10000));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (!Main.isInternetOnline() && !Main.isOfflineVersion() && !Main.isLibrariesExists()) {
|
||||
TLocale.Logger.error("TLIB.LOAD-FAIL-OFFLINE", Main.getInst().getDescription().getVersion());
|
||||
for (; ; ) {
|
||||
// 停止主线程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setupClasses(Plugin plugin) {
|
||||
if (TabooLib.isTabooLib(plugin) || TabooLib.isDependTabooLib(plugin)) {
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
List<Class> classes;
|
||||
IgnoreClasses annotation = plugin.getClass().getAnnotation(IgnoreClasses.class);
|
||||
if (annotation != null) {
|
||||
classes = FileUtils.getClasses(plugin, annotation.value());
|
||||
} else {
|
||||
classes = FileUtils.getClasses(plugin);
|
||||
}
|
||||
TabooLib.debug("Saved " + classes.size() + " classes (" + plugin.getName() + ") (" + (System.currentTimeMillis() - time) + "ms)");
|
||||
pluginClasses.put(plugin.getName(), classes);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setupClasses() {
|
||||
Arrays.stream(Bukkit.getPluginManager().getPlugins()).forEach(TabooLibLoader::setupClasses);
|
||||
pluginClasses.get("TabooLib").stream().filter(TabooLibLoader::isLoader).forEach(pluginClass -> {
|
||||
try {
|
||||
loaders.add((Loader) ReflectionUtils.instantiateObject(pluginClass));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
loaders.sort(Comparator.comparingInt(Loader::priority));
|
||||
});
|
||||
}
|
||||
|
||||
static void preLoadClass(Plugin plugin, Class<?> loadClass) {
|
||||
loaders.forEach(loader -> {
|
||||
try {
|
||||
loader.preLoad(plugin, loadClass);
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void postLoadClass(Plugin plugin, Class<?> loadClass) {
|
||||
loaders.forEach(loader -> {
|
||||
try {
|
||||
loader.postLoad(plugin, loadClass);
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void unloadClass(Plugin plugin, Class<?> loadClass) {
|
||||
loaders.forEach(loader -> {
|
||||
try {
|
||||
loader.unload(plugin, loadClass);
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onEnablePre(TPluginEnableEvent e) {
|
||||
setupClasses(e.getPlugin());
|
||||
Optional.ofNullable(pluginClasses.get(e.getPlugin().getName())).ifPresent(classes -> classes.forEach(pluginClass -> preLoadClass(e.getPlugin(), pluginClass)));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEnablePost(TPluginEnableEvent e) {
|
||||
Optional.ofNullable(pluginClasses.get(e.getPlugin().getName())).ifPresent(classes -> classes.forEach(pluginClass -> postLoadClass(e.getPlugin(), pluginClass)));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onDisable(PluginDisableEvent e) {
|
||||
Optional.ofNullable(pluginClasses.remove(e.getPlugin().getName())).ifPresent(classes -> classes.forEach(pluginClass -> unloadClass(e.getPlugin(), pluginClass)));
|
||||
}
|
||||
|
||||
public interface Loader {
|
||||
|
||||
default void preLoad(Plugin plugin, Class<?> loadClass) {
|
||||
}
|
||||
|
||||
default void postLoad(Plugin plugin, Class<?> loadClass) {
|
||||
}
|
||||
|
||||
default void unload(Plugin plugin, Class<?> cancelClass) {
|
||||
}
|
||||
|
||||
default int priority() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface IgnoreClasses {
|
||||
|
||||
String[] value();
|
||||
|
||||
}
|
||||
}
|
||||
29
src/main/scala/me/skymc/taboolib/anvil/AnvilContainer.java
Normal file
29
src/main/scala/me/skymc/taboolib/anvil/AnvilContainer.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package me.skymc.taboolib.anvil;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* @Author sky
|
||||
* @Since 2018-09-08 15:47
|
||||
*/
|
||||
public class AnvilContainer extends net.minecraft.server.v1_12_R1.ContainerAnvil {
|
||||
|
||||
public AnvilContainer(net.minecraft.server.v1_12_R1.EntityHuman player) {
|
||||
super(player.inventory, player.world, new net.minecraft.server.v1_12_R1.BlockPosition(0, 0, 0), player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean a(net.minecraft.server.v1_12_R1.EntityHuman player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void openAnvil(Player p) {
|
||||
net.minecraft.server.v1_12_R1.EntityPlayer player = ((org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer) p).getHandle();
|
||||
AnvilContainer container = new AnvilContainer(player);
|
||||
int c = player.nextContainerCounter();
|
||||
player.playerConnection.sendPacket(new net.minecraft.server.v1_12_R1.PacketPlayOutOpenWindow(c, "minecraft:anvil", new net.minecraft.server.v1_12_R1.ChatMessage("Repairing"), 0));
|
||||
player.activeContainer = container;
|
||||
player.activeContainer.windowId = c;
|
||||
player.activeContainer.addSlotListener(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.skymc.taboolib.anvil;
|
||||
|
||||
import com.ilummc.tlib.util.asm.AsmClassLoader;
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import me.skymc.taboolib.common.loader.Instantiable;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
/**
|
||||
* @author sky
|
||||
*/
|
||||
@Instantiable("AnvilContainerAPI")
|
||||
public class AnvilContainerAPI implements Listener {
|
||||
|
||||
private static Class<?> impl;
|
||||
|
||||
public AnvilContainerAPI() {
|
||||
try {
|
||||
impl = AsmClassLoader.createNewClass("me.skymc.taboolib.anvil.AnvilContainer", AnvilContainerGenerator.generate());
|
||||
Bukkit.getPluginManager().registerEvents(this, TabooLib.instance());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void openAnvil(Player player) {
|
||||
try {
|
||||
impl.getMethod("openAnvil", Player.class).invoke(impl, player);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void example(PlayerCommandPreprocessEvent e) {
|
||||
if (e.getMessage().equalsIgnoreCase("/anvilExample") && e.getPlayer().hasPermission("taboolib.admin")) {
|
||||
e.setCancelled(true);
|
||||
openAnvil(e.getPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package me.skymc.taboolib.anvil;
|
||||
|
||||
import me.skymc.taboolib.TabooLib;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
/**
|
||||
* @author idea
|
||||
*/
|
||||
public class AnvilContainerGenerator {
|
||||
|
||||
public static byte[] generate() {
|
||||
String version = TabooLib.getVersion();
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, "me/skymc/taboolib/anvil/AnvilContainer", null, "net/minecraft/server/" + version + "/ContainerAnvil", null);
|
||||
cw.visitSource("AnvilContainer.java", null);
|
||||
{
|
||||
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Lnet/minecraft/server/" + version + "/EntityHuman;)V", null, null);
|
||||
mv.visitCode();
|
||||
Label l0 = new Label();
|
||||
mv.visitLabel(l0);
|
||||
mv.visitLineNumber(12, l0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/server/" + version + "/EntityHuman", "inventory", "Lnet/minecraft/server/" + version + "/PlayerInventory;");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/server/" + version + "/EntityHuman", "world", "Lnet/minecraft/server/" + version + "/World;");
|
||||
mv.visitTypeInsn(Opcodes.NEW, "net/minecraft/server/" + version + "/BlockPosition");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "net/minecraft/server/" + version + "/BlockPosition", "<init>", "(III)V", false);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "net/minecraft/server/" + version + "/ContainerAnvil", "<init>", "(Lnet/minecraft/server/" + version + "/PlayerInventory;Lnet/minecraft/server/" + version + "/World;Lnet/minecraft/server/" + version + "/BlockPosition;Lnet/minecraft/server/" + version + "/EntityHuman;)V", false);
|
||||
Label l1 = new Label();
|
||||
mv.visitLabel(l1);
|
||||
mv.visitLineNumber(13, l1);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
Label l2 = new Label();
|
||||
mv.visitLabel(l2);
|
||||
mv.visitLocalVariable("this", "Lme/skymc/taboolib/anvil/AnvilContainer;", null, l0, l2, 0);
|
||||
mv.visitLocalVariable("player", "Lnet/minecraft/server/" + version + "/EntityHuman;", null, l0, l2, 1);
|
||||
mv.visitMaxs(8, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "a", "(Lnet/minecraft/server/" + version + "/EntityHuman;)Z", null, null);
|
||||
mv.visitCode();
|
||||
Label l0 = new Label();
|
||||
mv.visitLabel(l0);
|
||||
mv.visitLineNumber(17, l0);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
mv.visitInsn(Opcodes.IRETURN);
|
||||
Label l1 = new Label();
|
||||
mv.visitLabel(l1);
|
||||
mv.visitLocalVariable("this", "Lme/skymc/taboolib/anvil/AnvilContainer;", null, l0, l1, 0);
|
||||
mv.visitLocalVariable("player", "Lnet/minecraft/server/" + version + "/EntityHuman;", null, l0, l1, 1);
|
||||
mv.visitMaxs(1, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "openAnvil", "(Lorg/bukkit/entity/Player;)V", null, null);
|
||||
mv.visitCode();
|
||||
Label l0 = new Label();
|
||||
mv.visitLabel(l0);
|
||||
mv.visitLineNumber(21, l0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, "org/bukkit/craftbukkit/" + version + "/entity/CraftPlayer");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/bukkit/craftbukkit/" + version + "/entity/CraftPlayer", "getHandle", "()Lnet/minecraft/server/" + version + "/EntityPlayer;", false);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 1);
|
||||
Label l1 = new Label();
|
||||
mv.visitLabel(l1);
|
||||
mv.visitLineNumber(22, l1);
|
||||
mv.visitTypeInsn(Opcodes.NEW, "me/skymc/taboolib/anvil/AnvilContainer");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "me/skymc/taboolib/anvil/AnvilContainer", "<init>", "(Lnet/minecraft/server/" + version + "/EntityHuman;)V", false);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 2);
|
||||
Label l2 = new Label();
|
||||
mv.visitLabel(l2);
|
||||
mv.visitLineNumber(23, l2);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "net/minecraft/server/" + version + "/EntityPlayer", "nextContainerCounter", "()I", false);
|
||||
mv.visitVarInsn(Opcodes.ISTORE, 3);
|
||||
Label l3 = new Label();
|
||||
mv.visitLabel(l3);
|
||||
mv.visitLineNumber(24, l3);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/server/" + version + "/EntityPlayer", "playerConnection", "Lnet/minecraft/server/" + version + "/PlayerConnection;");
|
||||
mv.visitTypeInsn(Opcodes.NEW, "net/minecraft/server/" + version + "/PacketPlayOutOpenWindow");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 3);
|
||||
mv.visitLdcInsn("minecraft:anvil");
|
||||
mv.visitTypeInsn(Opcodes.NEW, "net/minecraft/server/" + version + "/ChatMessage");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitLdcInsn("Repairing");
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "net/minecraft/server/" + version + "/ChatMessage", "<init>", "(Ljava/lang/String;[Ljava/lang/Object;)V", false);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "net/minecraft/server/" + version + "/PacketPlayOutOpenWindow", "<init>", "(ILjava/lang/String;Lnet/minecraft/server/" + version + "/IChatBaseComponent;I)V", false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "net/minecraft/server/" + version + "/PlayerConnection", "sendPacket", "(Lnet/minecraft/server/" + version + "/Packet;)V", false);
|
||||
Label l4 = new Label();
|
||||
mv.visitLabel(l4);
|
||||
mv.visitLineNumber(25, l4);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, "net/minecraft/server/" + version + "/EntityPlayer", "activeContainer", "Lnet/minecraft/server/" + version + "/Container;");
|
||||
Label l5 = new Label();
|
||||
mv.visitLabel(l5);
|
||||
mv.visitLineNumber(26, l5);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/server/" + version + "/EntityPlayer", "activeContainer", "Lnet/minecraft/server/" + version + "/Container;");
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 3);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, "net/minecraft/server/" + version + "/Container", "windowId", "I");
|
||||
Label l6 = new Label();
|
||||
mv.visitLabel(l6);
|
||||
mv.visitLineNumber(27, l6);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/server/" + version + "/EntityPlayer", "activeContainer", "Lnet/minecraft/server/" + version + "/Container;");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "net/minecraft/server/" + version + "/Container", "addSlotListener", "(Lnet/minecraft/server/" + version + "/ICrafting;)V", false);
|
||||
Label l7 = new Label();
|
||||
mv.visitLabel(l7);
|
||||
mv.visitLineNumber(28, l7);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
Label l8 = new Label();
|
||||
mv.visitLabel(l8);
|
||||
mv.visitLocalVariable("p", "Lorg/bukkit/entity/Player;", null, l0, l8, 0);
|
||||
mv.visitLocalVariable("player", "Lnet/minecraft/server/" + version + "/EntityPlayer;", null, l1, l8, 1);
|
||||
mv.visitLocalVariable("container", "Lme/skymc/taboolib/anvil/AnvilContainer;", null, l2, l8, 2);
|
||||
mv.visitLocalVariable("c", "I", null, l3, l8, 3);
|
||||
mv.visitMaxs(9, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.skymc.taboolib.bookformatter;
|
||||
|
||||
import org.bukkit.Achievement;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.bukkit.Achievement.*;
|
||||
|
||||
public final class BookAchievement {
|
||||
|
||||
private static final HashMap<Achievement, String> achievements = new HashMap<>();
|
||||
|
||||
static {
|
||||
achievements.put(OPEN_INVENTORY, "openInventory");
|
||||
achievements.put(MINE_WOOD, "mineWood");
|
||||
achievements.put(BUILD_WORKBENCH, "buildWorkBench");
|
||||
achievements.put(BUILD_PICKAXE, "buildPickaxe");
|
||||
achievements.put(BUILD_FURNACE, "buildFurnace");
|
||||
achievements.put(ACQUIRE_IRON, "aquireIron");
|
||||
achievements.put(BUILD_HOE, "buildHoe");
|
||||
achievements.put(MAKE_BREAD, "makeBread");
|
||||
achievements.put(BAKE_CAKE, "bakeCake");
|
||||
achievements.put(BUILD_BETTER_PICKAXE, "buildBetterPickaxe");
|
||||
achievements.put(COOK_FISH, "cookFish");
|
||||
achievements.put(ON_A_RAIL, "onARail");
|
||||
achievements.put(BUILD_SWORD, "buildSword");
|
||||
achievements.put(KILL_ENEMY, "killEnemy");
|
||||
achievements.put(KILL_COW, "killCow");
|
||||
achievements.put(FLY_PIG, "flyPig");
|
||||
achievements.put(SNIPE_SKELETON, "snipeSkeleton");
|
||||
achievements.put(GET_DIAMONDS, "diamonds");
|
||||
achievements.put(NETHER_PORTAL, "portal");
|
||||
achievements.put(GHAST_RETURN, "ghast");
|
||||
achievements.put(GET_BLAZE_ROD, "blazerod");
|
||||
achievements.put(BREW_POTION, "potion");
|
||||
achievements.put(END_PORTAL, "thEnd");
|
||||
achievements.put(THE_END, "theEnd2");
|
||||
achievements.put(ENCHANTMENTS, "enchantments");
|
||||
achievements.put(OVERKILL, "overkill");
|
||||
achievements.put(BOOKCASE, "bookacase");
|
||||
achievements.put(EXPLORE_ALL_BIOMES, "exploreAllBiomes");
|
||||
achievements.put(SPAWN_WITHER, "spawnWither");
|
||||
achievements.put(KILL_WITHER, "killWither");
|
||||
achievements.put(FULL_BEACON, "fullBeacon");
|
||||
achievements.put(BREED_COW, "breedCow");
|
||||
achievements.put(DIAMONDS_TO_YOU, "diamondsToYou");
|
||||
achievements.put(OVERPOWERED, "overpowered");
|
||||
}
|
||||
|
||||
private BookAchievement() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the json id from the bukkit achievement passed as argument
|
||||
*
|
||||
* @param achievement the achievement
|
||||
* @return the achievement's id or null if not found
|
||||
*/
|
||||
public static String toId(Achievement achievement) {
|
||||
return achievements.get(achievement);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user