mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2026-06-05 01:46:53 +00:00
.
This commit is contained in:
@@ -1,705 +0,0 @@
|
||||
package com.boydti.fawe.bukkit;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"}) public class BStats {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System
|
||||
.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k',
|
||||
'k', 'i', 't'});
|
||||
final String examplePackage =
|
||||
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class
|
||||
.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException(
|
||||
"bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
*/
|
||||
public BStats(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n"
|
||||
+ "To honor their work, you should not disable it.\n"
|
||||
+ "This has nearly no effect on the server performance!\n"
|
||||
+ "Check out https://bStats.org/ to learn more :)").copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager()
|
||||
.register(BStats.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer =
|
||||
new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
String pluginName = "FastAsyncWorldEdit";
|
||||
String pluginVersion = Fawe.get().getVersion() + "";
|
||||
|
||||
data.put("pluginName", pluginName); // Append the name of the plugin
|
||||
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
JSONArray customCharts = new JSONArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JSONObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.put("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JSONObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount = getPlayerCount();
|
||||
int onlineMode = Fawe.imp().isOnlineMode() ? 1 : 0;
|
||||
String serverVersion = Fawe.imp().getPlatformVersion();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("serverUUID", serverUUID);
|
||||
|
||||
data.put("playerAmount", playerAmount);
|
||||
data.put("onlineMode", onlineMode);
|
||||
data.put("bukkitVersion", serverVersion);
|
||||
|
||||
data.put("javaVersion", javaVersion);
|
||||
data.put("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getPlayerCount() {
|
||||
return Fawe.imp() == null ? 1 : Fawe.imp().getPlayerCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager()
|
||||
.getRegistrations(service)) {
|
||||
try {
|
||||
pluginData.add(provider.getService().getMethod("getPluginData")
|
||||
.invoke(provider.getProvider()));
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger()
|
||||
.log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException(
|
||||
"This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type",
|
||||
"application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
try (InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(inputStream))) {
|
||||
|
||||
builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger()
|
||||
.info("Sent data to bStats and received response: " + builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JSONObject getRequestJsonObject() {
|
||||
JSONObject chart = new JSONObject();
|
||||
chart.put("chartId", chartId);
|
||||
try {
|
||||
JSONObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.put("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING,
|
||||
"Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JSONObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override public JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JSONObject value = new JSONObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey())
|
||||
.entrySet()) {
|
||||
value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.put(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
categoryValues.add(entry.getValue());
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(categoryValue);
|
||||
}
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,13 @@ package com.boydti.fawe.bukkit;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.IFawe;
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
import com.boydti.fawe.bukkit.beta.BukkitQueue;
|
||||
import com.boydti.fawe.bukkit.beta.BukkitQueueHandler;
|
||||
import com.boydti.fawe.bukkit.chat.BukkitChatManager;
|
||||
import com.boydti.fawe.bukkit.listener.BrushListener;
|
||||
import com.boydti.fawe.bukkit.listener.BukkitImageListener;
|
||||
import com.boydti.fawe.bukkit.listener.CFIPacketListener;
|
||||
import com.boydti.fawe.bukkit.listener.RenderListener;
|
||||
import com.boydti.fawe.bukkit.regions.*;
|
||||
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
|
||||
@@ -17,6 +21,7 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_All;
|
||||
import com.boydti.fawe.bukkit.v0.ChunkListener_8;
|
||||
import com.boydti.fawe.bukkit.v0.ChunkListener_9;
|
||||
import com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13;
|
||||
import com.boydti.fawe.bukkit.v1_14.BukkitQueue_1_14;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.FaweCommand;
|
||||
@@ -29,7 +34,9 @@ import com.boydti.fawe.util.TaskManager;
|
||||
import com.boydti.fawe.util.image.ImageViewer;
|
||||
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bstats.bukkit.MetricsLite;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -37,9 +44,11 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -61,7 +70,6 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
private BukkitImageListener imageListener;
|
||||
//private CFIPacketListener packetListener;
|
||||
|
||||
|
||||
public VaultUtil getVault() {
|
||||
return this.vault;
|
||||
}
|
||||
@@ -118,6 +126,19 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
});
|
||||
}
|
||||
|
||||
// @Override // Please don't delete this again, it's WIP
|
||||
// public void registerPacketListener() {
|
||||
// PluginManager manager = Bukkit.getPluginManager();
|
||||
// if (packetListener == null && manager.getPlugin("ProtocolLib") != null) {
|
||||
// packetListener = new CFIPacketListener(plugin);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public QueueHandler getQueueHandler() {
|
||||
return new BukkitQueueHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ImageViewer getImageViewer(FawePlayer fp) {
|
||||
if (listeningImages && imageListener == null) return null;
|
||||
@@ -219,7 +240,7 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
}
|
||||
|
||||
@Override public void startMetrics() {
|
||||
new BStats(plugin);
|
||||
new MetricsLite(plugin);
|
||||
}
|
||||
|
||||
public ItemUtil getItemUtil() {
|
||||
@@ -529,6 +550,7 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
}
|
||||
|
||||
public enum Version {
|
||||
v1_14_R1,
|
||||
v1_13_R2,
|
||||
NONE,
|
||||
}
|
||||
@@ -537,6 +559,8 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
switch (getVersion()) {
|
||||
case v1_13_R2:
|
||||
return new BukkitQueue_1_13(world);
|
||||
case v1_14_R1:
|
||||
return new BukkitQueue_1_14(world);
|
||||
default:
|
||||
case NONE:
|
||||
return new BukkitQueue_All(world);
|
||||
@@ -547,6 +571,8 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
switch (getVersion()) {
|
||||
case v1_13_R2:
|
||||
return new BukkitQueue_1_13(world);
|
||||
case v1_14_R1:
|
||||
return new BukkitQueue_1_14(world);
|
||||
default:
|
||||
case NONE:
|
||||
return new BukkitQueue_All(world);
|
||||
|
||||
+18
-7
@@ -64,6 +64,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
@@ -102,16 +103,16 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit
|
||||
nbtCreateTagMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
public int[] idbToStateOrdinal;
|
||||
public char[] idbToStateOrdinal;
|
||||
|
||||
private boolean init() {
|
||||
private synchronized boolean init() {
|
||||
if (idbToStateOrdinal != null) return false;
|
||||
idbToStateOrdinal = new int[Block.REGISTRY_ID.a()]; // size
|
||||
idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size
|
||||
for (int i = 0; i < idbToStateOrdinal.length; i++) {
|
||||
BlockState state = BlockTypes.states[i];
|
||||
BlockMaterial_1_13 material = (BlockMaterial_1_13) state.getMaterial();
|
||||
int id = Block.REGISTRY_ID.getId(material.getState());
|
||||
idbToStateOrdinal[id] = state.getOrdinal();
|
||||
idbToStateOrdinal[id] = state.getOrdinalChar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -365,7 +366,7 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit
|
||||
property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.d()));
|
||||
} else if (state instanceof BlockStateDirection) {
|
||||
property = new DirectionalProperty(state.a(),
|
||||
(List<Direction>) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList()));
|
||||
(List<Direction>) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase(Locale.ROOT))).collect(Collectors.toList()));
|
||||
} else if (state instanceof BlockStateEnum) {
|
||||
property = new EnumProperty(state.a(),
|
||||
(List<String>) state.d().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList()));
|
||||
@@ -533,8 +534,18 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return idbToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
if (init()) return adaptToInt(ibd);
|
||||
throw e;
|
||||
init();
|
||||
return adaptToInt(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
public char adaptToChar(IBlockData ibd) {
|
||||
try {
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return idbToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
init();
|
||||
return adaptToChar(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
package com.boydti.fawe.bukkit.beta;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
|
||||
import com.boydti.fawe.util.MemUtil;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.LongTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import net.minecraft.server.v1_14_R1.BiomeBase;
|
||||
import net.minecraft.server.v1_14_R1.BlockPosition;
|
||||
import net.minecraft.server.v1_14_R1.Chunk;
|
||||
import net.minecraft.server.v1_14_R1.ChunkSection;
|
||||
import net.minecraft.server.v1_14_R1.Entity;
|
||||
import net.minecraft.server.v1_14_R1.EntityTypes;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagCompound;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagInt;
|
||||
import net.minecraft.server.v1_14_R1.TileEntity;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class BukkitChunkHolder<T extends Future<T>> extends ChunkHolder {
|
||||
@Override
|
||||
public void init(final IQueueExtent extent, final int X, final int Z) {
|
||||
super.init(extent, X, Z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet get() {
|
||||
BukkitQueue extent = (BukkitQueue) getExtent();
|
||||
return new BukkitGetBlocks(extent.getNmsWorld(), getX(), getZ(), MemUtil.isMemoryFree());
|
||||
}
|
||||
|
||||
private void updateGet(BukkitGetBlocks get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) {
|
||||
synchronized (get) {
|
||||
if (get.nmsChunk != nmsChunk) {
|
||||
get.nmsChunk = nmsChunk;
|
||||
get.sections = sections.clone();
|
||||
get.reset();
|
||||
}
|
||||
if (get.sections == null) {
|
||||
get.sections = sections.clone();
|
||||
}
|
||||
if (get.sections[layer] != section) {
|
||||
get.sections[layer] = section;
|
||||
}
|
||||
get.blocks[layer] = arr;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeEntity(Entity entity) {
|
||||
entity.die();
|
||||
entity.valid = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T call() {
|
||||
try {
|
||||
int X = getX();
|
||||
int Z = getZ();
|
||||
BukkitQueue extent = (BukkitQueue) getExtent();
|
||||
BukkitGetBlocks get = (BukkitGetBlocks) getOrCreateGet();
|
||||
IChunkSet set = getOrCreateSet();
|
||||
|
||||
Chunk nmsChunk = extent.ensureLoaded(X, Z);
|
||||
|
||||
// Remove existing tiles
|
||||
{
|
||||
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
|
||||
if (!tiles.isEmpty()) {
|
||||
final Iterator<Map.Entry<BlockPosition, TileEntity>> iterator = tiles.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final Map.Entry<BlockPosition, TileEntity> entry = iterator.next();
|
||||
final BlockPosition pos = entry.getKey();
|
||||
final int lx = pos.getX() & 15;
|
||||
final int ly = pos.getY();
|
||||
final int lz = pos.getZ() & 15;
|
||||
final int layer = ly >> 4;
|
||||
if (!set.hasSection(layer)) {
|
||||
continue;
|
||||
}
|
||||
if (set.getBlock(lx, ly, lz).getOrdinal() != 0) {
|
||||
TileEntity tile = entry.getValue();
|
||||
tile.n();
|
||||
tile.invalidateBlockCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bitMask = 0;
|
||||
synchronized (nmsChunk) {
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
World world = extent.getBukkitWorld();
|
||||
boolean hasSky = world.getEnvironment() == World.Environment.NORMAL;
|
||||
|
||||
for (int layer = 0; layer < 16; layer++) {
|
||||
if (!set.hasSection(layer)) continue;
|
||||
|
||||
bitMask |= 1 << layer;
|
||||
|
||||
char[] setArr = set.getArray(layer);
|
||||
ChunkSection newSection;
|
||||
ChunkSection existingSection = sections[layer];
|
||||
if (existingSection == null) {
|
||||
newSection = extent.newChunkSection(layer, setArr);
|
||||
if (BukkitQueue.setSectionAtomic(sections, null, newSection, layer)) {
|
||||
updateGet(get, nmsChunk, sections, newSection, setArr, layer);
|
||||
continue;
|
||||
} else {
|
||||
existingSection = sections[layer];
|
||||
if (existingSection == null) {
|
||||
System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateLock lock = BukkitQueue.applyLock(existingSection);
|
||||
synchronized (get) {
|
||||
synchronized (lock) {
|
||||
lock.untilFree();
|
||||
|
||||
ChunkSection getSection;
|
||||
if (get.nmsChunk != nmsChunk) {
|
||||
get.nmsChunk = nmsChunk;
|
||||
get.sections = null;
|
||||
get.reset();
|
||||
} else {
|
||||
getSection = get.getSections()[layer];
|
||||
if (getSection != existingSection) {
|
||||
get.sections[layer] = existingSection;
|
||||
get.reset();
|
||||
} else if (lock.isModified()) {
|
||||
get.reset(layer);
|
||||
}
|
||||
}
|
||||
char[] getArr = get.load(layer);
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char value = setArr[i];
|
||||
if (value != 0) {
|
||||
getArr[i] = value;
|
||||
}
|
||||
}
|
||||
newSection = extent.newChunkSection(layer, getArr);
|
||||
if (!BukkitQueue.setSectionAtomic(sections, existingSection, newSection, layer)) {
|
||||
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
|
||||
continue;
|
||||
} else {
|
||||
updateGet(get, nmsChunk, sections, newSection, setArr, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Biomes
|
||||
BiomeType[] biomes = set.getBiomes();
|
||||
if (biomes != null) {
|
||||
// set biomes
|
||||
final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex();
|
||||
for (int i = 0; i < biomes.length; i++) {
|
||||
final BiomeType biome = biomes[i];
|
||||
if (biome != null) {
|
||||
final Biome craftBiome = BukkitAdapter.adapt(biome);
|
||||
currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Runnable[] syncTasks = null;
|
||||
|
||||
net.minecraft.server.v1_14_R1.World nmsWorld = nmsChunk.getWorld();
|
||||
int bx = X << 4;
|
||||
int bz = Z << 4;
|
||||
|
||||
Set<UUID> entityRemoves = set.getEntityRemoves();
|
||||
if (entityRemoves != null && !entityRemoves.isEmpty()) {
|
||||
if (syncTasks == null) syncTasks = new Runnable[3];
|
||||
|
||||
syncTasks[2] = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final List<Entity>[] entities = nmsChunk.getEntitySlices();
|
||||
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
final Collection<Entity> ents = entities[i];
|
||||
if (!ents.isEmpty()) {
|
||||
final Iterator<Entity> iter = ents.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final Entity entity = iter.next();
|
||||
if (entityRemoves.contains(entity.getUniqueID())) {
|
||||
iter.remove();
|
||||
removeEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Set<CompoundTag> entities = set.getEntities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) syncTasks = new Runnable[2];
|
||||
|
||||
syncTasks[1] = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (final CompoundTag nativeTag : entities) {
|
||||
final Map<String, Tag> entityTagMap = ReflectionUtils.getMap(nativeTag.getValue());
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
if (idTag == null || posTag == null || rotTag == null) {
|
||||
Fawe.debug("Unknown entity tag: " + nativeTag);
|
||||
continue;
|
||||
}
|
||||
final double x = posTag.getDouble(0);
|
||||
final double y = posTag.getDouble(1);
|
||||
final double z = posTag.getDouble(2);
|
||||
final float yaw = rotTag.getFloat(0);
|
||||
final float pitch = rotTag.getFloat(1);
|
||||
final String id = idTag.getValue();
|
||||
|
||||
EntityTypes<?> type = EntityTypes.a(id).orElse(null);
|
||||
if (type != null) {
|
||||
Entity entity = type.a(nmsWorld);
|
||||
if (entity != null) {
|
||||
UUID uuid = entity.getUniqueID();
|
||||
entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits()));
|
||||
entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits()));
|
||||
if (nativeTag != null) {
|
||||
final NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_14.fromNative(nativeTag);
|
||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
entity.f(tag);
|
||||
}
|
||||
entity.setLocation(x, y, z, yaw, pitch);
|
||||
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// set tiles
|
||||
Map<Short, CompoundTag> tiles = set.getTiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) syncTasks = new Runnable[1];
|
||||
|
||||
syncTasks[0] = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (final Map.Entry<Short, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
final short blockHash = entry.getKey();
|
||||
final int x = (blockHash >> 12 & 0xF) + bx;
|
||||
final int y = (blockHash & 0xFF);
|
||||
final int z = (blockHash >> 8 & 0xF) + bz;
|
||||
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||
synchronized (nmsWorld) {
|
||||
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
|
||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||
nmsWorld.removeTileEntity(pos);
|
||||
tileEntity = nmsWorld.getTileEntity(pos);
|
||||
}
|
||||
if (tileEntity != null) {
|
||||
final NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
||||
tag.set("x", new NBTTagInt(x));
|
||||
tag.set("y", new NBTTagInt(y));
|
||||
tag.set("z", new NBTTagInt(z));
|
||||
tileEntity.load(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Runnable callback;
|
||||
if (bitMask == 0) {
|
||||
callback = null;
|
||||
} else {
|
||||
int finalMask = bitMask;
|
||||
callback = () -> {
|
||||
// Set Modified
|
||||
nmsChunk.d(true); // Set Modified
|
||||
nmsChunk.mustNotSave = false;
|
||||
nmsChunk.markDirty();
|
||||
// send to player
|
||||
extent.sendChunk(X, Z, finalMask);
|
||||
|
||||
extent.returnToPool(BukkitChunkHolder.this);
|
||||
};
|
||||
}
|
||||
if (syncTasks != null) {
|
||||
QueueHandler queueHandler = Fawe.get().getQueueHandler();
|
||||
Runnable[] finalSyncTasks = syncTasks;
|
||||
|
||||
// Chain the sync tasks and the callback
|
||||
Callable<Future> chain = new Callable<Future>() {
|
||||
@Override
|
||||
public Future call() {
|
||||
// Run the sync tasks
|
||||
for (int i = 1; i < finalSyncTasks.length; i++) {
|
||||
Runnable task = finalSyncTasks[i];
|
||||
if (task != null) {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
if (callback == null) {
|
||||
extent.returnToPool(BukkitChunkHolder.this);
|
||||
return null;
|
||||
} else {
|
||||
return queueHandler.async(callback, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (T) (Future) queueHandler.sync(chain);
|
||||
} else {
|
||||
if (callback == null) {
|
||||
extent.returnToPool(BukkitChunkHolder.this);
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.boydti.fawe.bukkit.beta;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
|
||||
import com.boydti.fawe.bukkit.v1_14.BukkitQueue_1_14;
|
||||
import com.boydti.fawe.bukkit.v1_14.adapter.Spigot_v1_14_R1;
|
||||
import com.boydti.fawe.jnbt.anvil.BitArray4096;
|
||||
import com.boydti.fawe.util.MemUtil;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import net.minecraft.server.v1_14_R1.BiomeBase;
|
||||
import net.minecraft.server.v1_14_R1.Chunk;
|
||||
import net.minecraft.server.v1_14_R1.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_14_R1.ChunkProviderServer;
|
||||
import net.minecraft.server.v1_14_R1.ChunkSection;
|
||||
import net.minecraft.server.v1_14_R1.DataBits;
|
||||
import net.minecraft.server.v1_14_R1.DataPalette;
|
||||
import net.minecraft.server.v1_14_R1.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_14_R1.DataPaletteHash;
|
||||
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_14_R1.IBlockData;
|
||||
import net.minecraft.server.v1_14_R1.World;
|
||||
import net.minecraft.server.v1_14_R1.WorldServer;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BukkitGetBlocks extends CharGetBlocks {
|
||||
public ChunkSection[] sections;
|
||||
public Chunk nmsChunk;
|
||||
public World nmsWorld;
|
||||
public int X, Z;
|
||||
private boolean forceLoad;
|
||||
|
||||
public BukkitGetBlocks(World nmsWorld, int X, int Z, boolean forceLoad) {
|
||||
this.nmsWorld = nmsWorld;
|
||||
this.X = X;
|
||||
this.Z = Z;
|
||||
if (forceLoad) {
|
||||
((WorldServer) nmsWorld).setForceLoaded(X, Z, this.forceLoad = true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (forceLoad) {
|
||||
((WorldServer) nmsWorld).setForceLoaded(X, Z, forceLoad = false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int z) {
|
||||
BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x];
|
||||
return BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTag(int x, int y, int z) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
return load(layer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized char[] load(int layer, char[] data) {
|
||||
ChunkSection section = getSections()[layer];
|
||||
// Section is null, return empty array
|
||||
if (section == null) {
|
||||
return FaweCache.EMPTY_CHAR_4096;
|
||||
}
|
||||
if (data == null || data == FaweCache.EMPTY_CHAR_4096) {
|
||||
data = new char[4096];
|
||||
}
|
||||
DelegateLock lock = BukkitQueue.applyLock(section);
|
||||
synchronized (lock) {
|
||||
lock.untilFree();
|
||||
lock.setModified(false);
|
||||
// Efficiently convert ChunkSection to raw data
|
||||
try {
|
||||
Spigot_v1_14_R1 adapter = ((Spigot_v1_14_R1) WorldEditPlugin.getInstance().getBukkitImplAdapter());
|
||||
|
||||
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
final DataBits bits = (DataBits) BukkitQueue_1_14.fieldBits.get(blocks);
|
||||
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitQueue_1_14.fieldPalette.get(blocks);
|
||||
|
||||
final int bitsPerEntry = bits.c();
|
||||
final long[] blockStates = bits.a();
|
||||
|
||||
new BitArray4096(blockStates, bitsPerEntry).toRaw(data);
|
||||
|
||||
int num_palette;
|
||||
if (palette instanceof DataPaletteLinear) {
|
||||
num_palette = ((DataPaletteLinear<IBlockData>) palette).b();
|
||||
} else if (palette instanceof DataPaletteHash) {
|
||||
num_palette = ((DataPaletteHash<IBlockData>) palette).b();
|
||||
} else {
|
||||
num_palette = 0;
|
||||
int[] paletteToBlockInts = FaweCache.PALETTE_TO_BLOCK.get();
|
||||
char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get();
|
||||
try {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char paletteVal = data[i];
|
||||
char ordinal = paletteToBlockChars[paletteVal];
|
||||
if (ordinal == Character.MAX_VALUE) {
|
||||
paletteToBlockInts[num_palette++] = paletteVal;
|
||||
IBlockData ibd = palette.a(data[i]);
|
||||
if (ibd == null) {
|
||||
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
|
||||
} else {
|
||||
ordinal = adapter.adaptToChar(ibd);
|
||||
}
|
||||
paletteToBlockChars[paletteVal] = ordinal;
|
||||
}
|
||||
data[i] = ordinal;
|
||||
}
|
||||
} finally {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int paletteVal = paletteToBlockInts[i];
|
||||
paletteToBlockChars[paletteVal] = Character.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get();
|
||||
try {
|
||||
final int size = num_palette;
|
||||
if (size != 1) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
char ordinal = ordinal(palette.a(i), adapter);
|
||||
paletteToBlockChars[i] = ordinal;
|
||||
}
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char paletteVal = data[i];
|
||||
char val = paletteToBlockChars[paletteVal];
|
||||
data[i] = val;
|
||||
}
|
||||
} else {
|
||||
char ordinal = ordinal(palette.a(0), adapter);
|
||||
Arrays.fill(data, ordinal);
|
||||
}
|
||||
} finally {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
paletteToBlockChars[i] = Character.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private final char ordinal(IBlockData ibd, Spigot_v1_14_R1 adapter) {
|
||||
if (ibd == null) {
|
||||
return BlockTypes.AIR.getDefaultState().getOrdinalChar();
|
||||
} else {
|
||||
return adapter.adaptToChar(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkSection[] getSections() {
|
||||
ChunkSection[] tmp = sections;
|
||||
if (tmp == null) {
|
||||
synchronized (this) {
|
||||
tmp = sections;
|
||||
if (tmp == null) {
|
||||
Chunk chunk = getChunk();
|
||||
sections = tmp = chunk.getSections().clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
Chunk tmp = nmsChunk;
|
||||
if (tmp == null) {
|
||||
synchronized (this) {
|
||||
tmp = nmsChunk;
|
||||
if (tmp == null) {
|
||||
nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
return getSections()[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
if (aggressive) {
|
||||
sections = null;
|
||||
nmsChunk = null;
|
||||
}
|
||||
return super.trim(aggressive);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package com.boydti.fawe.bukkit.beta;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IChunk;
|
||||
import com.boydti.fawe.beta.implementation.SimpleCharQueueExtent;
|
||||
import com.boydti.fawe.beta.implementation.WorldChunkCache;
|
||||
import com.boydti.fawe.bukkit.v1_14.adapter.BlockMaterial_1_14;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.jnbt.anvil.BitArray4096;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import net.jpountz.util.UnsafeUtils;
|
||||
import net.minecraft.server.v1_14_R1.Block;
|
||||
import net.minecraft.server.v1_14_R1.Chunk;
|
||||
import net.minecraft.server.v1_14_R1.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_14_R1.ChunkProviderServer;
|
||||
import net.minecraft.server.v1_14_R1.ChunkSection;
|
||||
import net.minecraft.server.v1_14_R1.ChunkStatus;
|
||||
import net.minecraft.server.v1_14_R1.DataBits;
|
||||
import net.minecraft.server.v1_14_R1.DataPalette;
|
||||
import net.minecraft.server.v1_14_R1.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_14_R1.GameProfileSerializer;
|
||||
import net.minecraft.server.v1_14_R1.IBlockData;
|
||||
import net.minecraft.server.v1_14_R1.IChunkAccess;
|
||||
import net.minecraft.server.v1_14_R1.PlayerChunk;
|
||||
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
|
||||
import net.minecraft.server.v1_14_R1.ProtoChunkExtension;
|
||||
import net.minecraft.server.v1_14_R1.WorldServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class BukkitQueue extends SimpleCharQueueExtent {
|
||||
|
||||
private org.bukkit.World bukkitWorld;
|
||||
private WorldServer nmsWorld;
|
||||
|
||||
@Override
|
||||
public synchronized void init(WorldChunkCache cache) {
|
||||
World world = cache.getWorld();
|
||||
if (world instanceof BukkitWorld) {
|
||||
this.bukkitWorld = ((BukkitWorld) world).getWorld();
|
||||
} else {
|
||||
this.bukkitWorld = Bukkit.getWorld(world.getName());
|
||||
}
|
||||
checkNotNull(this.bukkitWorld);
|
||||
CraftWorld craftWorld = ((CraftWorld) bukkitWorld);
|
||||
this.nmsWorld = craftWorld.getHandle();
|
||||
super.init(cache);
|
||||
}
|
||||
|
||||
public WorldServer getNmsWorld() {
|
||||
return nmsWorld;
|
||||
}
|
||||
|
||||
public org.bukkit.World getBukkitWorld() {
|
||||
return bukkitWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void reset() {
|
||||
super.reset();
|
||||
}
|
||||
|
||||
// private static final IterableThreadLocal<BukkitFullChunk> FULL_CHUNKS = new IterableThreadLocal<BukkitFullChunk>() {
|
||||
// @Override
|
||||
// public BukkitFullChunk init() {
|
||||
// return new BukkitFullChunk();
|
||||
// }
|
||||
// };
|
||||
|
||||
@Override
|
||||
public IChunk create(boolean full) {
|
||||
// if (full) {
|
||||
// //TODO implement
|
||||
// return FULL_CHUNKS.get();
|
||||
// }
|
||||
return new BukkitChunkHolder();
|
||||
}
|
||||
|
||||
/*
|
||||
NMS fields
|
||||
*/
|
||||
public final static Field fieldBits;
|
||||
public final static Field fieldPalette;
|
||||
public final static Field fieldSize;
|
||||
|
||||
public final static Field fieldFluidCount;
|
||||
public final static Field fieldTickingBlockCount;
|
||||
public final static Field fieldNonEmptyBlockCount;
|
||||
|
||||
private final static Field fieldDirtyCount;
|
||||
private final static Field fieldDirtyBits;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldSize = DataPaletteBlock.class.getDeclaredField("i");
|
||||
fieldSize.setAccessible(true);
|
||||
fieldBits = DataPaletteBlock.class.getDeclaredField("a");
|
||||
fieldBits.setAccessible(true);
|
||||
fieldPalette = DataPaletteBlock.class.getDeclaredField("h");
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldFluidCount = ChunkSection.class.getDeclaredField("e");
|
||||
fieldFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount");
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount");
|
||||
fieldDirtyCount.setAccessible(true);
|
||||
fieldDirtyBits = PlayerChunk.class.getDeclaredField("h");
|
||||
fieldDirtyBits.setAccessible(true);
|
||||
|
||||
{
|
||||
Field tmp = null;
|
||||
try {
|
||||
tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
} catch (NoSuchFieldException paper) {
|
||||
tmp = DataPaletteBlock.class.getDeclaredField("writeLock");
|
||||
}
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
int modifiers = modifiersField.getInt(fieldLock);
|
||||
int newModifiers = modifiers & (~Modifier.FINAL);
|
||||
if (newModifiers != modifiers) modifiersField.setInt(fieldLock, newModifiers);
|
||||
}
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("data type scale not a power of two");
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean setSectionAtomic(ChunkSection[] sections, ChunkSection expected, ChunkSection value, int layer) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return UnsafeUtils.getUNSAFE().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static DelegateLock applyLock(ChunkSection section) {
|
||||
try {
|
||||
synchronized (section) {
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
Lock currentLock = (Lock) fieldLock.get(blocks);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean PAPER = true;
|
||||
|
||||
public Chunk ensureLoaded(int X, int Z) {
|
||||
return ensureLoaded(nmsWorld, X, Z);
|
||||
}
|
||||
|
||||
public static Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) {
|
||||
Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return nmsWorld.getChunkAt(X, Z);
|
||||
}
|
||||
if (PAPER) {
|
||||
CraftWorld craftWorld = nmsWorld.getWorld();
|
||||
CompletableFuture<org.bukkit.Chunk> future = craftWorld.getChunkAtAsync(X, Z, true);
|
||||
try {
|
||||
CraftChunk chunk = (CraftChunk) future.get();
|
||||
return chunk.getHandle();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
System.out.println("Error, cannot load chunk async (paper not installed?)");
|
||||
PAPER = false;
|
||||
}
|
||||
}
|
||||
// TODO optimize
|
||||
return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z));
|
||||
}
|
||||
|
||||
private PlayerChunk getPlayerChunk(final int cx, final int cz) {
|
||||
PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap;
|
||||
PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz));
|
||||
if (playerChunk == null) {
|
||||
return null;
|
||||
}
|
||||
return playerChunk;
|
||||
}
|
||||
|
||||
public boolean sendChunk(final int X, final int Z, final int mask) {
|
||||
PlayerChunk playerChunk = getPlayerChunk(X, Z);
|
||||
if (playerChunk == null) {
|
||||
return false;
|
||||
}
|
||||
// ChunkSection[] sections = nmsChunk.getSections();
|
||||
// for (int layer = 0; layer < 16; layer++) {
|
||||
// if (sections[layer] == null && (mask & (1 << layer)) != 0) {
|
||||
// sections[layer] = new ChunkSection(layer << 4);
|
||||
// }
|
||||
// }
|
||||
if (playerChunk.k()) {
|
||||
TaskManager.IMP.sync(new Supplier<Object>() {
|
||||
@Override
|
||||
public Object get() {
|
||||
try {
|
||||
int dirtyBits = fieldDirtyBits.getInt(playerChunk);
|
||||
if (dirtyBits == 0) {
|
||||
nmsWorld.getChunkProvider().playerChunkMap.a(playerChunk);
|
||||
}
|
||||
if (mask == 0) {
|
||||
dirtyBits = 65535;
|
||||
} else {
|
||||
dirtyBits |= mask;
|
||||
}
|
||||
|
||||
fieldDirtyBits.set(playerChunk, dirtyBits);
|
||||
fieldDirtyCount.set(playerChunk, 64);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
|
||||
public static ChunkSection newChunkSection(final int layer, final char[] blocks) {
|
||||
ChunkSection section = new ChunkSection(layer << 4);
|
||||
if (blocks == null) {
|
||||
return section;
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockstates = FaweCache.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette = 0;
|
||||
int air = 0;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = blocks[i];
|
||||
switch (ordinal) {
|
||||
case 0:
|
||||
case BlockID.AIR:
|
||||
case BlockID.CAVE_AIR:
|
||||
case BlockID.VOID_AIR:
|
||||
air++;
|
||||
}
|
||||
int palette = blockToPalette[ordinal];
|
||||
if (palette == Integer.MAX_VALUE) {
|
||||
blockToPalette[ordinal] = palette = num_palette;
|
||||
paletteToBlock[num_palette] = ordinal;
|
||||
num_palette++;
|
||||
}
|
||||
blocksCopy[i] = palette;
|
||||
}
|
||||
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
|
||||
final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) blockstates[i] = 0;
|
||||
} else {
|
||||
final BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
// set palette & data bits
|
||||
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
final long[] bits = Arrays.copyOfRange(blockstates, 0, blockBitArrayEnd);
|
||||
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
|
||||
final DataPalette<IBlockData> palette;
|
||||
// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a);
|
||||
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
|
||||
|
||||
// set palette
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
final int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypes.states[ordinal];
|
||||
final IBlockData ibd = ((BlockMaterial_1_14) state.getMaterial()).getState();
|
||||
palette.a(ibd);
|
||||
}
|
||||
try {
|
||||
fieldBits.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, palette);
|
||||
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
|
||||
setCount(0, 4096 - air, section);
|
||||
} catch (final IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return section;
|
||||
} catch (final Throwable e){
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException {
|
||||
fieldFluidCount.set(section, 0); // TODO FIXME
|
||||
fieldTickingBlockCount.set(section, tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.boydti.fawe.bukkit.beta;
|
||||
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.implementation.QueueHandler;
|
||||
|
||||
public class BukkitQueueHandler extends QueueHandler {
|
||||
@Override
|
||||
public IQueueExtent create() {
|
||||
return new BukkitQueue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.boydti.fawe.bukkit.beta;
|
||||
|
||||
import org.apache.commons.lang.mutable.MutableInt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class DelegateLock extends ReentrantLock {
|
||||
private final Lock parent;
|
||||
private volatile boolean modified;
|
||||
private final AtomicInteger count;
|
||||
|
||||
public DelegateLock(Lock parent) {
|
||||
this.parent = parent;
|
||||
if (!(parent instanceof ReentrantLock)) {
|
||||
count = new AtomicInteger();
|
||||
} else {
|
||||
count = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(boolean modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void lock() {
|
||||
modified = true;
|
||||
parent.lock();
|
||||
if (count != null) {
|
||||
count.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void lockInterruptibly() throws InterruptedException {
|
||||
parent.lockInterruptibly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean tryLock() {
|
||||
return parent.tryLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return parent.tryLock(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
modified = true;
|
||||
parent.unlock();
|
||||
if (count != null) {
|
||||
if (count.getAndDecrement() <= 0) {
|
||||
count.incrementAndGet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Lock getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Condition newCondition() {
|
||||
return parent.newCondition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getHoldCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isHeldByCurrentThread() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isLocked() {
|
||||
if (parent instanceof ReentrantLock) {
|
||||
return ((ReentrantLock) parent).isLocked();
|
||||
}
|
||||
return count.get() > 0;
|
||||
}
|
||||
|
||||
public void untilFree() {
|
||||
if (parent instanceof ReentrantLock) {
|
||||
ReentrantLock rl = (ReentrantLock) parent;
|
||||
if (rl.isLocked()) {
|
||||
rl.lock();
|
||||
rl.unlock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
while (count.get() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean hasWaiters(Condition condition) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getWaitQueueLength(Condition condition) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return parent.toString();
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -85,11 +85,17 @@ public class CFIPacketListener implements Listener {
|
||||
ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand();
|
||||
if (hand.getType().isBlock()) {
|
||||
Material type = hand.getType();
|
||||
if (!type.isEmpty()) {
|
||||
BlockStateHolder block = BukkitAdapter.asBlockState(hand);
|
||||
if (block != null) {
|
||||
gen.setBlock(pt, block);
|
||||
return;
|
||||
switch (type) {
|
||||
case AIR:
|
||||
case CAVE_AIR:
|
||||
case VOID_AIR:
|
||||
break;
|
||||
default: {
|
||||
BlockStateHolder block = BukkitAdapter.asBlockState(hand);
|
||||
if (block != null) {
|
||||
gen.setBlock(pt, block);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,8 @@ public class RenderListener implements Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.setViewDistance(value);
|
||||
throw new UnsupportedOperationException("TODO FIXME: PAPER 1.14");
|
||||
// player.setViewDistance(value);
|
||||
}
|
||||
|
||||
private int getViewDistance(Player player) {
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v0;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.IntFaweChunk;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.object.RunnableVal2;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.LongTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitChunk_All extends IntFaweChunk<Chunk, BukkitQueue_All> {
|
||||
|
||||
private int layer = -1;
|
||||
private int index;
|
||||
private boolean place = true;
|
||||
|
||||
/**
|
||||
* A FaweSections object represents a chunk and the blocks that you wish to change in it.
|
||||
*
|
||||
* @param parent
|
||||
* @param x
|
||||
* @param z
|
||||
*/
|
||||
public BukkitChunk_All(FaweQueue parent, int x, int z) {
|
||||
super(parent, x, z);
|
||||
}
|
||||
|
||||
public BukkitChunk_All(FaweQueue parent, int x, int z, int[][] ids, short[] count, short[] air) {
|
||||
super(parent, x, z, ids, count, air);
|
||||
}
|
||||
|
||||
private static boolean canTick(BlockType type) {
|
||||
return type.getMaterial().isTicksRandomly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntFaweChunk copy(boolean shallow) {
|
||||
BukkitChunk_All copy;
|
||||
if (shallow) {
|
||||
copy = new BukkitChunk_All(getParent(), getX(), getZ(), setBlocks, count, air);
|
||||
copy.biomes = biomes;
|
||||
} else {
|
||||
copy = new BukkitChunk_All(getParent(), getX(), getZ(), (int[][]) MainUtil.copyNd(setBlocks), count.clone(), air.clone());
|
||||
copy.biomes = biomes != null ? biomes.clone() : null;
|
||||
}
|
||||
copy.chunk = chunk;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getNewChunk() {
|
||||
return Bukkit.getWorld(getParent().getWorldName()).getChunkAt(getX(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
getChunk().load(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public FaweChunk call() {
|
||||
long start = System.currentTimeMillis();
|
||||
int recommended = 25 + Settings.IMP.QUEUE.EXTRA_TIME_MS;
|
||||
boolean more = true;
|
||||
final BukkitQueue_All parent = getParent();
|
||||
BukkitImplAdapter adapter = BukkitQueue_0.getAdapter();
|
||||
final Chunk chunk = getChunk();
|
||||
Object[] disableResult = parent.disableLighting(chunk);
|
||||
final World world = chunk.getWorld();
|
||||
int[][] sections = getCombinedIdArrays();
|
||||
final int bx = getX() << 4;
|
||||
final int bz = getZ() << 4;
|
||||
boolean update = adapter == null || adapter.isChunkInUse(chunk);
|
||||
if (layer == -1) {
|
||||
if (adapter != null) {
|
||||
// Run change task
|
||||
RunnableVal2<FaweChunk, FaweChunk> task = parent.getChangeTask();
|
||||
BukkitChunk_All_ReadonlySnapshot previous;
|
||||
if (task != null) {
|
||||
ChunkSnapshot snapshot = parent.ensureChunkLoaded(getX(), getZ());
|
||||
previous = new BukkitChunk_All_ReadonlySnapshot(parent, this, snapshot, biomes != null);
|
||||
for (BlockState tile : chunk.getTileEntities()) {
|
||||
int x = tile.getX();
|
||||
int y = tile.getY();
|
||||
int z = tile.getZ();
|
||||
if (getBlockCombinedId(x & 15, y, z & 15) != 0) {
|
||||
CompoundTag nbt = adapter.getBlock(new Location(world, x, y, z)).getNbtData();
|
||||
if (nbt != null) {
|
||||
previous.setTile(x & 15, y, z & 15, nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previous = null;
|
||||
}
|
||||
// Set entities
|
||||
if (adapter != null) {
|
||||
Set<CompoundTag> entitiesToSpawn = this.getEntities();
|
||||
if (!entitiesToSpawn.isEmpty()) {
|
||||
for (CompoundTag tag : entitiesToSpawn) {
|
||||
String id = tag.getString("Id");
|
||||
ListTag posTag = tag.getListTag("Pos");
|
||||
ListTag rotTag = tag.getListTag("Rotation");
|
||||
if (id == null || posTag == null || rotTag == null) {
|
||||
Fawe.debug("Unknown entity tag: " + tag);
|
||||
continue;
|
||||
}
|
||||
double x = posTag.getDouble(0);
|
||||
double y = posTag.getDouble(1);
|
||||
double z = posTag.getDouble(2);
|
||||
float yaw = rotTag.getFloat(0);
|
||||
float pitch = rotTag.getFloat(1);
|
||||
Location loc = new Location(world, x, y, z, yaw, pitch);
|
||||
Entity created = adapter.createEntity(loc, new BaseEntity(EntityTypes.get(id), tag));
|
||||
if (created != null) {
|
||||
UUID uuid = created.getUniqueId();
|
||||
Map<String, Tag> map = ReflectionUtils.getMap(tag.getValue());
|
||||
map.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits()));
|
||||
map.put("UUIDMost", new LongTag(uuid.getMostSignificantBits()));
|
||||
}
|
||||
}
|
||||
}
|
||||
HashSet<UUID> entsToRemove = this.getEntityRemoves();
|
||||
if (!entsToRemove.isEmpty()) {
|
||||
for (Entity entity : chunk.getEntities()) {
|
||||
if (entsToRemove.contains(entity.getUniqueId()) && !(entity instanceof Player)) {
|
||||
entity.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previous != null) {
|
||||
task.run(previous, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Biomes
|
||||
final BiomeType[] biomes = getBiomeArray();
|
||||
if (biomes != null) {
|
||||
int index = 0;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int zz = bz + z;
|
||||
for (int x = 0; x < 16; x++, index++) {
|
||||
int xx = bx + x;
|
||||
BiomeType biome = biomes[index];
|
||||
if (biome == null) continue;
|
||||
Biome bukkitBiome = adapter.adapt(biome);
|
||||
if (bukkitBiome != null) {
|
||||
world.setBiome(xx, zz, bukkitBiome);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (index != 0) {
|
||||
if (place) {
|
||||
layer--;
|
||||
} else {
|
||||
layer++;
|
||||
}
|
||||
}
|
||||
mainloop:
|
||||
do {
|
||||
if (place) {
|
||||
if (++layer >= sections.length) {
|
||||
place = false;
|
||||
layer = sections.length - 1;
|
||||
}
|
||||
} else if (--layer < 0) {
|
||||
more = false;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
// Efficiently merge sections
|
||||
int changes = getCount(layer);
|
||||
if (changes == 0) {
|
||||
continue;
|
||||
}
|
||||
final int[] newArray = sections[layer];
|
||||
if (newArray == null) {
|
||||
continue;
|
||||
}
|
||||
// final byte[] cacheX = FaweCache.CACHE_X[layer];
|
||||
// final short[] cacheY = FaweCache.CACHE_Y[layer];
|
||||
// final byte[] cacheZ = FaweCache.CACHE_Z[layer];
|
||||
boolean checkTime = !((getAir(layer) == 4096 || (getCount(layer) == 4096 && getAir(layer) == 0) || (getCount(layer) == getAir(layer))));
|
||||
|
||||
Location mutableLoc = new Location(world, 0, 0, 0);
|
||||
|
||||
if (!checkTime) {
|
||||
int index = 0;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int yy = (layer << 4) + y;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int zz = bz + z;
|
||||
for (int x = 0; x < 16; x++, index++) {
|
||||
int combined = newArray[index];
|
||||
if (combined == 0) continue;
|
||||
int xx = bx + x;
|
||||
|
||||
BlockType type = BlockTypes.getFromStateId(combined);
|
||||
if (type == BlockTypes.__RESERVED__) continue;
|
||||
if (type.getMaterial().isAir()) {
|
||||
if (!place) {
|
||||
mutableLoc.setX(xx);
|
||||
mutableLoc.setY(yy);
|
||||
mutableLoc.setZ(zz);
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (place) {
|
||||
if (type.getMaterial().hasContainer() && adapter != null) {
|
||||
CompoundTag nbt = getTile(x, yy, z);
|
||||
if (nbt != null) {
|
||||
synchronized (BukkitChunk_All.this) {
|
||||
BaseBlock state =
|
||||
BaseBlock.getFromInternalId(combined, nbt);
|
||||
adapter.setBlock(chunk, xx, yy, zz, state, update);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (type.getMaterial().isTicksRandomly()) {
|
||||
synchronized (BukkitChunk_All.this) {
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
} else {
|
||||
mutableLoc.setX(xx);
|
||||
mutableLoc.setY(yy);
|
||||
mutableLoc.setZ(zz);
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int yStart = layer << 4;
|
||||
for (; index < 4096; index++) {
|
||||
int j = place ? index : 4095 - index;
|
||||
int combined = newArray[j];
|
||||
if (combined == 0) continue;
|
||||
BlockType type = BlockTypes.getFromStateId(combined);
|
||||
if (type.getMaterial().isAir()) {
|
||||
if (!place) {
|
||||
int x = j & 15;
|
||||
int y = yStart + (j >> 8);
|
||||
int z = (j >> 4) & 15;
|
||||
mutableLoc.setX(bx + x);
|
||||
mutableLoc.setY(y);
|
||||
mutableLoc.setZ(bz + z);
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
boolean light = type.getMaterial().getLightValue() > 0;
|
||||
if (light) {
|
||||
if (place) {
|
||||
continue;
|
||||
}
|
||||
light = light && getParent().getSettings().LIGHTING.MODE != 0;
|
||||
if (light) {
|
||||
parent.enableLighting(disableResult);
|
||||
}
|
||||
} else if (!place) {
|
||||
continue;
|
||||
}
|
||||
int x = j & 15;
|
||||
int y = yStart + (j >> 8);
|
||||
int z = (j >> 4) & 15;
|
||||
if (type.getMaterial().hasContainer() && adapter != null) {
|
||||
CompoundTag tile = getTile(x, y, z);
|
||||
if (tile != null) {
|
||||
synchronized (BukkitChunk_All.this) {
|
||||
BaseBlock state = BaseBlock.getFromInternalId(combined, tile);
|
||||
adapter.setBlock(chunk, bx + x, y, bz + z, state, update);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (type.getMaterial().isTicksRandomly()) {
|
||||
synchronized (BukkitChunk_All.this) {
|
||||
mutableLoc.setX(bx + x);
|
||||
mutableLoc.setY(y);
|
||||
mutableLoc.setZ(bz + z);
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
} else {
|
||||
mutableLoc.setX(bx + x);
|
||||
mutableLoc.setY(y);
|
||||
mutableLoc.setZ(bz + z);
|
||||
setBlock(adapter, chunk, mutableLoc, combined, update);
|
||||
}
|
||||
if (light) {
|
||||
parent.disableLighting(disableResult);
|
||||
}
|
||||
}
|
||||
if (System.currentTimeMillis() - start > recommended) {
|
||||
index++;
|
||||
break mainloop;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} while (System.currentTimeMillis() - start < recommended);
|
||||
if (more || place) {
|
||||
this.addToQueue();
|
||||
}
|
||||
parent.resetLighting(disableResult);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setBlock(BukkitImplAdapter adapter, Chunk chunk, Location location, int combinedId, boolean update) {
|
||||
com.sk89q.worldedit.world.block.BaseBlock base = com.sk89q.worldedit.world.block.BlockState.getFromInternalId(combinedId).toBaseBlock();
|
||||
if (adapter != null) {
|
||||
adapter.setBlock(chunk, (int) location.getX(), (int) location.getY(), (int) location.getZ(), base, update);
|
||||
} else {
|
||||
Block block = location.getWorld().getBlockAt(location);
|
||||
block.setBlockData(BukkitAdapter.adapt(base), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v0;
|
||||
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BukkitChunk_All_ReadonlySnapshot extends FaweChunk {
|
||||
private final ChunkSnapshot snapshot;
|
||||
private final boolean hasBiomes;
|
||||
private final BukkitChunk_All next;
|
||||
private Set<CompoundTag> entities = new HashSet<>();
|
||||
private Map<Short, CompoundTag> tiles = new HashMap<>();
|
||||
|
||||
public BukkitChunk_All_ReadonlySnapshot(BukkitQueue_All parent, BukkitChunk_All next, ChunkSnapshot snapshot, boolean biomes) {
|
||||
super(parent, snapshot.getX(), snapshot.getZ());
|
||||
this.next = next;
|
||||
this.snapshot = snapshot;
|
||||
this.hasBiomes = biomes;
|
||||
}
|
||||
|
||||
public void setTiles(Map<Short, CompoundTag> tiles) {
|
||||
this.tiles = tiles;
|
||||
}
|
||||
|
||||
public void setEntities(Set<CompoundTag> entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitQueue_All getParent() {
|
||||
return (BukkitQueue_All) super.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBitMask() {
|
||||
return Character.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockCombinedId(int x, int y, int z) {
|
||||
BlockData blockData = snapshot.getBlockData(x, y, z);
|
||||
return BukkitAdapter.adapt(blockData).getInternalId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType[] getBiomeArray() {
|
||||
if (!hasBiomes || next.biomes == null) return null;
|
||||
BukkitImplAdapter adapter = getParent().getAdapter();
|
||||
BiomeType[] biomes = Arrays.copyOf(next.biomes, next.biomes.length);
|
||||
int index = 0;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++, index++) {
|
||||
if (biomes[index] != null) {
|
||||
Biome biome = snapshot.getBiome(x, z);
|
||||
biomes[index] = adapter.adapt(biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
return biomes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public int[] getIdArray(int layer) {
|
||||
int[] nextLayer = next.setBlocks[layer];
|
||||
if (nextLayer == null) return null;
|
||||
int[] ids = Arrays.copyOf(nextLayer, nextLayer.length);
|
||||
int index = 0;
|
||||
int by = layer << 4;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int yy = by + y;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++, index++) {
|
||||
if (ids[index] != 0) {
|
||||
ids[index] = getBlockCombinedId(x, yy, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChunk() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTile(int x, int y, int z, CompoundTag tile) {
|
||||
tiles.put(MathMan.tripleBlockCoord(x, y, z), tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntity(CompoundTag entity) {
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(UUID uuid) {
|
||||
throw new UnsupportedOperationException("Read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, int combinedId) {
|
||||
throw new UnsupportedOperationException("Read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getEntityRemoves() {
|
||||
throw new UnsupportedOperationException("Read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Short, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
if (tiles == null) return null;
|
||||
short pair = MathMan.tripleBlockCoord(x, y, z);
|
||||
return tiles.get(pair);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int z, BiomeType biome) {
|
||||
throw new UnsupportedOperationException("Read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk copy(boolean shallow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk call() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,394 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v0;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.bukkit.BukkitPlayer;
|
||||
import com.boydti.fawe.bukkit.FaweBukkit;
|
||||
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
|
||||
import com.boydti.fawe.example.NMSMappedFaweQueue;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import com.boydti.fawe.object.RunnableVal;
|
||||
import com.boydti.fawe.object.queue.LazyFaweChunk;
|
||||
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMappedFaweQueue<World, CHUNK, CHUNKSECTIONS, SECTION> implements Listener {
|
||||
|
||||
protected static boolean PAPER = true;
|
||||
private static BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
private static Method methodGetHandle;
|
||||
|
||||
static {
|
||||
Class<?> classCraftChunk = BukkitReflectionUtils.getCbClass("CraftChunk");
|
||||
try {
|
||||
methodGetHandle = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle"));
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public BukkitQueue_0(final com.sk89q.worldedit.world.World world) {
|
||||
super(world);
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, ((FaweBukkit) Fawe.imp()).getPlugin());
|
||||
}
|
||||
}
|
||||
|
||||
public BukkitQueue_0(String world) {
|
||||
super(world);
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, ((FaweBukkit) Fawe.imp()).getPlugin());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Capability capability) {
|
||||
switch (capability) {
|
||||
case CHUNK_PACKETS:
|
||||
Plugin plib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
|
||||
return plib != null && plib.isEnabled();
|
||||
}
|
||||
return super.supports(capability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(FaweChunk fc) {
|
||||
if (!Fawe.isMainThread()) {
|
||||
startSet(true);
|
||||
try {
|
||||
super.sendChunk(fc);
|
||||
} finally {
|
||||
endSet(true);
|
||||
}
|
||||
} else super.sendChunk(fc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunkUpdate(FaweChunk chunk, FawePlayer... players) {
|
||||
if (supports(Capability.CHUNK_PACKETS)) {
|
||||
sendChunkUpdatePLIB(chunk, players);
|
||||
} else {
|
||||
sendBlockUpdate(chunk, players);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) {
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
WirePacket packet = null;
|
||||
int viewDistance = Bukkit.getViewDistance();
|
||||
try {
|
||||
for (FawePlayer fawePlayer : players) {
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
|
||||
Player player = ((BukkitPlayer) fawePlayer).parent;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
if (Math.abs((loc.getBlockX() >> 4) - cx) <= viewDistance
|
||||
&& Math.abs((loc.getBlockZ() >> 4) - cz) <= viewDistance) {
|
||||
if (packet == null) {
|
||||
byte[] data;
|
||||
byte[] buffer = new byte[8192];
|
||||
if (chunk instanceof LazyFaweChunk) {
|
||||
chunk = (FaweChunk) chunk.getChunk();
|
||||
}
|
||||
// TODO FIXME
|
||||
// if (chunk instanceof MCAChunk) {
|
||||
// data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer);
|
||||
// } else {
|
||||
// data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer);
|
||||
// }
|
||||
// packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data);
|
||||
}
|
||||
manager.sendWirePacket(player, packet);
|
||||
}
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueChunkLoad(int cx, int cz, RunnableVal<CHUNK> operation) {
|
||||
if (PAPER) {
|
||||
try {
|
||||
new PaperChunkCallback(getImpWorld(), cx, cz) {
|
||||
@Override
|
||||
public void onLoad(Chunk bukkitChunk) {
|
||||
try {
|
||||
CHUNK chunk = (CHUNK) methodGetHandle.invoke(bukkitChunk);
|
||||
try {
|
||||
operation.run(chunk);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
PAPER = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
return true;
|
||||
} catch (Throwable ignore) {
|
||||
PAPER = false;
|
||||
}
|
||||
}
|
||||
return super.queueChunkLoad(cx, cz);
|
||||
}
|
||||
|
||||
public static BukkitImplAdapter getAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public static Tag toNative(Object tag) {
|
||||
BukkitImplAdapter adapter = getAdapter();
|
||||
return adapter.toNative(tag);
|
||||
}
|
||||
|
||||
public static Object fromNative(Tag tag) {
|
||||
BukkitImplAdapter adapter = getAdapter();
|
||||
return adapter.fromNative(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSaveFolder() {
|
||||
return new File(Bukkit.getWorldContainer(), getWorldName() + File.separator + "region");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullbright(CHUNKSECTIONS sections) {}
|
||||
|
||||
@Override
|
||||
public void relight(int x, int y, int z) {}
|
||||
|
||||
@Override
|
||||
public void relightBlock(int x, int y, int z) {}
|
||||
|
||||
@Override
|
||||
public void relightSky(int x, int y, int z) {}
|
||||
|
||||
@Override
|
||||
public boolean removeSectionLighting(SECTION sections, int layer, boolean hasSky) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void checkVersion(String supported) {
|
||||
String version = Bukkit.getServer().getClass().getPackage().getName();
|
||||
if (!version.contains(supported)) {
|
||||
throw new IllegalStateException("Unsupported version: " + version + " (supports: " + supported + ")");
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean registered = false;
|
||||
protected static boolean disableChunkLoad = false;
|
||||
|
||||
@EventHandler
|
||||
public static void onWorldLoad(WorldInitEvent event) {
|
||||
if (disableChunkLoad) {
|
||||
World world = event.getWorld();
|
||||
world.setKeepSpawnInMemory(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConcurrentHashMap<Long, Long> keepLoaded = new ConcurrentHashMap<>(8, 0.9f, 1);
|
||||
|
||||
|
||||
@EventHandler
|
||||
public static void onChunkLoad(ChunkLoadEvent event) {
|
||||
Chunk chunk = event.getChunk();
|
||||
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
|
||||
keepLoaded.putIfAbsent(pair, Fawe.get().getTimer().getTickStart());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public static void onChunkUnload(ChunkUnloadEvent event) {
|
||||
Chunk chunk = event.getChunk();
|
||||
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
|
||||
Long lastLoad = keepLoaded.get(pair);
|
||||
if (lastLoad != null) {
|
||||
if (Fawe.get().getTimer().getTickStart() - lastLoad < 10000) {
|
||||
event.setCancelled(true);
|
||||
} else {
|
||||
keepLoaded.remove(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueChunkLoad(int cx, int cz) {
|
||||
if (super.queueChunkLoad(cx, cz)) {
|
||||
keepLoaded.put(MathMan.pairInt(cx, cz), System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public World createWorld(final WorldCreator creator) {
|
||||
return TaskManager.IMP.sync(new RunnableVal<World>() {
|
||||
@Override
|
||||
public void run(World value) {
|
||||
disableChunkLoad = true;
|
||||
this.value = creator.createWorld();
|
||||
disableChunkLoad = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getImpWorld() {
|
||||
return getWorldName() != null ? Bukkit.getWorld(getWorldName()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(int x, int z, int bitMask) {}
|
||||
|
||||
@Override
|
||||
public void refreshChunk(FaweChunk fs) {
|
||||
World world = getWorld();
|
||||
if(world != null) {
|
||||
world.refreshChunk(fs.getX(), fs.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerateChunk(World world, int x, int z, BiomeType biome, Long seed) {
|
||||
if (!keepLoaded.isEmpty()) keepLoaded.remove(MathMan.pairInt(x, z));
|
||||
return world.regenerateChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSky() {
|
||||
World world = getWorld();
|
||||
return world == null || world.getEnvironment() == World.Environment.NORMAL;
|
||||
}
|
||||
|
||||
private volatile boolean timingsEnabled;
|
||||
private static boolean alertTimingsChange = true;
|
||||
|
||||
private static Field fieldTimingsEnabled;
|
||||
private static Field fieldAsyncCatcherEnabled;
|
||||
private static Method methodCheck;
|
||||
static {
|
||||
try {
|
||||
fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled");
|
||||
fieldAsyncCatcherEnabled.setAccessible(true);
|
||||
} catch (Throwable ignore) {}
|
||||
try {
|
||||
fieldTimingsEnabled = Class.forName("co.aikar.timings.Timings").getDeclaredField("timingsEnabled");
|
||||
fieldTimingsEnabled.setAccessible(true);
|
||||
methodCheck = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled");
|
||||
methodCheck.setAccessible(true);
|
||||
} catch (Throwable ignore){}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSet(boolean parallel) {
|
||||
ChunkListener.physicsFreeze = true;
|
||||
if (parallel) {
|
||||
try {
|
||||
if (fieldAsyncCatcherEnabled != null) {
|
||||
fieldAsyncCatcherEnabled.set(null, false);
|
||||
}
|
||||
if (fieldTimingsEnabled != null) {
|
||||
timingsEnabled = (boolean) fieldTimingsEnabled.get(null);
|
||||
if (timingsEnabled) {
|
||||
if (alertTimingsChange) {
|
||||
alertTimingsChange = false;
|
||||
Fawe.debug("Having `parallel-threads` > 1 interferes with the timings.");
|
||||
}
|
||||
fieldTimingsEnabled.set(null, false);
|
||||
methodCheck.invoke(null);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endSet(boolean parallel) {
|
||||
ChunkListener.physicsFreeze = false;
|
||||
if (parallel) {
|
||||
try {
|
||||
if (fieldAsyncCatcherEnabled != null) {
|
||||
fieldAsyncCatcherEnabled.set(null, true);
|
||||
}
|
||||
if (fieldTimingsEnabled != null && timingsEnabled) {
|
||||
fieldTimingsEnabled.set(null, true);
|
||||
methodCheck.invoke(null);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBlockUpdate(final FaweChunk chunk, FawePlayer... players) {
|
||||
if (players.length == 0) {
|
||||
return;
|
||||
}
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
int view = Bukkit.getServer().getViewDistance();
|
||||
boolean sendAny = false;
|
||||
boolean[] send = new boolean[players.length];
|
||||
for (int i = 0; i < players.length; i++) {
|
||||
FawePlayer player = players[i];
|
||||
Player bp = ((BukkitPlayer) player).parent;
|
||||
Location loc = bp.getLocation();
|
||||
if (Math.abs((loc.getBlockX() >> 4) - cx) <= view && Math.abs((loc.getBlockZ() >> 4) - cz) <= view) {
|
||||
sendAny = true;
|
||||
send[i] = true;
|
||||
}
|
||||
}
|
||||
if (!sendAny) {
|
||||
return;
|
||||
}
|
||||
final World world = getWorld();
|
||||
final int bx = cx << 4;
|
||||
final int bz = cz << 4;
|
||||
chunk.forEachQueuedBlock(new FaweChunkVisitor() {
|
||||
@Override
|
||||
public void run(int localX, int y, int localZ, int combined) {
|
||||
Location loc = new Location(world, bx + localX, y, bz + localZ);
|
||||
for (int i = 0; i < players.length; i++) {
|
||||
if (send[i]) {
|
||||
((BukkitPlayer) players[i]).parent.sendBlockChange(loc, BukkitAdapter.adapt(BlockState.getFromInternalId(combined)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v0;
|
||||
|
||||
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.NullRelighter;
|
||||
import com.boydti.fawe.example.Relighter;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.RegionWrapper;
|
||||
import com.boydti.fawe.object.RunnableVal;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.SetQueue;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class BukkitQueue_All extends BukkitQueue_0<ChunkSnapshot, ChunkSnapshot, ChunkSnapshot> {
|
||||
|
||||
private ConcurrentMap<Long, ChunkSnapshot> chunkCache = new MapMaker()
|
||||
.weakValues()
|
||||
.makeMap();
|
||||
|
||||
public BukkitQueue_All(com.sk89q.worldedit.world.World world) {
|
||||
super(world);
|
||||
Settings.IMP.QUEUE.PARALLEL_THREADS = 1;
|
||||
}
|
||||
|
||||
public BukkitQueue_All(String world) {
|
||||
super(world);
|
||||
Settings.IMP.QUEUE.PARALLEL_THREADS = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueChunkLoad(int cx, int cz, RunnableVal<ChunkSnapshot> operation) {
|
||||
if (PAPER) {
|
||||
try {
|
||||
new PaperChunkCallback(getImpWorld(), cx, cz) {
|
||||
@Override
|
||||
public void onLoad(Chunk chunk) {
|
||||
try {
|
||||
ChunkSnapshot snapshot = tryGetSnasphot(chunk);
|
||||
operation.run(snapshot);
|
||||
} catch (Throwable e) {
|
||||
PAPER = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
return true;
|
||||
} catch (Throwable ignore) {
|
||||
PAPER = false;
|
||||
}
|
||||
}
|
||||
return super.queueChunkLoad(cx, cz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relighter getRelighter() {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
|
||||
private static Class<?> classRegionFileCache;
|
||||
private static Method methodGetHandleChunk;
|
||||
private static Method methodGetHandleWorld;
|
||||
private static Method methodFlush;
|
||||
private static Method methodNeedsSaving;
|
||||
private static Field fieldChunkProvider;
|
||||
private static Field fieldChunkLoader;
|
||||
private static Field fieldRegionMap;
|
||||
private static Field fieldRegionRAF;
|
||||
|
||||
static {
|
||||
try {
|
||||
BukkitReflectionUtils.init();
|
||||
classRegionFileCache = BukkitReflectionUtils.getNmsClass("RegionFileCache");
|
||||
Class<?> classRegionFile = BukkitReflectionUtils.getNmsClass("RegionFile");
|
||||
Class<?> classCraftChunk = BukkitReflectionUtils.getCbClass("CraftChunk");
|
||||
Class<?> classNMSChunk = BukkitReflectionUtils.getNmsClass("Chunk");
|
||||
Class<?> classCraftWorld = BukkitReflectionUtils.getCbClass("CraftWorld");
|
||||
Class<?> classNMSWorld = BukkitReflectionUtils.getNmsClass("World");
|
||||
Class<?> classChunkProviderServer = BukkitReflectionUtils.getNmsClass("ChunkProviderServer");
|
||||
Class<?> classIChunkProvider = BukkitReflectionUtils.getNmsClass("IChunkProvider");
|
||||
Class<?> classIChunkLoader = BukkitReflectionUtils.getNmsClass("IChunkLoader");
|
||||
Class<?> classChunkRegionLoader = BukkitReflectionUtils.getNmsClass("ChunkRegionLoader");
|
||||
|
||||
methodGetHandleChunk = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle"));
|
||||
methodGetHandleWorld = ReflectionUtils.setAccessible(classCraftWorld.getDeclaredMethod("getHandle"));
|
||||
methodFlush = ReflectionUtils.findMethod(classChunkRegionLoader, boolean.class);
|
||||
methodNeedsSaving = ReflectionUtils.findMethod(classNMSChunk, boolean.class, boolean.class);
|
||||
|
||||
fieldChunkProvider = ReflectionUtils.findField(classNMSWorld, classIChunkProvider);
|
||||
fieldChunkLoader = ReflectionUtils.findField(classChunkProviderServer, classIChunkLoader);
|
||||
|
||||
fieldRegionMap = ReflectionUtils.findField(classRegionFileCache, Map.class);
|
||||
fieldRegionRAF = ReflectionUtils.findField(classRegionFile, RandomAccessFile.class);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper allowed, Runnable whileLocked, boolean saveChunks, boolean load) {
|
||||
if (classRegionFileCache == null) {
|
||||
return super.setMCA(mcaX, mcaZ, allowed, whileLocked, saveChunks, load);
|
||||
}
|
||||
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean value) {
|
||||
long start = System.currentTimeMillis();
|
||||
long last = start;
|
||||
synchronized (classRegionFileCache) {
|
||||
try {
|
||||
World world = getWorld();
|
||||
boolean autoSave = world.isAutoSave();
|
||||
|
||||
if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
|
||||
|
||||
ArrayDeque<Chunk> unloaded = null;
|
||||
if (load) {
|
||||
int bcx = mcaX << 5;
|
||||
int bcz = mcaZ << 5;
|
||||
int tcx = bcx + 31;
|
||||
int tcz = bcz + 31;
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
|
||||
Object nmsChunk = methodGetHandleChunk.invoke(chunk);
|
||||
boolean mustSave = saveChunks && (boolean) methodNeedsSaving.invoke(nmsChunk, false);
|
||||
chunk.unload(mustSave, false);
|
||||
if (unloaded == null) unloaded = new ArrayDeque<>();
|
||||
unloaded.add(chunk);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
world.save();
|
||||
}
|
||||
|
||||
Object nmsWorld = methodGetHandleWorld.invoke(world);
|
||||
Object chunkProviderServer = fieldChunkProvider.get(nmsWorld);
|
||||
Object chunkRegionLoader = fieldChunkLoader.get(chunkProviderServer);
|
||||
while ((boolean) methodFlush.invoke(chunkRegionLoader));
|
||||
|
||||
if (unloaded != null) {
|
||||
Map regionMap = (Map) fieldRegionMap.get(null);
|
||||
File file = new File(world.getWorldFolder(), "region" + File.separator + "r." + mcaX + "." + mcaZ + ".mca");
|
||||
Object regionFile = regionMap.remove(file);
|
||||
if (regionFile != null) {
|
||||
RandomAccessFile raf = (RandomAccessFile) fieldRegionRAF.get(regionFile);
|
||||
raf.close();
|
||||
}
|
||||
}
|
||||
|
||||
whileLocked.run();
|
||||
|
||||
if (load && unloaded != null) {
|
||||
final ArrayDeque<Chunk> finalUnloaded = unloaded;
|
||||
TaskManager.IMP.async(() -> {
|
||||
for (Chunk chunk : finalUnloaded) {
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
if (world.isChunkLoaded(cx, cz)) continue;
|
||||
SetQueue.IMP.addTask(() -> {
|
||||
world.loadChunk(chunk.getX(), chunk.getZ(), false);
|
||||
world.refreshChunk(chunk.getX(), chunk.getZ());
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
// load chunks
|
||||
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLight(ChunkSnapshot chunk, int x, int y, int z, int value) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockLight(ChunkSnapshot chunk, int x, int y, int z, int value) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCombinedId4Data(ChunkSnapshot chunk, int x, int y, int z) {
|
||||
if (chunk.isSectionEmpty(y >> 4)) {
|
||||
return BlockTypes.AIR.getInternalId();
|
||||
}
|
||||
BlockData blockData = chunk.getBlockData(x & 15, y, z & 15);
|
||||
return BukkitAdapter.adapt(blockData).getInternalId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(ChunkSnapshot chunkSnapshot, int x, int z) {
|
||||
Biome biome = chunkSnapshot.getBiome(x & 15, z & 15);
|
||||
return getAdapter().adapt(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSnapshot getSections(ChunkSnapshot chunkSnapshot) {
|
||||
return chunkSnapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSnapshot getCachedChunk(World world, int cx, int cz) {
|
||||
long pair = MathMan.pairInt(cx, cz);
|
||||
ChunkSnapshot cached = chunkCache.get(pair);
|
||||
if (cached != null) return cached;
|
||||
if (world.isChunkLoaded(cx, cz)) {
|
||||
Long originalKeep = keepLoaded.get(pair);
|
||||
keepLoaded.put(pair, Long.MAX_VALUE);
|
||||
if (world.isChunkLoaded(cx, cz)) {
|
||||
Chunk chunk = world.getChunkAt(cx, cz);
|
||||
ChunkSnapshot snapshot = getAndCacheChunk(chunk);
|
||||
if (originalKeep != null) {
|
||||
keepLoaded.put(pair, originalKeep);
|
||||
} else {
|
||||
keepLoaded.remove(pair);
|
||||
}
|
||||
return snapshot;
|
||||
} else {
|
||||
keepLoaded.remove(pair);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmmittedLight(final ChunkSnapshot chunk, int x, int y, int z) {
|
||||
return chunk.getBlockEmittedLight(x & 15, y, z & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(final ChunkSnapshot chunk, int x, int y, int z) {
|
||||
return chunk.getBlockSkyLight(x & 15, y, z & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight(final ChunkSnapshot chunk, int x, int y, int z) {
|
||||
x = x & 15;
|
||||
z = z & 15;
|
||||
return Math.max(chunk.getBlockEmittedLight(x, y, z), chunk.getBlockSkyLight(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSnapshot loadChunk(World world, int x, int z, boolean generate) {
|
||||
Chunk chunk = world.getChunkAt(x, z);
|
||||
chunk.load(generate);
|
||||
return chunk.isLoaded() ? getAndCacheChunk(chunk) : null;
|
||||
}
|
||||
|
||||
private ChunkSnapshot tryGetSnasphot(Chunk chunk) {
|
||||
try {
|
||||
return chunk.getChunkSnapshot(false, true, false);
|
||||
} catch (Throwable throwable) {
|
||||
Throwable cause = throwable;
|
||||
while (cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
if (cause instanceof IllegalStateException) {
|
||||
return null;
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
}
|
||||
|
||||
private ChunkSnapshot getAndCacheChunk(Chunk chunk) {
|
||||
ChunkSnapshot snapshot = tryGetSnasphot(chunk);
|
||||
if (snapshot == null) {
|
||||
snapshot = tryGetSnasphot(chunk);
|
||||
if (snapshot == null) {
|
||||
snapshot = TaskManager.IMP.sync(() -> tryGetSnasphot(chunk));
|
||||
if (snapshot == null) {
|
||||
snapshot = chunk.getChunkSnapshot(false, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
chunkCache.put(MathMan.pairInt(chunk.getX(), chunk.getZ()), snapshot);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSnapshot getCachedSections(World impWorld, int cx, int cz) {
|
||||
return getCachedChunk(impWorld, cx, cz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTileEntity(ChunkSnapshot chunk, int x, int y, int z) {
|
||||
if (getAdapter() == null) {
|
||||
return null;
|
||||
}
|
||||
Location loc = new Location(getWorld(), x, y, z);
|
||||
BaseBlock block = getAdapter().getBlock(loc);
|
||||
return block.getNbtData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk getFaweChunk(int x, int z) {
|
||||
return new BukkitChunk_All(this, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Capability capability) {
|
||||
switch (capability) {
|
||||
case CHANGE_TASKS: return getAdapter() != null;
|
||||
}
|
||||
return super.supports(capability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSet(boolean parallel) {
|
||||
super.startSet(true);
|
||||
}
|
||||
|
||||
private Field fieldNeighbors;
|
||||
private Method chunkGetHandle;
|
||||
|
||||
/**
|
||||
* Exploiting a bug in the vanilla lighting algorithm for faster block placement
|
||||
* - Could have been achieved without reflection by force unloading specific chunks
|
||||
* - Much faster just setting the variable manually though
|
||||
* @param chunk
|
||||
* @return
|
||||
*/
|
||||
protected Object[] disableLighting(Chunk chunk) {
|
||||
try {
|
||||
if (chunkGetHandle == null) {
|
||||
chunkGetHandle = chunk.getClass().getDeclaredMethod("getHandle");
|
||||
chunkGetHandle.setAccessible(true);
|
||||
}
|
||||
Object nmsChunk = chunkGetHandle.invoke(chunk);
|
||||
if (fieldNeighbors == null) {
|
||||
fieldNeighbors = nmsChunk.getClass().getDeclaredField("neighbors");
|
||||
fieldNeighbors.setAccessible(true);
|
||||
}
|
||||
Object value = fieldNeighbors.get(nmsChunk);
|
||||
fieldNeighbors.set(nmsChunk, 0);
|
||||
return new Object[] {nmsChunk, value};
|
||||
} catch (Throwable ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void disableLighting(Object[] disableResult) {
|
||||
if (disableResult != null) {
|
||||
try {
|
||||
fieldNeighbors.set(disableResult[0], 0);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetLighting(Object[] disableResult) {
|
||||
if (disableResult != null) {
|
||||
try {
|
||||
fieldNeighbors.set(disableResult[0], disableResult[1]);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void enableLighting(Object[] disableResult) {
|
||||
if (disableResult != null) {
|
||||
try {
|
||||
fieldNeighbors.set(disableResult[0], 0x739C0);
|
||||
} catch (Throwable ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endSet(boolean parallel) {
|
||||
super.endSet(true);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v0;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
|
||||
public abstract class PaperChunkCallback {
|
||||
public PaperChunkCallback(World world, int x, int z) {
|
||||
world.getChunkAtAsync(x, z, PaperChunkCallback.this::onLoad);
|
||||
}
|
||||
|
||||
public abstract void onLoad(Chunk chunk);
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v1_13;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.bukkit.adapter.v1_13_1.BlockMaterial_1_13;
|
||||
import com.boydti.fawe.bukkit.adapter.v1_13_1.Spigot_v1_13_R2;
|
||||
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.IntFaweChunk;
|
||||
import com.boydti.fawe.jnbt.anvil.BitArray4096;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.LongTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import net.minecraft.server.v1_13_R2.BiomeBase;
|
||||
import net.minecraft.server.v1_13_R2.Block;
|
||||
import net.minecraft.server.v1_13_R2.BlockPosition;
|
||||
import net.minecraft.server.v1_13_R2.Blocks;
|
||||
import net.minecraft.server.v1_13_R2.ChunkSection;
|
||||
import net.minecraft.server.v1_13_R2.DataBits;
|
||||
import net.minecraft.server.v1_13_R2.DataPalette;
|
||||
import net.minecraft.server.v1_13_R2.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_13_R2.DataPaletteHash;
|
||||
import net.minecraft.server.v1_13_R2.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_13_R2.Entity;
|
||||
import net.minecraft.server.v1_13_R2.EntityPlayer;
|
||||
import net.minecraft.server.v1_13_R2.EntityTypes;
|
||||
import net.minecraft.server.v1_13_R2.GameProfileSerializer;
|
||||
import net.minecraft.server.v1_13_R2.IBlockData;
|
||||
import net.minecraft.server.v1_13_R2.MinecraftKey;
|
||||
import net.minecraft.server.v1_13_R2.NBTTagCompound;
|
||||
import net.minecraft.server.v1_13_R2.NBTTagInt;
|
||||
import net.minecraft.server.v1_13_R2.NibbleArray;
|
||||
import net.minecraft.server.v1_13_R2.RegistryID;
|
||||
import net.minecraft.server.v1_13_R2.TileEntity;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlock;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.boydti.fawe.bukkit.v0.BukkitQueue_0.getAdapter;
|
||||
import static com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13.fieldRegistryb;
|
||||
import static com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13.fieldRegistryc;
|
||||
import static com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13.fieldRegistryd;
|
||||
import static com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13.fieldRegistrye;
|
||||
import static com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13.fieldRegistryf;
|
||||
|
||||
public class BukkitChunk_1_13 extends IntFaweChunk<Chunk, BukkitQueue_1_13> {
|
||||
|
||||
public ChunkSection[] sectionPalettes;
|
||||
|
||||
private static final IBlockData AIR = ((BlockMaterial_1_13) BlockTypes.AIR.getMaterial()).getState();
|
||||
|
||||
/**
|
||||
* A FaweSections object represents a chunk and the blocks that you wish to change in it.
|
||||
*
|
||||
* @param parent
|
||||
* @param x
|
||||
* @param z
|
||||
*/
|
||||
public BukkitChunk_1_13(FaweQueue parent, int x, int z) {
|
||||
super(parent, x, z);
|
||||
}
|
||||
|
||||
public BukkitChunk_1_13(FaweQueue parent, int x, int z, int[][] ids, short[] count, short[] air) {
|
||||
super(parent, x, z, ids, count, air);
|
||||
}
|
||||
|
||||
public void storeBiomes(BiomeBase[] biomes) {
|
||||
if (biomes != null) {
|
||||
if (this.biomes == null) {
|
||||
this.biomes = new BiomeType[256];
|
||||
}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
this.biomes[i] = BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(biomes[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][] getCombinedIdArrays() {
|
||||
if (this.sectionPalettes != null) {
|
||||
for (int i = 0; i < setBlocks.length; i++) {
|
||||
getIdArray(i);
|
||||
}
|
||||
}
|
||||
return this.setBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIdArray(int layer) {
|
||||
if (this.setBlocks[layer] == null && this.sectionPalettes != null) {
|
||||
ChunkSection section = this.sectionPalettes[layer];
|
||||
int[] idsArray = this.setBlocks[layer];
|
||||
if (section != null && idsArray == null) {
|
||||
this.setBlocks[layer] = idsArray = new int[4096];
|
||||
if (!section.a()) {
|
||||
try {
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
DataBits bits = (DataBits) BukkitQueue_1_13.fieldBits.get(blocks);
|
||||
DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitQueue_1_13.fieldPalette.get(blocks);
|
||||
|
||||
long[] raw = bits.a();
|
||||
int bitsPerEntry = bits.c();
|
||||
|
||||
new BitArray4096(raw, bitsPerEntry).toRaw(idsArray);
|
||||
IBlockData defaultBlock = (IBlockData) BukkitQueue_1_13.fieldDefaultBlock.get(blocks);
|
||||
// TODO optimize away palette.a
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
IBlockData ibd = palette.a(idsArray[i]);
|
||||
if (ibd == null) {
|
||||
ibd = defaultBlock;
|
||||
}
|
||||
int ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToInt(ibd);
|
||||
idsArray[i] = BlockTypes.states[ordinal].getInternalId();
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.setBlocks[layer];
|
||||
}
|
||||
|
||||
public boolean storeTile(TileEntity tile, BlockPosition pos) {
|
||||
CompoundTag nativeTag = getParent().getTag(tile);
|
||||
setTile(pos.getX() & 15, pos.getY(), pos.getZ() & 15, nativeTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean storeEntity(Entity ent) throws InvocationTargetException, IllegalAccessException {
|
||||
if (ent instanceof EntityPlayer || BukkitQueue_0.getAdapter() == null) {
|
||||
return false;
|
||||
}
|
||||
EntityTypes<?> type = ent.P();
|
||||
MinecraftKey id = EntityTypes.getName(type);
|
||||
if (id != null) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
ent.save(tag); // readEntityIntoTag
|
||||
CompoundTag nativeTag = (CompoundTag) BukkitQueue_0.toNative(tag);
|
||||
Map<String, Tag> map = ReflectionUtils.getMap(nativeTag.getValue());
|
||||
map.put("Id", new StringTag(id.toString()));
|
||||
setEntity(nativeTag);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean storeSection(ChunkSection section, int layer) throws IllegalAccessException {
|
||||
if (sectionPalettes == null) {
|
||||
// TODO optimize don't copy light
|
||||
sectionPalettes = new ChunkSection[16];
|
||||
}
|
||||
sectionPalettes[layer] = section;
|
||||
return true;
|
||||
}
|
||||
|
||||
public ChunkSection copy(ChunkSection current) throws IllegalAccessException, InvocationTargetException, NoSuchFieldException {
|
||||
ChunkSection newSection = new ChunkSection(current.getYPosition(), current.getSkyLightArray() != null);
|
||||
|
||||
// Copy light
|
||||
NibbleArray skyLight = current.getSkyLightArray();
|
||||
NibbleArray blockLight = current.getEmittedLightArray();
|
||||
|
||||
NibbleArray newBlockLight = newSection.getEmittedLightArray();
|
||||
NibbleArray newSkyLight = newSection.getSkyLightArray();
|
||||
|
||||
byte[] newBlockBytes = newBlockLight.asBytes();
|
||||
byte[] blockLightBytes = blockLight.asBytes();
|
||||
for (int i = 0; i < 2048; i++) newBlockBytes[i] = blockLightBytes[i];
|
||||
if (skyLight != null) {
|
||||
byte[] newSkyBytes = newSkyLight.asBytes();
|
||||
byte[] skyLightBytes = skyLight.asBytes();
|
||||
for (int i = 0; i < 2048; i++) newSkyBytes[i] = skyLightBytes[i];
|
||||
}
|
||||
|
||||
// Copy counters
|
||||
Object nonEmptyBlockCount = BukkitQueue_1_13.fieldNonEmptyBlockCount.get(current);
|
||||
BukkitQueue_1_13.fieldNonEmptyBlockCount.set(newSection, nonEmptyBlockCount);
|
||||
|
||||
Object tickingBlockCount = BukkitQueue_1_13.fieldTickingBlockCount.get(current);
|
||||
BukkitQueue_1_13.fieldTickingBlockCount.set(newSection, tickingBlockCount);
|
||||
|
||||
Object liquidCount = BukkitQueue_1_13.fieldLiquidCount.get(current);
|
||||
BukkitQueue_1_13.fieldLiquidCount.set(newSection, liquidCount);
|
||||
|
||||
// Copy blocks
|
||||
DataPaletteBlock<IBlockData> blocks = current.getBlocks();
|
||||
DataPaletteBlock<IBlockData> blocksCopy = copy(blocks);
|
||||
BukkitQueue_1_13.fieldSection.set(newSection, blocksCopy);
|
||||
|
||||
return newSection;
|
||||
}
|
||||
|
||||
public DataPaletteBlock<IBlockData> copy(DataPaletteBlock current) throws IllegalAccessException, InvocationTargetException, NoSuchFieldException {
|
||||
// Clone palette
|
||||
DataPalette currentPalette = (DataPalette) BukkitQueue_1_13.fieldPalette.get(current);
|
||||
DataPaletteBlock<IBlockData> paletteBlock = newDataPaletteBlock();
|
||||
int size = BukkitQueue_1_13.fieldSize.getInt(current);
|
||||
|
||||
DataPalette<IBlockData> newPalette = currentPalette;
|
||||
if (currentPalette instanceof DataPaletteHash) {
|
||||
// TODO optimize resize
|
||||
newPalette = new DataPaletteHash<>(Block.REGISTRY_ID, size, paletteBlock, GameProfileSerializer::d, GameProfileSerializer::a);
|
||||
RegistryID<IBlockData> currReg = (RegistryID<IBlockData>) BukkitQueue_1_13.fieldHashBlocks.get(currentPalette);
|
||||
RegistryID<IBlockData> newReg = (RegistryID<IBlockData>) BukkitQueue_1_13.fieldHashBlocks.get(newPalette);
|
||||
int arrLen = 1 << size;
|
||||
System.arraycopy(fieldRegistryb.get(currReg), 0, fieldRegistryb.get(newReg), 0, arrLen);
|
||||
System.arraycopy(fieldRegistryc.get(currReg), 0, fieldRegistryc.get(newReg), 0, arrLen);
|
||||
System.arraycopy(fieldRegistryd.get(currReg), 0, fieldRegistryd.get(newReg), 0, arrLen);
|
||||
fieldRegistrye.set(newReg, fieldRegistrye.get(currReg));
|
||||
fieldRegistryf.set(newReg, fieldRegistryf.get(currReg));
|
||||
} else if (currentPalette instanceof DataPaletteLinear) {
|
||||
// TODO optimize resize
|
||||
newPalette = new DataPaletteLinear<>(Block.REGISTRY_ID, size, paletteBlock, GameProfileSerializer::d);
|
||||
Object[] currArray = ((Object[]) BukkitQueue_1_13.fieldLinearBlocks.get(currentPalette));
|
||||
Object[] newArray = ((Object[]) BukkitQueue_1_13.fieldLinearBlocks.get(newPalette));
|
||||
BukkitQueue_1_13.fieldLinearIndex.set(newPalette, BukkitQueue_1_13.fieldLinearIndex.get(currentPalette));
|
||||
for (int i = 0; i < newArray.length; i++) newArray[i] = currArray[i];
|
||||
}
|
||||
|
||||
BukkitQueue_1_13.fieldPalette.set(paletteBlock, newPalette);
|
||||
// Clone size
|
||||
BukkitQueue_1_13.fieldSize.set(paletteBlock, size);
|
||||
// Clone palette
|
||||
DataBits currentBits = (DataBits) BukkitQueue_1_13.fieldBits.get(current);
|
||||
DataBits newBits = new DataBits(currentBits.c(), currentBits.b(), currentBits.a().clone());
|
||||
BukkitQueue_1_13.fieldBits.set(paletteBlock, newBits);
|
||||
|
||||
// TODO copy only if different
|
||||
Object defaultBlock = BukkitQueue_1_13.fieldDefaultBlock.get(current);
|
||||
if (defaultBlock != AIR) {
|
||||
ReflectionUtils.setFailsafeFieldValue(BukkitQueue_1_13.fieldDefaultBlock, paletteBlock, BukkitQueue_1_13.fieldDefaultBlock.get(current));
|
||||
}
|
||||
|
||||
return paletteBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntFaweChunk<Chunk, BukkitQueue_1_13> copy(boolean shallow) {
|
||||
BukkitChunk_1_13 copy;
|
||||
if (shallow) {
|
||||
copy = new BukkitChunk_1_13(getParent(), getX(), getZ(), setBlocks, count, air);
|
||||
copy.biomes = biomes;
|
||||
copy.chunk = chunk;
|
||||
} else {
|
||||
copy = new BukkitChunk_1_13(getParent(), getX(), getZ(), (int[][]) MainUtil.copyNd(setBlocks), count.clone(), air.clone());
|
||||
copy.biomes = biomes != null ? biomes.clone() : null;
|
||||
copy.chunk = chunk;
|
||||
}
|
||||
if (sectionPalettes != null) {
|
||||
copy.sectionPalettes = new ChunkSection[16];
|
||||
try {
|
||||
for (int i = 0; i < sectionPalettes.length; i++) {
|
||||
ChunkSection current = sectionPalettes[i];
|
||||
if (current == null) {
|
||||
continue;
|
||||
}
|
||||
copy.sectionPalettes[i] = copy(current);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
private DataPaletteBlock<IBlockData> newDataPaletteBlock() {
|
||||
return new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getNewChunk() {
|
||||
return getParent().getWorld().getChunkAt(getX(), getZ());
|
||||
}
|
||||
|
||||
public void optimize() {
|
||||
if (sectionPalettes != null) {
|
||||
return;
|
||||
}
|
||||
int[][] arrays = getCombinedIdArrays();
|
||||
for (int layer = 0; layer < 16; layer++) {
|
||||
if (getCount(layer) > 0) {
|
||||
if (sectionPalettes == null) {
|
||||
sectionPalettes = new ChunkSection[16];
|
||||
}
|
||||
int[] array = arrays[layer];
|
||||
sectionPalettes[layer] = BukkitQueue_1_13.newChunkSection(layer, getParent().hasSky(), array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
getChunk().load(true);
|
||||
}
|
||||
|
||||
private void removeEntity(Entity entity) {
|
||||
entity.b(false);
|
||||
entity.die();
|
||||
entity.valid = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk call() {
|
||||
Spigot_v1_13_R2 adapter = (Spigot_v1_13_R2) BukkitQueue_0.getAdapter();
|
||||
try {
|
||||
BukkitChunk_1_13 copy = getParent().getChangeTask() != null ? new BukkitChunk_1_13(getParent(), getX(), getZ()) : null;
|
||||
final Chunk chunk = this.getChunk();
|
||||
final World world = chunk.getWorld();
|
||||
Settings settings = getParent().getSettings();
|
||||
int bx = this.getX() << 4;
|
||||
int bz = this.getZ() << 4;
|
||||
final boolean flag = world.getEnvironment() == World.Environment.NORMAL;
|
||||
net.minecraft.server.v1_13_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
|
||||
nmsChunk.f(true); // Set Modified
|
||||
nmsChunk.mustSave = true;
|
||||
nmsChunk.markDirty();
|
||||
net.minecraft.server.v1_13_R2.World nmsWorld = nmsChunk.world;
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
List<Entity>[] entities = nmsChunk.getEntitySlices();
|
||||
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
|
||||
// Remove entities
|
||||
HashSet<UUID> entsToRemove = this.getEntityRemoves();
|
||||
if (!entsToRemove.isEmpty()) {
|
||||
for (Collection<Entity> ents : entities) {
|
||||
if (!ents.isEmpty()) {
|
||||
Iterator<Entity> iter = ents.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entity entity = iter.next();
|
||||
if (entsToRemove.contains(entity.getUniqueID())) {
|
||||
if (copy != null) {
|
||||
copy.storeEntity(entity);
|
||||
}
|
||||
iter.remove();
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
removeEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
int count = this.getCount(i);
|
||||
if (count == 0 || settings.EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) {
|
||||
continue;
|
||||
} else if (count >= 4096) {
|
||||
Collection<Entity> ents = entities[i];
|
||||
if (!ents.isEmpty()) {
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
Iterator<Entity> iter = ents.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entity entity = iter.next();
|
||||
if (entity instanceof EntityPlayer) {
|
||||
continue;
|
||||
}
|
||||
iter.remove();
|
||||
if (copy != null) {
|
||||
copy.storeEntity(entity);
|
||||
}
|
||||
removeEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Collection<Entity> ents = entities[i];
|
||||
if (!ents.isEmpty()) {
|
||||
int layerYStart = i << 4;
|
||||
int layerYEnd = layerYStart + 15;
|
||||
int[] array = this.getIdArray(i);
|
||||
if (array == null) continue;
|
||||
Iterator<Entity> iter = ents.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entity entity = iter.next();
|
||||
if (entity instanceof EntityPlayer) {
|
||||
continue;
|
||||
}
|
||||
int y = MathMan.roundInt(entity.locY);
|
||||
if (y > layerYEnd || y < layerYStart) continue;
|
||||
int x = (MathMan.roundInt(entity.locX) & 15);
|
||||
int z = (MathMan.roundInt(entity.locZ) & 15);
|
||||
|
||||
int index = (((y & 0xF) << 8) | (z << 4) | x);
|
||||
if (array[index] != 0) {
|
||||
if (copy != null) {
|
||||
copy.storeEntity(entity);
|
||||
}
|
||||
iter.remove();
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
removeEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set entities
|
||||
Set<CompoundTag> entitiesToSpawn = this.getEntities();
|
||||
if (!entitiesToSpawn.isEmpty()) {
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
for (CompoundTag nativeTag : entitiesToSpawn) {
|
||||
Map<String, Tag> entityTagMap = ReflectionUtils.getMap(nativeTag.getValue());
|
||||
StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
if (idTag == null || posTag == null || rotTag == null) {
|
||||
Fawe.debug("Unknown entity tag: " + nativeTag);
|
||||
continue;
|
||||
}
|
||||
double x = posTag.getDouble(0);
|
||||
double y = posTag.getDouble(1);
|
||||
double z = posTag.getDouble(2);
|
||||
float yaw = rotTag.getFloat(0);
|
||||
float pitch = rotTag.getFloat(1);
|
||||
String id = idTag.getValue();
|
||||
Entity entity = EntityTypes.a(nmsWorld, new MinecraftKey(id));
|
||||
if (entity != null) {
|
||||
UUID uuid = entity.getUniqueID();
|
||||
entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits()));
|
||||
entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits()));
|
||||
NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag);
|
||||
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
entity.f(tag);
|
||||
entity.setLocation(x, y, z, yaw, pitch);
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set blocks
|
||||
for (int j = 0; j < sections.length; j++) {
|
||||
int count = this.getCount(j);
|
||||
if (count == 0) {
|
||||
continue;
|
||||
}
|
||||
int countAir = this.getAir(j);
|
||||
final int[] array = this.getIdArray(j);
|
||||
if (array == null) {
|
||||
continue;
|
||||
}
|
||||
ChunkSection section = sections[j];
|
||||
if (copy != null) {
|
||||
if (section != null) {
|
||||
copy.storeSection(copy(section), j);
|
||||
}
|
||||
}
|
||||
if (section == null) {
|
||||
if (count == countAir) {
|
||||
continue;
|
||||
}
|
||||
if (this.sectionPalettes != null && this.sectionPalettes[j] != null) {
|
||||
section = sections[j] = this.sectionPalettes[j];
|
||||
continue;
|
||||
} else {
|
||||
section = sections[j] = getParent().newChunkSection(j, flag, array);
|
||||
continue;
|
||||
}
|
||||
} else if (count >= 4096 && false) {
|
||||
if (countAir >= 4096) {
|
||||
sections[j] = null;
|
||||
continue;
|
||||
}
|
||||
if (this.sectionPalettes != null && this.sectionPalettes[j] != null) {
|
||||
section = sections[j] = this.sectionPalettes[j];
|
||||
continue;
|
||||
} else {
|
||||
section = sections[j] = getParent().newChunkSection(j, flag, array);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int by = j << 4;
|
||||
DataPaletteBlock<IBlockData> nibble = section.getBlocks();
|
||||
int nonEmptyBlockCount = 0;
|
||||
IBlockData existing;
|
||||
|
||||
for (int y = 0, i = 0; y < 16; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x= 0; x < 16; x++, i++) {
|
||||
int combinedId = array[i];
|
||||
switch (combinedId) {
|
||||
case 0:
|
||||
continue;
|
||||
case BlockID.AIR:
|
||||
case BlockID.CAVE_AIR:
|
||||
case BlockID.VOID_AIR:
|
||||
existing = nibble.a(x, y, z);
|
||||
if (!existing.isAir()) {
|
||||
if (existing.e() > 0) {
|
||||
getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z);
|
||||
}
|
||||
nonEmptyBlockCount--;
|
||||
nibble.setBlock(x, y, z, AIR);
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
existing = nibble.a(x, y, z);
|
||||
if (!existing.isAir()) {
|
||||
if (existing.e() > 0) {
|
||||
getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z);
|
||||
}
|
||||
} else {
|
||||
nonEmptyBlockCount++;
|
||||
}
|
||||
BlockState state = BlockState.getFromInternalId(combinedId);
|
||||
IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState();
|
||||
nibble.setBlock(x, y, z, ibd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + nonEmptyBlockCount, section);
|
||||
}
|
||||
|
||||
// Trim tiles
|
||||
HashMap<BlockPosition, TileEntity> toRemove = null;
|
||||
if (!tiles.isEmpty()) {
|
||||
for (Map.Entry<BlockPosition, TileEntity> tile : tiles.entrySet()) {
|
||||
BlockPosition pos = tile.getKey();
|
||||
int lx = pos.getX() & 15;
|
||||
int ly = pos.getY();
|
||||
int lz = pos.getZ() & 15;
|
||||
int layer = ly >> 4;
|
||||
int[] array = this.getIdArray(layer);
|
||||
if (array == null) {
|
||||
continue;
|
||||
}
|
||||
int index = (((ly & 0xF) << 8) | (lz << 4) | lx);
|
||||
if (array[index] != 0) {
|
||||
if (toRemove == null) {
|
||||
toRemove = new HashMap<>();
|
||||
}
|
||||
if (copy != null) {
|
||||
copy.storeTile(tile.getValue(), tile.getKey());
|
||||
}
|
||||
toRemove.put(tile.getKey(), tile.getValue());
|
||||
}
|
||||
}
|
||||
if (toRemove != null) {
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
for (Map.Entry<BlockPosition, TileEntity> entry : toRemove.entrySet()) {
|
||||
BlockPosition bp = entry.getKey();
|
||||
TileEntity tile = entry.getValue();
|
||||
nmsWorld.n(bp);
|
||||
tiles.remove(bp);
|
||||
tile.z();
|
||||
tile.invalidateBlockCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set biomes
|
||||
if (this.biomes != null) {
|
||||
BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex();
|
||||
if (copy != null) {
|
||||
copy.storeBiomes(currentBiomes);
|
||||
}
|
||||
for (int i = 0 ; i < this.biomes.length; i++) {
|
||||
BiomeType biome = this.biomes[i];
|
||||
if (biome != null) {
|
||||
Biome craftBiome = adapter.adapt(biome);
|
||||
currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set tiles
|
||||
Map<Short, CompoundTag> tilesToSpawn = this.getTiles();
|
||||
if (!tilesToSpawn.isEmpty()) {
|
||||
for (Map.Entry<Short, CompoundTag> entry : tilesToSpawn.entrySet()) {
|
||||
CompoundTag nativeTag = entry.getValue();
|
||||
short blockHash = entry.getKey();
|
||||
int x = (blockHash >> 12 & 0xF) + bx;
|
||||
int y = (blockHash & 0xFF);
|
||||
int z = (blockHash >> 8 & 0xF) + bz;
|
||||
BlockPosition pos = new BlockPosition(x, y, z); // Set pos
|
||||
synchronized (BukkitQueue_0.class) {
|
||||
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
|
||||
if (tileEntity != null) {
|
||||
NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag);
|
||||
tag.set("x", new NBTTagInt(x));
|
||||
tag.set("y", new NBTTagInt(y));
|
||||
tag.set("z", new NBTTagInt(z));
|
||||
tileEntity.load(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Change task
|
||||
if (copy != null) {
|
||||
getParent().getChangeTask().run(copy, this);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,722 +0,0 @@
|
||||
package com.boydti.fawe.bukkit.v1_13;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.bukkit.BukkitPlayer;
|
||||
import com.boydti.fawe.bukkit.adapter.v1_13_1.BlockMaterial_1_13;
|
||||
import com.boydti.fawe.bukkit.adapter.v1_13_1.Spigot_v1_13_R2;
|
||||
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.IntFaweChunk;
|
||||
import com.boydti.fawe.jnbt.anvil.BitArray4096;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import com.boydti.fawe.object.RegionWrapper;
|
||||
import com.boydti.fawe.object.brush.visualization.VisualChunk;
|
||||
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockID;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import net.minecraft.server.v1_13_R2.*;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
public class BukkitQueue_1_13 extends BukkitQueue_0<net.minecraft.server.v1_13_R2.Chunk, ChunkSection[], ChunkSection> {
|
||||
|
||||
final static Field fieldBits;
|
||||
final static Field fieldPalette;
|
||||
final static Field fieldSize;
|
||||
|
||||
final static Field fieldHashBlocks;
|
||||
final static Field fieldLinearBlocks;
|
||||
private final static Field fieldHashIndex;
|
||||
final static Field fieldRegistryb;
|
||||
final static Field fieldRegistryc;
|
||||
final static Field fieldRegistryd;
|
||||
final static Field fieldRegistrye;
|
||||
final static Field fieldRegistryf;
|
||||
|
||||
final static Field fieldLinearIndex;
|
||||
final static Field fieldDefaultBlock;
|
||||
|
||||
private final static Field fieldFluidCount;
|
||||
final static Field fieldTickingBlockCount;
|
||||
final static Field fieldNonEmptyBlockCount;
|
||||
final static Field fieldSection;
|
||||
final static Field fieldLiquidCount;
|
||||
private final static ChunkSection emptySection;
|
||||
|
||||
private final static Field fieldDirtyCount;
|
||||
private final static Field fieldDirtyBits;
|
||||
|
||||
static {
|
||||
try {
|
||||
emptySection = new ChunkSection(0, true);
|
||||
Arrays.fill(emptySection.getSkyLightArray().asBytes(), (byte) 255);
|
||||
fieldSection = ChunkSection.class.getDeclaredField("blockIds");
|
||||
fieldLiquidCount = ChunkSection.class.getDeclaredField("e");
|
||||
fieldSection.setAccessible(true);
|
||||
fieldLiquidCount.setAccessible(true);
|
||||
|
||||
fieldFluidCount = ChunkSection.class.getDeclaredField("e");
|
||||
fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount");
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
fieldFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldHashBlocks = DataPaletteHash.class.getDeclaredField("b");
|
||||
fieldHashBlocks.setAccessible(true);
|
||||
fieldLinearBlocks = DataPaletteLinear.class.getDeclaredField("b");
|
||||
fieldLinearBlocks.setAccessible(true);
|
||||
|
||||
fieldHashIndex = DataPaletteHash.class.getDeclaredField("f");
|
||||
fieldHashIndex.setAccessible(true);
|
||||
|
||||
fieldRegistryb = RegistryID.class.getDeclaredField("b");
|
||||
fieldRegistryc = RegistryID.class.getDeclaredField("c");
|
||||
fieldRegistryd = RegistryID.class.getDeclaredField("d");
|
||||
fieldRegistrye = RegistryID.class.getDeclaredField("e");
|
||||
fieldRegistryf = RegistryID.class.getDeclaredField("f");
|
||||
fieldRegistryb.setAccessible(true);
|
||||
fieldRegistryc.setAccessible(true);
|
||||
fieldRegistryd.setAccessible(true);
|
||||
fieldRegistrye.setAccessible(true);
|
||||
fieldRegistryf.setAccessible(true);
|
||||
|
||||
fieldLinearIndex = DataPaletteLinear.class.getDeclaredField("f");
|
||||
fieldLinearIndex.setAccessible(true);
|
||||
|
||||
fieldDefaultBlock = DataPaletteBlock.class.getDeclaredField("g");
|
||||
fieldDefaultBlock.setAccessible(true);
|
||||
|
||||
fieldSize = DataPaletteBlock.class.getDeclaredField("i");
|
||||
fieldSize.setAccessible(true);
|
||||
|
||||
fieldBits = DataPaletteBlock.class.getDeclaredField("a");
|
||||
fieldBits.setAccessible(true);
|
||||
|
||||
fieldPalette = DataPaletteBlock.class.getDeclaredField("h");
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount");
|
||||
fieldDirtyBits = PlayerChunk.class.getDeclaredField("h");
|
||||
fieldDirtyCount.setAccessible(true);
|
||||
fieldDirtyBits.setAccessible(true);
|
||||
|
||||
System.out.println("Using adapter: " + getAdapter());
|
||||
System.out.println("=========================================");
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public BukkitQueue_1_13(final com.sk89q.worldedit.world.World world) {
|
||||
super(world);
|
||||
getImpWorld();
|
||||
}
|
||||
|
||||
public BukkitQueue_1_13(final String world) {
|
||||
super(world);
|
||||
getImpWorld();
|
||||
}
|
||||
|
||||
private boolean save(net.minecraft.server.v1_13_R2.Chunk chunk, ChunkProviderServer cps) {
|
||||
cps.saveChunk(chunk, false);
|
||||
chunk.a(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSection[] getSections(net.minecraft.server.v1_13_R2.Chunk chunk) {
|
||||
return chunk.getSections();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.server.v1_13_R2.Chunk loadChunk(World world, int x, int z, boolean generate) {
|
||||
ChunkProviderServer provider = ((CraftWorld) world).getHandle().getChunkProvider();
|
||||
if (generate) {
|
||||
return provider.getChunkAt(x, z, true, true);
|
||||
} else {
|
||||
return provider.getChunkAt(x, z, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSection[] getCachedSections(World world, int cx, int cz) {
|
||||
net.minecraft.server.v1_13_R2.Chunk chunk = ((CraftWorld) world).getHandle().getChunkProvider().getChunkAt(cx, cz, false, false);
|
||||
if (chunk != null) {
|
||||
return chunk.getSections();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.server.v1_13_R2.Chunk getCachedChunk(World world, int cx, int cz) {
|
||||
return ((CraftWorld) world).getHandle().getChunkProvider().getChunkAt(cx, cz, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSection getCachedSection(ChunkSection[] chunkSections, int cy) {
|
||||
return chunkSections[cy];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChunk(net.minecraft.server.v1_13_R2.Chunk chunk) {
|
||||
chunk.f(true); // Set Modified
|
||||
chunk.mustSave = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerateChunk(World world, int x, int z, BiomeType biome, Long seed) {
|
||||
return super.regenerateChunk(world, x, z, biome, seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) {
|
||||
throw new UnsupportedOperationException("Anvil not implemented yet");
|
||||
// TaskManager.IMP.sync(new RunnableVal<Boolean>() {
|
||||
// @Override
|
||||
// public void run(Boolean value) {
|
||||
// long start = System.currentTimeMillis();
|
||||
// long last = start;
|
||||
// synchronized (RegionFileCache.class) {
|
||||
// World world = getWorld();
|
||||
// if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
|
||||
// ChunkProviderServer provider = nmsWorld.getChunkProvider();
|
||||
//
|
||||
// boolean mustSave = false;
|
||||
// boolean[][] chunksUnloaded = null;
|
||||
// { // Unload chunks
|
||||
// Iterator<net.minecraft.server.v1_13_R2.Chunk> iter = provider.a().iterator();
|
||||
// while (iter.hasNext()) {
|
||||
// net.minecraft.server.v1_13_R2.Chunk chunk = iter.next();
|
||||
// if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) {
|
||||
// boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ);
|
||||
// if (isIn) {
|
||||
// if (!load) {
|
||||
// mustSave |= saveChunks && save(chunk, provider);
|
||||
// continue;
|
||||
// }
|
||||
// iter.remove();
|
||||
// boolean save = saveChunks && chunk.a(false);
|
||||
// mustSave |= save;
|
||||
// provider.unloadChunk(chunk, save);
|
||||
// if (chunksUnloaded == null) {
|
||||
// chunksUnloaded = new boolean[32][];
|
||||
// }
|
||||
// int relX = chunk.locX & 31;
|
||||
// boolean[] arr = chunksUnloaded[relX];
|
||||
// if (arr == null) {
|
||||
// arr = chunksUnloaded[relX] = new boolean[32];
|
||||
// }
|
||||
// arr[chunk.locZ & 31] = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (mustSave) {
|
||||
// provider.c(); // TODO only the necessary chunks
|
||||
// }
|
||||
//
|
||||
// File unloadedRegion = null;
|
||||
// if (load && !RegionFileCache.a.isEmpty()) {
|
||||
// Map<File, RegionFile> map = RegionFileCache.a;
|
||||
// Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
|
||||
// String requiredPath = world.getName() + File.separator + "region";
|
||||
// while (iter.hasNext()) {
|
||||
// Map.Entry<File, RegionFile> entry = iter.next();
|
||||
// File file = entry.getKey();
|
||||
// int[] regPos = MainUtil.regionNameToCoords(file.getPath());
|
||||
// if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) {
|
||||
// if (file.exists()) {
|
||||
// unloadedRegion = file;
|
||||
// RegionFile regionFile = entry.getValue();
|
||||
// iter.remove();
|
||||
// try {
|
||||
// regionFile.c();
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// long now = System.currentTimeMillis();
|
||||
// if (whileLocked != null) whileLocked.run();
|
||||
// if (!load) return;
|
||||
//
|
||||
// { // Load the region again
|
||||
// if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) {
|
||||
// final boolean[][] finalChunksUnloaded = chunksUnloaded;
|
||||
// TaskManager.IMP.async(() -> {
|
||||
// int bx = mcaX << 5;
|
||||
// int bz = mcaZ << 5;
|
||||
// for (int x = 0; x < finalChunksUnloaded.length; x++) {
|
||||
// boolean[] arr = finalChunksUnloaded[x];
|
||||
// if (arr != null) {
|
||||
// for (int z = 0; z < arr.length; z++) {
|
||||
// if (arr[z]) {
|
||||
// int cx = bx + x;
|
||||
// int cz = bz + z;
|
||||
// SetQueue.IMP.addTask(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// net.minecraft.server.v1_13_R2.Chunk chunk = provider.getChunkAt(cx, cz, null, false);
|
||||
// if (chunk != null) {
|
||||
// PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz);
|
||||
// if (pc != null) {
|
||||
// sendChunk(pc, chunk, 0);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next(int amount, long time) {
|
||||
return super.next(amount, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLight(ChunkSection section, int x, int y, int z, int value) {
|
||||
section.getSkyLightArray().a(x & 15, y & 15, z & 15, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockLight(ChunkSection section, int x, int y, int z, int value) {
|
||||
section.getEmittedLightArray().a(x & 15, y & 15, z & 15, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCombinedId4Data(ChunkSection lastSection, int x, int y, int z) {
|
||||
DataPaletteBlock<IBlockData> dataPalette = lastSection.getBlocks();
|
||||
IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15);
|
||||
int ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToInt(ibd);
|
||||
return BlockTypes.states[ordinal].getInternalId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiome(net.minecraft.server.v1_13_R2.Chunk chunk, int x, int z) {
|
||||
BiomeBase base = chunk.getBiomeIndex()[((z & 15) << 4) + (x & 15)];
|
||||
return getAdapter().adapt(CraftBlock.biomeBaseToBiome(base));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity(ChunkSection section, int x, int y, int z) {
|
||||
DataPaletteBlock<IBlockData> dataPalette = section.getBlocks();
|
||||
IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15);
|
||||
pos.a(x, y, z);
|
||||
return ibd.b(nmsWorld, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightness(ChunkSection section, int x, int y, int z) {
|
||||
DataPaletteBlock<IBlockData> dataPalette = section.getBlocks();
|
||||
IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15);
|
||||
return ibd.e();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacityBrightnessPair(ChunkSection section, int x, int y, int z) {
|
||||
DataPaletteBlock<IBlockData> dataPalette = section.getBlocks();
|
||||
IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15);
|
||||
pos.a(x, y, z);
|
||||
int opacity = ibd.b(nmsWorld, pos);
|
||||
int brightness = ibd.e();
|
||||
return MathMan.pair16(brightness, opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(int x, int z, int bitMask) {
|
||||
net.minecraft.server.v1_13_R2.Chunk chunk = getCachedChunk(getWorld(), x, z);
|
||||
if (chunk != null) {
|
||||
sendChunk(getPlayerChunk((WorldServer) chunk.getWorld(), chunk.locX, chunk.locZ), chunk, bitMask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) {
|
||||
// PlayerChunkMap playerManager = ((CraftWorld) getWorld()).getHandle().getPlayerChunkMap();
|
||||
// ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
// WirePacket packet = null;
|
||||
// try {
|
||||
// for (int i = 0; i < players.length; i++) {
|
||||
// CraftPlayer bukkitPlayer = ((CraftPlayer) ((BukkitPlayer) players[i]).parent);
|
||||
// EntityPlayer player = bukkitPlayer.getHandle();
|
||||
//
|
||||
// if (playerManager.a(player, chunk.getX(), chunk.getZ())) {
|
||||
// if (packet == null) {
|
||||
// byte[] data;
|
||||
// byte[] buffer = new byte[8192];
|
||||
// if (chunk instanceof LazyFaweChunk) {
|
||||
// chunk = (FaweChunk) chunk.getChunk();
|
||||
// }
|
||||
// if (chunk instanceof MCAChunk) {
|
||||
// data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer);
|
||||
// } else {
|
||||
// data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer);
|
||||
// }
|
||||
// packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data);
|
||||
// }
|
||||
// manager.sendWirePacket(bukkitPlayer, packet);
|
||||
// }
|
||||
// }
|
||||
// } catch (InvocationTargetException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
super.sendChunkUpdatePLIB(chunk, players); // TODO remove
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) {
|
||||
try {
|
||||
PlayerChunkMap playerManager = ((CraftWorld) getWorld()).getHandle().getPlayerChunkMap();
|
||||
boolean watching = false;
|
||||
boolean[] watchingArr = new boolean[players.length];
|
||||
for (int i = 0; i < players.length; i++) {
|
||||
EntityPlayer player = ((CraftPlayer) ((BukkitPlayer) players[i]).parent).getHandle();
|
||||
if (playerManager.a(player, chunk.getX(), chunk.getZ())) {
|
||||
watchingArr[i] = true;
|
||||
watching = true;
|
||||
}
|
||||
}
|
||||
if (!watching) return;
|
||||
final LongAdder size = new LongAdder();
|
||||
if (chunk instanceof VisualChunk) {
|
||||
size.add(((VisualChunk) chunk).size());
|
||||
} else if (chunk instanceof IntFaweChunk) {
|
||||
size.add(((IntFaweChunk) chunk).getTotalCount());
|
||||
} else {
|
||||
chunk.forEachQueuedBlock(new FaweChunkVisitor() {
|
||||
@Override
|
||||
public void run(int localX, int y, int localZ, int combined) {
|
||||
size.add(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (size.intValue() == 0) return;
|
||||
PacketPlayOutMultiBlockChange packet = new PacketPlayOutMultiBlockChange();
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
|
||||
final PacketDataSerializer buffer = new PacketDataSerializer(byteBuf);
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.d(size.intValue());
|
||||
chunk.forEachQueuedBlock(new FaweChunkVisitor() {
|
||||
@Override
|
||||
public void run(int localX, int y, int localZ, int combined) {
|
||||
short index = (short) (localX << 12 | localZ << 8 | y);
|
||||
if (combined < 16) combined = 0;
|
||||
buffer.writeShort(index);
|
||||
buffer.d(combined);
|
||||
}
|
||||
});
|
||||
packet.a(buffer);
|
||||
for (int i = 0; i < players.length; i++) {
|
||||
if (watchingArr[i]) ((CraftPlayer) ((BukkitPlayer) players[i]).parent).getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshChunk(FaweChunk fc) {
|
||||
sendChunk(fc.getX(), fc.getZ(), fc.getBitMask());
|
||||
}
|
||||
|
||||
public void sendPacket(int cx, int cz, Packet packet) {
|
||||
PlayerChunk chunk = getPlayerChunk(nmsWorld, cx, cz);
|
||||
if (chunk != null) {
|
||||
for (EntityPlayer player : chunk.players) {
|
||||
player.playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerChunk getPlayerChunk(WorldServer w, int cx, int cz) {
|
||||
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
|
||||
PlayerChunk playerChunk = chunkMap.getChunk(cx, cz);
|
||||
if (playerChunk == null) {
|
||||
return null;
|
||||
}
|
||||
if (playerChunk.players.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return playerChunk;
|
||||
}
|
||||
|
||||
private boolean sendChunk(PlayerChunk playerChunk, net.minecraft.server.v1_13_R2.Chunk nmsChunk, int mask) {
|
||||
if (playerChunk == null) {
|
||||
return false;
|
||||
}
|
||||
if (playerChunk.e()) {
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
for (int layer = 0; layer < 16; layer++) {
|
||||
if (sections[layer] == null && (mask & (1 << layer)) != 0) {
|
||||
sections[layer] = new ChunkSection(layer << 4, nmsWorld.worldProvider.g());
|
||||
}
|
||||
}
|
||||
TaskManager.IMP.sync(() -> {
|
||||
try {
|
||||
int dirtyBits = fieldDirtyBits.getInt(playerChunk);
|
||||
if (dirtyBits == 0) {
|
||||
((CraftWorld) getWorld()).getHandle().getPlayerChunkMap().a(playerChunk);
|
||||
}
|
||||
if (mask == 0) {
|
||||
dirtyBits = 65535;
|
||||
} else {
|
||||
dirtyBits |= mask;
|
||||
}
|
||||
|
||||
fieldDirtyBits.set(playerChunk, dirtyBits);
|
||||
fieldDirtyCount.set(playerChunk, 64);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasEntities(net.minecraft.server.v1_13_R2.Chunk nmsChunk) {
|
||||
try {
|
||||
final Collection<Entity>[] entities = nmsChunk.entitySlices;
|
||||
for (Collection<Entity> slice : entities) {
|
||||
if (slice != null && !slice.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeSectionLighting(ChunkSection section, int layer, boolean sky) {
|
||||
if (section != null) {
|
||||
Arrays.fill(section.getEmittedLightArray().asBytes(), (byte) 0);
|
||||
if (sky) {
|
||||
byte[] light = section.getSkyLightArray().asBytes();
|
||||
if (light != null) {
|
||||
Arrays.fill(light, (byte) 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullbright(ChunkSection[] sections) {
|
||||
for (ChunkSection section : sections) {
|
||||
if (section != null) {
|
||||
byte[] bytes = section.getSkyLightArray().asBytes();
|
||||
Arrays.fill(bytes, (byte) 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(ChunkSection section, int x, int y, int z) {
|
||||
return section.c(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmmittedLight(ChunkSection section, int x, int y, int z) {
|
||||
return section.d(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relightBlock(int x, int y, int z) {
|
||||
pos.c(x, y, z);
|
||||
nmsWorld.c(EnumSkyBlock.BLOCK, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relightSky(int x, int y, int z) {
|
||||
pos.c(x, y, z);
|
||||
nmsWorld.c(EnumSkyBlock.SKY, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relight(int x, int y, int z) {
|
||||
pos.c(x, y, z);
|
||||
nmsWorld.r(pos);
|
||||
}
|
||||
|
||||
private WorldServer nmsWorld;
|
||||
|
||||
@Override
|
||||
public World getImpWorld() {
|
||||
World world = super.getImpWorld();
|
||||
if (world != null) {
|
||||
this.nmsWorld = ((CraftWorld) world).getHandle();
|
||||
return super.getImpWorld();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static void setCount(int tickingBlockCount, int nonEmptyBlockCount, ChunkSection section) throws NoSuchFieldException, IllegalAccessException {
|
||||
fieldFluidCount.set(section, 0); // TODO FIXME
|
||||
fieldTickingBlockCount.set(section, tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount);
|
||||
}
|
||||
|
||||
int getNonEmptyBlockCount(ChunkSection section) throws IllegalAccessException {
|
||||
return (int) fieldNonEmptyBlockCount.get(section);
|
||||
}
|
||||
|
||||
public void setPalette(ChunkSection section, DataPaletteBlock palette) throws NoSuchFieldException, IllegalAccessException {
|
||||
fieldSection.set(section, palette);
|
||||
Arrays.fill(section.getEmittedLightArray().asBytes(), (byte) 0);
|
||||
}
|
||||
|
||||
static ChunkSection newChunkSection(int y2, boolean flag, int[] blocks) {
|
||||
if (blocks == null) {
|
||||
return new ChunkSection(y2 << 4, flag);
|
||||
} else {
|
||||
ChunkSection section = new ChunkSection(y2 << 4, flag);
|
||||
int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get();
|
||||
int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get();
|
||||
long[] blockstates = FaweCache.BLOCK_STATES.get();
|
||||
int[] blocksCopy = FaweCache.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette = 0;
|
||||
int air = 0;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
int stateId = blocks[i];
|
||||
switch (stateId) {
|
||||
case 0:
|
||||
case BlockID.AIR:
|
||||
case BlockID.CAVE_AIR:
|
||||
case BlockID.VOID_AIR:
|
||||
stateId = BlockID.AIR;
|
||||
air++;
|
||||
}
|
||||
int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary
|
||||
int palette = blockToPalette[ordinal];
|
||||
if (palette == Integer.MAX_VALUE) {
|
||||
blockToPalette[ordinal] = palette = num_palette;
|
||||
paletteToBlock[num_palette] = ordinal;
|
||||
num_palette++;
|
||||
}
|
||||
blocksCopy[i] = palette;
|
||||
}
|
||||
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
|
||||
int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) blockstates[i] = 0;
|
||||
} else {
|
||||
BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
// set palette & data bits
|
||||
DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
long[] bits = Arrays.copyOfRange(blockstates, 0, blockBitArrayEnd);
|
||||
DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
|
||||
DataPalette<IBlockData> palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
|
||||
|
||||
// set palette
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
BlockState state = BlockTypes.states[ordinal];
|
||||
IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState();
|
||||
palette.a(ibd);
|
||||
}
|
||||
try {
|
||||
fieldBits.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, palette);
|
||||
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
|
||||
setCount(0, 4096 - air, section);
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return section;
|
||||
} catch (Throwable e){
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0);
|
||||
|
||||
@Override
|
||||
public CompoundTag getTileEntity(net.minecraft.server.v1_13_R2.Chunk chunk, int x, int y, int z) {
|
||||
Map<BlockPosition, TileEntity> tiles = chunk.getTileEntities();
|
||||
pos.c(x, y, z);
|
||||
TileEntity tile = tiles.get(pos);
|
||||
return tile != null ? getTag(tile) : null;
|
||||
}
|
||||
|
||||
CompoundTag getTag(TileEntity tile) {
|
||||
try {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tile.save(tag); // readTagIntoEntity
|
||||
return (CompoundTag) toNative(tag);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean unloadChunk(final String world, final Chunk chunk) {
|
||||
net.minecraft.server.v1_13_R2.Chunk c = ((CraftChunk) chunk).getHandle();
|
||||
c.mustSave = false;
|
||||
if (chunk.isLoaded()) {
|
||||
chunk.unload(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitChunk_1_13 getFaweChunk(int x, int z) {
|
||||
return new BukkitChunk_1_13(this, x, z);
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package com.boydti.fawe.bukkit.v1_14.adapter;
|
||||
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_14_R1.Block;
|
||||
import net.minecraft.server.v1_14_R1.EnumPistonReaction;
|
||||
import net.minecraft.server.v1_14_R1.IBlockData;
|
||||
import net.minecraft.server.v1_14_R1.ITileEntity;
|
||||
import net.minecraft.server.v1_14_R1.Material;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData;
|
||||
|
||||
public class BlockMaterial_1_14 implements BlockMaterial {
|
||||
private final Block block;
|
||||
private final IBlockData defaultState;
|
||||
private final Material material;
|
||||
private final boolean isTranslucent;
|
||||
private final CraftBlockData craftBlockData;
|
||||
|
||||
public BlockMaterial_1_14(Block block) {
|
||||
this(block, block.getBlockData());
|
||||
}
|
||||
|
||||
public BlockMaterial_1_14(Block block, IBlockData defaultState) {
|
||||
this.block = block;
|
||||
this.defaultState = defaultState;
|
||||
this.material = defaultState.getMaterial();
|
||||
this.craftBlockData = CraftBlockData.fromData(defaultState);
|
||||
this.isTranslucent = ReflectionUtil.getField(Block.class, block, "v");
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public IBlockData getState() {
|
||||
return defaultState;
|
||||
}
|
||||
|
||||
public CraftBlockData getCraftBlockData() {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return defaultState.isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return defaultState.g();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return defaultState.isPowerSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isBuildable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return block.strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return block.getDurability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return block.m();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return defaultState.h();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return isTranslucent() ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == EnumPistonReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return block.isTicking(defaultState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isBurnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
return !material.isAlwaysDestroyable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainer() {
|
||||
return block instanceof ITileEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
return material.i().rgb;
|
||||
}
|
||||
}
|
||||
+629
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.boydti.fawe.bukkit.v1_14.adapter;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.sk89q.jnbt.ByteArrayTag;
|
||||
import com.sk89q.jnbt.ByteTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.DoubleTag;
|
||||
import com.sk89q.jnbt.EndTag;
|
||||
import com.sk89q.jnbt.FloatTag;
|
||||
import com.sk89q.jnbt.IntArrayTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.LongArrayTag;
|
||||
import com.sk89q.jnbt.LongTag;
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.ShortTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
||||
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
||||
import com.sk89q.worldedit.registry.state.EnumProperty;
|
||||
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_14_R1.Block;
|
||||
import net.minecraft.server.v1_14_R1.BlockPosition;
|
||||
import net.minecraft.server.v1_14_R1.BlockStateBoolean;
|
||||
import net.minecraft.server.v1_14_R1.BlockStateDirection;
|
||||
import net.minecraft.server.v1_14_R1.BlockStateEnum;
|
||||
import net.minecraft.server.v1_14_R1.BlockStateInteger;
|
||||
import net.minecraft.server.v1_14_R1.BlockStateList;
|
||||
import net.minecraft.server.v1_14_R1.Chunk;
|
||||
import net.minecraft.server.v1_14_R1.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_14_R1.ChunkSection;
|
||||
import net.minecraft.server.v1_14_R1.Entity;
|
||||
import net.minecraft.server.v1_14_R1.EntityTypes;
|
||||
import net.minecraft.server.v1_14_R1.IBlockData;
|
||||
import net.minecraft.server.v1_14_R1.IBlockState;
|
||||
import net.minecraft.server.v1_14_R1.INamable;
|
||||
import net.minecraft.server.v1_14_R1.IRegistry;
|
||||
import net.minecraft.server.v1_14_R1.MinecraftKey;
|
||||
import net.minecraft.server.v1_14_R1.NBTBase;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagByte;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagByteArray;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagCompound;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagDouble;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagEnd;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagFloat;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagInt;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagIntArray;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagList;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagLong;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagLongArray;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagShort;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagString;
|
||||
import net.minecraft.server.v1_14_R1.PacketPlayOutEntityStatus;
|
||||
import net.minecraft.server.v1_14_R1.PacketPlayOutTileEntityData;
|
||||
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
|
||||
import net.minecraft.server.v1_14_R1.TileEntity;
|
||||
import net.minecraft.server.v1_14_R1.World;
|
||||
import net.minecraft.server.v1_14_R1.WorldServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public final class Spigot_v1_14_R1 extends CachedBukkitAdapter implements BukkitImplAdapter<NBTBase>{
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final Field nbtListTagListField;
|
||||
private final Method nbtCreateTagMethod;
|
||||
|
||||
static {
|
||||
// A simple test
|
||||
if (!Bukkit.getServer().getClass().getName().endsWith("DummyServer")) CraftServer.class.cast(Bukkit.getServer());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public Spigot_v1_14_R1() throws NoSuchFieldException, NoSuchMethodException {
|
||||
// The list of tags on an NBTTagList
|
||||
nbtListTagListField = NBTTagList.class.getDeclaredField("list");
|
||||
nbtListTagListField.setAccessible(true);
|
||||
|
||||
// The method to create an NBTBase tag given its type ID
|
||||
nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", byte.class);
|
||||
nbtCreateTagMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
public char[] idbToStateOrdinal;
|
||||
|
||||
private synchronized boolean init() {
|
||||
if (idbToStateOrdinal != null) return false;
|
||||
idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size
|
||||
for (int i = 0; i < idbToStateOrdinal.length; i++) {
|
||||
BlockState state = BlockTypes.states[i];
|
||||
BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial();
|
||||
int id = Block.REGISTRY_ID.getId(material.getState());
|
||||
idbToStateOrdinal[id] = state.getOrdinalChar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the given NBT data into the given tile entity.
|
||||
*
|
||||
* @param tileEntity the tile entity
|
||||
* @param tag the tag
|
||||
*/
|
||||
private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity) {
|
||||
try {
|
||||
tileEntity.load(tag);
|
||||
} catch (Throwable e) {
|
||||
Fawe.debug("Invalid tag " + tag + " | " + tileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the tile entity's NBT data to the given tag.
|
||||
*
|
||||
* @param tileEntity the tile entity
|
||||
* @param tag the tag
|
||||
*/
|
||||
private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag) {
|
||||
tileEntity.save(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID string of the given entity.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @return the entity ID or null if one is not known
|
||||
*/
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
MinecraftKey minecraftkey = EntityTypes.getName(entity.getBukkitEntity().getHandle().getEntityType());
|
||||
return minecraftkey == null ? null : minecraftkey.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entity using the given entity ID.
|
||||
*
|
||||
* @param id the entity ID
|
||||
* @param world the world
|
||||
* @return an entity or null
|
||||
*/
|
||||
@Nullable
|
||||
private static Entity createEntityFromId(String id, World world) {
|
||||
return EntityTypes.a(id).get().a(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given NBT data into the given entity.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param tag the tag
|
||||
*/
|
||||
private static void readTagIntoEntity(NBTTagCompound tag, Entity entity) {
|
||||
entity.f(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the entity's NBT data to the given tag.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param tag the tag
|
||||
*/
|
||||
private static void readEntityIntoTag(Entity entity, NBTTagCompound tag) {
|
||||
entity.save(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
return new BlockMaterial_1_14(getBlock(blockType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockState state) {
|
||||
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||
return new BlockMaterial_1_14(bs.getBlock(), bs);
|
||||
}
|
||||
|
||||
public Block getBlock(BlockType blockType) {
|
||||
return IRegistry.BLOCK.get(new MinecraftKey(blockType.getNamespace(), blockType.getResource()));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that is less likely to break
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public BaseBlock getBlock(Location location) {
|
||||
checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
BlockState state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
||||
//Read the NBT data
|
||||
TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
|
||||
if (te != null) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
readTileEntityIntoTag(te, tag); // Load data
|
||||
return state.toBaseBlock((CompoundTag) toNative(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkInUse(org.bukkit.Chunk chunk) {
|
||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
||||
PlayerChunkMap chunkMap = ((WorldServer) craftChunk.getHandle().getWorld()).getChunkProvider().playerChunkMap;
|
||||
return chunkMap.visibleChunks.containsKey(ChunkCoordIntPair.pair(chunk.getX(), chunk.getZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
|
||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
||||
Chunk nmsChunk = craftChunk.getHandle();
|
||||
World nmsWorld = nmsChunk.getWorld();
|
||||
|
||||
IBlockData blockData = ((BlockMaterial_1_14) state.getMaterial()).getState();
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
int y4 = y >> 4;
|
||||
ChunkSection section = sections[y4];
|
||||
|
||||
IBlockData existing;
|
||||
if (section == null) {
|
||||
existing = ((BlockMaterial_1_14) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
||||
} else {
|
||||
existing = section.getType(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
BlockPosition pos = new BlockPosition(x, y, z);
|
||||
|
||||
nmsChunk.removeTileEntity(pos); // Force delete the old tile entity
|
||||
|
||||
CompoundTag nativeTag = state instanceof BaseBlock ? ((BaseBlock)state).getNbtData() : null;
|
||||
if (nativeTag != null || existing instanceof TileEntityBlock) {
|
||||
nmsWorld.setTypeAndData(pos, blockData, 0);
|
||||
// remove tile
|
||||
if (nativeTag != null) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the Forge version
|
||||
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
|
||||
if (tileEntity != null) {
|
||||
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
||||
tag.set("x", new NBTTagInt(x));
|
||||
tag.set("y", new NBTTagInt(y));
|
||||
tag.set("z", new NBTTagInt(z));
|
||||
readTagIntoTileEntity(tag, tileEntity); // Load data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (existing == blockData) return true;
|
||||
if (section == null) {
|
||||
if (blockData.isAir()) return true;
|
||||
sections[y4] = section = new ChunkSection(y4 << 4);
|
||||
}
|
||||
nmsChunk.setType(pos = new BlockPosition(x, y, z), blockData, false);
|
||||
}
|
||||
if (update) {
|
||||
nmsWorld.getMinecraftWorld().notify(pos, existing, blockData, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||
checkNotNull(entity);
|
||||
|
||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||
Entity mcEntity = craftEntity.getHandle();
|
||||
|
||||
String id = getEntityId(mcEntity);
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundTag> saveTag = new Supplier<CompoundTag>() {
|
||||
@Override
|
||||
public CompoundTag get() {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
readEntityIntoTag(mcEntity, tag);
|
||||
return (CompoundTag) toNative(tag);
|
||||
}
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) {
|
||||
checkNotNull(location);
|
||||
checkNotNull(state);
|
||||
if (state.getType() == com.sk89q.worldedit.world.entity.EntityTypes.PLAYER) return null;
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
WorldServer worldServer = craftWorld.getHandle();
|
||||
|
||||
Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
|
||||
|
||||
if (createdEntity != null) {
|
||||
CompoundTag nativeTag = state.getNbtData();
|
||||
if (nativeTag != null) {
|
||||
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
||||
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
readTagIntoEntity(tag, createdEntity);
|
||||
}
|
||||
|
||||
createdEntity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
|
||||
worldServer.addEntity(createdEntity, SpawnReason.CUSTOM);
|
||||
return createdEntity.getBukkitEntity();
|
||||
} else {
|
||||
Fawe.debug("Invalid entity " + state.getType().getId());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
||||
Block block;
|
||||
try {
|
||||
block = IRegistry.BLOCK.get(new MinecraftKey(blockType.getNamespace(), blockType.getResource()));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
if (block == null) {
|
||||
logger.warn("Failed to find properties for " + blockType.getId());
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Property<?>> properties = Maps.newLinkedHashMap();
|
||||
BlockStateList<Block, IBlockData> blockStateList = block.getStates();
|
||||
for (IBlockState state : blockStateList.d()) {
|
||||
Property property;
|
||||
if (state instanceof BlockStateBoolean) {
|
||||
property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.d()));
|
||||
} else if (state instanceof BlockStateDirection) {
|
||||
property = new DirectionalProperty(state.a(),
|
||||
(List<Direction>) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList()));
|
||||
} else if (state instanceof BlockStateEnum) {
|
||||
property = new EnumProperty(state.a(),
|
||||
(List<String>) state.d().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList()));
|
||||
} else if (state instanceof BlockStateInteger) {
|
||||
property = new IntegerProperty(state.a(), ImmutableList.copyOf(state.d()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
properties.put(property.getName(), property);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
||||
* structure.
|
||||
*
|
||||
* @param foreign non-native NMS NBT structure
|
||||
* @return native WorldEdit NBT structure
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Tag toNative(NBTBase foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof NBTTagCompound) {
|
||||
Map<String, Tag> values = new HashMap<>();
|
||||
Set<String> foreignKeys = ((NBTTagCompound) foreign).getKeys(); // map.keySet
|
||||
|
||||
for (String str : foreignKeys) {
|
||||
NBTBase base = ((NBTTagCompound) foreign).get(str);
|
||||
values.put(str, toNative(base));
|
||||
}
|
||||
return new CompoundTag(values);
|
||||
} else if (foreign instanceof NBTTagByte) {
|
||||
return new ByteTag(((NBTTagByte) foreign).asByte());
|
||||
} else if (foreign instanceof NBTTagByteArray) {
|
||||
return new ByteArrayTag(((NBTTagByteArray) foreign).getBytes()); // data
|
||||
} else if (foreign instanceof NBTTagDouble) {
|
||||
return new DoubleTag(((NBTTagDouble) foreign).asDouble()); // getDouble
|
||||
} else if (foreign instanceof NBTTagFloat) {
|
||||
return new FloatTag(((NBTTagFloat) foreign).asFloat());
|
||||
} else if (foreign instanceof NBTTagInt) {
|
||||
return new IntTag(((NBTTagInt) foreign).asInt());
|
||||
} else if (foreign instanceof NBTTagIntArray) {
|
||||
return new IntArrayTag(((NBTTagIntArray) foreign).getInts()); // data
|
||||
} else if (foreign instanceof NBTTagLongArray) {
|
||||
return new LongArrayTag(((NBTTagLongArray) foreign).getLongs()); // data
|
||||
} else if (foreign instanceof NBTTagList) {
|
||||
try {
|
||||
return toNativeList((NBTTagList) foreign);
|
||||
} catch (Throwable e) {
|
||||
logger.warn("Failed to convert NBTTagList", e);
|
||||
return new ListTag(ByteTag.class, new ArrayList<ByteTag>());
|
||||
}
|
||||
} else if (foreign instanceof NBTTagLong) {
|
||||
return new LongTag(((NBTTagLong) foreign).asLong());
|
||||
} else if (foreign instanceof NBTTagShort) {
|
||||
return new ShortTag(((NBTTagShort) foreign).asShort());
|
||||
} else if (foreign instanceof NBTTagString) {
|
||||
return new StringTag(foreign.asString());
|
||||
} else if (foreign instanceof NBTTagEnd) {
|
||||
return new EndTag();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a foreign NBT list tag into a native WorldEdit one.
|
||||
*
|
||||
* @param foreign the foreign tag
|
||||
* @return the converted tag
|
||||
* @throws NoSuchFieldException on error
|
||||
* @throws SecurityException on error
|
||||
* @throws IllegalArgumentException on error
|
||||
* @throws IllegalAccessException on error
|
||||
*/
|
||||
public ListTag toNativeList(NBTTagList foreign) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
List<Tag> values = new ArrayList<>();
|
||||
int type = foreign.getTypeId();
|
||||
|
||||
List foreignList;
|
||||
foreignList = (List) nbtListTagListField.get(foreign);
|
||||
for (int i = 0; i < foreign.size(); i++) {
|
||||
NBTBase element = (NBTBase) foreignList.get(i);
|
||||
values.add(toNative(element)); // List elements shouldn't have names
|
||||
}
|
||||
|
||||
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
||||
return new ListTag(cls, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
||||
*
|
||||
* @param foreign structure to convert
|
||||
* @return non-native structure
|
||||
*/
|
||||
@Override
|
||||
public NBTBase fromNative(Tag foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof CompoundTag) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
||||
.getValue().entrySet()) {
|
||||
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
||||
}
|
||||
return tag;
|
||||
} else if (foreign instanceof ByteTag) {
|
||||
return new NBTTagByte(((ByteTag) foreign).getValue());
|
||||
} else if (foreign instanceof ByteArrayTag) {
|
||||
return new NBTTagByteArray(((ByteArrayTag) foreign).getValue());
|
||||
} else if (foreign instanceof DoubleTag) {
|
||||
return new NBTTagDouble(((DoubleTag) foreign).getValue());
|
||||
} else if (foreign instanceof FloatTag) {
|
||||
return new NBTTagFloat(((FloatTag) foreign).getValue());
|
||||
} else if (foreign instanceof IntTag) {
|
||||
return new NBTTagInt(((IntTag) foreign).getValue());
|
||||
} else if (foreign instanceof IntArrayTag) {
|
||||
return new NBTTagIntArray(((IntArrayTag) foreign).getValue());
|
||||
} else if (foreign instanceof LongArrayTag) {
|
||||
return new NBTTagLongArray(((LongArrayTag) foreign).getValue());
|
||||
} else if (foreign instanceof ListTag) {
|
||||
NBTTagList tag = new NBTTagList();
|
||||
ListTag foreignList = (ListTag) foreign;
|
||||
for (Tag t : foreignList.getValue()) {
|
||||
tag.add(fromNative(t));
|
||||
}
|
||||
return tag;
|
||||
} else if (foreign instanceof LongTag) {
|
||||
return new NBTTagLong(((LongTag) foreign).getValue());
|
||||
} else if (foreign instanceof ShortTag) {
|
||||
return new NBTTagShort(((ShortTag) foreign).getValue());
|
||||
} else if (foreign instanceof StringTag) {
|
||||
return new NBTTagString(((StringTag) foreign).getValue());
|
||||
} else if (foreign instanceof EndTag) {
|
||||
try {
|
||||
return (NBTBase) nbtCreateTagMethod.invoke(null, (byte) 0);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState adapt(BlockData blockData) {
|
||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
||||
IBlockData ibd = cbd.getState();
|
||||
return adapt(ibd);
|
||||
}
|
||||
|
||||
public BlockState adapt(IBlockData ibd) {
|
||||
return BlockTypes.states[adaptToInt(ibd)];
|
||||
}
|
||||
|
||||
public int adaptToInt(IBlockData ibd) {
|
||||
try {
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return idbToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
init();
|
||||
return adaptToInt(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
public char adaptToChar(IBlockData ibd) {
|
||||
try {
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return idbToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
init();
|
||||
return adaptToChar(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData adapt(BlockStateHolder state) {
|
||||
BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial();
|
||||
return material.getCraftBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundTag nbtData) {
|
||||
((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutTileEntityData(
|
||||
new BlockPosition(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
|
||||
7,
|
||||
(NBTTagCompound) fromNative(nbtData)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAndLightBlock(Location position, BlockState previousType) {
|
||||
this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(Location location, BlockStateHolder<?> state, boolean notifyAndLight) {
|
||||
return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeOP(Player player) {
|
||||
((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutEntityStatus(
|
||||
((CraftPlayer) player).getHandle(), (byte) 28
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.wrapper;
|
||||
import com.boydti.fawe.bukkit.wrapper.state.AsyncSign;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.destroystokyo.paper.block.BlockSoundGroup;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
@@ -221,15 +222,26 @@ public class AsyncBlock implements Block {
|
||||
int combined = queue.getCombinedId4Data(x, y, z, 0);
|
||||
BlockType type = BlockTypes.getFromStateId(combined);
|
||||
switch (type.getInternalId()) {
|
||||
case BlockID.SIGN:
|
||||
case BlockID.WALL_SIGN:
|
||||
case BlockID.ACACIA_SIGN:
|
||||
case BlockID.SPRUCE_SIGN:
|
||||
case BlockID.ACACIA_WALL_SIGN:
|
||||
case BlockID.BIRCH_SIGN:
|
||||
case BlockID.SPRUCE_WALL_SIGN:
|
||||
case BlockID.BIRCH_WALL_SIGN:
|
||||
case BlockID.DARK_OAK_SIGN:
|
||||
case BlockID.DARK_OAK_WALL_SIGN:
|
||||
case BlockID.JUNGLE_SIGN:
|
||||
case BlockID.JUNGLE_WALL_SIGN:
|
||||
case BlockID.OAK_SIGN:
|
||||
case BlockID.OAK_WALL_SIGN:
|
||||
return new AsyncSign(this, combined);
|
||||
default:
|
||||
return new AsyncBlockState(this, combined);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull @Override
|
||||
@Override
|
||||
@NotNull
|
||||
public AsyncBlockState getState(boolean useSnapshot) {
|
||||
return getState();
|
||||
}
|
||||
@@ -277,7 +289,14 @@ public class AsyncBlock implements Block {
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getType().isEmpty();
|
||||
switch (getType()) {
|
||||
case AIR:
|
||||
case CAVE_AIR:
|
||||
case VOID_AIR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -361,4 +380,9 @@ public class AsyncBlock implements Block {
|
||||
public BoundingBox getBoundingBox() {
|
||||
return this.getUnsafeBlock().getBoundingBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockSoundGroup getSoundGroup() {
|
||||
return TaskManager.IMP.sync(() -> getUnsafeBlock().getSoundGroup());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class AsyncChunk implements Chunk {
|
||||
|
||||
@@ -86,16 +87,8 @@ public class AsyncChunk implements Chunk {
|
||||
if (queue instanceof BukkitQueue_0) {
|
||||
BukkitQueue_0 bq = (BukkitQueue_0) queue;
|
||||
if (world.isChunkLoaded(x, z)) {
|
||||
long pair = MathMan.pairInt(x, z);
|
||||
Long originalKeep = BukkitQueue_0.keepLoaded.get(pair);
|
||||
BukkitQueue_0.keepLoaded.put(pair, Long.MAX_VALUE);
|
||||
if (world.isChunkLoaded(x, z)) {
|
||||
task.run();
|
||||
if (originalKeep != null) {
|
||||
BukkitQueue_0.keepLoaded.put(pair, originalKeep);
|
||||
} else {
|
||||
BukkitQueue_0.keepLoaded.remove(pair);
|
||||
}
|
||||
return task.value;
|
||||
}
|
||||
}
|
||||
@@ -130,14 +123,14 @@ public class AsyncChunk implements Chunk {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState[] getTileEntities(boolean b) {
|
||||
public @NotNull BlockState[] getTileEntities(boolean useSnapshot) {
|
||||
if (!isLoaded()) {
|
||||
return new BlockState[0];
|
||||
}
|
||||
return TaskManager.IMP.sync(new RunnableVal<BlockState[]>() {
|
||||
@Override
|
||||
public void run(BlockState[] value) {
|
||||
this.value = world.getChunkAt(x, z).getTileEntities(b);
|
||||
this.value = world.getChunkAt(x, z).getTileEntities(useSnapshot);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -162,13 +155,8 @@ public class AsyncChunk implements Chunk {
|
||||
return load(false);
|
||||
}
|
||||
|
||||
//Do not use the safe variable in this method for compatibility with 1.14+
|
||||
public boolean unload(boolean save, boolean safe) {
|
||||
return world.unloadChunk(x, z, save);
|
||||
}
|
||||
|
||||
public boolean unload(boolean save) {
|
||||
return unload(true, false);
|
||||
return world.unloadChunk(x, z, save);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.boydti.fawe.bukkit.wrapper;
|
||||
|
||||
import com.avaje.ebean.validation.NotNull;
|
||||
import com.bekvon.bukkit.residence.commands.material;
|
||||
import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
@@ -29,6 +29,7 @@ import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
@@ -47,6 +48,7 @@ import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Consumer;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Modify the world from an async thread<br>
|
||||
@@ -68,28 +70,6 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, v6, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity(UUID uuid) {
|
||||
return TaskManager.IMP.sync(() -> parent.getEntity(uuid));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean createExplosion(Entity source, Location loc, float power, boolean setFire, boolean breakBlocks) {
|
||||
return TaskManager.IMP.sync(() -> parent.createExplosion(source, loc, power, setFire, breakBlocks));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> void spawnParticle(Particle particle, List<Player> receivers, Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
parent.spawnParticle(particle, receivers, source, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void spawnParticle(Particle particle, List<Player> list, Player player, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t, boolean b) {
|
||||
parent.spawnParticle(particle, list, player, v, v1, v2, i, v3, v4, v5, v6, t, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #wrap(World)} instead
|
||||
* @param parent Parent world
|
||||
@@ -187,15 +167,6 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
}
|
||||
}
|
||||
|
||||
public int getHighestBlockYAt(int x, int z, com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
|
||||
return TaskManager.IMP.sync(new Supplier<Integer>() {
|
||||
@Override
|
||||
public Integer get() {
|
||||
return parent.getHighestBlockYAt(x, z, heightmap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldBorder getWorldBorder() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<WorldBorder>() {
|
||||
@@ -322,21 +293,6 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
return parent.isChunkGenerated(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(int x, int z, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(x, z, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(Location location, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(location, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(Block block, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(block, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkLoaded(Chunk chunk) {
|
||||
return chunk.isLoaded();
|
||||
@@ -428,17 +384,11 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
|
||||
@Override
|
||||
public boolean unloadChunk(int x, int z, boolean save) {
|
||||
return unloadChunk(x, z, save, false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean unloadChunk(final int x, final int z, final boolean save, final boolean safe) {
|
||||
if (isChunkLoaded(x, z)) {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean value) {
|
||||
this.value = parent.unloadChunk(x, z, save, safe);
|
||||
this.value = parent.unloadChunk(x, z, save);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -447,12 +397,15 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
|
||||
@Override
|
||||
public boolean unloadChunkRequest(int x, int z) {
|
||||
return unloadChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unloadChunkRequest(int x, int z, boolean safe) {
|
||||
return unloadChunk(x, z, safe);
|
||||
if (isChunkLoaded(x, z)) {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean value) {
|
||||
this.value = parent.unloadChunkRequest(x, z);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -503,8 +456,8 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Arrow> T spawnArrow(Location location, Vector vector, float v, float v1, Class<T> aClass) {
|
||||
return parent.spawnArrow(location, vector, v, v1, aClass);
|
||||
public <T extends AbstractArrow> @NotNull T spawnArrow(@NotNull Location location, @NotNull Vector direction, float speed, float spread, @NotNull Class<T> clazz) {
|
||||
return parent.spawnArrow(location, direction, speed, spread, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1134,61 +1087,6 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTileEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getTileEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTickableTileEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getTickableTileEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChunkCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getChunkCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getPlayerCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Chunk> getChunkAtAsync(int arg0, int arg1, boolean arg2) {
|
||||
return parent.getChunkAtAsync(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(BoundingBox arg0) {
|
||||
return parent.getNearbyEntities(arg0);
|
||||
@@ -1210,11 +1108,6 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
return parent.isChunkForceLoaded(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDayTime() {
|
||||
return parent.isDayTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location locateNearestStructure(Location arg0, StructureType arg1, int arg2, boolean arg3) {
|
||||
return parent.locateNearestStructure(arg0, arg1, arg2, arg3);
|
||||
@@ -1285,4 +1178,111 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
|
||||
public Collection<Chunk> getForceLoadedChunks() {
|
||||
return parent.getForceLoadedChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighestBlockYAt(int x, int z, com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
|
||||
return TaskManager.IMP.sync(new Supplier<Integer>() {
|
||||
@Override
|
||||
public Integer get() {
|
||||
return parent.getHighestBlockYAt(x, z, heightmap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTileEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getTileEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTickableTileEntityCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getTickableTileEntityCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChunkCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getChunkCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
|
||||
@Override
|
||||
public void run(Integer value) {
|
||||
this.value = parent.getPlayerCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Chunk> getChunkAtAsync(int arg0, int arg1, boolean arg2) {
|
||||
return parent.getChunkAtAsync(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDayTime() {
|
||||
return parent.isDayTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(int x, int z, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(x, z, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(Location location, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(location, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChunkAtAsync(Block block, ChunkLoadCallback cb) {
|
||||
parent.getChunkAtAsync(block, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity(UUID uuid) {
|
||||
return TaskManager.IMP.sync(() -> parent.getEntity(uuid));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean createExplosion(Entity source, Location loc, float power, boolean setFire, boolean breakBlocks) {
|
||||
return TaskManager.IMP.sync(() -> parent.createExplosion(source, loc, power, setFire, breakBlocks));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> void spawnParticle(Particle particle, List<Player> receivers, Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) {
|
||||
parent.spawnParticle(particle, receivers, source, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void spawnParticle(Particle particle, List<Player> list, Player player, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t, boolean b) {
|
||||
parent.spawnParticle(particle, list, player, v, v1, v2, i, v3, v4, v5, v6, t, b);
|
||||
}
|
||||
}
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package com.boydti.fawe.bukkit.wrapper.state;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import net.minecraft.server.v1_14_R1.NBTBase;
|
||||
import net.minecraft.server.v1_14_R1.NBTTagCompound;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.persistence.CraftPersistentDataAdapterContext;
|
||||
import org.bukkit.craftbukkit.v1_14_R1.persistence.CraftPersistentDataTypeRegistry;
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
public final class AsyncDataContainer implements PersistentDataContainer {
|
||||
private final CompoundTag root;
|
||||
|
||||
public AsyncDataContainer(CompoundTag root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
private CompoundTag root() {
|
||||
CompoundTag value = (CompoundTag) root.getValue().get("PublicBukkitValues");
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Tag> get() {
|
||||
return get(true);
|
||||
}
|
||||
|
||||
private Map<String, Tag> get(boolean create) {
|
||||
CompoundTag tag = root();
|
||||
Map<String, Tag> raw;
|
||||
if (tag == null) {
|
||||
if (!create) return Collections.emptyMap();
|
||||
Map<String, Tag> map = ReflectionUtils.getMap(root.getValue());
|
||||
map.put("PublicBukkitValues", new CompoundTag(raw = new HashMap<>()));
|
||||
} else {
|
||||
raw = ReflectionUtils.getMap(tag.getValue());
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
public <T, Z> void set(NamespacedKey key, PersistentDataType<T, Z> type, Z value) {
|
||||
Validate.notNull(key, "The provided key for the custom value was null");
|
||||
Validate.notNull(type, "The provided type for the custom value was null");
|
||||
Validate.notNull(value, "The provided value for the custom value was null");
|
||||
get().put(key.toString(), FaweCache.asTag(type.toPrimitive(value, null)));
|
||||
}
|
||||
|
||||
public <T, Z> boolean has(NamespacedKey key, PersistentDataType<T, Z> type) {
|
||||
Validate.notNull(key, "The provided key for the custom value was null");
|
||||
Validate.notNull(type, "The provided type for the custom value was null");
|
||||
Tag value = get(false).get(key.toString());
|
||||
if (value == null) return type == null;
|
||||
return type.getPrimitiveType() == value.getValue().getClass();
|
||||
}
|
||||
|
||||
public <T, Z> Z get(NamespacedKey key, PersistentDataType<T, Z> type) {
|
||||
Validate.notNull(key, "The provided key for the custom value was null");
|
||||
Validate.notNull(type, "The provided type for the custom value was null");
|
||||
Tag value = get(false).get(key.toString());
|
||||
return (Z) value.toRaw();
|
||||
}
|
||||
|
||||
public <T, Z> Z getOrDefault(NamespacedKey key, PersistentDataType<T, Z> type, Z defaultValue) {
|
||||
Z z = this.get(key, type);
|
||||
return z != null ? z : defaultValue;
|
||||
}
|
||||
|
||||
public void remove(NamespacedKey key) {
|
||||
Validate.notNull(key, "The provided key for the custom value was null");
|
||||
get(false).remove(key.toString());
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return get(false).isEmpty();
|
||||
}
|
||||
|
||||
public PersistentDataAdapterContext getAdapterContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof AsyncDataContainer)) {
|
||||
return false;
|
||||
} else {
|
||||
Map<String, Tag> myRawMap = this.getRaw();
|
||||
Map<String, Tag> theirRawMap = ((AsyncDataContainer)obj).getRaw();
|
||||
return Objects.equals(myRawMap, theirRawMap);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Tag> getRaw() {
|
||||
return get(false);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return get(false).hashCode();
|
||||
}
|
||||
|
||||
public Map<String, Object> serialize() {
|
||||
return new CompoundTag(get(false)).toRaw();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,13 @@ import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.server.v1_14_R1.TileEntitySign;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AsyncSign extends AsyncBlockState implements Sign {
|
||||
public AsyncSign(AsyncBlock block, int combined) {
|
||||
@@ -63,4 +69,28 @@ public class AsyncSign extends AsyncBlockState implements Sign {
|
||||
public void setEditable(boolean arg0) {
|
||||
this.isEditable = arg0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PersistentDataContainer getPersistentDataContainer() {
|
||||
return new AsyncDataContainer(getNbtData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DyeColor getColor() {
|
||||
CompoundTag nbt = getNbtData();
|
||||
if (nbt != null) {
|
||||
String color = nbt.getString("Color").toUpperCase();
|
||||
if (color != null) return DyeColor.valueOf(color);
|
||||
}
|
||||
return DyeColor.BLACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(DyeColor color) {
|
||||
CompoundTag nbt = getNbtData();
|
||||
if (nbt != null) {
|
||||
Map<String, Tag> map = ReflectionUtils.getMap(nbt.getValue());
|
||||
map.put("Color", new StringTag(color.name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -88,7 +89,7 @@ public class BukkitEntity implements Entity {
|
||||
|
||||
@Override
|
||||
public com.sk89q.worldedit.world.entity.EntityType getType() {
|
||||
return EntityTypes.get(type.getName().toUpperCase());
|
||||
return EntityTypes.get(type.getName().toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
package org.bstats.bukkit;
|
||||
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class MetricsLite {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = defaultPackage;
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
*/
|
||||
public MetricsLite(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", Settings.IMP.METRICS);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
|
||||
// Load the data
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
enabled = config.getBoolean("enabled", Settings.IMP.METRICS);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JsonObject getPluginData() {
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.addProperty("pluginName", pluginName); // Append the name of the plugin
|
||||
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
data.add("customCharts", new JsonArray());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JsonObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
String bukkitName = Bukkit.getName();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bukkitVersion", bukkitVersion);
|
||||
data.addProperty("bukkitName", bukkitName);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JsonObject data = getServerData();
|
||||
|
||||
JsonArray pluginData = new JsonArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
} else { // old bstats version compatibility
|
||||
try {
|
||||
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
|
||||
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
|
||||
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
|
||||
jsonStringGetter.setAccessible(true);
|
||||
String jsonString = (String) jsonStringGetter.invoke(plugin);
|
||||
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
pluginData.add(object);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// minecraft version 1.14+
|
||||
if (logFailedRequests) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
|
||||
}
|
||||
continue; // continue looping since we cannot do any other thing.
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
|
||||
data.add("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user