Upstream Paper
This commit is contained in:
132
src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
132
src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class MinecraftTimings {
|
||||
|
||||
public static final Timing playerListTimer = Timings.ofSafe("Player List");
|
||||
public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions");
|
||||
public static final Timing connectionTimer = Timings.ofSafe("Connection Handler");
|
||||
public static final Timing tickablesTimer = Timings.ofSafe("Tickables");
|
||||
public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler");
|
||||
public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler");
|
||||
public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending");
|
||||
public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing");
|
||||
public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick");
|
||||
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
|
||||
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
|
||||
public static final Timing savePlayers = Timings.ofSafe("Save Players");
|
||||
|
||||
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
|
||||
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
|
||||
public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing");
|
||||
public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks");
|
||||
public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation");
|
||||
|
||||
public static final Timing processQueueTimer = Timings.ofSafe("processQueue");
|
||||
|
||||
public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand");
|
||||
|
||||
public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck");
|
||||
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
|
||||
private static final Map<Class<?>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
|
||||
/**
|
||||
* Gets a timer associated with a plugins tasks.
|
||||
* @param bukkitTask
|
||||
* @param period
|
||||
* @return
|
||||
*/
|
||||
public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) {
|
||||
if (!bukkitTask.isSync()) {
|
||||
return null;
|
||||
}
|
||||
Plugin plugin;
|
||||
|
||||
CraftTask craftTask = (CraftTask) bukkitTask;
|
||||
|
||||
final Class<?> taskClass = craftTask.getTaskClass();
|
||||
if (bukkitTask.getOwner() != null) {
|
||||
plugin = bukkitTask.getOwner();
|
||||
} else {
|
||||
plugin = TimingsManager.getPluginByClassloader(taskClass);
|
||||
}
|
||||
|
||||
final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz ->
|
||||
clazz.isAnonymousClass() || clazz.isLocalClass()
|
||||
? clazz.getName()
|
||||
: clazz.getCanonicalName());
|
||||
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
name.append("Task: ").append(taskname);
|
||||
if (period > 0) {
|
||||
name.append(" (interval:").append(period).append(")");
|
||||
} else {
|
||||
name.append(" (Single)");
|
||||
}
|
||||
|
||||
if (plugin == null) {
|
||||
return Timings.ofSafe(null, name.toString());
|
||||
}
|
||||
|
||||
return Timings.ofSafe(plugin, name.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getEntityTimings(Entity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified tile entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getTileEntityTimings(TileEntity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer);
|
||||
}
|
||||
public static Timing getCancelTasksTimer() {
|
||||
return Timings.ofSafe("Cancel Tasks");
|
||||
}
|
||||
public static Timing getCancelTasksTimer(Plugin plugin) {
|
||||
return Timings.ofSafe(plugin, "Cancel Tasks");
|
||||
}
|
||||
|
||||
public static void stopServer() {
|
||||
TimingsManager.stopServer();
|
||||
}
|
||||
|
||||
public static Timing getBlockTiming(Block block) {
|
||||
return Timings.ofSafe("## Scheduled Block: " + block.toString(), scheduledBlocksTimer);
|
||||
}
|
||||
/*
|
||||
public static Timing getStructureTiming(StructureGenerator structureGenerator) {
|
||||
return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer);
|
||||
}*/
|
||||
|
||||
public static Timing getPacketTiming(Packet packet) {
|
||||
return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer);
|
||||
}
|
||||
|
||||
public static Timing getCommandFunctionTiming(CustomFunction function) {
|
||||
return Timings.ofSafe("Command Function - " + function.getMinecraftKey().toString());
|
||||
}
|
||||
}
|
||||
108
src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
108
src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
/**
|
||||
* Set of timers per world, to track world specific timings.
|
||||
*/
|
||||
public class WorldTimingsHandler {
|
||||
public final Timing mobSpawn;
|
||||
public final Timing doChunkUnload;
|
||||
public final Timing doPortalForcer;
|
||||
public final Timing scheduledBlocks;
|
||||
public final Timing scheduledBlocksCleanup;
|
||||
public final Timing scheduledBlocksTicking;
|
||||
public final Timing chunkTicks;
|
||||
public final Timing lightChunk;
|
||||
public final Timing chunkTicksBlocks;
|
||||
public final Timing doVillages;
|
||||
public final Timing doChunkMap;
|
||||
public final Timing doChunkMapUpdate;
|
||||
public final Timing doChunkMapToUpdate;
|
||||
public final Timing doChunkMapSortMissing;
|
||||
public final Timing doChunkMapSortSendToPlayers;
|
||||
public final Timing doChunkMapPlayersNeedingChunks;
|
||||
public final Timing doChunkMapPendingSendToPlayers;
|
||||
public final Timing doChunkMapUnloadChunks;
|
||||
public final Timing doChunkGC;
|
||||
public final Timing doSounds;
|
||||
public final Timing entityRemoval;
|
||||
public final Timing entityTick;
|
||||
public final Timing tileEntityTick;
|
||||
public final Timing tileEntityPending;
|
||||
public final Timing tracker1;
|
||||
public final Timing tracker2;
|
||||
public final Timing doTick;
|
||||
public final Timing tickEntities;
|
||||
|
||||
public final Timing syncChunkLoadTimer;
|
||||
public final Timing syncChunkLoadDataTimer;
|
||||
public final Timing syncChunkLoadStructuresTimer;
|
||||
public final Timing syncChunkLoadPostTimer;
|
||||
public final Timing syncChunkLoadPopulateTimer;
|
||||
public final Timing chunkLoadLevelTimer;
|
||||
public final Timing chunkGeneration;
|
||||
public final Timing chunkIOStage1;
|
||||
public final Timing chunkIOStage2;
|
||||
public final Timing worldSave;
|
||||
public final Timing worldSaveChunks;
|
||||
public final Timing worldSaveLevel;
|
||||
public final Timing chunkSaveData;
|
||||
|
||||
public final Timing lightingQueueTimer;
|
||||
|
||||
public WorldTimingsHandler(World server) {
|
||||
String name = server.worldData.getName() +" - ";
|
||||
|
||||
mobSpawn = Timings.ofSafe(name + "mobSpawn");
|
||||
doChunkUnload = Timings.ofSafe(name + "doChunkUnload");
|
||||
scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks");
|
||||
scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup");
|
||||
scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking");
|
||||
chunkTicks = Timings.ofSafe(name + "Chunk Ticks");
|
||||
lightChunk = Timings.ofSafe(name + "Light Chunk");
|
||||
chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks");
|
||||
doVillages = Timings.ofSafe(name + "doVillages");
|
||||
doChunkMap = Timings.ofSafe(name + "doChunkMap");
|
||||
doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update");
|
||||
doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update");
|
||||
doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing");
|
||||
doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players");
|
||||
doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks");
|
||||
doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players");
|
||||
doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks");
|
||||
doSounds = Timings.ofSafe(name + "doSounds");
|
||||
doChunkGC = Timings.ofSafe(name + "doChunkGC");
|
||||
doPortalForcer = Timings.ofSafe(name + "doPortalForcer");
|
||||
entityTick = Timings.ofSafe(name + "entityTick");
|
||||
entityRemoval = Timings.ofSafe(name + "entityRemoval");
|
||||
tileEntityTick = Timings.ofSafe(name + "tileEntityTick");
|
||||
tileEntityPending = Timings.ofSafe(name + "tileEntityPending");
|
||||
|
||||
syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad");
|
||||
syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data");
|
||||
syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures");
|
||||
syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post");
|
||||
syncChunkLoadPopulateTimer = Timings.ofSafe(name + "chunkLoad - Populate");
|
||||
chunkLoadLevelTimer = Timings.ofSafe(name + "chunkLoad - Load Level");
|
||||
chunkGeneration = Timings.ofSafe(name + "chunkGeneration");
|
||||
chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO");
|
||||
chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load");
|
||||
worldSave = Timings.ofSafe(name + "World Save");
|
||||
worldSaveLevel = Timings.ofSafe(name + "World Save - Level");
|
||||
worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks");
|
||||
chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data");
|
||||
|
||||
tracker1 = Timings.ofSafe(name + "tracker stage 1");
|
||||
tracker2 = Timings.ofSafe(name + "tracker stage 2");
|
||||
doTick = Timings.ofSafe(name + "doTick");
|
||||
tickEntities = Timings.ofSafe(name + "tickEntities");
|
||||
|
||||
lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
|
||||
}
|
||||
|
||||
public static Timing getTickList(WorldServer worldserver, String timingsType) {
|
||||
return Timings.ofSafe(worldserver.getWorldData().getName() + " - Scheduled " + timingsType);
|
||||
}
|
||||
}
|
||||
627
src/main/java/com/destroystokyo/paper/Metrics.java
Normal file
627
src/main/java/com/destroystokyo/paper/Metrics.java
Normal file
@@ -0,0 +1,627 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
*
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
public class Metrics {
|
||||
|
||||
// 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/server-implementation";
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests = false;
|
||||
|
||||
// The logger for the failed requests
|
||||
private static Logger logger = Logger.getLogger("bStats");
|
||||
|
||||
// The name of the server software
|
||||
private final String name;
|
||||
|
||||
// The uuid of the server
|
||||
private final String serverUUID;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param name The name of the server software.
|
||||
* @param serverUUID The uuid of the server.
|
||||
* @param logFailedRequests Whether failed requests should be logged or not.
|
||||
* @param logger The logger for the failed requests.
|
||||
*/
|
||||
public Metrics(String name, String serverUUID, boolean logFailedRequests, Logger logger) {
|
||||
this.name = name;
|
||||
this.serverUUID = serverUUID;
|
||||
Metrics.logFailedRequests = logFailedRequests;
|
||||
Metrics.logger = logger;
|
||||
|
||||
// Start submitting the data
|
||||
startSubmitting();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
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.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
private JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("pluginName", name); // Append the name of the server software
|
||||
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() {
|
||||
// OS specific data
|
||||
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("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
pluginData.add(getPluginData());
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
try {
|
||||
// We are still in the Thread of the timer, so nothing get blocked :)
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
logger.log(Level.WARNING, "Could not submit stats of " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
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();
|
||||
|
||||
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("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) {
|
||||
logger.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PaperMetrics {
|
||||
static void startMetrics() {
|
||||
// Get the config file
|
||||
File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "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);
|
||||
|
||||
// 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
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
// Only start Metrics, if it's enabled in the config
|
||||
if (config.getBoolean("enabled", true)) {
|
||||
Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
|
||||
|
||||
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
||||
String minecraftVersion = Bukkit.getVersion();
|
||||
minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1);
|
||||
return minecraftVersion;
|
||||
}));
|
||||
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"));
|
||||
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
Map<String, Integer> entry = new HashMap<>();
|
||||
entry.put(javaVersion, 1);
|
||||
|
||||
// http://openjdk.java.net/jeps/223
|
||||
// Java decided to change their versioning scheme and in doing so modified the java.version system
|
||||
// property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier
|
||||
// we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+
|
||||
String majorVersion = javaVersion.split("\\.")[0];
|
||||
String release;
|
||||
|
||||
int indexOf = javaVersion.lastIndexOf('.');
|
||||
|
||||
if (majorVersion.equals("1")) {
|
||||
release = "Java " + javaVersion.substring(0, indexOf);
|
||||
} else {
|
||||
// of course, it really wouldn't be all that simple if they didn't add a quirk, now would it
|
||||
// valid strings for the major may potentially include values such as -ea to deannotate a pre release
|
||||
Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
|
||||
if (versionMatcher.find()) {
|
||||
majorVersion = versionMatcher.group(0);
|
||||
}
|
||||
release = "Java " + majorVersion;
|
||||
}
|
||||
map.put(release, entry);
|
||||
|
||||
return map;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
238
src/main/java/com/destroystokyo/paper/PaperCommand.java
Normal file
238
src/main/java/com/destroystokyo/paper/PaperCommand.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.server.*;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
this.description = "Paper related commands";
|
||||
this.usageMessage = "/paper [heap | entity | reload | version]";
|
||||
this.setPermission("bukkit.command.paper");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
||||
if (args.length <= 1)
|
||||
return getListMatchingLast(args, "heap", "entity", "reload", "version");
|
||||
|
||||
switch (args[0].toLowerCase(Locale.ENGLISH))
|
||||
{
|
||||
case "entity":
|
||||
if (args.length == 2)
|
||||
return getListMatchingLast(args, "help", "list");
|
||||
if (args.length == 3)
|
||||
return getListMatchingLast(args, EntityTypes.getEntityNameList().stream().map(MinecraftKey::toString).sorted().toArray(String[]::new));
|
||||
break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Code from Mojang - copyright them
|
||||
public static List<String> getListMatchingLast(String[] args, String... matches) {
|
||||
return getListMatchingLast(args, (Collection) Arrays.asList(matches));
|
||||
}
|
||||
|
||||
public static boolean matches(String s, String s1) {
|
||||
return s1.regionMatches(true, 0, s, 0, s.length());
|
||||
}
|
||||
|
||||
public static List<String> getListMatchingLast(String[] strings, Collection<?> collection) {
|
||||
String last = strings[strings.length - 1];
|
||||
ArrayList<String> results = Lists.newArrayList();
|
||||
|
||||
if (!collection.isEmpty()) {
|
||||
Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
String s1 = (String) iterator.next();
|
||||
|
||||
if (matches(last, s1)) {
|
||||
results.add(s1);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
iterator = collection.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Object object = iterator.next();
|
||||
|
||||
if (object instanceof MinecraftKey && matches(last, ((MinecraftKey) object).getKey())) {
|
||||
results.add(String.valueOf(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
// end copy stuff
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||
if (!testPermission(sender)) return true;
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase(Locale.ENGLISH)) {
|
||||
case "heap":
|
||||
dumpHeap(sender);
|
||||
break;
|
||||
case "entity":
|
||||
listEntities(sender, args);
|
||||
break;
|
||||
case "reload":
|
||||
doReload(sender);
|
||||
break;
|
||||
case "ver":
|
||||
case "version":
|
||||
org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version").execute(sender, commandLabel, new String[0]);
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
|
||||
*/
|
||||
private void listEntities(CommandSender sender, String[] args) {
|
||||
if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
sender.sendMessage(ChatColor.RED + "Use /paper entity [list] help for more information on a specific command.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[1].toLowerCase(Locale.ENGLISH)) {
|
||||
case "list":
|
||||
String filter = "*";
|
||||
if (args.length > 2) {
|
||||
if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
sender.sendMessage(ChatColor.RED + "Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.");
|
||||
return;
|
||||
}
|
||||
filter = args[2];
|
||||
}
|
||||
final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
||||
Set<MinecraftKey> names = EntityTypes.getEntityNameList().stream()
|
||||
.filter(n -> n.toString().matches(cleanfilter))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (names.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.RED + "Invalid filter, does not match any entities. Use /paper entity list for a proper list");
|
||||
return;
|
||||
}
|
||||
|
||||
String worldName;
|
||||
if (args.length > 3) {
|
||||
worldName = args[3];
|
||||
} else if (sender instanceof Player) {
|
||||
worldName = ((Player) sender).getWorld().getName();
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Please specify the name of a world");
|
||||
sender.sendMessage(ChatColor.RED + "To do so without a filter, specify '*' as the filter");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<MinecraftKey, MutablePair<Integer, Map<ChunkCoordIntPair, Integer>>> list = Maps.newHashMap();
|
||||
World bukkitWorld = Bukkit.getWorld(worldName);
|
||||
if (bukkitWorld == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Could not load world for " + worldName + ". Please select a valid world.");
|
||||
return;
|
||||
}
|
||||
WorldServer world = ((CraftWorld) Bukkit.getWorld(worldName)).getHandle();
|
||||
|
||||
List<Entity> entities = world.entityList;
|
||||
entities.forEach(e -> {
|
||||
MinecraftKey key = e.getMinecraftKey();
|
||||
if (e.shouldBeRemoved) return; // Paper
|
||||
|
||||
MutablePair<Integer, Map<ChunkCoordIntPair, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
|
||||
ChunkCoordIntPair chunk = new ChunkCoordIntPair(e.getChunkX(), e.getChunkZ());
|
||||
info.left++;
|
||||
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||
});
|
||||
|
||||
if (names.size() == 1) {
|
||||
MinecraftKey name = names.iterator().next();
|
||||
Pair<Integer, Map<ChunkCoordIntPair, Integer>> info = list.get(name);
|
||||
if (info == null) {
|
||||
sender.sendMessage(ChatColor.RED + "No entities found.");
|
||||
return;
|
||||
}
|
||||
sender.sendMessage("Entity: " + name + " Total: " + info.getLeft());
|
||||
info.getRight().entrySet().stream()
|
||||
.sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
.limit(10).forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z));
|
||||
} else {
|
||||
List<Pair<MinecraftKey, Integer>> info = list.entrySet().stream()
|
||||
.filter(e -> names.contains(e.getKey()))
|
||||
.map(e -> Pair.of(e.getKey(), e.getValue().left))
|
||||
.sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (info == null || info.size() == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "No entities found.");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = info.stream().mapToInt(Pair::getRight).sum();
|
||||
sender.sendMessage("Total: " + count);
|
||||
info.forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpHeap(CommandSender sender) {
|
||||
File file = new File(new File(new File("."), "dumps"),
|
||||
"heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + "-server.hprof");
|
||||
Command.broadcastCommandMessage(sender, ChatColor.YELLOW + "Writing JVM heap data to " + file);
|
||||
if (CraftServer.dumpHeap(file)) {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Heap dump complete");
|
||||
} else {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed to write heap dump, see sever log for details");
|
||||
}
|
||||
}
|
||||
|
||||
private void doReload(CommandSender sender) {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
|
||||
|
||||
MinecraftServer console = MinecraftServer.getServer();
|
||||
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings"));
|
||||
for (WorldServer world : console.getWorlds()) {
|
||||
world.paperConfig.init();
|
||||
}
|
||||
console.server.reloadCount++;
|
||||
|
||||
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete.");
|
||||
}
|
||||
}
|
||||
465
src/main/java/com/destroystokyo/paper/PaperConfig.java
Normal file
465
src/main/java/com/destroystokyo/paper/PaperConfig.java
Normal file
@@ -0,0 +1,465 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import co.aikar.timings.Timings;
|
||||
import co.aikar.timings.TimingsManager;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
|
||||
public class PaperConfig {
|
||||
|
||||
private static File CONFIG_FILE;
|
||||
private static final String HEADER = "This is the main configuration file for Paper.\n"
|
||||
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
|
||||
+ "with caution, and make sure you know what each option does before configuring.\n"
|
||||
+ "\n"
|
||||
+ "If you need help with the configuration or have any questions related to Paper,\n"
|
||||
+ "join us in our Discord or IRC channel.\n"
|
||||
+ "\n"
|
||||
+ "Discord: https://paperdiscord.emc.gs\n"
|
||||
+ "IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )\n"
|
||||
+ "Website: https://papermc.io/ \n"
|
||||
+ "Docs: https://paper.readthedocs.org/ \n";
|
||||
/*========================================================================*/
|
||||
public static YamlConfiguration config;
|
||||
static int version;
|
||||
static Map<String, Command> commands;
|
||||
private static boolean verbose;
|
||||
private static boolean fatalError;
|
||||
/*========================================================================*/
|
||||
private static boolean metricsStarted;
|
||||
|
||||
public static void init(File configFile) {
|
||||
CONFIG_FILE = configFile;
|
||||
config = new YamlConfiguration();
|
||||
try {
|
||||
config.load(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
} catch (InvalidConfigurationException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not load paper.yml, please correct your syntax errors", ex);
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
config.options().header(HEADER);
|
||||
config.options().copyDefaults(true);
|
||||
verbose = getBoolean("verbose", false);
|
||||
|
||||
commands = new HashMap<String, Command>();
|
||||
commands.put("paper", new PaperCommand("paper"));
|
||||
|
||||
version = getInt("config-version", 17);
|
||||
set("config-version", 17);
|
||||
readConfig(PaperConfig.class, null);
|
||||
}
|
||||
|
||||
protected static void logError(String s) {
|
||||
Bukkit.getLogger().severe(s);
|
||||
}
|
||||
|
||||
protected static void fatal(String s) {
|
||||
fatalError = true;
|
||||
throw new RuntimeException("Fatal paper.yml config error: " + s);
|
||||
}
|
||||
|
||||
protected static void log(String s) {
|
||||
if (verbose) {
|
||||
Bukkit.getLogger().info(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerCommands() {
|
||||
for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
||||
MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue());
|
||||
}
|
||||
|
||||
if (!metricsStarted) {
|
||||
Metrics.PaperMetrics.startMetrics();
|
||||
metricsStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void readConfig(Class<?> clazz, Object instance) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (Modifier.isPrivate(method.getModifiers())) {
|
||||
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(instance);
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw Throwables.propagate(ex.getCause());
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern SPACE = Pattern.compile(" ");
|
||||
private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]");
|
||||
public static int getSeconds(String str) {
|
||||
str = SPACE.matcher(str).replaceAll("");
|
||||
final char unit = str.charAt(str.length() - 1);
|
||||
str = NOT_NUMERIC.matcher(str).replaceAll("");
|
||||
double num;
|
||||
try {
|
||||
num = Double.parseDouble(str);
|
||||
} catch (Exception e) {
|
||||
num = 0D;
|
||||
}
|
||||
switch (unit) {
|
||||
case 'd': num *= (double) 60*60*24; break;
|
||||
case 'h': num *= (double) 60*60; break;
|
||||
case 'm': num *= (double) 60; break;
|
||||
default: case 's': break;
|
||||
}
|
||||
return (int) num;
|
||||
}
|
||||
|
||||
protected static String timeSummary(int seconds) {
|
||||
String time = "";
|
||||
|
||||
if (seconds > 60 * 60 * 24) {
|
||||
time += TimeUnit.SECONDS.toDays(seconds) + "d";
|
||||
seconds %= 60 * 60 * 24;
|
||||
}
|
||||
|
||||
if (seconds > 60 * 60) {
|
||||
time += TimeUnit.SECONDS.toHours(seconds) + "h";
|
||||
seconds %= 60 * 60;
|
||||
}
|
||||
|
||||
if (seconds > 0) {
|
||||
time += TimeUnit.SECONDS.toMinutes(seconds) + "m";
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
private static void set(String path, Object val) {
|
||||
config.set(path, val);
|
||||
}
|
||||
|
||||
private static boolean getBoolean(String path, boolean def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getBoolean(path, config.getBoolean(path));
|
||||
}
|
||||
|
||||
private static double getDouble(String path, double def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getDouble(path, config.getDouble(path));
|
||||
}
|
||||
|
||||
private static float getFloat(String path, float def) {
|
||||
// TODO: Figure out why getFloat() always returns the default value.
|
||||
return (float) getDouble(path, (double) def);
|
||||
}
|
||||
|
||||
private static int getInt(String path, int def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getInt(path, config.getInt(path));
|
||||
}
|
||||
|
||||
private static <T> List getList(String path, T def) {
|
||||
config.addDefault(path, def);
|
||||
return (List<T>) config.getList(path, config.getList(path));
|
||||
}
|
||||
|
||||
private static String getString(String path, String def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getString(path, config.getString(path));
|
||||
}
|
||||
|
||||
public static int maxTickMsLostLightQueue;
|
||||
private static void lightQueue() {
|
||||
int badSetting = config.getInt("queue-light-updates-max-loss", 10);
|
||||
config.set("queue-light-updates-max-loss", null);
|
||||
maxTickMsLostLightQueue = getInt("settings.queue-light-updates-max-loss", badSetting);
|
||||
}
|
||||
|
||||
private static void timings() {
|
||||
boolean timings = getBoolean("timings.enabled", true);
|
||||
boolean verboseTimings = getBoolean("timings.verbose", true);
|
||||
TimingsManager.privacy = getBoolean("timings.server-name-privacy", false);
|
||||
TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses"));
|
||||
int timingHistoryInterval = getInt("timings.history-interval", 300);
|
||||
int timingHistoryLength = getInt("timings.history-length", 3600);
|
||||
|
||||
|
||||
Timings.setVerboseTimingsEnabled(verboseTimings);
|
||||
Timings.setTimingsEnabled(timings);
|
||||
Timings.setHistoryInterval(timingHistoryInterval * 20);
|
||||
Timings.setHistoryLength(timingHistoryLength * 20);
|
||||
|
||||
log("Timings: " + timings +
|
||||
" - Verbose: " + verboseTimings +
|
||||
" - Interval: " + timeSummary(Timings.getHistoryInterval() / 20) +
|
||||
" - Length: " + timeSummary(Timings.getHistoryLength() / 20));
|
||||
}
|
||||
|
||||
public static boolean enableFileIOThreadSleep;
|
||||
private static void enableFileIOThreadSleep() {
|
||||
enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false);
|
||||
if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues");
|
||||
}
|
||||
|
||||
public static boolean loadPermsBeforePlugins = true;
|
||||
private static void loadPermsBeforePlugins() {
|
||||
loadPermsBeforePlugins = getBoolean("settings.load-permissions-yml-before-plugins", true);
|
||||
}
|
||||
|
||||
public static int regionFileCacheSize = 256;
|
||||
private static void regionFileCacheSize() {
|
||||
regionFileCacheSize = getInt("settings.region-file-cache-size", 256);
|
||||
}
|
||||
|
||||
public static boolean enablePlayerCollisions = true;
|
||||
private static void enablePlayerCollisions() {
|
||||
enablePlayerCollisions = getBoolean("settings.enable-player-collisions", true);
|
||||
}
|
||||
|
||||
public static boolean saveEmptyScoreboardTeams = false;
|
||||
private static void saveEmptyScoreboardTeams() {
|
||||
saveEmptyScoreboardTeams = getBoolean("settings.save-empty-scoreboard-teams", false);
|
||||
}
|
||||
|
||||
public static boolean bungeeOnlineMode = true;
|
||||
private static void bungeeOnlineMode() {
|
||||
bungeeOnlineMode = getBoolean("settings.bungee-online-mode", true);
|
||||
}
|
||||
|
||||
public static boolean isProxyOnlineMode() {
|
||||
return Bukkit.getOnlineMode() || (SpigotConfig.bungee && bungeeOnlineMode) || (velocitySupport && velocityOnlineMode);
|
||||
}
|
||||
|
||||
public static int packetInSpamThreshold = 300;
|
||||
private static void packetInSpamThreshold() {
|
||||
if (version < 11) {
|
||||
int oldValue = getInt("settings.play-in-use-item-spam-threshold", 300);
|
||||
set("settings.incoming-packet-spam-threshold", oldValue);
|
||||
}
|
||||
packetInSpamThreshold = getInt("settings.incoming-packet-spam-threshold", 300);
|
||||
}
|
||||
|
||||
public static String flyingKickPlayerMessage = "Flying is not enabled on this server";
|
||||
public static String flyingKickVehicleMessage = "Flying is not enabled on this server";
|
||||
private static void flyingKickMessages() {
|
||||
flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage);
|
||||
flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage);
|
||||
}
|
||||
|
||||
public static int playerAutoSaveRate = -1;
|
||||
public static int maxPlayerAutoSavePerTick = 10;
|
||||
private static void playerAutoSaveRate() {
|
||||
playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1);
|
||||
maxPlayerAutoSavePerTick = getInt("settings.max-player-auto-save-per-tick", -1);
|
||||
if (maxPlayerAutoSavePerTick == -1) { // -1 Automatic / "Recommended"
|
||||
// 10 should be safe for everyone unless your mass spamming player auto save
|
||||
maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean suggestPlayersWhenNullTabCompletions = true;
|
||||
private static void suggestPlayersWhenNull() {
|
||||
suggestPlayersWhenNullTabCompletions = getBoolean("settings.suggest-player-names-when-null-tab-completions", suggestPlayersWhenNullTabCompletions);
|
||||
}
|
||||
|
||||
public static String authenticationServersDownKickMessage = ""; // empty = use translatable message
|
||||
private static void authenticationServersDownKickMessage() {
|
||||
authenticationServersDownKickMessage = Strings.emptyToNull(getString("messages.kick.authentication-servers-down", authenticationServersDownKickMessage));
|
||||
}
|
||||
|
||||
public static String connectionThrottleKickMessage = "Connection throttled! Please wait before reconnecting.";
|
||||
private static void connectionThrottleKickMessage() {
|
||||
connectionThrottleKickMessage = getString("messages.kick.connection-throttle", connectionThrottleKickMessage);
|
||||
}
|
||||
|
||||
public static String noPermissionMessage = "&cI'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.";
|
||||
private static void noPermissionMessage() {
|
||||
noPermissionMessage = ChatColor.translateAlternateColorCodes('&', getString("messages.no-permission", noPermissionMessage));
|
||||
}
|
||||
|
||||
public static boolean savePlayerData = true;
|
||||
private static void savePlayerData() {
|
||||
savePlayerData = getBoolean("settings.save-player-data", savePlayerData);
|
||||
if(!savePlayerData) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Player Data Saving is currently disabled. Any changes to your players data, " +
|
||||
"such as inventories, experience points, advancements and the like will not be saved when they log out.");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean useAlternativeLuckFormula = false;
|
||||
private static void useAlternativeLuckFormula() {
|
||||
useAlternativeLuckFormula = getBoolean("settings.use-alternative-luck-formula", false);
|
||||
if (useAlternativeLuckFormula) {
|
||||
Bukkit.getLogger().log(Level.INFO, "Using Aikar's Alternative Luck Formula to apply Luck attribute to all loot pool calculations. See https://luckformula.emc.gs");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean useVersionedWorld = false;
|
||||
private static void useVersionedWorld() {
|
||||
useVersionedWorld = getBoolean("settings.use-versioned-world", false);
|
||||
if (useVersionedWorld) {
|
||||
Logger logger = Bukkit.getLogger();
|
||||
String ver = MinecraftServer.getServer().getVersion();
|
||||
logger.log(Level.INFO, "******************************************************");
|
||||
logger.log(Level.INFO, "*** Using a versioned world folder. Your world will be saved");
|
||||
logger.log(Level.INFO, "*** to into the " + ver + " folder, but copied from your current world.");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** This setting should not be used in your real world!!!");
|
||||
logger.log(Level.INFO, "*** If you want to retain the new world, you need to move ");
|
||||
logger.log(Level.INFO, "*** the folders out of the " + ver + " folder and overwrite existing");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** Deleting the " + ver + " folder will cause it to recreate again");
|
||||
logger.log(Level.INFO, "*** from your unversioned world files.");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** You should backup your original world files incase something goes");
|
||||
logger.log(Level.INFO, "*** wrong with this system! This is not a backup system.");
|
||||
logger.log(Level.INFO, "******************************************************");
|
||||
}
|
||||
}
|
||||
|
||||
public static int watchdogPrintEarlyWarningEvery = 5000;
|
||||
public static int watchdogPrintEarlyWarningDelay = 10000;
|
||||
private static void watchdogEarlyWarning() {
|
||||
watchdogPrintEarlyWarningEvery = getInt("settings.watchdog.early-warning-every", 5000);
|
||||
watchdogPrintEarlyWarningDelay = getInt("settings.watchdog.early-warning-delay", 10000);
|
||||
WatchdogThread.doStart(SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash );
|
||||
}
|
||||
|
||||
public static int tabSpamIncrement = 1;
|
||||
public static int tabSpamLimit = 500;
|
||||
private static void tabSpamLimiters() {
|
||||
tabSpamIncrement = getInt("settings.spam-limiter.tab-spam-increment", tabSpamIncrement);
|
||||
// Older versions used a smaller limit, which is too low for 1.13, we'll bump this up if default
|
||||
if (version < 14) {
|
||||
if (tabSpamIncrement == 10) {
|
||||
set("settings.spam-limiter.tab-spam-increment", 2);
|
||||
tabSpamIncrement = 2;
|
||||
}
|
||||
}
|
||||
tabSpamLimit = getInt("settings.spam-limiter.tab-spam-limit", tabSpamLimit);
|
||||
}
|
||||
|
||||
public static Map<String, Long> seedOverride = new java.util.HashMap<>();
|
||||
private static void worldSeedOverrides() {
|
||||
ConfigurationSection seeds = config.getConfigurationSection("seed-overrides");
|
||||
if (seeds != null) {
|
||||
TimingsManager.hiddenConfigs.add("seed-overrides");
|
||||
for (String key : seeds.getKeys(false)) {
|
||||
String seedString = seeds.getString(key);
|
||||
long seed;
|
||||
try {
|
||||
seed = Long.parseLong(seedString);
|
||||
} catch (Exception e) {
|
||||
seed = (long) seedString.hashCode();
|
||||
}
|
||||
log("Seed Override: " + key + " => " + seed);
|
||||
seedOverride.put(key, seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean asyncChunks = false;
|
||||
public static boolean asyncChunkGeneration = true;
|
||||
public static boolean asyncChunkGenThreadPerWorld = true;
|
||||
public static int asyncChunkLoadThreads = -1;
|
||||
private static void asyncChunks() {
|
||||
if (version < 15) {
|
||||
boolean enabled = config.getBoolean("settings.async-chunks", true);
|
||||
ConfigurationSection section = config.createSection("settings.async-chunks");
|
||||
section.set("enable", enabled);
|
||||
section.set("load-threads", -1);
|
||||
section.set("generation", true);
|
||||
section.set("thread-per-world-generation", true);
|
||||
}
|
||||
|
||||
asyncChunks = getBoolean("settings.async-chunks.enable", true);
|
||||
asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true);
|
||||
asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true);
|
||||
asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1);
|
||||
if (asyncChunkLoadThreads <= 0) {
|
||||
asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Runtime.getRuntime().availableProcessors() * 1.5);
|
||||
}
|
||||
|
||||
// Let Shared Host set some limits
|
||||
String sharedHostEnvGen = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN");
|
||||
String sharedHostEnvLoad = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD");
|
||||
if ("1".equals(sharedHostEnvGen)) {
|
||||
log("Async Chunks - Generation: Your host has requested to use a single thread world generation");
|
||||
asyncChunkGenThreadPerWorld = false;
|
||||
} else if ("2".equals(sharedHostEnvGen)) {
|
||||
log("Async Chunks - Generation: Your host has disabled async world generation - You will experience lag from world generation");
|
||||
asyncChunkGeneration = false;
|
||||
}
|
||||
|
||||
if (sharedHostEnvLoad != null) {
|
||||
try {
|
||||
asyncChunkLoadThreads = Math.max(1, Math.min(asyncChunkLoadThreads, Integer.parseInt(sharedHostEnvLoad)));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
if (!asyncChunks) {
|
||||
log("Async Chunks: Disabled - Chunks will be managed synchronosuly, and will cause tremendous lag.");
|
||||
} else {
|
||||
log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag.");
|
||||
if (!asyncChunkGeneration) {
|
||||
log("Async Chunks - Generation: Disabled - Chunks will be generated synchronosuly, and will cause tremendous lag.");
|
||||
} else if (asyncChunkGenThreadPerWorld) {
|
||||
log("Async Chunks - Generation: Enabled - Chunks will be generated much faster, without lag.");
|
||||
} else {
|
||||
log("Async Chunks - Generation: Enabled (Single Thread) - Chunks will be generated much faster, without lag.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean velocitySupport;
|
||||
public static boolean velocityOnlineMode;
|
||||
public static byte[] velocitySecretKey;
|
||||
private static void velocitySupport() {
|
||||
velocitySupport = getBoolean("settings.velocity-support.enabled", false);
|
||||
velocityOnlineMode = getBoolean("settings.velocity-support.online-mode", false);
|
||||
String secret = getString("settings.velocity-support.secret", "");
|
||||
if (velocitySupport && secret.isEmpty()) {
|
||||
fatal("Velocity support is enabled, but no secret key was specified. A secret key is required!");
|
||||
} else {
|
||||
velocitySecretKey = secret.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
public static int maxBookPageSize = 2560;
|
||||
public static double maxBookTotalSizeMultiplier = 0.98D;
|
||||
private static void maxBookSize() {
|
||||
maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize);
|
||||
maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier);
|
||||
}
|
||||
}
|
||||
597
src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
Normal file
597
src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
Normal file
@@ -0,0 +1,597 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode;
|
||||
import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import static com.destroystokyo.paper.PaperConfig.log;
|
||||
import static com.destroystokyo.paper.PaperConfig.logError;
|
||||
|
||||
public class PaperWorldConfig {
|
||||
|
||||
private final String worldName;
|
||||
private final SpigotWorldConfig spigotConfig;
|
||||
private final YamlConfiguration config;
|
||||
private boolean verbose;
|
||||
|
||||
public PaperWorldConfig(String worldName, SpigotWorldConfig spigotConfig) {
|
||||
this.worldName = worldName;
|
||||
this.spigotConfig = spigotConfig;
|
||||
this.config = PaperConfig.config;
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
log("-------- World Settings For [" + worldName + "] --------");
|
||||
PaperConfig.readConfig(PaperWorldConfig.class, this);
|
||||
}
|
||||
|
||||
private void set(String path, Object val) {
|
||||
config.set("world-settings.default." + path, val);
|
||||
if (config.get("world-settings." + worldName + "." + path) != null) {
|
||||
config.set("world-settings." + worldName + "." + path, val);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getBoolean(String path, boolean def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private double getDouble(String path, double def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return config.getDouble("world-settings." + worldName + "." + path, config.getDouble("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private int getInt(String path, int def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return config.getInt("world-settings." + worldName + "." + path, config.getInt("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private float getFloat(String path, float def) {
|
||||
// TODO: Figure out why getFloat() always returns the default value.
|
||||
return (float) getDouble(path, (double) def);
|
||||
}
|
||||
|
||||
private <T> List<T> getList(String path, List<T> def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return (List<T>) config.getList("world-settings." + worldName + "." + path, config.getList("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private String getString(String path, String def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return config.getString("world-settings." + worldName + "." + path, config.getString("world-settings.default." + path));
|
||||
}
|
||||
|
||||
public int cactusMaxHeight;
|
||||
public int reedMaxHeight;
|
||||
private void blockGrowthHeight() {
|
||||
cactusMaxHeight = getInt("max-growth-height.cactus", 3);
|
||||
reedMaxHeight = getInt("max-growth-height.reeds", 3);
|
||||
log("Max height for cactus growth " + cactusMaxHeight + ". Max height for reed growth " + reedMaxHeight);
|
||||
|
||||
}
|
||||
|
||||
public double babyZombieMovementSpeed;
|
||||
private void babyZombieMovementSpeed() {
|
||||
babyZombieMovementSpeed = getDouble("baby-zombie-movement-speed", 0.5D); // Player moves at 0.1F, for reference
|
||||
log("Baby zombies will move at the speed of " + babyZombieMovementSpeed);
|
||||
}
|
||||
|
||||
public int fishingMinTicks;
|
||||
public int fishingMaxTicks;
|
||||
private void fishingTickRange() {
|
||||
fishingMinTicks = getInt("fishing-time-range.MinimumTicks", 100);
|
||||
fishingMaxTicks = getInt("fishing-time-range.MaximumTicks", 600);
|
||||
log("Fishing time ranges are between " + fishingMinTicks +" and " + fishingMaxTicks + " ticks");
|
||||
}
|
||||
|
||||
public boolean nerfedMobsShouldJump;
|
||||
private void nerfedMobsShouldJump() {
|
||||
nerfedMobsShouldJump = getBoolean("spawner-nerfed-mobs-should-jump", false);
|
||||
}
|
||||
|
||||
public int softDespawnDistance;
|
||||
public int hardDespawnDistance;
|
||||
private void despawnDistances() {
|
||||
softDespawnDistance = getInt("despawn-ranges.soft", 32); // 32^2 = 1024, Minecraft Default
|
||||
hardDespawnDistance = getInt("despawn-ranges.hard", 128); // 128^2 = 16384, Minecraft Default
|
||||
|
||||
if (softDespawnDistance > hardDespawnDistance) {
|
||||
softDespawnDistance = hardDespawnDistance;
|
||||
}
|
||||
|
||||
log("Living Entity Despawn Ranges: Soft: " + softDespawnDistance + " Hard: " + hardDespawnDistance);
|
||||
|
||||
softDespawnDistance = softDespawnDistance*softDespawnDistance;
|
||||
hardDespawnDistance = hardDespawnDistance*hardDespawnDistance;
|
||||
}
|
||||
|
||||
public boolean keepSpawnInMemory;
|
||||
private void keepSpawnInMemory() {
|
||||
keepSpawnInMemory = getBoolean("keep-spawn-loaded", true);
|
||||
log("Keep spawn chunk loaded: " + keepSpawnInMemory);
|
||||
}
|
||||
|
||||
public int fallingBlockHeightNerf;
|
||||
public int entityTNTHeightNerf;
|
||||
private void heightNerfs() {
|
||||
fallingBlockHeightNerf = getInt("falling-block-height-nerf", 0);
|
||||
entityTNTHeightNerf = getInt("tnt-entity-height-nerf", 0);
|
||||
|
||||
if (fallingBlockHeightNerf != 0) log("Falling Block Height Limit set to Y: " + fallingBlockHeightNerf);
|
||||
if (entityTNTHeightNerf != 0) log("TNT Entity Height Limit set to Y: " + entityTNTHeightNerf);
|
||||
}
|
||||
|
||||
public boolean netherVoidTopDamage;
|
||||
private void netherVoidTopDamage() {
|
||||
netherVoidTopDamage = getBoolean( "nether-ceiling-void-damage", false );
|
||||
log("Top of the nether void damage: " + netherVoidTopDamage);
|
||||
}
|
||||
|
||||
public boolean queueLightUpdates;
|
||||
private void queueLightUpdates() {
|
||||
queueLightUpdates = getBoolean("queue-light-updates", false);
|
||||
log("Lighting Queue enabled: " + queueLightUpdates);
|
||||
log("Warning: This feature may help reduce TPS loss from light, but comes at the cost of buggy light data");
|
||||
log("We are working to improve this feature.");
|
||||
}
|
||||
|
||||
public boolean disableEndCredits;
|
||||
private void disableEndCredits() {
|
||||
disableEndCredits = getBoolean("game-mechanics.disable-end-credits", false);
|
||||
log("End credits disabled: " + disableEndCredits);
|
||||
}
|
||||
|
||||
public boolean optimizeExplosions;
|
||||
private void optimizeExplosions() {
|
||||
optimizeExplosions = getBoolean("optimize-explosions", false);
|
||||
log("Optimize explosions: " + optimizeExplosions);
|
||||
}
|
||||
|
||||
public boolean disableExplosionKnockback;
|
||||
private void disableExplosionKnockback(){
|
||||
disableExplosionKnockback = getBoolean("disable-explosion-knockback", false);
|
||||
}
|
||||
|
||||
public boolean disableThunder;
|
||||
private void disableThunder() {
|
||||
disableThunder = getBoolean("disable-thunder", false);
|
||||
}
|
||||
|
||||
public boolean disableIceAndSnow;
|
||||
private void disableIceAndSnow(){
|
||||
disableIceAndSnow = getBoolean("disable-ice-and-snow", false);
|
||||
}
|
||||
|
||||
public int mobSpawnerTickRate;
|
||||
private void mobSpawnerTickRate() {
|
||||
mobSpawnerTickRate = getInt("mob-spawner-tick-rate", 1);
|
||||
}
|
||||
|
||||
public int containerUpdateTickRate;
|
||||
private void containerUpdateTickRate() {
|
||||
containerUpdateTickRate = getInt("container-update-tick-rate", 1);
|
||||
}
|
||||
|
||||
public boolean disableChestCatDetection;
|
||||
private void disableChestCatDetection() {
|
||||
disableChestCatDetection = getBoolean("game-mechanics.disable-chest-cat-detection", false);
|
||||
}
|
||||
|
||||
public boolean disablePlayerCrits;
|
||||
private void disablePlayerCrits() {
|
||||
disablePlayerCrits = getBoolean("game-mechanics.disable-player-crits", false);
|
||||
}
|
||||
|
||||
public boolean allChunksAreSlimeChunks;
|
||||
private void allChunksAreSlimeChunks() {
|
||||
allChunksAreSlimeChunks = getBoolean("all-chunks-are-slime-chunks", false);
|
||||
}
|
||||
|
||||
public int portalSearchRadius;
|
||||
private void portalSearchRadius() {
|
||||
portalSearchRadius = getInt("portal-search-radius", 128);
|
||||
}
|
||||
|
||||
public boolean disableTeleportationSuffocationCheck;
|
||||
private void disableTeleportationSuffocationCheck() {
|
||||
disableTeleportationSuffocationCheck = getBoolean("disable-teleportation-suffocation-check", false);
|
||||
}
|
||||
|
||||
public boolean nonPlayerEntitiesOnScoreboards = false;
|
||||
private void nonPlayerEntitiesOnScoreboards() {
|
||||
nonPlayerEntitiesOnScoreboards = getBoolean("allow-non-player-entities-on-scoreboards", false);
|
||||
}
|
||||
|
||||
public boolean allowLeashingUndeadHorse = false;
|
||||
private void allowLeashingUndeadHorse() {
|
||||
allowLeashingUndeadHorse = getBoolean("allow-leashing-undead-horse", false);
|
||||
}
|
||||
|
||||
public int nonPlayerArrowDespawnRate = -1;
|
||||
public int creativeArrowDespawnRate = -1;
|
||||
private void nonPlayerArrowDespawnRate() {
|
||||
nonPlayerArrowDespawnRate = getInt("non-player-arrow-despawn-rate", -1);
|
||||
if (nonPlayerArrowDespawnRate == -1) {
|
||||
nonPlayerArrowDespawnRate = spigotConfig.arrowDespawnRate;
|
||||
}
|
||||
creativeArrowDespawnRate = getInt("creative-arrow-despawn-rate", -1);
|
||||
if (creativeArrowDespawnRate == -1) {
|
||||
creativeArrowDespawnRate = spigotConfig.arrowDespawnRate;
|
||||
}
|
||||
log("Non Player Arrow Despawn Rate: " + nonPlayerArrowDespawnRate);
|
||||
log("Creative Arrow Despawn Rate: " + creativeArrowDespawnRate);
|
||||
}
|
||||
|
||||
public double skeleHorseSpawnChance;
|
||||
private void skeleHorseSpawnChance() {
|
||||
skeleHorseSpawnChance = getDouble("skeleton-horse-thunder-spawn-chance", 0.01D);
|
||||
if (skeleHorseSpawnChance < 0) {
|
||||
skeleHorseSpawnChance = 0.01D; // Vanilla value
|
||||
}
|
||||
}
|
||||
|
||||
public double sqrMaxThunderDistance;
|
||||
public double sqrMaxLightningImpactSoundDistance;
|
||||
public double maxLightningFlashDistance;
|
||||
private void lightningStrikeDistanceLimit() {
|
||||
sqrMaxThunderDistance = getInt("lightning-strike-distance-limit.sound", -1);
|
||||
if (sqrMaxThunderDistance > 0) {
|
||||
sqrMaxThunderDistance *= sqrMaxThunderDistance;
|
||||
}
|
||||
|
||||
sqrMaxLightningImpactSoundDistance = getInt("lightning-strike-distance-limit.impact-sound", -1);
|
||||
if (sqrMaxLightningImpactSoundDistance < 0) {
|
||||
sqrMaxLightningImpactSoundDistance = 32 * 32; //Vanilla value
|
||||
} else {
|
||||
sqrMaxLightningImpactSoundDistance *= sqrMaxLightningImpactSoundDistance;
|
||||
}
|
||||
|
||||
maxLightningFlashDistance = getInt("lightning-strike-distance-limit.flash", -1);
|
||||
if (maxLightningFlashDistance < 0) {
|
||||
maxLightningFlashDistance = 512; // Vanilla value
|
||||
}
|
||||
}
|
||||
|
||||
public boolean firePhysicsEventForRedstone = false;
|
||||
private void firePhysicsEventForRedstone() {
|
||||
firePhysicsEventForRedstone = getBoolean("fire-physics-event-for-redstone", firePhysicsEventForRedstone);
|
||||
}
|
||||
|
||||
public int fixedInhabitedTime;
|
||||
private void fixedInhabitedTime() {
|
||||
if (PaperConfig.version < 16) {
|
||||
if (!config.getBoolean("world-settings.default.use-chunk-inhabited-timer", true)) config.set("world-settings.default.fixed-chunk-inhabited-time", 0);
|
||||
if (!config.getBoolean("world-settings." + worldName + ".use-chunk-inhabited-timer", true)) config.set("world-settings." + worldName + ".fixed-chunk-inhabited-time", 0);
|
||||
set("use-chunk-inhabited-timer", null);
|
||||
}
|
||||
fixedInhabitedTime = getInt("fixed-chunk-inhabited-time", -1);
|
||||
}
|
||||
|
||||
public int grassUpdateRate = 1;
|
||||
private void grassUpdateRate() {
|
||||
grassUpdateRate = Math.max(0, getInt("grass-spread-tick-rate", grassUpdateRate));
|
||||
log("Grass Spread Tick Rate: " + grassUpdateRate);
|
||||
}
|
||||
|
||||
public short keepLoadedRange;
|
||||
private void keepLoadedRange() {
|
||||
keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 8)) * 16);
|
||||
log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16));
|
||||
}
|
||||
|
||||
public boolean useVanillaScoreboardColoring;
|
||||
private void useVanillaScoreboardColoring() {
|
||||
useVanillaScoreboardColoring = getBoolean("use-vanilla-world-scoreboard-name-coloring", false);
|
||||
}
|
||||
|
||||
public boolean frostedIceEnabled = true;
|
||||
public int frostedIceDelayMin = 20;
|
||||
public int frostedIceDelayMax = 40;
|
||||
private void frostedIce() {
|
||||
this.frostedIceEnabled = this.getBoolean("frosted-ice.enabled", this.frostedIceEnabled);
|
||||
this.frostedIceDelayMin = this.getInt("frosted-ice.delay.min", this.frostedIceDelayMin);
|
||||
this.frostedIceDelayMax = this.getInt("frosted-ice.delay.max", this.frostedIceDelayMax);
|
||||
log("Frosted Ice: " + (this.frostedIceEnabled ? "enabled" : "disabled") + " / delay: min=" + this.frostedIceDelayMin + ", max=" + this.frostedIceDelayMax);
|
||||
}
|
||||
|
||||
public boolean autoReplenishLootables;
|
||||
public boolean restrictPlayerReloot;
|
||||
public boolean changeLootTableSeedOnFill;
|
||||
public int maxLootableRefills;
|
||||
public int lootableRegenMin;
|
||||
public int lootableRegenMax;
|
||||
private void enhancedLootables() {
|
||||
autoReplenishLootables = getBoolean("lootables.auto-replenish", false);
|
||||
restrictPlayerReloot = getBoolean("lootables.restrict-player-reloot", true);
|
||||
changeLootTableSeedOnFill = getBoolean("lootables.reset-seed-on-fill", true);
|
||||
maxLootableRefills = getInt("lootables.max-refills", -1);
|
||||
lootableRegenMin = PaperConfig.getSeconds(getString("lootables.refresh-min", "12h"));
|
||||
lootableRegenMax = PaperConfig.getSeconds(getString("lootables.refresh-max", "2d"));
|
||||
if (autoReplenishLootables) {
|
||||
log("Lootables: Replenishing every " +
|
||||
PaperConfig.timeSummary(lootableRegenMin) + " to " +
|
||||
PaperConfig.timeSummary(lootableRegenMax) +
|
||||
(restrictPlayerReloot ? " (restricting reloot)" : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preventTntFromMovingInWater;
|
||||
private void preventTntFromMovingInWater() {
|
||||
if (PaperConfig.version < 13) {
|
||||
boolean oldVal = getBoolean("enable-old-tnt-cannon-behaviors", false);
|
||||
set("prevent-tnt-from-moving-in-water", oldVal);
|
||||
}
|
||||
preventTntFromMovingInWater = getBoolean("prevent-tnt-from-moving-in-water", false);
|
||||
log("Prevent TNT from moving in water: " + preventTntFromMovingInWater);
|
||||
}
|
||||
|
||||
public long delayChunkUnloadsBy;
|
||||
private void delayChunkUnloadsBy() {
|
||||
delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s"));
|
||||
if (delayChunkUnloadsBy > 0) {
|
||||
log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds");
|
||||
delayChunkUnloadsBy *= 1000;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean skipEntityTickingInChunksScheduledForUnload = true;
|
||||
private void skipEntityTickingInChunksScheduledForUnload() {
|
||||
skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload);
|
||||
}
|
||||
|
||||
public int autoSavePeriod = -1;
|
||||
private void autoSavePeriod() {
|
||||
autoSavePeriod = getInt("auto-save-interval", -1);
|
||||
if (autoSavePeriod > 0) {
|
||||
log("Auto Save Interval: " +autoSavePeriod + " (" + (autoSavePeriod / 20) + "s)");
|
||||
} else if (autoSavePeriod < 0) {
|
||||
autoSavePeriod = MinecraftServer.getServer().autosavePeriod;
|
||||
}
|
||||
}
|
||||
|
||||
public int maxAutoSaveChunksPerTick = 24;
|
||||
private void maxAutoSaveChunksPerTick() {
|
||||
maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24);
|
||||
}
|
||||
|
||||
public int queueSizeAutoSaveThreshold = 50;
|
||||
private void queueSizeAutoSaveThreshold() {
|
||||
queueSizeAutoSaveThreshold = getInt("save-queue-limit-for-auto-save", 50);
|
||||
}
|
||||
|
||||
public boolean removeCorruptTEs = false;
|
||||
private void removeCorruptTEs() {
|
||||
removeCorruptTEs = getBoolean("remove-corrupt-tile-entities", false);
|
||||
}
|
||||
|
||||
public boolean filterNBTFromSpawnEgg = true;
|
||||
private void fitlerNBTFromSpawnEgg() {
|
||||
filterNBTFromSpawnEgg = getBoolean("filter-nbt-data-from-spawn-eggs-and-related", true);
|
||||
if (!filterNBTFromSpawnEgg) {
|
||||
Bukkit.getLogger().warning("Spawn Egg and Armor Stand NBT filtering disabled, this is a potential security risk");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean enableTreasureMaps = true;
|
||||
public boolean treasureMapsAlreadyDiscovered = false;
|
||||
private void treasureMapsAlreadyDiscovered() {
|
||||
enableTreasureMaps = getBoolean("enable-treasure-maps", true);
|
||||
treasureMapsAlreadyDiscovered = getBoolean("treasure-maps-return-already-discovered", false);
|
||||
if (treasureMapsAlreadyDiscovered) {
|
||||
log("Treasure Maps will return already discovered locations");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean armorStandEntityLookups = true;
|
||||
private void armorStandEntityLookups() {
|
||||
armorStandEntityLookups = getBoolean("armor-stands-do-collision-entity-lookups", true);
|
||||
}
|
||||
|
||||
public int maxCollisionsPerEntity;
|
||||
private void maxEntityCollision() {
|
||||
maxCollisionsPerEntity = getInt( "max-entity-collisions", this.spigotConfig.getInt("max-entity-collisions", 8) );
|
||||
log( "Max Entity Collisions: " + maxCollisionsPerEntity );
|
||||
}
|
||||
|
||||
public boolean parrotsHangOnBetter;
|
||||
private void parrotsHangOnBetter() {
|
||||
parrotsHangOnBetter = getBoolean("parrots-are-unaffected-by-player-movement", false);
|
||||
log("Parrots are unaffected by player movement: " + parrotsHangOnBetter);
|
||||
}
|
||||
|
||||
public boolean disableCreeperLingeringEffect;
|
||||
private void setDisableCreeperLingeringEffect() {
|
||||
disableCreeperLingeringEffect = getBoolean("disable-creeper-lingering-effect", false);
|
||||
log("Creeper lingering effect: " + disableCreeperLingeringEffect);
|
||||
}
|
||||
|
||||
public int expMergeMaxValue;
|
||||
private void expMergeMaxValue() {
|
||||
expMergeMaxValue = getInt("experience-merge-max-value", -1);
|
||||
log("Experience Merge Max Value: " + expMergeMaxValue);
|
||||
}
|
||||
|
||||
public int maxChunkSendsPerTick = 81;
|
||||
private void maxChunkSendsPerTick() {
|
||||
maxChunkSendsPerTick = getInt("max-chunk-sends-per-tick", maxChunkSendsPerTick);
|
||||
if (maxChunkSendsPerTick <= 0) {
|
||||
maxChunkSendsPerTick = 81;
|
||||
}
|
||||
log("Max Chunk Sends Per Tick: " + maxChunkSendsPerTick);
|
||||
}
|
||||
|
||||
public int maxChunkGensPerTick = 10;
|
||||
private void maxChunkGensPerTick() {
|
||||
maxChunkGensPerTick = getInt("max-chunk-gens-per-tick", maxChunkGensPerTick);
|
||||
if (maxChunkGensPerTick <= 0) {
|
||||
maxChunkGensPerTick = Integer.MAX_VALUE;
|
||||
log("Max Chunk Gens Per Tick: Unlimited (NOT RECOMMENDED)");
|
||||
} else {
|
||||
log("Max Chunk Gens Per Tick: " + maxChunkGensPerTick);
|
||||
}
|
||||
}
|
||||
|
||||
public double squidMaxSpawnHeight;
|
||||
private void squidMaxSpawnHeight() {
|
||||
squidMaxSpawnHeight = getDouble("squid-spawn-height.maximum", 0.0D);
|
||||
}
|
||||
|
||||
public boolean cooldownHopperWhenFull = true;
|
||||
public boolean disableHopperMoveEvents = false;
|
||||
private void hopperOptimizations() {
|
||||
cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull);
|
||||
log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled"));
|
||||
disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents);
|
||||
log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled"));
|
||||
}
|
||||
|
||||
public boolean disableSprintInterruptionOnAttack;
|
||||
private void disableSprintInterruptionOnAttack() {
|
||||
disableSprintInterruptionOnAttack = getBoolean("game-mechanics.disable-sprint-interruption-on-attack", false);
|
||||
}
|
||||
|
||||
public boolean disableEnderpearlExploit = true;
|
||||
private void disableEnderpearlExploit() {
|
||||
disableEnderpearlExploit = getBoolean("game-mechanics.disable-unloaded-chunk-enderpearl-exploit", disableEnderpearlExploit);
|
||||
log("Disable Unloaded Chunk Enderpearl Exploit: " + (disableEnderpearlExploit ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
public boolean villagesLoadChunks = false;
|
||||
private void villagesLoadChunks() {
|
||||
villagesLoadChunks = getBoolean("game-mechanics.villages-load-chunks", false);
|
||||
if (villagesLoadChunks) {
|
||||
log("Villages can load chunks - Warning this can cause intense TPS loss. Strongly consider disabling this.");
|
||||
}
|
||||
}
|
||||
|
||||
public int shieldBlockingDelay = 5;
|
||||
private void shieldBlockingDelay() {
|
||||
shieldBlockingDelay = getInt("game-mechanics.shield-blocking-delay", 5);
|
||||
}
|
||||
|
||||
public boolean scanForLegacyEnderDragon = true;
|
||||
private void scanForLegacyEnderDragon() {
|
||||
scanForLegacyEnderDragon = getBoolean("game-mechanics.scan-for-legacy-ender-dragon", true);
|
||||
}
|
||||
|
||||
public int bedSearchRadius = 1;
|
||||
private void bedSearchRadius() {
|
||||
bedSearchRadius = getInt("bed-search-radius", 1);
|
||||
if (bedSearchRadius < 1) {
|
||||
bedSearchRadius = 1;
|
||||
}
|
||||
if (bedSearchRadius > 1) {
|
||||
log("Bed Search Radius: " + bedSearchRadius);
|
||||
}
|
||||
}
|
||||
|
||||
public int waterOverLavaFlowSpeed;
|
||||
private void waterOverLavaFlowSpeed() {
|
||||
waterOverLavaFlowSpeed = getInt("water-over-lava-flow-speed", 5);
|
||||
log("Water over lava flow speed: " + waterOverLavaFlowSpeed);
|
||||
}
|
||||
|
||||
public enum DuplicateUUIDMode {
|
||||
SAFE_REGEN, DELETE, NOTHING, WARN
|
||||
}
|
||||
public DuplicateUUIDMode duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
|
||||
public int duplicateUUIDDeleteRange = 32;
|
||||
private void repairDuplicateUUID() {
|
||||
String desiredMode = getString("duplicate-uuid-resolver", "saferegen").toLowerCase().trim();
|
||||
duplicateUUIDDeleteRange = getInt("duplicate-uuid-saferegen-delete-range", duplicateUUIDDeleteRange);
|
||||
switch (desiredMode.toLowerCase()) {
|
||||
case "regen":
|
||||
case "regenerate":
|
||||
case "saferegen":
|
||||
case "saferegenerate":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
|
||||
log("Duplicate UUID Resolve: Regenerate New UUID if distant (Delete likely duplicates within " + duplicateUUIDDeleteRange + " blocks)");
|
||||
break;
|
||||
case "remove":
|
||||
case "delete":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.DELETE;
|
||||
log("Duplicate UUID Resolve: Delete Entity");
|
||||
break;
|
||||
case "silent":
|
||||
case "nothing":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.NOTHING;
|
||||
logError("Duplicate UUID Resolve: Do Nothing (no logs) - Warning, may lose indication of bad things happening");
|
||||
break;
|
||||
case "log":
|
||||
case "warn":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.WARN;
|
||||
log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
|
||||
break;
|
||||
default:
|
||||
duplicateUUIDMode = DuplicateUUIDMode.WARN;
|
||||
logError("Warning: Invalid duplicate-uuid-resolver config " + desiredMode + " - must be one of: regen, delete, nothing, warn");
|
||||
log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean armorStandTick = true;
|
||||
private void armorStandTick() {
|
||||
this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick);
|
||||
log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default");
|
||||
}
|
||||
|
||||
public boolean optimizeLight = true;
|
||||
private void optimizeLight() {
|
||||
this.optimizeLight = getBoolean("optimize-light", optimizeLight);
|
||||
}
|
||||
|
||||
public boolean antiXray;
|
||||
public boolean asynchronous;
|
||||
public EngineMode engineMode;
|
||||
public ChunkEdgeMode chunkEdgeMode;
|
||||
public int maxChunkSectionIndex;
|
||||
public int updateRadius;
|
||||
public List<String> hiddenBlocks;
|
||||
public List<String> replacementBlocks;
|
||||
private void antiXray() {
|
||||
antiXray = getBoolean("anti-xray.enabled", false);
|
||||
asynchronous = true;
|
||||
engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId()));
|
||||
engineMode = engineMode == null ? EngineMode.HIDE : engineMode;
|
||||
chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()));
|
||||
chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode;
|
||||
|
||||
if (chunkEdgeMode != ChunkEdgeMode.WAIT) {
|
||||
log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")");
|
||||
chunkEdgeMode = ChunkEdgeMode.WAIT;
|
||||
set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId());
|
||||
}
|
||||
|
||||
maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3);
|
||||
maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex;
|
||||
updateRadius = getInt("anti-xray.update-radius", 2);
|
||||
hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest"));
|
||||
replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "planks"));
|
||||
log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius);
|
||||
}
|
||||
|
||||
public boolean preventMovingIntoUnloadedChunks = false;
|
||||
private void preventMovingIntoUnloadedChunks() {
|
||||
preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false);
|
||||
}
|
||||
|
||||
public boolean useEigencraftRedstone = false;
|
||||
private void useEigencraftRedstone() {
|
||||
useEigencraftRedstone = this.getBoolean("use-faster-eigencraft-redstone", false);
|
||||
if (useEigencraftRedstone) {
|
||||
log("Using Eigencraft redstone algorithm by theosib.");
|
||||
} else {
|
||||
log("Using vanilla redstone algorithm.");
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
Normal file
121
src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityInsentient;
|
||||
import net.minecraft.server.EnumCreatureType;
|
||||
import net.minecraft.server.IAnimal;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PaperWorldEntityList extends ArrayList<Entity> {
|
||||
|
||||
private final WorldServer world;
|
||||
private final int[] entityCounts = new int[EnumCreatureType.values().length];
|
||||
|
||||
|
||||
public PaperWorldEntityList(World world) {
|
||||
this.world = (WorldServer) world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Entity> c) {
|
||||
for (Entity e : c) {
|
||||
updateEntityCount(e, 1);
|
||||
}
|
||||
|
||||
return super.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
for (Object e : c) {
|
||||
if (e instanceof Entity && ((Entity) e).getWorld() == world) {
|
||||
updateEntityCount((Entity) e, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return super.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Entity e) {
|
||||
updateEntityCount(e, 1);
|
||||
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity remove(int index) {
|
||||
guard();
|
||||
Entity entity = super.remove(index);
|
||||
if (entity != null) updateEntityCount(entity, -1);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
guard();
|
||||
if (super.remove(o)) {
|
||||
updateEntityCount((Entity) o, -1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void guard() {
|
||||
if (world.guardEntityList) {
|
||||
throw new java.util.ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCreatureCount(EnumCreatureType type) {
|
||||
return entityCounts[type.ordinal()];
|
||||
}
|
||||
|
||||
private void updateEntityCount(EnumCreatureType type, int amt) {
|
||||
int count = entityCounts[type.ordinal()];
|
||||
|
||||
count += amt;
|
||||
|
||||
if (count < 0) {
|
||||
MinecraftServer.LOGGER.error("Paper - Entity count cache has gone negative");
|
||||
count = 0;
|
||||
}
|
||||
|
||||
entityCounts[type.ordinal()] = count;
|
||||
}
|
||||
|
||||
public void updateEntityCount(Entity entity, int amt) {
|
||||
if (!(entity instanceof IAnimal)) return;
|
||||
|
||||
if (entity instanceof EntityInsentient) {
|
||||
EntityInsentient entityinsentient = (EntityInsentient) entity;
|
||||
if (amt > 0 && entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (amt < 0) {
|
||||
if (!entity.hasBeenCounted) {
|
||||
return;
|
||||
}
|
||||
// Only remove once, we remove from if the entity list is guarded, but may be called later
|
||||
entity.hasBeenCounted = false;
|
||||
} else {
|
||||
if (entity.hasBeenCounted) {
|
||||
return;
|
||||
}
|
||||
entity.hasBeenCounted = true;
|
||||
}
|
||||
|
||||
for (EnumCreatureType type : EnumCreatureType.values()) {
|
||||
if (type.matches(entity)) {
|
||||
updateEntityCount(type, amt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
191
src/main/java/com/destroystokyo/paper/PaperWorldMap.java
Normal file
191
src/main/java/com/destroystokyo/paper/PaperWorldMap.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.DimensionManager;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class PaperWorldMap extends HashMap<DimensionManager, WorldServer> {
|
||||
private final List<WorldServer> worlds = new ArrayList<>();
|
||||
private final List<WorldServer> worldsIterable = new ArrayList<WorldServer>() {
|
||||
@Override
|
||||
public Iterator<WorldServer> iterator() {
|
||||
Iterator<WorldServer> iterator = super.iterator();
|
||||
return new Iterator<WorldServer>() {
|
||||
private WorldServer last;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer next() {
|
||||
this.last = iterator.next();
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
worlds.set(last.dimension.getDimensionID()+1, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public int size() {
|
||||
return worldsIterable.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return worldsIterable.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer get(Object key) {
|
||||
// Will hit the below method
|
||||
return key instanceof DimensionManager ? get((DimensionManager) key) : null;
|
||||
}
|
||||
|
||||
public WorldServer get(DimensionManager key) {
|
||||
int id = key.getDimensionID()+1;
|
||||
return worlds.size() > id ? worlds.get(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
// will hit below method
|
||||
return key instanceof DimensionManager && containsKey((DimensionManager) key);
|
||||
}
|
||||
public boolean containsKey(DimensionManager key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer put(DimensionManager key, WorldServer value) {
|
||||
while (worlds.size() <= key.getDimensionID()+1) {
|
||||
worlds.add(null);
|
||||
}
|
||||
WorldServer old = worlds.set(key.getDimensionID()+1, value);
|
||||
if (old != null) {
|
||||
worldsIterable.remove(old);
|
||||
}
|
||||
worldsIterable.add(value);
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends DimensionManager, ? extends WorldServer> m) {
|
||||
for (Entry<? extends DimensionManager, ? extends WorldServer> e : m.entrySet()) {
|
||||
put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer remove(Object key) {
|
||||
return key instanceof DimensionManager ? remove((DimensionManager) key) : null;
|
||||
}
|
||||
|
||||
public WorldServer remove(DimensionManager key) {
|
||||
WorldServer old;
|
||||
if (key.getDimensionID()+1 == worlds.size() - 1) {
|
||||
old = worlds.remove(key.getDimensionID()+1);
|
||||
} else {
|
||||
old = worlds.set(key.getDimensionID() + 1, null);
|
||||
}
|
||||
if (old != null) {
|
||||
worldsIterable.remove(old);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new RuntimeException("What the hell are you doing?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return value instanceof WorldServer && get(((WorldServer) value).dimension) != null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<DimensionManager> keySet() {
|
||||
return new AbstractSet<DimensionManager>() {
|
||||
@Override
|
||||
public Iterator<DimensionManager> iterator() {
|
||||
Iterator<WorldServer> iterator = worldsIterable.iterator();
|
||||
return new Iterator<DimensionManager>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DimensionManager next() {
|
||||
return iterator.next().dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return worlds.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<WorldServer> values() {
|
||||
return worldsIterable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<DimensionManager, WorldServer>> entrySet() {
|
||||
return new AbstractSet<Entry<DimensionManager, WorldServer>>() {
|
||||
@Override
|
||||
public Iterator<Entry<DimensionManager, WorldServer>> iterator() {
|
||||
Iterator<WorldServer> iterator = worldsIterable.iterator();
|
||||
return new Iterator<Entry<DimensionManager, WorldServer>>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<DimensionManager, WorldServer> next() {
|
||||
WorldServer entry = iterator.next();
|
||||
return new SimpleEntry<>(entry.dimension, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return worldsIterable.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
|
||||
import com.destroystokyo.paper.exception.ServerSchedulerException;
|
||||
|
||||
/**
|
||||
* Reporting wrapper to catch exceptions not natively
|
||||
*/
|
||||
public class ServerSchedulerReportingWrapper implements Runnable {
|
||||
|
||||
private final CraftTask internalTask;
|
||||
|
||||
public ServerSchedulerReportingWrapper(CraftTask internalTask) {
|
||||
this.internalTask = Preconditions.checkNotNull(internalTask, "internalTask");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
internalTask.run();
|
||||
} catch (RuntimeException e) {
|
||||
internalTask.getOwner().getServer().getPluginManager().callEvent(
|
||||
new ServerExceptionEvent(new ServerSchedulerException(e, internalTask))
|
||||
);
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
internalTask.getOwner().getServer().getPluginManager().callEvent(
|
||||
new ServerExceptionEvent(new ServerSchedulerException(t, internalTask))
|
||||
); //Do not rethrow, since it is not permitted with Runnable#run
|
||||
}
|
||||
}
|
||||
|
||||
public CraftTask getInternalTask() {
|
||||
return internalTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.IChunkAccess;
|
||||
import net.minecraft.server.IWorldReader;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.PlayerInteractManager;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
public class ChunkPacketBlockController {
|
||||
|
||||
public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
|
||||
|
||||
protected ChunkPacketBlockController() {
|
||||
|
||||
}
|
||||
|
||||
public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ChunkPacketInfo<IBlockData> getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
||||
packetPlayOutMapChunk.setReady(true);
|
||||
}
|
||||
|
||||
public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
|
||||
|
||||
}
|
||||
|
||||
public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.minecraft.server.IRegistry;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import org.bukkit.World.Environment;
|
||||
|
||||
import com.destroystokyo.paper.PaperWorldConfig;
|
||||
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.GeneratorAccess;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.IChunkAccess;
|
||||
import net.minecraft.server.IWorldReader;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.PlayerInteractManager;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
|
||||
|
||||
private static ExecutorService executorServiceInstance = null;
|
||||
private final ExecutorService executorService;
|
||||
private final boolean asynchronous;
|
||||
private final EngineMode engineMode;
|
||||
private final ChunkEdgeMode chunkEdgeMode;
|
||||
private final int maxChunkSectionIndex;
|
||||
private final int updateRadius;
|
||||
private final IBlockData[] predefinedBlockData;
|
||||
private final IBlockData[] predefinedBlockDataStone;
|
||||
private final IBlockData[] predefinedBlockDataNetherrack;
|
||||
private final IBlockData[] predefinedBlockDataEndStone;
|
||||
private final int[] predefinedBlockDataBitsGlobal;
|
||||
private final int[] predefinedBlockDataBitsStoneGlobal;
|
||||
private final int[] predefinedBlockDataBitsNetherrackGlobal;
|
||||
private final int[] predefinedBlockDataBitsEndStoneGlobal;
|
||||
private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION};
|
||||
private final int maxBlockYUpdatePosition;
|
||||
|
||||
public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) {
|
||||
asynchronous = paperWorldConfig.asynchronous;
|
||||
engineMode = paperWorldConfig.engineMode;
|
||||
chunkEdgeMode = paperWorldConfig.chunkEdgeMode;
|
||||
maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex;
|
||||
updateRadius = paperWorldConfig.updateRadius;
|
||||
|
||||
if (asynchronous) {
|
||||
executorService = getExecutorServiceInstance();
|
||||
} else {
|
||||
executorService = null;
|
||||
}
|
||||
|
||||
if (engineMode == EngineMode.HIDE) {
|
||||
predefinedBlockData = null;
|
||||
predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()};
|
||||
predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()};
|
||||
predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()};
|
||||
predefinedBlockDataBitsGlobal = null;
|
||||
predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())};
|
||||
predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())};
|
||||
predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())};
|
||||
} else {
|
||||
Set<IBlockData> predefinedBlockDataSet = new HashSet<IBlockData>();
|
||||
|
||||
for (String id : paperWorldConfig.hiddenBlocks) {
|
||||
Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
|
||||
|
||||
if (block != null && !block.isTileEntity()) {
|
||||
predefinedBlockDataSet.add(block.getBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]);
|
||||
predefinedBlockDataStone = null;
|
||||
predefinedBlockDataNetherrack = null;
|
||||
predefinedBlockDataEndStone = null;
|
||||
predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length];
|
||||
|
||||
for (int i = 0; i < predefinedBlockData.length; i++) {
|
||||
predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]);
|
||||
}
|
||||
|
||||
predefinedBlockDataBitsStoneGlobal = null;
|
||||
predefinedBlockDataBitsNetherrackGlobal = null;
|
||||
predefinedBlockDataBitsEndStoneGlobal = null;
|
||||
}
|
||||
|
||||
for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) {
|
||||
Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
|
||||
|
||||
if (block != null) {
|
||||
obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < solidGlobal.length; i++) {
|
||||
IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i);
|
||||
|
||||
if (blockData != null) {
|
||||
solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER;
|
||||
}
|
||||
}
|
||||
|
||||
this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1;
|
||||
}
|
||||
|
||||
private static ExecutorService getExecutorServiceInstance() {
|
||||
if (executorServiceInstance == null) {
|
||||
executorServiceInstance = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
return executorServiceInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) {
|
||||
//Return the block data which should be added to the data palettes so that they can be used for the obfuscation
|
||||
if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) {
|
||||
switch (engineMode) {
|
||||
case HIDE:
|
||||
if (world instanceof GeneratorAccess) {
|
||||
switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) {
|
||||
case NETHER:
|
||||
return predefinedBlockDataNetherrack;
|
||||
case THE_END:
|
||||
return predefinedBlockDataEndStone;
|
||||
default:
|
||||
return predefinedBlockDataStone;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
default:
|
||||
return predefinedBlockData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
||||
//Load nearby chunks if necessary
|
||||
if (force) {
|
||||
// if forced, load NOW;
|
||||
chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ);
|
||||
chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ);
|
||||
chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1);
|
||||
chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1);
|
||||
} else if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) {
|
||||
if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) {
|
||||
//Don't create the chunk packet now, wait until nearby chunks are loaded and create it later
|
||||
return false;
|
||||
}
|
||||
} else if (chunkEdgeMode == ChunkEdgeMode.LOAD) {
|
||||
boolean missingChunk = false;
|
||||
//noinspection ConstantConditions
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null;
|
||||
|
||||
if (missingChunk) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Create the chunk packet now
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
//Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
|
||||
ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this);
|
||||
chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1));
|
||||
return chunkPacketInfoAntiXray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
||||
if (asynchronous) {
|
||||
executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo);
|
||||
} else {
|
||||
obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo);
|
||||
}
|
||||
}
|
||||
|
||||
//Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay
|
||||
private int[] predefinedBlockDataBits;
|
||||
private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()];
|
||||
//These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate
|
||||
private boolean[][] current = new boolean[16][16];
|
||||
private boolean[][] next = new boolean[16][16];
|
||||
private boolean[][] nextNext = new boolean[16][16];
|
||||
private final DataBitsReader dataBitsReader = new DataBitsReader();
|
||||
private final DataBitsWriter dataBitsWriter = new DataBitsWriter();
|
||||
private final ChunkSection[] nearbyChunkSections = new ChunkSection[4];
|
||||
|
||||
public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
|
||||
boolean[] solidTemp = null;
|
||||
boolean[] obfuscateTemp = null;
|
||||
dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData());
|
||||
dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData());
|
||||
int counter = 0;
|
||||
|
||||
for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
|
||||
if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) {
|
||||
int[] predefinedBlockDataBitsTemp;
|
||||
|
||||
if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) {
|
||||
predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal;
|
||||
} else {
|
||||
predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits;
|
||||
|
||||
for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) {
|
||||
predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
||||
|
||||
//Check if the chunk section below was not obfuscated
|
||||
if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) {
|
||||
//If so, initialize some stuff
|
||||
dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
||||
dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
||||
solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal);
|
||||
obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
|
||||
//Read the blocks of the upper layer of the chunk section below if it exists
|
||||
ChunkSection belowChunkSection = null;
|
||||
boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION;
|
||||
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
current[z][x] = true;
|
||||
next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))];
|
||||
}
|
||||
}
|
||||
|
||||
//Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
|
||||
dataBitsWriter.setBitsPerObject(0);
|
||||
obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
||||
nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex];
|
||||
|
||||
//Obfuscate all layers of the current chunk section except the upper one
|
||||
for (int y = 0; y < 15; y++) {
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
//Check if the chunk section above doesn't need obfuscation
|
||||
if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) {
|
||||
//If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
|
||||
ChunkSection aboveChunkSection;
|
||||
|
||||
if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) {
|
||||
current[z][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There is nothing to read anymore
|
||||
dataBitsReader.setBitsPerObject(0);
|
||||
solid[0] = true;
|
||||
counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
} else {
|
||||
//If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
|
||||
dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1));
|
||||
dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1));
|
||||
solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal);
|
||||
obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
dataBitsWriter.finish();
|
||||
}
|
||||
}
|
||||
|
||||
chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true);
|
||||
}
|
||||
|
||||
private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) {
|
||||
//First block of first line
|
||||
int dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][1] = true;
|
||||
next[1][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][0] = true;
|
||||
}
|
||||
|
||||
//First line
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][x - 1] = true;
|
||||
next[0][x + 1] = true;
|
||||
next[1][x] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block of first line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][14] = true;
|
||||
next[1][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][15] = true;
|
||||
}
|
||||
|
||||
//All inner lines
|
||||
for (int z = 1; z < 15; z++) {
|
||||
//First block
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][1] = true;
|
||||
next[z - 1][0] = true;
|
||||
next[z + 1][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][0] = true;
|
||||
}
|
||||
|
||||
//All inner blocks
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][x - 1] = true;
|
||||
next[z][x + 1] = true;
|
||||
next[z - 1][x] = true;
|
||||
next[z + 1][x] = true;
|
||||
} else {
|
||||
if (current[z][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][14] = true;
|
||||
next[z - 1][15] = true;
|
||||
next[z + 1][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][15] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//First block of last line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][1] = true;
|
||||
next[14][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][0] = true;
|
||||
}
|
||||
|
||||
//Last line
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][x - 1] = true;
|
||||
next[15][x + 1] = true;
|
||||
next[14][x] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block of last line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][14] = true;
|
||||
next[14][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][15] = true;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
private boolean[] readDataPalette(DataPalette<IBlockData> dataPalette, boolean[] temp, boolean[] global) {
|
||||
if (dataPalette == ChunkSection.GLOBAL_PALETTE) {
|
||||
return global;
|
||||
}
|
||||
|
||||
IBlockData blockData;
|
||||
|
||||
for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) {
|
||||
temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)];
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
|
||||
if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) {
|
||||
updateNearbyBlocks(world, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) {
|
||||
if (blockPosition.getY() <= maxBlockYUpdatePosition) {
|
||||
updateNearbyBlocks(playerInteractManager.world, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNearbyBlocks(World world, BlockPosition blockPosition) {
|
||||
if (updateRadius >= 2) {
|
||||
BlockPosition temp = blockPosition.west();
|
||||
updateBlock(world, temp);
|
||||
updateBlock(world, temp.west());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.east());
|
||||
updateBlock(world, temp.east());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.down());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.up());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.north());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp = blockPosition.south());
|
||||
updateBlock(world, temp.south());
|
||||
} else if (updateRadius == 1) {
|
||||
updateBlock(world, blockPosition.west());
|
||||
updateBlock(world, blockPosition.east());
|
||||
updateBlock(world, blockPosition.down());
|
||||
updateBlock(world, blockPosition.up());
|
||||
updateBlock(world, blockPosition.north());
|
||||
updateBlock(world, blockPosition.south());
|
||||
} else {
|
||||
//Do nothing if updateRadius <= 0 (test mode)
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBlock(World world, BlockPosition blockPosition) {
|
||||
IBlockData blockData = world.getTypeIfLoaded(blockPosition);
|
||||
|
||||
if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) {
|
||||
world.notify(blockPosition, blockData, blockData, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EngineMode {
|
||||
|
||||
HIDE(1, "hide ores"),
|
||||
OBFUSCATE(2, "obfuscate");
|
||||
|
||||
private final int id;
|
||||
private final String description;
|
||||
|
||||
EngineMode(int id, String description) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static EngineMode getById(int id) {
|
||||
for (EngineMode engineMode : values()) {
|
||||
if (engineMode.id == id) {
|
||||
return engineMode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChunkEdgeMode {
|
||||
|
||||
DEFAULT(1, "default"),
|
||||
WAIT(2, "wait until nearby chunks are loaded"),
|
||||
LOAD(3, "load nearby chunks");
|
||||
|
||||
private final int id;
|
||||
private final String description;
|
||||
|
||||
ChunkEdgeMode(int id, String description) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static ChunkEdgeMode getById(int id) {
|
||||
for (ChunkEdgeMode chunkEdgeMode : values()) {
|
||||
if (chunkEdgeMode.id == id) {
|
||||
return chunkEdgeMode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
public class ChunkPacketInfo<T> {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private byte[] data;
|
||||
private final int[] bitsPerObject = new int[16];
|
||||
private final Object[] dataPalettes = new Object[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final Object[][] predefinedObjects = new Object[16][];
|
||||
|
||||
public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
}
|
||||
|
||||
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||
return packetPlayOutMapChunk;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkSectionSelector() {
|
||||
return chunkSectionSelector;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerObject(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) {
|
||||
this.bitsPerObject[chunkSectionIndex] = bitsPerObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DataPalette<T> getDataPalette(int chunkSectionIndex) {
|
||||
return (DataPalette<T>) dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
public int getOrCreateIdForIndex(int chunkSectionIndex) {
|
||||
return dataBitsIndexes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T[] getPredefinedObjects(int chunkSectionIndex) {
|
||||
return (T[]) predefinedObjects[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
|
||||
this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
public class ChunkPacketInfoAntiXray extends ChunkPacketInfo<IBlockData> implements Runnable {
|
||||
|
||||
private Chunk[] nearbyChunks;
|
||||
private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray;
|
||||
|
||||
public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
|
||||
super(packetPlayOutMapChunk, chunk, chunkSectionSelector);
|
||||
this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
|
||||
}
|
||||
|
||||
public Chunk[] getNearbyChunks() {
|
||||
return nearbyChunks;
|
||||
}
|
||||
|
||||
public void setNearbyChunks(Chunk... nearbyChunks) {
|
||||
this.nearbyChunks = nearbyChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
chunkPacketBlockControllerAntiXray.obfuscate(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
public class DataBitsReader {
|
||||
|
||||
private byte[] dataBits;
|
||||
private int bitsPerObject;
|
||||
private int mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
|
||||
public void setDataBits(byte[] dataBits) {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.length > longInDataBitsIndex + 7) {
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
}
|
||||
}
|
||||
|
||||
public int read() {
|
||||
int value = (int) (current >>> bitInLongIndex) & mask;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
value |= current << bitsPerObject - bitInLongIndex & mask;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
public class DataBitsWriter {
|
||||
|
||||
private byte[] dataBits;
|
||||
private int bitsPerObject;
|
||||
private long mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
private boolean dirty;
|
||||
|
||||
public void setDataBits(byte[] dataBits) {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.length > longInDataBitsIndex + 7) {
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if (dirty && dataBits.length > longInDataBitsIndex + 7) {
|
||||
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int value) {
|
||||
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
||||
dirty = true;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.destroystokyo.paper.console;
|
||||
|
||||
import net.minecraft.server.DedicatedServer;
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
import org.bukkit.craftbukkit.command.ConsoleCommandCompleter;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
|
||||
public final class PaperConsole extends SimpleTerminalConsole {
|
||||
|
||||
private final DedicatedServer server;
|
||||
|
||||
public PaperConsole(DedicatedServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LineReader buildReader(LineReaderBuilder builder) {
|
||||
return super.buildReader(builder
|
||||
.appName("Paper")
|
||||
.completer(new ConsoleCommandCompleter(this.server))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRunning() {
|
||||
return !this.server.isStopped() && this.server.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runCommand(String command) {
|
||||
this.server.issueCommand(command, this.server.getServerCommandListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
this.server.safeShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.destroystokyo.paper.console;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
|
||||
|
||||
public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getRootLogger();
|
||||
|
||||
@Override
|
||||
public void sendRawMessage(String message) {
|
||||
// TerminalConsoleAppender supports color codes directly in log messages
|
||||
LOGGER.info(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.destroystokyo.paper.entity;
|
||||
|
||||
import net.minecraft.server.IRangedEntity;
|
||||
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public interface CraftRangedEntity<T extends IRangedEntity> extends RangedEntity {
|
||||
T getHandle();
|
||||
|
||||
@Override
|
||||
default void rangedAttack(LivingEntity target, float charge) {
|
||||
getHandle().rangedAttack(((CraftLivingEntity) target).getHandle(), charge);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setChargingAttack(boolean raiseHands) {
|
||||
getHandle().setChargingAttack(raiseHands);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.destroystokyo.paper.entity;
|
||||
|
||||
import net.minecraft.server.EntityInsentient;
|
||||
import net.minecraft.server.PathEntity;
|
||||
import net.minecraft.server.PathPoint;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Mob;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PaperPathfinder implements com.destroystokyo.paper.entity.Pathfinder {
|
||||
|
||||
private final EntityInsentient entity;
|
||||
|
||||
public PaperPathfinder(EntityInsentient entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mob getEntity() {
|
||||
return entity.getBukkitMob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopPathfinding() {
|
||||
entity.getNavigation().stopPathfinding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPath() {
|
||||
return entity.getNavigation().getPathEntity() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult getCurrentPath() {
|
||||
PathEntity path = entity.getNavigation().getPathEntity();
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult findPath(Location loc) {
|
||||
Validate.notNull(loc, "Location can not be null");
|
||||
PathEntity path = entity.getNavigation().calculateDestination(loc.getX(), loc.getY(), loc.getZ());
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult findPath(LivingEntity target) {
|
||||
Validate.notNull(target, "Target can not be null");
|
||||
PathEntity path = entity.getNavigation().calculateDestination(((CraftLivingEntity) target).getHandle());
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveTo(@Nonnull PathResult path, double speed) {
|
||||
Validate.notNull(path, "PathResult can not be null");
|
||||
PathEntity pathEntity = ((PaperPathResult) path).path;
|
||||
return entity.getNavigation().setDestination(pathEntity, speed);
|
||||
}
|
||||
|
||||
public class PaperPathResult implements com.destroystokyo.paper.entity.PaperPathfinder.PathResult {
|
||||
|
||||
private final PathEntity path;
|
||||
PaperPathResult(PathEntity path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getFinalPoint() {
|
||||
PathPoint point = path.getFinalPoint();
|
||||
return point != null ? toLoc(point) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getPoints() {
|
||||
int pathCount = path.getPathCount();
|
||||
List<Location> points = new ArrayList<>();
|
||||
PathPoint[] pathPoints = path.getPoints();
|
||||
for (int i = 0; i < pathCount; i++) {
|
||||
points.add(toLoc(pathPoints[i]));
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextPointIndex() {
|
||||
return path.getNextIndex();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getNextPoint() {
|
||||
if (!path.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return toLoc(path.getPoints()[path.getNextIndex()]);
|
||||
}
|
||||
}
|
||||
|
||||
private Location toLoc(PathPoint point) {
|
||||
return new Location(entity.world.getWorld(), point.getX(), point.getY(), point.getZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.TileEntityLootable;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory {
|
||||
|
||||
TileEntityLootable getTileEntity();
|
||||
|
||||
@Override
|
||||
default LootableInventory getAPILootableInventory() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default World getNMSWorld() {
|
||||
return getTileEntity().getWorld();
|
||||
}
|
||||
|
||||
default Block getBlock() {
|
||||
final BlockPosition position = getTileEntity().getPosition();
|
||||
final Chunk bukkitChunk = getTileEntity().getWorld().getChunkAtWorldCoords(position).bukkitChunk;
|
||||
return bukkitChunk.getBlock(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
default PaperLootableInventoryData getLootableData() {
|
||||
return getTileEntity().lootableData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
public interface PaperLootableEntityInventory extends LootableEntityInventory, PaperLootableInventory {
|
||||
|
||||
net.minecraft.server.Entity getHandle();
|
||||
|
||||
@Override
|
||||
default LootableInventory getAPILootableInventory() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default Entity getEntity() {
|
||||
return getHandle().getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
default World getNMSWorld() {
|
||||
return getHandle().getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
default PaperLootableInventoryData getLootableData() {
|
||||
return getHandle().lootableData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.loot.Lootable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PaperLootableInventory extends LootableInventory, Lootable {
|
||||
|
||||
PaperLootableInventoryData getLootableData();
|
||||
LootableInventory getAPILootableInventory();
|
||||
|
||||
World getNMSWorld();
|
||||
|
||||
default org.bukkit.World getBukkitWorld() {
|
||||
return getNMSWorld().getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isRefillEnabled() {
|
||||
return getNMSWorld().paperConfig.autoReplenishLootables;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasBeenFilled() {
|
||||
return getLastFilled() != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasPlayerLooted(UUID player) {
|
||||
return getLootableData().hasPlayerLooted(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Long getLastLooted(UUID player) {
|
||||
return getLootableData().getLastLooted(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean setHasPlayerLooted(UUID player, boolean looted) {
|
||||
final boolean hasLooted = hasPlayerLooted(player);
|
||||
if (hasLooted != looted) {
|
||||
getLootableData().setPlayerLootedState(player, looted);
|
||||
}
|
||||
return hasLooted;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasPendingRefill() {
|
||||
long nextRefill = getLootableData().getNextRefill();
|
||||
return nextRefill != -1 && nextRefill > getLootableData().getLastFill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long getLastFilled() {
|
||||
return getLootableData().getLastFill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long getNextRefill() {
|
||||
return getLootableData().getNextRefill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long setNextRefill(long refillAt) {
|
||||
if (refillAt < -1) {
|
||||
refillAt = -1;
|
||||
}
|
||||
return getLootableData().setNextRefill(refillAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import com.destroystokyo.paper.PaperWorldConfig;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.loot.LootTable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PaperLootableInventoryData {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private long lastFill = -1;
|
||||
private long nextRefill = -1;
|
||||
private int numRefills = 0;
|
||||
private Map<UUID, Long> lootedPlayers;
|
||||
private final PaperLootableInventory lootable;
|
||||
|
||||
public PaperLootableInventoryData(PaperLootableInventory lootable) {
|
||||
this.lootable = lootable;
|
||||
}
|
||||
|
||||
long getLastFill() {
|
||||
return this.lastFill;
|
||||
}
|
||||
|
||||
long getNextRefill() {
|
||||
return this.nextRefill;
|
||||
}
|
||||
|
||||
long setNextRefill(long nextRefill) {
|
||||
long prev = this.nextRefill;
|
||||
this.nextRefill = nextRefill;
|
||||
return prev;
|
||||
}
|
||||
|
||||
public boolean shouldReplenish(@Nullable EntityHuman player) {
|
||||
LootTable table = this.lootable.getLootTable();
|
||||
|
||||
// No Loot Table associated
|
||||
if (table == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ALWAYS process the first fill or if the feature is disabled
|
||||
if (this.lastFill == -1 || !this.lootable.getNMSWorld().paperConfig.autoReplenishLootables) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only process refills when a player is set
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Chest is not scheduled for refill
|
||||
if (this.nextRefill == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig;
|
||||
|
||||
// Check if max refills has been hit
|
||||
if (paperConfig.maxLootableRefills != -1 && this.numRefills >= paperConfig.maxLootableRefills) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refill has not been reached
|
||||
if (this.nextRefill > System.currentTimeMillis()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final Player bukkitPlayer = (Player) player.getBukkitEntity();
|
||||
LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, lootable.getAPILootableInventory());
|
||||
if (paperConfig.restrictPlayerReloot && hasPlayerLooted(player.getUniqueID())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return event.callEvent();
|
||||
}
|
||||
public void processRefill(@Nullable EntityHuman player) {
|
||||
this.lastFill = System.currentTimeMillis();
|
||||
final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig;
|
||||
if (paperConfig.autoReplenishLootables) {
|
||||
int min = paperConfig.lootableRegenMin;
|
||||
int max = paperConfig.lootableRegenMax;
|
||||
this.nextRefill = this.lastFill + (min + RANDOM.nextInt(max - min + 1)) * 1000L;
|
||||
this.numRefills++;
|
||||
if (paperConfig.changeLootTableSeedOnFill) {
|
||||
this.lootable.setSeed(0);
|
||||
}
|
||||
if (player != null) { // This means that numRefills can be incremented without a player being in the lootedPlayers list - Seems to be EntityMinecartChest specific
|
||||
this.setPlayerLootedState(player.getUniqueID(), true);
|
||||
}
|
||||
} else {
|
||||
this.lootable.clearLootTable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadNbt(NBTTagCompound base) {
|
||||
if (!base.hasKeyOfType("Paper.LootableData", 10)) { // 10 = compound
|
||||
return;
|
||||
}
|
||||
NBTTagCompound comp = base.getCompound("Paper.LootableData");
|
||||
if (comp.hasKey("lastFill")) {
|
||||
this.lastFill = comp.getLong("lastFill");
|
||||
}
|
||||
if (comp.hasKey("nextRefill")) {
|
||||
this.nextRefill = comp.getLong("nextRefill");
|
||||
}
|
||||
|
||||
if (comp.hasKey("numRefills")) {
|
||||
this.numRefills = comp.getInt("numRefills");
|
||||
}
|
||||
if (comp.hasKeyOfType("lootedPlayers", 9)) { // 9 = list
|
||||
NBTTagList list = comp.getList("lootedPlayers", 10); // 10 = compound
|
||||
final int size = list.size();
|
||||
if (size > 0) {
|
||||
this.lootedPlayers = new HashMap<>(list.size());
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
final NBTTagCompound cmp = list.getCompound(i);
|
||||
lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time"));
|
||||
}
|
||||
}
|
||||
}
|
||||
public void saveNbt(NBTTagCompound base) {
|
||||
NBTTagCompound comp = new NBTTagCompound();
|
||||
if (this.nextRefill != -1) {
|
||||
comp.setLong("nextRefill", this.nextRefill);
|
||||
}
|
||||
if (this.lastFill != -1) {
|
||||
comp.setLong("lastFill", this.lastFill);
|
||||
}
|
||||
if (this.numRefills != 0) {
|
||||
comp.setInt("numRefills", this.numRefills);
|
||||
}
|
||||
if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) {
|
||||
NBTTagList list = new NBTTagList();
|
||||
for (Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) {
|
||||
NBTTagCompound cmp = new NBTTagCompound();
|
||||
cmp.setUUID("UUID", entry.getKey());
|
||||
cmp.setLong("Time", entry.getValue());
|
||||
list.add(cmp);
|
||||
}
|
||||
comp.set("lootedPlayers", list);
|
||||
}
|
||||
|
||||
if (!comp.isEmpty()) {
|
||||
base.set("Paper.LootableData", comp);
|
||||
}
|
||||
}
|
||||
|
||||
void setPlayerLootedState(UUID player, boolean looted) {
|
||||
if (looted && this.lootedPlayers == null) {
|
||||
this.lootedPlayers = new HashMap<>();
|
||||
}
|
||||
if (looted) {
|
||||
if (!this.lootedPlayers.containsKey(player)) {
|
||||
this.lootedPlayers.put(player, System.currentTimeMillis());
|
||||
}
|
||||
} else if (this.lootedPlayers != null) {
|
||||
this.lootedPlayers.remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasPlayerLooted(UUID player) {
|
||||
return this.lootedPlayers != null && this.lootedPlayers.containsKey(player);
|
||||
}
|
||||
|
||||
Long getLastLooted(UUID player) {
|
||||
return lootedPlayers != null ? lootedPlayers.get(player) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityMinecartContainer;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
|
||||
public class PaperMinecartLootableInventory implements PaperLootableEntityInventory {
|
||||
|
||||
private EntityMinecartContainer entity;
|
||||
|
||||
public PaperMinecartLootableInventory(EntityMinecartContainer entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
return entity.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.getLootTableKey())) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table, long seed) {
|
||||
setLootTable(table);
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
entity.lootTableSeed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return entity.lootTableSeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table) {
|
||||
MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey());
|
||||
entity.setLootTable(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperLootableInventoryData getLootableData() {
|
||||
return entity.lootableData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getHandle() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootableInventory getAPILootableInventory() {
|
||||
return (LootableInventory) entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getNMSWorld() {
|
||||
return entity.world;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.TileEntityLootable;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
|
||||
public class PaperTileEntityLootableInventory implements PaperLootableBlockInventory {
|
||||
private TileEntityLootable tileEntityLootable;
|
||||
|
||||
public PaperTileEntityLootableInventory(TileEntityLootable tileEntityLootable) {
|
||||
this.tileEntityLootable = tileEntityLootable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
return tileEntityLootable.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.getLootTableKey())) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table, long seed) {
|
||||
setLootTable(table);
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table) {
|
||||
MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey());
|
||||
tileEntityLootable.setLootTable(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
tileEntityLootable.setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return tileEntityLootable.getSeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperLootableInventoryData getLootableData() {
|
||||
return tileEntityLootable.lootableData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntityLootable getTileEntity() {
|
||||
return tileEntityLootable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootableInventory getAPILootableInventory() {
|
||||
World world = tileEntityLootable.getWorld();
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, tileEntityLootable.getPosition())).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getNMSWorld() {
|
||||
return tileEntityLootable.getWorld();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class PaperLegacyStatusClient implements StatusClient {
|
||||
|
||||
private final InetSocketAddress address;
|
||||
private final int protocolVersion;
|
||||
@Nullable private final InetSocketAddress virtualHost;
|
||||
|
||||
private PaperLegacyStatusClient(InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) {
|
||||
this.address = address;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.virtualHost = virtualHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
return this.virtualHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static PaperServerListPingEvent processRequest(MinecraftServer server,
|
||||
InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) {
|
||||
|
||||
PaperServerListPingEvent event = new PaperServerListPingEventImpl(server,
|
||||
new PaperLegacyStatusClient(address, protocolVersion, virtualHost), Byte.MAX_VALUE, null);
|
||||
server.server.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
public static String getMotd(PaperServerListPingEvent event) {
|
||||
return getFirstLine(event.getMotd());
|
||||
}
|
||||
|
||||
public static String getUnformattedMotd(PaperServerListPingEvent event) {
|
||||
// Strip color codes and all other occurrences of the color char (because it's used as delimiter)
|
||||
return getFirstLine(StringUtils.remove(ChatColor.stripColor(event.getMotd()), ChatColor.COLOR_CHAR));
|
||||
}
|
||||
|
||||
private static String getFirstLine(String s) {
|
||||
int pos = s.indexOf('\n');
|
||||
return pos >= 0 ? s.substring(0, pos) : s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import net.minecraft.server.NetworkManager;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperNetworkClient implements NetworkClient {
|
||||
|
||||
private final NetworkManager networkManager;
|
||||
|
||||
PaperNetworkClient(NetworkManager networkManager) {
|
||||
this.networkManager = networkManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return (InetSocketAddress) this.networkManager.getSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return this.networkManager.protocolVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
return this.networkManager.virtualHost;
|
||||
}
|
||||
|
||||
public static InetSocketAddress prepareVirtualHost(String host, int port) {
|
||||
int len = host.length();
|
||||
|
||||
// FML appends a marker to the host to recognize FML clients (\0FML\0)
|
||||
int pos = host.indexOf('\0');
|
||||
if (pos >= 0) {
|
||||
len = pos;
|
||||
}
|
||||
|
||||
// When clients connect with a SRV record, their host contains a trailing '.'
|
||||
if (len > 0 && host.charAt(len - 1) == '.') {
|
||||
len--;
|
||||
}
|
||||
|
||||
return InetSocketAddress.createUnresolved(host.substring(0, len), port);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class PaperServerListPingEventImpl extends PaperServerListPingEvent {
|
||||
|
||||
private final MinecraftServer server;
|
||||
|
||||
PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) {
|
||||
super(client, server.getMotd(), server.getPlayerCount(), server.getMaxPlayers(),
|
||||
server.getServerModName() + ' ' + server.getVersion(), protocolVersion, icon);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Object[] getOnlinePlayers() {
|
||||
return this.server.getPlayerList().players.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Player getBukkitPlayer(Object player) {
|
||||
return ((EntityPlayer) player).getBukkitEntity();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import net.minecraft.server.NetworkManager;
|
||||
|
||||
class PaperStatusClient extends PaperNetworkClient implements StatusClient {
|
||||
|
||||
PaperStatusClient(NetworkManager networkManager) {
|
||||
super(networkManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.ChatComponentText;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.NetworkManager;
|
||||
import net.minecraft.server.PacketStatusOutServerInfo;
|
||||
import net.minecraft.server.ServerPing;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class StandardPaperServerListPingEventImpl extends PaperServerListPingEventImpl {
|
||||
|
||||
private static final GameProfile[] EMPTY_PROFILES = new GameProfile[0];
|
||||
private static final UUID FAKE_UUID = new UUID(0, 0);
|
||||
|
||||
private GameProfile[] originalSample;
|
||||
|
||||
private StandardPaperServerListPingEventImpl(MinecraftServer server, NetworkManager networkManager, ServerPing ping) {
|
||||
super(server, new PaperStatusClient(networkManager), ping.getServerData() != null ? ping.getServerData().getProtocolVersion() : -1, server.server.getServerIcon());
|
||||
this.originalSample = ping.getPlayers() == null ? null : ping.getPlayers().getSample(); // GH-1473 - pre-tick race condition NPE
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<PlayerProfile> getPlayerSample() {
|
||||
List<PlayerProfile> sample = super.getPlayerSample();
|
||||
|
||||
if (this.originalSample != null) {
|
||||
for (GameProfile profile : this.originalSample) {
|
||||
sample.add(CraftPlayerProfile.asBukkitCopy(profile));
|
||||
}
|
||||
this.originalSample = null;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
private GameProfile[] getPlayerSampleHandle() {
|
||||
if (this.originalSample != null) {
|
||||
return this.originalSample;
|
||||
}
|
||||
|
||||
List<PlayerProfile> entries = super.getPlayerSample();
|
||||
if (entries.isEmpty()) {
|
||||
return EMPTY_PROFILES;
|
||||
}
|
||||
|
||||
GameProfile[] profiles = new GameProfile[entries.size()];
|
||||
for (int i = 0; i < profiles.length; i++) {
|
||||
/*
|
||||
* Avoid null UUIDs/names since that will make the response invalid
|
||||
* on the client.
|
||||
* Instead, fall back to a fake/empty UUID and an empty string as name.
|
||||
* This can be used to create custom lines in the player list that do not
|
||||
* refer to a specific player.
|
||||
*/
|
||||
|
||||
PlayerProfile profile = entries.get(i);
|
||||
if (profile.getId() != null && profile.getName() != null) {
|
||||
profiles[i] = CraftPlayerProfile.asAuthlib(profile);
|
||||
} else {
|
||||
profiles[i] = new GameProfile(MoreObjects.firstNonNull(profile.getId(), FAKE_UUID), Strings.nullToEmpty(profile.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void processRequest(MinecraftServer server, NetworkManager networkManager) {
|
||||
StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getServerPing());
|
||||
server.server.getPluginManager().callEvent(event);
|
||||
|
||||
// Close connection immediately if event is cancelled
|
||||
if (event.isCancelled()) {
|
||||
networkManager.close(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup response
|
||||
ServerPing ping = new ServerPing();
|
||||
|
||||
// Description
|
||||
ping.setMOTD(new ChatComponentText(event.getMotd()));
|
||||
|
||||
// Players
|
||||
if (!event.shouldHidePlayers()) {
|
||||
ping.setPlayerSample(new ServerPing.ServerPingPlayerSample(event.getMaxPlayers(), event.getNumPlayers()));
|
||||
ping.getPlayers().setSample(event.getPlayerSampleHandle());
|
||||
}
|
||||
|
||||
// Version
|
||||
ping.setServerInfo(new ServerPing.ServerData(event.getVersion(), event.getProtocolVersion()));
|
||||
|
||||
// Favicon
|
||||
if (event.getServerIcon() != null) {
|
||||
ping.setFavicon(event.getServerIcon().getData());
|
||||
}
|
||||
|
||||
// Send response
|
||||
networkManager.sendPacket(new PacketStatusOutServerInfo(ping));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.UserCache;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CraftPlayerProfile implements PlayerProfile {
|
||||
|
||||
private GameProfile profile;
|
||||
private final PropertySet properties = new PropertySet();
|
||||
|
||||
public CraftPlayerProfile(CraftPlayer player) {
|
||||
this.profile = player.getHandle().getProfile();
|
||||
}
|
||||
|
||||
public CraftPlayerProfile(UUID id, String name) {
|
||||
this.profile = new GameProfile(id, name);
|
||||
}
|
||||
|
||||
public CraftPlayerProfile(GameProfile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(String property) {
|
||||
return profile.getProperties().containsKey(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(ProfileProperty property) {
|
||||
String name = property.getName();
|
||||
PropertyMap properties = profile.getProperties();
|
||||
properties.removeAll(name);
|
||||
properties.put(name, new Property(name, property.getValue(), property.getSignature()));
|
||||
}
|
||||
|
||||
public GameProfile getGameProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return profile.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID setId(@Nullable UUID uuid) {
|
||||
GameProfile prev = this.profile;
|
||||
this.profile = new GameProfile(uuid, prev.getName());
|
||||
copyProfileProperties(prev, this.profile);
|
||||
return prev.getId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
return profile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setName(@Nullable String name) {
|
||||
GameProfile prev = this.profile;
|
||||
this.profile = new GameProfile(prev.getId(), name);
|
||||
copyProfileProperties(prev, this.profile);
|
||||
return prev.getName();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<ProfileProperty> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Collection<ProfileProperty> properties) {
|
||||
properties.forEach(this::setProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearProperties() {
|
||||
profile.getProperties().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeProperty(String property) {
|
||||
return !profile.getProperties().removeAll(property).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CraftPlayerProfile that = (CraftPlayerProfile) o;
|
||||
return Objects.equals(profile, that.profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return profile.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return profile.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftPlayerProfile clone() {
|
||||
CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
|
||||
clone.setProperties(getProperties());
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return profile.isComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean completeFromCache() {
|
||||
return completeFromCache(false);
|
||||
}
|
||||
|
||||
public boolean completeFromCache(boolean lookupName) {
|
||||
if (profile.isComplete()) {
|
||||
return true;
|
||||
}
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
String name = profile.getName();
|
||||
UserCache userCache = server.getUserCache();
|
||||
if (profile.getId() == null) {
|
||||
final GameProfile profile;
|
||||
boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
|
||||
if (isOnlineMode) {
|
||||
profile = lookupName ? userCache.getProfile(name) : userCache.getProfileIfCached(name);
|
||||
} else {
|
||||
// Make an OfflinePlayer using an offline mode UUID since the name has no profile
|
||||
profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
|
||||
}
|
||||
if (profile != null) {
|
||||
this.profile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.getName() == null) {
|
||||
// If we need textures, skip this check, as we will get it below anyways.
|
||||
GameProfile profile = userCache.getProfile(this.profile.getId());
|
||||
if (profile != null) {
|
||||
this.profile = profile;
|
||||
}
|
||||
}
|
||||
return this.profile.isComplete();
|
||||
}
|
||||
|
||||
public boolean complete(boolean textures) {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
|
||||
boolean isCompleteFromCache = this.completeFromCache(true);
|
||||
if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
|
||||
GameProfile result = server.getSessionService().fillProfileProperties(profile, true);
|
||||
if (result != null) {
|
||||
this.profile = result;
|
||||
}
|
||||
}
|
||||
return profile.isComplete() && (!isOnlineMode || !textures || hasTextures());
|
||||
}
|
||||
|
||||
private static void copyProfileProperties(GameProfile source, GameProfile target) {
|
||||
PropertyMap sourceProperties = source.getProperties();
|
||||
if (sourceProperties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
PropertyMap properties = target.getProperties();
|
||||
properties.clear();
|
||||
|
||||
for (Property property : sourceProperties.values()) {
|
||||
properties.put(property.getName(), property);
|
||||
}
|
||||
}
|
||||
|
||||
private static ProfileProperty toBukkit(Property property) {
|
||||
return new ProfileProperty(property.getName(), property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
|
||||
CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
|
||||
copyProfileProperties(gameProfile, profile.profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
public static PlayerProfile asBukkitMirror(GameProfile profile) {
|
||||
return new CraftPlayerProfile(profile);
|
||||
}
|
||||
|
||||
public static Property asAuthlib(ProfileProperty property) {
|
||||
return new Property(property.getName(), property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
public static GameProfile asAuthlibCopy(PlayerProfile profile) {
|
||||
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
|
||||
return asAuthlib(craft.clone());
|
||||
}
|
||||
|
||||
public static GameProfile asAuthlib(PlayerProfile profile) {
|
||||
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
|
||||
return craft.getGameProfile();
|
||||
}
|
||||
|
||||
private class PropertySet extends AbstractSet<ProfileProperty> {
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Iterator<ProfileProperty> iterator() {
|
||||
return new ProfilePropertyIterator(profile.getProperties().values().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return profile.getProperties().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ProfileProperty property) {
|
||||
setProperty(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends ProfileProperty> c) {
|
||||
//noinspection unchecked
|
||||
setProperties((Collection<ProfileProperty>) c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName());
|
||||
}
|
||||
|
||||
private class ProfilePropertyIterator implements Iterator<ProfileProperty> {
|
||||
private final Iterator<Property> iterator;
|
||||
|
||||
ProfilePropertyIterator(Iterator<Property> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileProperty next() {
|
||||
return toBukkit(iterator.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.UserAuthentication;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
|
||||
import java.net.Proxy;
|
||||
|
||||
public class PaperAuthenticationService extends YggdrasilAuthenticationService {
|
||||
public PaperAuthenticationService(Proxy proxy, String clientToken) {
|
||||
super(proxy, clientToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthentication createUserAuthentication(Agent agent) {
|
||||
return new PaperUserAuthentication(this, agent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftSessionService createMinecraftSessionService() {
|
||||
return new PaperMinecraftSessionService(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfileRepository createProfileRepository() {
|
||||
return new PaperGameProfileRepository(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.event.profile.LookupProfileEvent;
|
||||
import com.destroystokyo.paper.event.profile.PreLookupProfileEvent;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.ProfileLookupCallback;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
|
||||
|
||||
public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService) {
|
||||
super(authenticationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
|
||||
Set<String> unfoundNames = Sets.newHashSet();
|
||||
for (String name : names) {
|
||||
PreLookupProfileEvent event = new PreLookupProfileEvent(name);
|
||||
event.callEvent();
|
||||
if (event.getUUID() != null) {
|
||||
// Plugin provided UUI, we can skip network call.
|
||||
GameProfile gameprofile = new GameProfile(event.getUUID(), name);
|
||||
// We might even have properties!
|
||||
Set<ProfileProperty> profileProperties = event.getProfileProperties();
|
||||
if (!profileProperties.isEmpty()) {
|
||||
for (ProfileProperty property : profileProperties) {
|
||||
gameprofile.getProperties().put(property.getName(), CraftPlayerProfile.asAuthlib(property));
|
||||
}
|
||||
}
|
||||
callback.onProfileLookupSucceeded(gameprofile);
|
||||
} else {
|
||||
unfoundNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Some things were not found.... Proceed to look up.
|
||||
if (!unfoundNames.isEmpty()) {
|
||||
String[] namesArr = unfoundNames.toArray(new String[unfoundNames.size()]);
|
||||
super.findProfilesByNames(namesArr, agent, new PreProfileLookupCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
private static class PreProfileLookupCallback implements ProfileLookupCallback {
|
||||
private final ProfileLookupCallback callback;
|
||||
|
||||
PreProfileLookupCallback(ProfileLookupCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupSucceeded(GameProfile gameProfile) {
|
||||
PlayerProfile from = CraftPlayerProfile.asBukkitMirror(gameProfile);
|
||||
new LookupProfileEvent(from).callEvent();
|
||||
callback.onProfileLookupSucceeded(gameProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupFailed(GameProfile gameProfile, Exception e) {
|
||||
callback.onProfileLookupFailed(gameProfile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.event.profile.FillProfileEvent;
|
||||
import com.destroystokyo.paper.event.profile.PreFillProfileEvent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
|
||||
protected PaperMinecraftSessionService(YggdrasilAuthenticationService authenticationService) {
|
||||
super(authenticationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
|
||||
return super.getTextures(profile, requireSecure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
|
||||
CraftPlayerProfile playerProfile = (CraftPlayerProfile) CraftPlayerProfile.asBukkitMirror(profile);
|
||||
new PreFillProfileEvent(playerProfile).callEvent();
|
||||
profile = playerProfile.getGameProfile();
|
||||
if (profile.isComplete() && profile.getProperties().containsKey("textures")) {
|
||||
return profile;
|
||||
}
|
||||
GameProfile gameProfile = super.fillProfileProperties(profile, requireSecure);
|
||||
new FillProfileEvent(CraftPlayerProfile.asBukkitMirror(gameProfile)).callEvent();
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) {
|
||||
return super.fillGameProfile(profile, requireSecure);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
|
||||
|
||||
public class PaperUserAuthentication extends YggdrasilUserAuthentication {
|
||||
public PaperUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) {
|
||||
super(authenticationService, agent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.destroystokyo.paper.proxy;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.PacketDataSerializer;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class VelocityProxy {
|
||||
private static final int SUPPORTED_FORWARDING_VERSION = 1;
|
||||
public static final MinecraftKey PLAYER_INFO_CHANNEL = new MinecraftKey("velocity", "player_info");
|
||||
|
||||
public static boolean checkIntegrity(final PacketDataSerializer buf) {
|
||||
final byte[] signature = new byte[32];
|
||||
buf.readBytes(signature);
|
||||
|
||||
final byte[] data = new byte[buf.readableBytes()];
|
||||
buf.getBytes(buf.readerIndex(), data);
|
||||
|
||||
try {
|
||||
final Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(PaperConfig.velocitySecretKey, "HmacSHA256"));
|
||||
final byte[] mySignature = mac.doFinal(data);
|
||||
if (!MessageDigest.isEqual(signature, mySignature)) {
|
||||
return false;
|
||||
}
|
||||
} catch (final InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
int version = buf.readVarInt();
|
||||
if (version != SUPPORTED_FORWARDING_VERSION) {
|
||||
throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + SUPPORTED_FORWARDING_VERSION);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static InetAddress readAddress(final PacketDataSerializer buf) {
|
||||
return InetAddresses.forString(buf.readUTF(Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
public static GameProfile createProfile(final PacketDataSerializer buf) {
|
||||
final GameProfile profile = new GameProfile(buf.readUUID(), buf.readUTF(16));
|
||||
readProperties(buf, profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private static void readProperties(final PacketDataSerializer buf, final GameProfile profile) {
|
||||
final int properties = buf.readVarInt();
|
||||
for (int i1 = 0; i1 < properties; i1++) {
|
||||
final String name = buf.readUTF(Short.MAX_VALUE);
|
||||
final String value = buf.readUTF(Short.MAX_VALUE);
|
||||
final String signature = buf.readBoolean() ? buf.readUTF(Short.MAX_VALUE) : null;
|
||||
profile.getProperties().put(name, new Property(name, value, signature));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package com.destroystokyo.paper.util;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Implements an Executor Service that allows specifying Task Priority
|
||||
* and bumping of task priority.
|
||||
*
|
||||
* This is a non blocking executor with 3 priority levels.
|
||||
*
|
||||
* URGENT: Rarely used, something that is critical to take action now.
|
||||
* HIGH: Something with more importance than the base tasks
|
||||
*
|
||||
* @author Daniel Ennis <aikar@aikar.co>
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"})
|
||||
public class PriorityQueuedExecutor extends AbstractExecutorService {
|
||||
|
||||
private final ConcurrentLinkedQueue<Runnable> urgent = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Runnable> high = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Runnable> normal = new ConcurrentLinkedQueue<>();
|
||||
private final List<Thread> threads = new ArrayList<>();
|
||||
private final RejectionHandler handler;
|
||||
|
||||
private volatile boolean shuttingDown = false;
|
||||
private volatile boolean shuttingDownNow = false;
|
||||
|
||||
public PriorityQueuedExecutor(String name) {
|
||||
this(name, Math.max(1, Runtime.getRuntime().availableProcessors() - 1));
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads) {
|
||||
this(name, threads, Thread.NORM_PRIORITY, null);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, int threadPriority) {
|
||||
this(name, threads, threadPriority, null);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, RejectionHandler handler) {
|
||||
this(name, threads, Thread.NORM_PRIORITY, handler);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, int threadPriority, RejectionHandler handler) {
|
||||
for (int i = 0; i < threads; i++) {
|
||||
ExecutorThread thread = new ExecutorThread(this::processQueues);
|
||||
thread.setDaemon(true);
|
||||
thread.setName(threads == 1 ? name : name + "-" + (i + 1));
|
||||
thread.setPriority(threadPriority);
|
||||
thread.start();
|
||||
this.threads.add(thread);
|
||||
}
|
||||
if (handler == null) {
|
||||
handler = ABORT_POLICY;
|
||||
}
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the Current thread belongs to a PriorityQueuedExecutor, return that Executro
|
||||
* @return The executor that controls this thread
|
||||
*/
|
||||
public static PriorityQueuedExecutor getExecutor() {
|
||||
if (!(Thread.currentThread() instanceof ExecutorThread)) {
|
||||
return null;
|
||||
}
|
||||
return ((ExecutorThread) Thread.currentThread()).getExecutor();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shuttingDown = true;
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shuttingDown = true;
|
||||
shuttingDownNow = true;
|
||||
List<Runnable> tasks = new ArrayList<>(high.size() + normal.size());
|
||||
Runnable run;
|
||||
while ((run = getTask()) != null) {
|
||||
tasks.add(run);
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shuttingDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
if (!shuttingDown) {
|
||||
return false;
|
||||
}
|
||||
return high.isEmpty() && normal.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) {
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
final long wait = unit.toNanos(timeout);
|
||||
final long max = System.nanoTime() + wait;
|
||||
for (;!threads.isEmpty() && System.nanoTime() < max;) {
|
||||
threads.removeIf(thread -> !thread.isAlive());
|
||||
}
|
||||
return isTerminated();
|
||||
}
|
||||
|
||||
|
||||
public PendingTask<Void> createPendingTask(Runnable task) {
|
||||
return createPendingTask(task, Priority.NORMAL);
|
||||
}
|
||||
public PendingTask<Void> createPendingTask(Runnable task, Priority priority) {
|
||||
return createPendingTask(() -> {
|
||||
task.run();
|
||||
return null;
|
||||
}, priority);
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> createPendingTask(Supplier<T> task) {
|
||||
return createPendingTask(task, Priority.NORMAL);
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> createPendingTask(Supplier<T> task, Priority priority) {
|
||||
return new PendingTask<>(task, priority);
|
||||
}
|
||||
|
||||
public PendingTask<Void> submitTask(Runnable run) {
|
||||
return createPendingTask(run).submit();
|
||||
}
|
||||
|
||||
public PendingTask<Void> submitTask(Runnable run, Priority priority) {
|
||||
return createPendingTask(run, priority).submit();
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> submitTask(Supplier<T> run) {
|
||||
return createPendingTask(run).submit();
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> submitTask(Supplier<T> run, Priority priority) {
|
||||
PendingTask<T> task = createPendingTask(run, priority);
|
||||
return task.submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable command) {
|
||||
submitTask(command);
|
||||
}
|
||||
|
||||
public boolean isCurrentThread() {
|
||||
final Thread thread = Thread.currentThread();
|
||||
if (!(thread instanceof ExecutorThread)) {
|
||||
return false;
|
||||
}
|
||||
return ((ExecutorThread) thread).getExecutor() == this;
|
||||
}
|
||||
|
||||
public Runnable getUrgentTask() {
|
||||
return urgent.poll();
|
||||
}
|
||||
|
||||
public Runnable getTask() {
|
||||
Runnable run = urgent.poll();
|
||||
if (run != null) {
|
||||
return run;
|
||||
}
|
||||
run = high.poll();
|
||||
if (run != null) {
|
||||
return run;
|
||||
}
|
||||
return normal.poll();
|
||||
}
|
||||
|
||||
private void processQueues() {
|
||||
Runnable run = null;
|
||||
while (true) {
|
||||
if (run != null) {
|
||||
run.run();
|
||||
}
|
||||
if (shuttingDownNow) {
|
||||
return;
|
||||
}
|
||||
if ((run = getTask()) != null) {
|
||||
continue;
|
||||
}
|
||||
synchronized (PriorityQueuedExecutor.this) {
|
||||
if ((run = getTask()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shuttingDown || shuttingDownNow) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PriorityQueuedExecutor.this.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean processUrgentTasks() {
|
||||
Runnable run;
|
||||
boolean hadTask = false;
|
||||
while ((run = getUrgentTask()) != null) {
|
||||
run.run();
|
||||
hadTask = true;
|
||||
}
|
||||
return hadTask;
|
||||
}
|
||||
|
||||
public enum Priority {
|
||||
NORMAL, HIGH, URGENT
|
||||
}
|
||||
|
||||
public class ExecutorThread extends Thread {
|
||||
public ExecutorThread(Runnable runnable) {
|
||||
super(runnable);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor getExecutor() {
|
||||
return PriorityQueuedExecutor.this;
|
||||
}
|
||||
}
|
||||
|
||||
public class PendingTask <T> implements Runnable {
|
||||
|
||||
private final AtomicBoolean hasRan = new AtomicBoolean();
|
||||
private final AtomicInteger submitted = new AtomicInteger(-1);
|
||||
private final AtomicInteger priority;
|
||||
private final Supplier<T> run;
|
||||
private final CompletableFuture<T> future = new CompletableFuture<>();
|
||||
private volatile PriorityQueuedExecutor executor;
|
||||
|
||||
public PendingTask(Supplier<T> run) {
|
||||
this(run, Priority.NORMAL);
|
||||
}
|
||||
|
||||
public PendingTask(Supplier<T> run, Priority priority) {
|
||||
this.priority = new AtomicInteger(priority.ordinal());
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
public boolean cancel() {
|
||||
return hasRan.compareAndSet(false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!hasRan.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
future.complete(run.get());
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void bumpPriority() {
|
||||
bumpPriority(Priority.HIGH);
|
||||
}
|
||||
|
||||
public void bumpPriority(Priority newPriority) {
|
||||
for (;;) {
|
||||
int current = this.priority.get();
|
||||
int ordinal = newPriority.ordinal();
|
||||
if (current >= ordinal || priority.compareAndSet(current, ordinal)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.submitted.get() == -1 || this.hasRan.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only resubmit if it hasnt ran yet and has been submitted
|
||||
submit();
|
||||
}
|
||||
|
||||
public CompletableFuture<T> onDone() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public PendingTask<T> submit() {
|
||||
if (shuttingDown) {
|
||||
handler.onRejection(this, PriorityQueuedExecutor.this);
|
||||
return this;
|
||||
}
|
||||
for (;;) {
|
||||
final int submitted = this.submitted.get();
|
||||
final int priority = this.priority.get();
|
||||
if (submitted == priority) {
|
||||
return this;
|
||||
}
|
||||
if (this.submitted.compareAndSet(submitted, priority)) {
|
||||
if (priority == Priority.URGENT.ordinal()) {
|
||||
urgent.add(this);
|
||||
} else if (priority == Priority.HIGH.ordinal()) {
|
||||
high.add(this);
|
||||
} else {
|
||||
normal.add(this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (PriorityQueuedExecutor.this) {
|
||||
// Wake up a thread to take this work
|
||||
PriorityQueuedExecutor.this.notify();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public interface RejectionHandler {
|
||||
void onRejection(Runnable run, PriorityQueuedExecutor executor);
|
||||
}
|
||||
|
||||
public static final RejectionHandler ABORT_POLICY = (run, executor) -> {
|
||||
throw new RejectedExecutionException("Executor has been shutdown");
|
||||
};
|
||||
public static final RejectionHandler CALLER_RUNS_POLICY = (run, executor) -> {
|
||||
run.run();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,910 @@
|
||||
package com.destroystokyo.paper.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.BlockRedstoneWire;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
/**
|
||||
* Used for the faster redstone algorithm.
|
||||
* Original author: theosib
|
||||
* Original license: MIT
|
||||
*
|
||||
* Ported to Paper and updated to 1.13 by egg82
|
||||
*/
|
||||
public class RedstoneWireTurbo {
|
||||
/*
|
||||
* This is Helper class for BlockRedstoneWire. It implements a minimally-invasive
|
||||
* bolt-on accelerator that performs a breadth-first search through redstone wire blocks
|
||||
* in order to more efficiently and deterministically compute new redstone wire power levels
|
||||
* and determine the order in which other blocks should be updated.
|
||||
*
|
||||
* Features:
|
||||
* - Changes to BlockRedstoneWire are very limited, no other classes are affected, and the
|
||||
* choice between old and new redstone wire update algorithms is switchable on-line.
|
||||
* - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone
|
||||
* wire blocks to communicate power level changes to each other, generating 36 block
|
||||
* updates per call. This improved implementation propagates power level changes directly
|
||||
* between redstone wire blocks. Redstone wire power levels are therefore computed more quickly,
|
||||
* and block updates are sent only to non-redstone blocks, many of which may perform an
|
||||
* action when informed of a change in redstone power level. (Note: Block updates are not
|
||||
* the same as state changes to redstone wire. Wire block states are updated as soon
|
||||
* as they are computed.)
|
||||
* - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange,
|
||||
* 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor).
|
||||
* These are eliminated.
|
||||
* - Updates to redstone wire and other connected blocks are propagated in a breath-first
|
||||
* manner, radiating out from the initial trigger (a block update to a redstone wire
|
||||
* from something other than redstone wire).
|
||||
* - Updates are scheduled both deterministically and in an intuitive order, addressing bug
|
||||
* MC-11193.
|
||||
* - All redstone behavior that used to be locational now works the same in all locations.
|
||||
* - All behaviors of redstone wire that used to be orientational now work the same in all
|
||||
* orientations, as long as orientation can be determined; random otherwise. Some other
|
||||
* redstone components still update directionally (e.g. switches), and this code can't
|
||||
* compensate for that.
|
||||
* - Information that is otherwise computed over and over again or which is expensive to
|
||||
* to compute is cached for faster lookup. This includes coordinates of block position
|
||||
* neighbors and block states that won't change behind our backs during the execution of
|
||||
* this search algorithm.
|
||||
* - Redundant block updates (both to redstone wire and to other blocks) are heavily
|
||||
* consolidated. For worst-case scenarios (depowering of redstone wire) this results
|
||||
* in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads,
|
||||
* empirical testing shows a speedup better than 10x. This addresses bug MC-81098.
|
||||
*
|
||||
* Extensive testing has been performed to ensure that existing redstone contraptions still
|
||||
* behave as expected. Results of early testing that identified undesirable behavior changes
|
||||
* were addressed. Additionally, real-time performance testing revealed compute inefficiencies
|
||||
* With earlier implementations of this accelerator. Some compatibility adjustments and
|
||||
* performance optimizations resulted in harmless increases in block updates above the
|
||||
* theoretical minimum.
|
||||
*
|
||||
* Only a single redstone machine was found to break: An instant dropper line hack that
|
||||
* relies on powered rails and quasi-connectivity but doesn't work in all directions. The
|
||||
* replacement is to lay redstone wire directly on top of the dropper line, which now works
|
||||
* reliably in any direction.
|
||||
*
|
||||
* There are numerous other optimization that can be made, but those will be provided later in
|
||||
* separate updates. This version is designed to be minimalistic.
|
||||
*
|
||||
* Many thanks to the following individuals for their help in testing this functionality:
|
||||
* - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango,
|
||||
* OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works,
|
||||
* Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince.
|
||||
*/
|
||||
|
||||
/* Reference to BlockRedstoneWire object, which uses this accelerator */
|
||||
private final BlockRedstoneWire wire;
|
||||
|
||||
/*
|
||||
* Implementation:
|
||||
*
|
||||
* RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the
|
||||
* initial block update that came from a call to BlockRedstoneWire.neighborChanged().
|
||||
* All nodes put in Layer N are those with Manhattan distance N from the trigger
|
||||
* position, reachable through connected redstone wire blocks.
|
||||
*
|
||||
* Layer 0 represents the trigger block position that was input to neighborChanged.
|
||||
* Layer 1 contains the immediate neighbors of that position.
|
||||
* Layer N contains the neighbors of blocks in layer N-1, not including
|
||||
* those in previous layers.
|
||||
*
|
||||
* Layers enforce an update order that is a function of Manhattan distance
|
||||
* from the initial coordinates input to neighborChanged. The same
|
||||
* coordinates may appear in multiple layers, but redundant updates are minimized.
|
||||
* Block updates are sent layer-by-layer. If multiple of a block's neighbors experience
|
||||
* redstone wire changes before its layer is processed, then those updates will be merged.
|
||||
* If a block's update has been sent, but its neighboring redstone changes
|
||||
* after that, then another update will be sent. This preserves compatibility with
|
||||
* machines that rely on zero-tick behavior, except that the new functionality is non-
|
||||
* locational.
|
||||
*
|
||||
* Within each layer, updates are ordered left-to-right relative to the direction of
|
||||
* information flow. This makes the implementation non-orientational. Only when
|
||||
* this direction is ambiguous is randomness applied (intentionally).
|
||||
*/
|
||||
private List<UpdateNode> updateQueue0 = Lists.newArrayList();
|
||||
private List<UpdateNode> updateQueue1 = Lists.newArrayList();
|
||||
private List<UpdateNode> updateQueue2 = Lists.newArrayList();
|
||||
|
||||
public RedstoneWireTurbo(BlockRedstoneWire wire) {
|
||||
this.wire = wire;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute neighbors of a block. When a redstone wire value changes, previously it called
|
||||
* World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in
|
||||
* west, east, down, up, north, south order. For each of those neighbors, their own
|
||||
* neighbors are updated in the same order. This generates 36 updates, but 12 of them are
|
||||
* redundant; for instance the west neighbor of a block's east neighbor.
|
||||
*
|
||||
* Note that this ordering is only used to create the initial list of neighbors. Once
|
||||
* the direction of signal flow is identified, the ordering of updates is completely
|
||||
* reorganized.
|
||||
*/
|
||||
public static BlockPosition[] computeAllNeighbors(final BlockPosition pos) {
|
||||
final int x = pos.getX();
|
||||
final int y = pos.getY();
|
||||
final int z = pos.getZ();
|
||||
final BlockPosition[] n = new BlockPosition[24];
|
||||
|
||||
// Immediate neighbors, in the same order as
|
||||
// World.notifyNeighborsOfStateChange, etc.:
|
||||
// west, east, down, up, north, south
|
||||
n[0] = new BlockPosition(x - 1, y, z);
|
||||
n[1] = new BlockPosition(x + 1, y, z);
|
||||
n[2] = new BlockPosition(x, y - 1, z);
|
||||
n[3] = new BlockPosition(x, y + 1, z);
|
||||
n[4] = new BlockPosition(x, y, z - 1);
|
||||
n[5] = new BlockPosition(x, y, z + 1);
|
||||
|
||||
// Neighbors of neighbors, in the same order,
|
||||
// except that duplicates are not included
|
||||
n[6] = new BlockPosition(x - 2, y, z);
|
||||
n[7] = new BlockPosition(x - 1, y - 1, z);
|
||||
n[8] = new BlockPosition(x - 1, y + 1, z);
|
||||
n[9] = new BlockPosition(x - 1, y, z - 1);
|
||||
n[10] = new BlockPosition(x - 1, y, z + 1);
|
||||
n[11] = new BlockPosition(x + 2, y, z);
|
||||
n[12] = new BlockPosition(x + 1, y - 1, z);
|
||||
n[13] = new BlockPosition(x + 1, y + 1, z);
|
||||
n[14] = new BlockPosition(x + 1, y, z - 1);
|
||||
n[15] = new BlockPosition(x + 1, y, z + 1);
|
||||
n[16] = new BlockPosition(x, y - 2, z);
|
||||
n[17] = new BlockPosition(x, y - 1, z - 1);
|
||||
n[18] = new BlockPosition(x, y - 1, z + 1);
|
||||
n[19] = new BlockPosition(x, y + 2, z);
|
||||
n[20] = new BlockPosition(x, y + 1, z - 1);
|
||||
n[21] = new BlockPosition(x, y + 1, z + 1);
|
||||
n[22] = new BlockPosition(x, y, z - 2);
|
||||
n[23] = new BlockPosition(x, y, z + 2);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only want redstone wires to update redstone wires that are
|
||||
* immediately adjacent. Some more distant updates can result
|
||||
* in cross-talk that (a) wastes time and (b) can make the update
|
||||
* order unintuitive. Therefore (relative to the neighbor order
|
||||
* computed by computeAllNeighbors), updates are not scheduled
|
||||
* for redstone wire in those non-connecting positions. On the
|
||||
* other hand, updates will always be sent to *other* types of blocks
|
||||
* in any of the 24 neighboring positions.
|
||||
*/
|
||||
private static final boolean[] update_redstone = {
|
||||
true, true, false, false, true, true, // 0 to 5
|
||||
false, true, true, false, false, false, // 6 to 11
|
||||
true, true, false, false, false, true, // 12 to 17
|
||||
true, false, true, true, false, false // 18 to 23
|
||||
};
|
||||
|
||||
// Internal numbering for cardinal directions
|
||||
private static final int North = 0;
|
||||
private static final int East = 1;
|
||||
private static final int South = 2;
|
||||
private static final int West = 3;
|
||||
|
||||
/*
|
||||
* These lookup tables completely remap neighbor positions into a left-to-right
|
||||
* ordering, based on the cardinal direction that is determined to be forward.
|
||||
* See below for more explanation.
|
||||
*/
|
||||
private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15};
|
||||
private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10};
|
||||
private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9};
|
||||
private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14};
|
||||
|
||||
/* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block
|
||||
* that is itself having an update computed, and this center position is marked with C.
|
||||
* - The update position marked 0 is computed first, and the one marked 23 is last.
|
||||
* - Forward is determined by the local direction of information flow into position C from prior updates.
|
||||
* - The first updates are scheduled for the four positions below and above C.
|
||||
* - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors.
|
||||
* - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate).
|
||||
* - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly
|
||||
* right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering.
|
||||
* - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no
|
||||
* updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C
|
||||
* are ALSO scheduled for layer N+2.
|
||||
* - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility
|
||||
* with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular
|
||||
* made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix,
|
||||
* along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including
|
||||
* NarcolepticFrog, nessie, and Pokechu22.
|
||||
*
|
||||
* - The forward direction is determined locally. So when there are branches in the redstone wire, the left one will get updated
|
||||
* before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch
|
||||
* having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed
|
||||
* by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15,
|
||||
* that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward
|
||||
* directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be
|
||||
* concerned about branches meeting up with each other.
|
||||
*
|
||||
* ^
|
||||
* |
|
||||
* Forward
|
||||
* <-- Left Right -->
|
||||
*
|
||||
* 18
|
||||
* 10 17 5 19 11
|
||||
* 2 8 0 12 16 4 C 6 20 9 1 13 3
|
||||
* 14 21 7 23 15
|
||||
* Further 22 Further
|
||||
* Down Down Up Up
|
||||
*
|
||||
* Backward
|
||||
* |
|
||||
* V
|
||||
*/
|
||||
|
||||
// This allows the above remapping tables to be looked up by cardial direction index
|
||||
private static final int[][] reordering = { forward_is_north, forward_is_east, forward_is_south, forward_is_west };
|
||||
|
||||
/*
|
||||
* Input: Array of UpdateNode objects in an order corresponding to the positions
|
||||
* computed by computeAllNeighbors above.
|
||||
* Output: Array of UpdateNode objects oriented using the above remapping tables
|
||||
* corresponding to the identified heading (direction of information flow).
|
||||
*/
|
||||
private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) {
|
||||
final int[] re = reordering[heading];
|
||||
for (int i = 0; i < 24; i++) {
|
||||
dst[i] = src[re[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure to keep track of redstone wire blocks and
|
||||
* neighbors that will receive updates.
|
||||
*/
|
||||
private static class UpdateNode {
|
||||
public static enum Type {
|
||||
UNKNOWN, REDSTONE, OTHER
|
||||
}
|
||||
|
||||
IBlockData currentState; // Keep track of redstone wire value
|
||||
UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges)
|
||||
BlockPosition self; // UpdateNode's own position
|
||||
BlockPosition parent; // Which block pos spawned/updated this node
|
||||
Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block
|
||||
int layer; // Highest layer this node is scheduled in
|
||||
boolean visited; // To keep track of information flow direction, visited restone wire is marked
|
||||
int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities.
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of all block positions discovered during search and their current states.
|
||||
* We want to remember one entry for each position.
|
||||
*/
|
||||
private final Map<BlockPosition, UpdateNode> nodeCache = Maps.newHashMap();
|
||||
|
||||
/*
|
||||
* For a newly created UpdateNode object, determine what type of block it is.
|
||||
*/
|
||||
private void identifyNode(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
final IBlockData oldState = worldIn.getType(pos);
|
||||
upd1.currentState = oldState;
|
||||
|
||||
// Some neighbors of redstone wire are other kinds of blocks.
|
||||
// These need to receive block updates to inform them that
|
||||
// redstone wire values have changed.
|
||||
final Block block = oldState.getBlock();
|
||||
if (block != wire) {
|
||||
// Mark this block as not redstone wire and therefore
|
||||
// requiring updates
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Non-redstone blocks may propagate updates, but those updates
|
||||
// are not handled by this accelerator. Therefore, we do not
|
||||
// expand this position's neighbors.
|
||||
return;
|
||||
}
|
||||
|
||||
// One job of BlockRedstoneWire.neighborChanged is to convert
|
||||
// redstone wires to items if the block beneath was removed.
|
||||
// With this accelerator, BlockRedstoneWire.neighborChanged
|
||||
// is only typically called for a single wire block, while
|
||||
// others are processed internally by the breadth first search
|
||||
// algorithm. To preserve this game behavior, this check must
|
||||
// be replicated here.
|
||||
if (!wire.canPlace(null, worldIn, pos)) {
|
||||
// Pop off the redstone dust
|
||||
oldState.dropNaturally(worldIn, pos, 0);
|
||||
worldIn.setAir(pos);
|
||||
|
||||
// Mark this position as not being redstone wire
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Note: Sending updates to air blocks leads to an empty method.
|
||||
// Testing shows this to be faster than explicitly avoiding updates to
|
||||
// air blocks.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the above conditions fail, then this is a redstone wire block.
|
||||
upd1.type = UpdateNode.Type.REDSTONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given which redstone wire blocks have been visited and not visited
|
||||
* around the position currently being updated, compute the cardinal
|
||||
* direction that is "forward."
|
||||
*
|
||||
* rx is the forward direction along the West/East axis
|
||||
* rz is the forward direction along the North/South axis
|
||||
*/
|
||||
static private int computeHeading(final int rx, final int rz) {
|
||||
// rx and rz can only take on values -1, 0, and 1, so we can
|
||||
// compute a code number that allows us to use a single switch
|
||||
// to determine the heading.
|
||||
final int code = (rx + 1) + 3 * (rz + 1);
|
||||
switch (code) {
|
||||
case 0: {
|
||||
// Both rx and rz are -1 (northwest)
|
||||
// Randomly choose one to be forward.
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? North : West;
|
||||
}
|
||||
case 1: {
|
||||
// rx=0, rz=-1
|
||||
// Definitively North
|
||||
return North;
|
||||
}
|
||||
case 2: {
|
||||
// rx=1, rz=-1 (northeast)
|
||||
// Choose randomly between north and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? North : East;
|
||||
}
|
||||
case 3: {
|
||||
// rx=-1, rz=0
|
||||
// Definitively West
|
||||
return West;
|
||||
}
|
||||
case 4: {
|
||||
// rx=0, rz=0
|
||||
// Heading is completely ambiguous. Choose
|
||||
// randomly among the four cardinal directions.
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
case 5: {
|
||||
// rx=1, rz=0
|
||||
// Definitively East
|
||||
return East;
|
||||
}
|
||||
case 6: {
|
||||
// rx=-1, rz=1 (southwest)
|
||||
// Choose randomly between south and west
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? South : West;
|
||||
}
|
||||
case 7: {
|
||||
// rx=0, rz=1
|
||||
// Definitively South
|
||||
return South;
|
||||
}
|
||||
case 8: {
|
||||
// rx=1, rz=1 (southeast)
|
||||
// Choose randomly between south and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? South : East;
|
||||
}
|
||||
}
|
||||
|
||||
// We should never get here
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
|
||||
// Select whether to use updateSurroundingRedstone from BlockRedstoneWire (old)
|
||||
// or this helper class (new)
|
||||
private static final boolean old_current_change = false;
|
||||
|
||||
/*
|
||||
* Process a node whose neighboring redstone wire has experienced value changes.
|
||||
*/
|
||||
private void updateNode(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// Mark this redstone wire as having been visited so that it can be used
|
||||
// to calculate direction of information flow.
|
||||
upd1.visited = true;
|
||||
|
||||
// Look up the last known state.
|
||||
// Due to the way other redstone components are updated, we do not
|
||||
// have to worry about a state changing behind our backs. The rare
|
||||
// exception is handled by scheduleReentrantNeighborChanged.
|
||||
final IBlockData oldState = upd1.currentState;
|
||||
|
||||
// Ask the wire block to compute its power level from its neighbors.
|
||||
// This will also update the wire's power level and return a new
|
||||
// state if it has changed. When a wire power level is changed,
|
||||
// calculateCurrentChanges will immediately update the block state in the world
|
||||
// and return the same value here to be cached in the corresponding
|
||||
// UpdateNode object.
|
||||
IBlockData newState;
|
||||
if (old_current_change) {
|
||||
newState = wire.calculateCurrentChanges(worldIn, pos, pos, oldState);
|
||||
} else {
|
||||
// Looking up block state is slow. This accelerator includes a version of
|
||||
// calculateCurrentChanges that uses cahed wire values for a
|
||||
// significant performance boost.
|
||||
newState = this.calculateCurrentChanges(worldIn, upd1);
|
||||
}
|
||||
|
||||
// Only inform neighbors if the state has changed
|
||||
if (newState != oldState) {
|
||||
// Store the new state
|
||||
upd1.currentState = newState;
|
||||
|
||||
// Inform neighbors of the change
|
||||
propagateChanges(worldIn, upd1, layer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This identifies the neighboring positions of a new UpdateNode object,
|
||||
* determines their types, and links those to into the graph. Then based on
|
||||
* what nodes in the redstone wire graph have been visited, the neighbors
|
||||
* are reordered left-to-right relative to the direction of information flow.
|
||||
*/
|
||||
private void findNeighbors(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// Get the list of neighbor coordinates
|
||||
final BlockPosition[] neighbors = computeAllNeighbors(pos);
|
||||
|
||||
// Temporary array of neighbors in cardinal ordering
|
||||
final UpdateNode[] neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
// Target array of neighbors sorted left-to-right
|
||||
upd1.neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
for (int i=0; i<24; i++) {
|
||||
// Look up each neighbor in the node cache
|
||||
final BlockPosition pos2 = neighbors[i];
|
||||
UpdateNode upd2 = nodeCache.get(pos2);
|
||||
if (upd2 == null) {
|
||||
// If this is a previously unreached position, create
|
||||
// a new update node, add it to the cache, and identify what it is.
|
||||
upd2 = new UpdateNode();
|
||||
upd2.self = pos2;
|
||||
upd2.parent = pos;
|
||||
nodeCache.put(pos2, upd2);
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
|
||||
// For non-redstone blocks, any of the 24 neighboring positions
|
||||
// should receive a block update. However, some block coordinates
|
||||
// may contain a redstone wire that does not directly connect to the
|
||||
// one being expanded. To avoid redundant calculations and confusing
|
||||
// cross-talk, those neighboring positions are not included.
|
||||
if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) {
|
||||
neighbor_nodes[i] = upd2;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the directions from which the redstone signal may have come from. This
|
||||
// checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the
|
||||
// block being expanded.
|
||||
final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited);
|
||||
final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited);
|
||||
final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited);
|
||||
final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited);
|
||||
|
||||
int cx = 0, cz = 0;
|
||||
if (fromWest) cx += 1;
|
||||
if (fromEast) cx -= 1;
|
||||
if (fromNorth) cz += 1;
|
||||
if (fromSouth) cz -= 1;
|
||||
|
||||
int heading;
|
||||
if (cx==0 && cz==0) {
|
||||
// If there is no clear direction, try to inherit the heading from ancestor nodes.
|
||||
heading = computeHeading(upd1.xbias, upd1.zbias);
|
||||
|
||||
// Propagate that heading to descendant nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = upd1.xbias;
|
||||
nn.zbias = upd1.zbias;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cx != 0 && cz != 0) {
|
||||
// If the heading is somewhat ambiguous, try to disambiguate based on
|
||||
// ancestor nodes.
|
||||
if (upd1.xbias != 0) cz = 0;
|
||||
if (upd1.zbias != 0) cx = 0;
|
||||
}
|
||||
heading = computeHeading(cx, cz);
|
||||
|
||||
// Propagate that heading to descendant nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = cx;
|
||||
nn.zbias = cz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder neighboring UpdateNode objects according to the forward direction
|
||||
// determined above.
|
||||
orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading);
|
||||
}
|
||||
|
||||
/*
|
||||
* For any redstone wire block in layer N, inform neighbors to recompute their states
|
||||
* in layers N+1 and N+2;
|
||||
*/
|
||||
private void propagateChanges(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
if (upd1.neighbor_nodes == null) {
|
||||
// If this node has not been expanded yet, find its neighbors
|
||||
findNeighbors(worldIn, upd1);
|
||||
}
|
||||
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// All neighbors may be scheduled for layer N+1
|
||||
final int layer1 = layer + 1;
|
||||
|
||||
// If the node being updated (upd1) has already been expanded, then merely
|
||||
// schedule updates to its neighbors.
|
||||
for (int i = 0; i < 24; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
|
||||
// This test ensures that an UpdateNode is never scheduled to the same layer
|
||||
// more than once. Also, skip non-connecting redstone wire blocks
|
||||
if (upd2 != null && layer1 > upd2.layer) {
|
||||
upd2.layer = layer1;
|
||||
updateQueue1.add(upd2);
|
||||
|
||||
// Keep track of which block updated this neighbor
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes above and below are scheduled ALSO for layer N+2
|
||||
final int layer2 = layer + 2;
|
||||
|
||||
// Repeat of the loop above, but only for the first four (above and below) neighbors
|
||||
// and for layer N+2;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
if (upd2 != null && layer2 > upd2.layer) {
|
||||
upd2.layer = layer2;
|
||||
updateQueue2.add(upd2);
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The breadth-first search below will send block updates to blocks
|
||||
// that are not redstone wire. If one of those updates results in
|
||||
// a distant redstone wire getting an update, then this.neighborChanged
|
||||
// will get called. This would be a reentrant call, and
|
||||
// it is necessary to properly integrate those updates into the
|
||||
// on-going search through redstone wire. Thus, we make the layer
|
||||
// currently being processed visible at the object level.
|
||||
|
||||
// The current layer being processed by the breadth-first search
|
||||
private int currentWalkLayer = 0;
|
||||
|
||||
private void shiftQueue() {
|
||||
final List<UpdateNode> t = updateQueue0;
|
||||
t.clear();
|
||||
updateQueue0 = updateQueue1;
|
||||
updateQueue1 = updateQueue2;
|
||||
updateQueue2 = t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a breadth-first (layer by layer) traversal through redstone
|
||||
* wire blocks, propagating value changes to neighbors in an order
|
||||
* that is a function of distance from the initial call to
|
||||
* this.neighborChanged.
|
||||
*/
|
||||
private void breadthFirstWalk(final World worldIn) {
|
||||
shiftQueue();
|
||||
currentWalkLayer = 1;
|
||||
|
||||
// Loop over all layers
|
||||
while (updateQueue0.size()>0 || updateQueue1.size()>0) {
|
||||
// Get the set of blocks in this layer
|
||||
final List<UpdateNode> thisLayer = updateQueue0;
|
||||
|
||||
// Loop over all blocks in the layer. Recall that
|
||||
// this is a List, preserving the insertion order of
|
||||
// left-to-right based on direction of information flow.
|
||||
for (UpdateNode upd : thisLayer) {
|
||||
if (upd.type == UpdateNode.Type.REDSTONE) {
|
||||
// If the node is is redstone wire,
|
||||
// schedule updates to neighbors if its value
|
||||
// has changed.
|
||||
updateNode(worldIn, upd, currentWalkLayer);
|
||||
} else {
|
||||
// If this block is not redstone wire, send a block update.
|
||||
// Redstone wire blocks get state updates, but they don't
|
||||
// need block updates. Only non-redstone neighbors need updates.
|
||||
|
||||
// World.neighborChanged is called from
|
||||
// World.notifyNeighborsOfStateChange, and
|
||||
// notifyNeighborsOfStateExcept. We don't use
|
||||
// World.notifyNeighborsOfStateChange here, since we are
|
||||
// already keeping track of all of the neighbor positions
|
||||
// that need to be updated. All on its own, handling neighbors
|
||||
// this way reduces block updates by 1/3 (24 instead of 36).
|
||||
worldIn.neighborChanged(upd.self, wire, upd.parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to the next layer
|
||||
shiftQueue();
|
||||
currentWalkLayer++;
|
||||
}
|
||||
|
||||
currentWalkLayer = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normally, when Minecraft is computing redstone wire power changes, and a wire power level
|
||||
* change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.),
|
||||
* those updates are queued. Only once all redstone wire updates are complete will any component
|
||||
* action generate any further block updates to redstone wire. Instant repeater lines, for instance,
|
||||
* will process all wire updates for one redstone line, after which the pistons will zero-tick,
|
||||
* after which the next redstone line performs all of its updates. Thus, each wire is processed in its
|
||||
* own discrete wave.
|
||||
*
|
||||
* However, there are some corner cases where this pattern breaks, with a proof of concept discovered
|
||||
* by Rays Works, which works the same in vanilla. The scenario is as follows:
|
||||
* (1) A redstone wire is conducting a signal.
|
||||
* (2) Part-way through that wave of updates, a neighbor is updated that causes an update to a completely
|
||||
* separate redstone wire.
|
||||
* (3) This results in a call to BlockRedstoneWire.neighborChanged for that other wire, in the middle of
|
||||
* an already on-going propagation through the first wire.
|
||||
*
|
||||
* The vanilla code, being depth-first, would end up fully processing the second wire before going back
|
||||
* to finish processing the first one. (Although technically, vanilla has no special concept of "being
|
||||
* in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this
|
||||
* situation special handling, where the updates for the second wire are incorporated into the schedule
|
||||
* for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in
|
||||
* order to continue processing both the first and second wire in the order of distance from the initial
|
||||
* trigger.
|
||||
*/
|
||||
private IBlockData scheduleReentrantNeighborChanged(final World worldIn, final BlockPosition pos, final IBlockData newState, final BlockPosition source) {
|
||||
if (source != null) {
|
||||
// If the cause of the redstone wire update is known, we can use that to help determine
|
||||
// direction of information flow.
|
||||
UpdateNode src = nodeCache.get(source);
|
||||
if (src == null) {
|
||||
src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
identifyNode(worldIn, src);
|
||||
nodeCache.put(source, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Find or generate a node for the redstone block position receiving the update
|
||||
UpdateNode upd = nodeCache.get(pos);
|
||||
if (upd == null) {
|
||||
upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = pos;
|
||||
upd.visited = true;
|
||||
identifyNode(worldIn, upd);
|
||||
nodeCache.put(pos, upd);
|
||||
}
|
||||
upd.currentState = newState;
|
||||
|
||||
// Receiving this block update may mean something in the world changed.
|
||||
// Therefore we clear the cached block info about all neighbors of
|
||||
// the position receiving the update and then re-identify what they are.
|
||||
if (upd.neighbor_nodes != null) {
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode upd2 = upd.neighbor_nodes[i];
|
||||
if (upd2 == null) continue;
|
||||
upd2.type = UpdateNode.Type.UNKNOWN;
|
||||
upd2.currentState = null;
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
}
|
||||
|
||||
// The block at 'pos' is a redstone wire and has been updated already by calling
|
||||
// wire.calculateCurrentChanges, so we don't schedule that. However, we do need
|
||||
// to schedule its neighbors. By passing the current value of 'currentWalkLayer' to
|
||||
// propagateChanges, the neighbors of 'pos' are scheduled for layers currentWalkLayer+1
|
||||
// and currentWalkLayer+2.
|
||||
propagateChanges(worldIn, upd, currentWalkLayer);
|
||||
|
||||
// Return here. The call stack will unwind back to the first call to
|
||||
// updateSurroundingRedstone, whereupon the new updates just scheduled will
|
||||
// be propagated. This also facilitates elimination of superfluous and
|
||||
// redundant block updates.
|
||||
return newState;
|
||||
}
|
||||
|
||||
/*
|
||||
* New version of pre-existing updateSurroundingRedstone, which is called from
|
||||
* wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a
|
||||
* few other methods in BlockRedstoneWire. This sets off the breadth-first
|
||||
* walk through all redstone dust connected to the initial position triggered.
|
||||
*/
|
||||
public IBlockData updateSurroundingRedstone(final World worldIn, final BlockPosition pos, final IBlockData state, final BlockPosition source) {
|
||||
// Check this block's neighbors and see if its power level needs to change
|
||||
// Use the calculateCurrentChanges method in BlockRedstoneWire since we have no
|
||||
// cached block states at this point.
|
||||
final IBlockData newState = wire.calculateCurrentChanges(worldIn, pos, pos, state);
|
||||
|
||||
// If no change, exit
|
||||
if (newState == state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Check to see if this update was received during an on-going breadth first search
|
||||
if (currentWalkLayer > 0 || nodeCache.size() > 0) {
|
||||
// As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those
|
||||
// neighbors may affect the world so as to cause yet another redstone wire block to receive
|
||||
// an update. If that happens, we need to integrate those redstone wire updates into the
|
||||
// already on-going graph walk being performed by breadthFirstWalk.
|
||||
return scheduleReentrantNeighborChanged(worldIn, pos, newState, source);
|
||||
}
|
||||
// If there are no on-going walks through redstone wire, then start a new walk.
|
||||
|
||||
// If the source of the block update to the redstone wire at 'pos' is known, we can use
|
||||
// that to help determine the direction of information flow.
|
||||
if (source != null) {
|
||||
final UpdateNode src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
nodeCache.put(source, src);
|
||||
identifyNode(worldIn, src);
|
||||
}
|
||||
|
||||
// Create a node representing the block at 'pos', and then propagate updates
|
||||
// to its neighbors. As stated above, the call to wire.calculateCurrentChanges
|
||||
// already performs the update to the block at 'pos', so it is not added to the schedule.
|
||||
final UpdateNode upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = source!=null ? source : pos;
|
||||
upd.currentState = newState;
|
||||
upd.type = UpdateNode.Type.REDSTONE;
|
||||
upd.visited = true;
|
||||
nodeCache.put(pos, upd);
|
||||
propagateChanges(worldIn, upd, 0);
|
||||
|
||||
// Perform the walk over all directly reachable redstone wire blocks, propagating wire value
|
||||
// updates in a breadth first order out from the initial update received for the block at 'pos'.
|
||||
breadthFirstWalk(worldIn);
|
||||
|
||||
// With the whole search completed, clear the list of all known blocks.
|
||||
// We do not want to keep around state information that may be changed by other code.
|
||||
// In theory, we could cache the neighbor block positions, but that is a separate
|
||||
// optimization.
|
||||
nodeCache.clear();
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
// For any array of neighbors in an UpdateNode object, these are always
|
||||
// the indices of the four immediate neighbors at the same Y coordinate.
|
||||
private static final int[] rs_neighbors = {4, 5, 6, 7};
|
||||
private static final int[] rs_neighbors_up = {9, 11, 13, 15};
|
||||
private static final int[] rs_neighbors_dn = {8, 10, 12, 14};
|
||||
|
||||
/*
|
||||
* Updated calculateCurrentChanges that is optimized for speed and uses
|
||||
* the UpdateNode's neighbor array to find the redstone states of neighbors
|
||||
* that might power it.
|
||||
*/
|
||||
private IBlockData calculateCurrentChanges(final World worldIn, final UpdateNode upd) {
|
||||
IBlockData state = upd.currentState;
|
||||
final int i = state.get(BlockRedstoneWire.POWER).intValue();
|
||||
int j = 0;
|
||||
j = getMaxCurrentStrength(upd, j);
|
||||
int l = 0;
|
||||
|
||||
wire.setCanProvidePower(false);
|
||||
// Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
|
||||
// and I'm not ready to try to replicate even more functionality from
|
||||
// elsewhere in Minecraft into this accelerator. So sadly, we must
|
||||
// suffer the performance hit of this very expensive call. If there
|
||||
// is consistency to what this call returns, we may be able to cache it.
|
||||
final int k = worldIn.isBlockIndirectlyGettingPowered(upd.self);
|
||||
wire.setCanProvidePower(true);
|
||||
|
||||
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
|
||||
// If 'k' has the highest level of all neighbors, then the power level of this
|
||||
// redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
|
||||
// following loop can affect the power level of the wire. Therefore, the loop is
|
||||
// skipped if k is already 15.
|
||||
if (k < 15) {
|
||||
if (upd.neighbor_nodes == null) {
|
||||
// If this node's neighbors are not known, expand the node
|
||||
findNeighbors(worldIn, upd);
|
||||
}
|
||||
|
||||
// These remain constant, so pull them out of the loop.
|
||||
// Regardless of which direction is forward, the UpdateNode for the
|
||||
// position directly above the node being calculated is always
|
||||
// at index 1.
|
||||
UpdateNode center_up = upd.neighbor_nodes[1];
|
||||
boolean center_up_is_cube = center_up.currentState.isOccluding();
|
||||
|
||||
for (int m = 0; m < 4; m++) {
|
||||
// Get the neighbor array index of each of the four cardinal
|
||||
// neighbors.
|
||||
int n = rs_neighbors[m];
|
||||
|
||||
// Get the max redstone power level of each of the cardinal
|
||||
// neighbors
|
||||
UpdateNode neighbor = upd.neighbor_nodes[n];
|
||||
l = getMaxCurrentStrength(neighbor, l);
|
||||
|
||||
// Also check the positions above and below the cardinal
|
||||
// neighbors
|
||||
boolean neighbor_is_cube = neighbor.currentState.isOccluding();
|
||||
if (!neighbor_is_cube) {
|
||||
UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]];
|
||||
l = getMaxCurrentStrength(neighbor_down, l);
|
||||
} else
|
||||
if (!center_up_is_cube) {
|
||||
UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]];
|
||||
l = getMaxCurrentStrength(neighbor_up, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The new code sets this RedstoneWire block's power level to the highest neighbor
|
||||
// minus 1. This usually results in wire power levels dropping by 2 at a time.
|
||||
// This optimization alone has no impact on update order, only the number of updates.
|
||||
j = l - 1;
|
||||
|
||||
// If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
|
||||
// always be in the range of 0 to 15, the following if will correct that.
|
||||
if (k > j) j = k;
|
||||
|
||||
// egg82's amendment
|
||||
// Adding Bukkit's BlockRedstoneEvent - er.. event.
|
||||
if (i != j) {
|
||||
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(upd.self.getX(), upd.self.getY(), upd.self.getZ()), i, j);
|
||||
worldIn.getServer().getPluginManager().callEvent(event);
|
||||
j = event.getNewCurrent();
|
||||
}
|
||||
|
||||
if (i != j) {
|
||||
// If the power level has changed from its previous value, compute a new state
|
||||
// and set it in the world.
|
||||
// Possible optimization: Don't commit state changes to the world until they
|
||||
// need to be known by some nearby non-redstone-wire block.
|
||||
state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(j));
|
||||
worldIn.setTypeAndData(upd.self, state, 2);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized function to compute a redstone wire's power level based on cached
|
||||
* state.
|
||||
*/
|
||||
private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
|
||||
if (upd.type != UpdateNode.Type.REDSTONE) return strength;
|
||||
final int i = upd.currentState.get(BlockRedstoneWire.POWER).intValue();
|
||||
return i > strength ? i : strength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.mojang.authlib.yggdrasil;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.HttpAuthenticationService;
|
||||
import com.mojang.authlib.ProfileLookupCallback;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class YggdrasilGameProfileRepository implements GameProfileRepository {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final String BASE_URL = "https://api.mojang.com/";
|
||||
private static final String SEARCH_PAGE_URL = BASE_URL + "profiles/";
|
||||
private static final int ENTRIES_PER_PAGE = 2;
|
||||
private static final int MAX_FAIL_COUNT = 3;
|
||||
private static final int DELAY_BETWEEN_PAGES = 100;
|
||||
private static final int DELAY_BETWEEN_FAILURES = 750;
|
||||
|
||||
private final YggdrasilAuthenticationService authenticationService;
|
||||
|
||||
public YggdrasilGameProfileRepository(final YggdrasilAuthenticationService authenticationService) {
|
||||
this.authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findProfilesByNames(final String[] names, final Agent agent, final ProfileLookupCallback callback) {
|
||||
final Set<String> criteria = Sets.newHashSet();
|
||||
|
||||
for (final String name : names) {
|
||||
if (!Strings.isNullOrEmpty(name)) {
|
||||
criteria.add(name.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
final int page = 0;
|
||||
boolean hasRequested = false; // Paper
|
||||
|
||||
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
|
||||
int failCount = 0;
|
||||
boolean failed;
|
||||
|
||||
do {
|
||||
failed = false;
|
||||
|
||||
try {
|
||||
final ProfileSearchResultsResponse response = authenticationService.makeRequest(HttpAuthenticationService.constantURL(SEARCH_PAGE_URL + agent.getName().toLowerCase()), request, ProfileSearchResultsResponse.class);
|
||||
failCount = 0;
|
||||
|
||||
LOGGER.debug("Page {} returned {} results, parsing", page, response.getProfiles().length);
|
||||
|
||||
final Set<String> missing = Sets.newHashSet(request);
|
||||
for (final GameProfile profile : response.getProfiles()) {
|
||||
LOGGER.debug("Successfully looked up profile {}", profile);
|
||||
missing.remove(profile.getName().toLowerCase());
|
||||
callback.onProfileLookupSucceeded(profile);
|
||||
}
|
||||
|
||||
for (final String name : missing) {
|
||||
LOGGER.debug("Couldn't find profile {}", name);
|
||||
callback.onProfileLookupFailed(new GameProfile(null, name), new ProfileNotFoundException("Server did not find the requested profile"));
|
||||
}
|
||||
// Paper start
|
||||
if (!hasRequested) {
|
||||
hasRequested = true;
|
||||
continue;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
try {
|
||||
Thread.sleep(DELAY_BETWEEN_PAGES);
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
} catch (final AuthenticationException e) {
|
||||
failCount++;
|
||||
|
||||
if (failCount == MAX_FAIL_COUNT) {
|
||||
for (final String name : request) {
|
||||
LOGGER.debug("Couldn't find profile {} because of a server error", name);
|
||||
callback.onProfileLookupFailed(new GameProfile(null, name), e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(DELAY_BETWEEN_FAILURES);
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
} while (failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
207
src/main/java/com/mojang/brigadier/tree/CommandNode.java
Normal file
207
src/main/java/com/mojang/brigadier/tree/CommandNode.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.AmbiguityConsumer;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.server.CommandListenerWrapper; // CraftBukkit
|
||||
|
||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||
private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
|
||||
private Map<String, LiteralCommandNode<S>> literals = Maps.newLinkedHashMap();
|
||||
private Map<String, ArgumentCommandNode<S, ?>> arguments = Maps.newLinkedHashMap();
|
||||
private final Predicate<S> requirement;
|
||||
private final CommandNode<S> redirect;
|
||||
private final RedirectModifier<S> modifier;
|
||||
private final boolean forks;
|
||||
private Command<S> command;
|
||||
// CraftBukkit start
|
||||
public void removeCommand(String name) {
|
||||
children.remove(name);
|
||||
literals.remove(name);
|
||||
arguments.remove(name);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||
this.command = command;
|
||||
this.requirement = requirement;
|
||||
this.redirect = redirect;
|
||||
this.modifier = modifier;
|
||||
this.forks = forks;
|
||||
}
|
||||
|
||||
public Command<S> getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public Collection<CommandNode<S>> getChildren() {
|
||||
return children.values();
|
||||
}
|
||||
|
||||
public CommandNode<S> getChild(final String name) {
|
||||
return children.get(name);
|
||||
}
|
||||
|
||||
public CommandNode<S> getRedirect() {
|
||||
return redirect;
|
||||
}
|
||||
|
||||
public RedirectModifier<S> getRedirectModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public boolean canUse(final S source) {
|
||||
// CraftBukkit start
|
||||
if (source instanceof CommandListenerWrapper) {
|
||||
try {
|
||||
((CommandListenerWrapper) source).currentCommand = this;
|
||||
return requirement.test(source);
|
||||
} finally {
|
||||
((CommandListenerWrapper) source).currentCommand = null;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
return requirement.test(source);
|
||||
}
|
||||
|
||||
public void addChild(final CommandNode<S> node) {
|
||||
if (node instanceof RootCommandNode) {
|
||||
throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
|
||||
}
|
||||
|
||||
final CommandNode<S> child = children.get(node.getName());
|
||||
if (child != null) {
|
||||
// We've found something to merge onto
|
||||
if (node.getCommand() != null) {
|
||||
child.command = node.getCommand();
|
||||
}
|
||||
for (final CommandNode<S> grandchild : node.getChildren()) {
|
||||
child.addChild(grandchild);
|
||||
}
|
||||
} else {
|
||||
children.put(node.getName(), node);
|
||||
if (node instanceof LiteralCommandNode) {
|
||||
literals.put(node.getName(), (LiteralCommandNode<S>) node);
|
||||
} else if (node instanceof ArgumentCommandNode) {
|
||||
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||
}
|
||||
}
|
||||
|
||||
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
||||
Set<String> matches = Sets.newHashSet();
|
||||
|
||||
for (final CommandNode<S> child : children.values()) {
|
||||
for (final CommandNode<S> sibling : children.values()) {
|
||||
if (child == sibling) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final String input : child.getExamples()) {
|
||||
if (sibling.isValidInput(input)) {
|
||||
matches.add(input);
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size() > 0) {
|
||||
consumer.ambiguous(this, child, sibling, matches);
|
||||
matches = Sets.newHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
child.findAmbiguities(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isValidInput(final String input);
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CommandNode)) return false;
|
||||
|
||||
final CommandNode<S> that = (CommandNode<S>) o;
|
||||
|
||||
if (!children.equals(that.children)) return false;
|
||||
if (command != null ? !command.equals(that.command) : that.command != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * children.hashCode() + (command != null ? command.hashCode() : 0);
|
||||
}
|
||||
|
||||
public Predicate<S> getRequirement() {
|
||||
return requirement;
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract String getUsageText();
|
||||
|
||||
public abstract void parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
|
||||
|
||||
public abstract CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) throws CommandSyntaxException;
|
||||
|
||||
public abstract ArgumentBuilder<S, ?> createBuilder();
|
||||
|
||||
protected abstract String getSortedKey();
|
||||
|
||||
public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
|
||||
if (literals.size() > 0) {
|
||||
final int cursor = input.getCursor();
|
||||
while (input.canRead() && input.peek() != ' ') {
|
||||
input.skip();
|
||||
}
|
||||
final String text = input.getString().substring(cursor, input.getCursor());
|
||||
input.setCursor(cursor);
|
||||
final LiteralCommandNode<S> literal = literals.get(text);
|
||||
if (literal != null) {
|
||||
return Collections.singleton(literal);
|
||||
} else {
|
||||
return arguments.values();
|
||||
}
|
||||
} else {
|
||||
return arguments.values();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final CommandNode<S> o) {
|
||||
return ComparisonChain
|
||||
.start()
|
||||
.compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode)
|
||||
.compare(getSortedKey(), o.getSortedKey())
|
||||
.result();
|
||||
}
|
||||
|
||||
public boolean isFork() {
|
||||
return forks;
|
||||
}
|
||||
|
||||
public abstract Collection<String> getExamples();
|
||||
}
|
||||
436
src/main/java/net/minecraft/server/Advancement.java
Normal file
436
src/main/java/net/minecraft/server/Advancement.java
Normal file
@@ -0,0 +1,436 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
public class Advancement {
|
||||
|
||||
private final Advancement a;
|
||||
private final AdvancementDisplay b;
|
||||
private final AdvancementRewards c;
|
||||
private final MinecraftKey d;
|
||||
private final Map<String, Criterion> e;
|
||||
private final String[][] f;
|
||||
private final Set<Advancement> g = Sets.newLinkedHashSet();
|
||||
private final IChatBaseComponent h;
|
||||
public final org.bukkit.advancement.Advancement bukkit = new org.bukkit.craftbukkit.advancement.CraftAdvancement(this); // CraftBukkit
|
||||
|
||||
public Advancement(MinecraftKey minecraftkey, @Nullable Advancement advancement, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map<String, Criterion> map, String[][] astring) {
|
||||
this.d = minecraftkey;
|
||||
this.b = advancementdisplay;
|
||||
this.e = ImmutableMap.copyOf(map);
|
||||
this.a = advancement;
|
||||
this.c = advancementrewards;
|
||||
this.f = astring;
|
||||
if (advancement != null) {
|
||||
advancement.a(this);
|
||||
}
|
||||
|
||||
if (advancementdisplay == null) {
|
||||
this.h = new ChatComponentText(minecraftkey.toString());
|
||||
} else {
|
||||
IChatBaseComponent ichatbasecomponent = advancementdisplay.a();
|
||||
EnumChatFormat enumchatformat = advancementdisplay.e().c();
|
||||
IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.h().a(enumchatformat).a("\n").addSibling(advancementdisplay.b());
|
||||
IChatBaseComponent ichatbasecomponent2 = ichatbasecomponent.h().a((chatmodifier) -> {
|
||||
chatmodifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, ichatbasecomponent1));
|
||||
});
|
||||
|
||||
this.h = (new ChatComponentText("[")).addSibling(ichatbasecomponent2).a("]").a(enumchatformat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a() {
|
||||
return new Advancement.SerializedAdvancement(this.a == null ? null : this.a.getName(), this.b, this.c, this.e, this.f);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement b() {
|
||||
return this.a;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AdvancementDisplay c() {
|
||||
return this.b;
|
||||
}
|
||||
|
||||
public AdvancementRewards d() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SimpleAdvancement{id=" + this.getName() + ", parent=" + (this.a == null ? "null" : this.a.getName()) + ", display=" + this.b + ", rewards=" + this.c + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}';
|
||||
}
|
||||
|
||||
public Iterable<Advancement> e() {
|
||||
return this.g;
|
||||
}
|
||||
|
||||
public Map<String, Criterion> getCriteria() {
|
||||
return this.e;
|
||||
}
|
||||
|
||||
public void a(Advancement advancement) {
|
||||
this.g.add(advancement);
|
||||
}
|
||||
|
||||
public MinecraftKey getName() {
|
||||
return this.d;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof Advancement)) {
|
||||
return false;
|
||||
} else {
|
||||
Advancement advancement = (Advancement) object;
|
||||
|
||||
return this.d.equals(advancement.d);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.d.hashCode();
|
||||
}
|
||||
|
||||
public String[][] i() {
|
||||
return this.f;
|
||||
}
|
||||
|
||||
public IChatBaseComponent j() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
public static class SerializedAdvancement {
|
||||
|
||||
private MinecraftKey a;
|
||||
private Advancement b;
|
||||
private AdvancementDisplay c;
|
||||
private AdvancementRewards d;
|
||||
private Map<String, Criterion> e;
|
||||
private String[][] f;
|
||||
private AdvancementRequirements g;
|
||||
|
||||
private SerializedAdvancement(@Nullable MinecraftKey minecraftkey, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map<String, Criterion> map, String[][] astring) {
|
||||
this.d = AdvancementRewards.a;
|
||||
this.e = Maps.newLinkedHashMap();
|
||||
this.g = AdvancementRequirements.AND;
|
||||
this.a = minecraftkey;
|
||||
this.c = advancementdisplay;
|
||||
this.d = advancementrewards;
|
||||
this.e = map;
|
||||
this.f = astring;
|
||||
}
|
||||
|
||||
private SerializedAdvancement() {
|
||||
this.d = AdvancementRewards.a;
|
||||
this.e = Maps.newLinkedHashMap();
|
||||
this.g = AdvancementRequirements.AND;
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement a() {
|
||||
return new Advancement.SerializedAdvancement();
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(Advancement advancement) {
|
||||
this.b = advancement;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(MinecraftKey minecraftkey) {
|
||||
this.a = minecraftkey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(IMaterial imaterial, IChatBaseComponent ichatbasecomponent, IChatBaseComponent ichatbasecomponent1, @Nullable MinecraftKey minecraftkey, AdvancementFrameType advancementframetype, boolean flag, boolean flag1, boolean flag2) {
|
||||
return this.a(new AdvancementDisplay(new ItemStack(imaterial.getItem()), ichatbasecomponent, ichatbasecomponent1, minecraftkey, advancementframetype, flag, flag1, flag2));
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementDisplay advancementdisplay) {
|
||||
this.c = advancementdisplay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRewards.a advancementrewards_a) {
|
||||
return this.a(advancementrewards_a.a());
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRewards advancementrewards) {
|
||||
this.d = advancementrewards;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(String s, CriterionInstance criterioninstance) {
|
||||
return this.a(s, new Criterion(criterioninstance));
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(String s, Criterion criterion) {
|
||||
if (this.e.containsKey(s)) {
|
||||
throw new IllegalArgumentException("Duplicate criterion " + s);
|
||||
} else {
|
||||
this.e.put(s, criterion);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRequirements advancementrequirements) {
|
||||
this.g = advancementrequirements;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean a(Function<MinecraftKey, Advancement> function) {
|
||||
if (this.a == null) {
|
||||
return true;
|
||||
} else {
|
||||
if (this.b == null) {
|
||||
this.b = (Advancement) function.apply(this.a);
|
||||
}
|
||||
|
||||
return this.b != null;
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement b(MinecraftKey minecraftkey) {
|
||||
if (!this.a((Function<MinecraftKey, Advancement>) (minecraftkey1) -> { // CraftBukkit - decompile error
|
||||
return null;
|
||||
})) {
|
||||
throw new IllegalStateException("Tried to build incomplete advancement!");
|
||||
} else {
|
||||
if (this.f == null) {
|
||||
this.f = this.g.createRequirements(this.e.keySet());
|
||||
}
|
||||
|
||||
return new Advancement(minecraftkey, this.b, this.c, this.d, this.e, this.f);
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement a(Consumer<Advancement> consumer, String s) {
|
||||
Advancement advancement = this.b(new MinecraftKey(s));
|
||||
|
||||
consumer.accept(advancement);
|
||||
return advancement;
|
||||
}
|
||||
|
||||
public JsonObject b() {
|
||||
if (this.f == null) {
|
||||
this.f = this.g.createRequirements(this.e.keySet());
|
||||
}
|
||||
|
||||
JsonObject jsonobject = new JsonObject();
|
||||
|
||||
if (this.b != null) {
|
||||
jsonobject.addProperty("parent", this.b.getName().toString());
|
||||
} else if (this.a != null) {
|
||||
jsonobject.addProperty("parent", this.a.toString());
|
||||
}
|
||||
|
||||
if (this.c != null) {
|
||||
jsonobject.add("display", this.c.k());
|
||||
}
|
||||
|
||||
jsonobject.add("rewards", this.d.b());
|
||||
JsonObject jsonobject1 = new JsonObject();
|
||||
Iterator iterator = this.e.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
|
||||
jsonobject1.add((String) entry.getKey(), ((Criterion) entry.getValue()).b());
|
||||
}
|
||||
|
||||
jsonobject.add("criteria", jsonobject1);
|
||||
JsonArray jsonarray = new JsonArray();
|
||||
String[][] astring = this.f;
|
||||
int i = astring.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
String[] astring1 = astring[j];
|
||||
JsonArray jsonarray1 = new JsonArray();
|
||||
String[] astring2 = astring1;
|
||||
int k = astring1.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
String s = astring2[l];
|
||||
|
||||
jsonarray1.add(s);
|
||||
}
|
||||
|
||||
jsonarray.add(jsonarray1);
|
||||
}
|
||||
|
||||
jsonobject.add("requirements", jsonarray);
|
||||
return jsonobject;
|
||||
}
|
||||
|
||||
public void a(PacketDataSerializer packetdataserializer) {
|
||||
if (this.a == null) {
|
||||
packetdataserializer.writeBoolean(false);
|
||||
} else {
|
||||
packetdataserializer.writeBoolean(true);
|
||||
packetdataserializer.a(this.a);
|
||||
}
|
||||
|
||||
if (this.c == null) {
|
||||
packetdataserializer.writeBoolean(false);
|
||||
} else {
|
||||
packetdataserializer.writeBoolean(true);
|
||||
this.c.a(packetdataserializer);
|
||||
}
|
||||
|
||||
Criterion.a(this.e, packetdataserializer);
|
||||
packetdataserializer.d(this.f.length);
|
||||
String[][] astring = this.f;
|
||||
int i = astring.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
String[] astring1 = astring[j];
|
||||
|
||||
packetdataserializer.d(astring1.length);
|
||||
String[] astring2 = astring1;
|
||||
int k = astring1.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
String s = astring2[l];
|
||||
|
||||
packetdataserializer.a(s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Task Advancement{parentId=" + this.a + ", display=" + this.c + ", rewards=" + this.d + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}';
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement a(JsonObject jsonobject, JsonDeserializationContext jsondeserializationcontext) {
|
||||
MinecraftKey minecraftkey = jsonobject.has("parent") ? new MinecraftKey(ChatDeserializer.h(jsonobject, "parent")) : null;
|
||||
AdvancementDisplay advancementdisplay = jsonobject.has("display") ? AdvancementDisplay.a(ChatDeserializer.t(jsonobject, "display"), jsondeserializationcontext) : null;
|
||||
AdvancementRewards advancementrewards = (AdvancementRewards) ChatDeserializer.a(jsonobject, "rewards", AdvancementRewards.a, jsondeserializationcontext, AdvancementRewards.class);
|
||||
Map<String, Criterion> map = Criterion.b(ChatDeserializer.t(jsonobject, "criteria"), jsondeserializationcontext);
|
||||
|
||||
if (map.isEmpty()) {
|
||||
throw new JsonSyntaxException("Advancement criteria cannot be empty");
|
||||
} else {
|
||||
JsonArray jsonarray = ChatDeserializer.a(jsonobject, "requirements", new JsonArray());
|
||||
String[][] astring = new String[jsonarray.size()][];
|
||||
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < jsonarray.size(); ++i) {
|
||||
JsonArray jsonarray1 = ChatDeserializer.n(jsonarray.get(i), "requirements[" + i + "]");
|
||||
|
||||
astring[i] = new String[jsonarray1.size()];
|
||||
|
||||
for (j = 0; j < jsonarray1.size(); ++j) {
|
||||
astring[i][j] = ChatDeserializer.a(jsonarray1.get(j), "requirements[" + i + "][" + j + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (astring.length == 0) {
|
||||
astring = new String[map.size()][];
|
||||
i = 0;
|
||||
|
||||
String s;
|
||||
|
||||
for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); astring[i++] = new String[] { s}) {
|
||||
s = (String) iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
String[][] astring1 = astring;
|
||||
int k = astring.length;
|
||||
|
||||
int l;
|
||||
|
||||
for (j = 0; j < k; ++j) {
|
||||
String[] astring2 = astring1[j];
|
||||
|
||||
if (astring2.length == 0 && map.isEmpty()) {
|
||||
throw new JsonSyntaxException("Requirement entry cannot be empty");
|
||||
}
|
||||
|
||||
String[] astring3 = astring2;
|
||||
|
||||
l = astring2.length;
|
||||
|
||||
for (int i1 = 0; i1 < l; ++i1) {
|
||||
String s1 = astring3[i1];
|
||||
|
||||
if (!map.containsKey(s1)) {
|
||||
throw new JsonSyntaxException("Unknown required criterion '" + s1 + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator iterator1 = map.keySet().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
String s2 = (String) iterator1.next();
|
||||
boolean flag = false;
|
||||
String[][] astring4 = astring;
|
||||
int j1 = astring.length;
|
||||
|
||||
l = 0;
|
||||
|
||||
while (true) {
|
||||
if (l < j1) {
|
||||
String[] astring5 = astring4[l];
|
||||
|
||||
if (!ArrayUtils.contains(astring5, s2)) {
|
||||
++l;
|
||||
continue;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
throw new JsonSyntaxException("Criterion '" + s2 + "' isn't a requirement for completion. This isn't supported behaviour, all criteria must be required.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, advancementrewards, map, astring);
|
||||
}
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement b(PacketDataSerializer packetdataserializer) {
|
||||
MinecraftKey minecraftkey = packetdataserializer.readBoolean() ? packetdataserializer.l() : null;
|
||||
AdvancementDisplay advancementdisplay = packetdataserializer.readBoolean() ? AdvancementDisplay.b(packetdataserializer) : null;
|
||||
Map<String, Criterion> map = Criterion.c(packetdataserializer);
|
||||
String[][] astring = new String[packetdataserializer.g()][];
|
||||
|
||||
for (int i = 0; i < astring.length; ++i) {
|
||||
astring[i] = new String[packetdataserializer.g()];
|
||||
|
||||
for (int j = 0; j < astring[i].length; ++j) {
|
||||
astring[i][j] = packetdataserializer.e(32767);
|
||||
}
|
||||
}
|
||||
|
||||
return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, AdvancementRewards.a, map, astring);
|
||||
}
|
||||
|
||||
public Map<String, Criterion> c() {
|
||||
return this.e;
|
||||
}
|
||||
}
|
||||
}
|
||||
466
src/main/java/net/minecraft/server/AdvancementDataPlayer.java
Normal file
466
src/main/java/net/minecraft/server/AdvancementDataPlayer.java
Normal file
@@ -0,0 +1,466 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.mojang.datafixers.DataFixTypes;
|
||||
import com.mojang.datafixers.Dynamic;
|
||||
import com.mojang.datafixers.types.JsonOps;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class AdvancementDataPlayer {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
private static final Gson b = (new GsonBuilder()).registerTypeAdapter(AdvancementProgress.class, new AdvancementProgress.a()).registerTypeAdapter(MinecraftKey.class, new MinecraftKey.a()).setPrettyPrinting().create();
|
||||
private static final TypeToken<Map<MinecraftKey, AdvancementProgress>> c = new TypeToken<Map<MinecraftKey, AdvancementProgress>>() {
|
||||
};
|
||||
private final MinecraftServer d;
|
||||
private final File e;
|
||||
public final Map<Advancement, AdvancementProgress> data = Maps.newLinkedHashMap();
|
||||
private final Set<Advancement> g = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> h = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> i = Sets.newLinkedHashSet();
|
||||
private EntityPlayer player;
|
||||
@Nullable
|
||||
private Advancement k;
|
||||
private boolean l = true;
|
||||
|
||||
public AdvancementDataPlayer(MinecraftServer minecraftserver, File file, EntityPlayer entityplayer) {
|
||||
this.d = minecraftserver;
|
||||
this.e = file;
|
||||
this.player = entityplayer;
|
||||
this.g();
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer) {
|
||||
this.player = entityplayer;
|
||||
}
|
||||
|
||||
public void a() {
|
||||
Iterator iterator = CriterionTriggers.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
CriterionTrigger<?> criteriontrigger = (CriterionTrigger) iterator.next();
|
||||
|
||||
criteriontrigger.a(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b() {
|
||||
this.a();
|
||||
this.data.clear();
|
||||
this.g.clear();
|
||||
this.h.clear();
|
||||
this.i.clear();
|
||||
this.l = true;
|
||||
this.k = null;
|
||||
this.g();
|
||||
}
|
||||
|
||||
private void d() {
|
||||
Iterator iterator = this.d.getAdvancementData().b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
this.c(advancement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void e() {
|
||||
List<Advancement> list = Lists.newArrayList();
|
||||
Iterator iterator = this.data.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Advancement, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
|
||||
if (((AdvancementProgress) entry.getValue()).isDone()) {
|
||||
list.add(entry.getKey());
|
||||
this.i.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void f() {
|
||||
Iterator iterator = this.d.getAdvancementData().b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
if (advancement.getCriteria().isEmpty()) {
|
||||
this.grantCriteria(advancement, "");
|
||||
advancement.d().a(this.player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void g() {
|
||||
if (this.e.isFile()) {
|
||||
try {
|
||||
JsonReader jsonreader = new JsonReader(new StringReader(Files.toString(this.e, StandardCharsets.UTF_8)));
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
jsonreader.setLenient(false);
|
||||
Dynamic<JsonElement> dynamic = new Dynamic(JsonOps.INSTANCE, Streams.parse(jsonreader));
|
||||
|
||||
if (!dynamic.get("DataVersion").flatMap(Dynamic::getNumberValue).isPresent()) {
|
||||
dynamic = dynamic.set("DataVersion", dynamic.createInt(1343));
|
||||
}
|
||||
|
||||
dynamic = this.d.az().update(DataFixTypes.ADVANCEMENTS, dynamic, dynamic.getInt("DataVersion"), 1631);
|
||||
dynamic = dynamic.remove("DataVersion");
|
||||
Map<MinecraftKey, AdvancementProgress> map = (Map) AdvancementDataPlayer.b.getAdapter(AdvancementDataPlayer.c).fromJsonTree((JsonElement) dynamic.getValue());
|
||||
|
||||
if (map == null) {
|
||||
throw new JsonParseException("Found null for advancements");
|
||||
}
|
||||
|
||||
Stream<Entry<MinecraftKey, AdvancementProgress>> stream = map.entrySet().stream().sorted(Comparator.comparing(Entry::getValue));
|
||||
Iterator iterator = ((List) stream.collect(Collectors.toList())).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<MinecraftKey, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
Advancement advancement = this.d.getAdvancementData().a((MinecraftKey) entry.getKey());
|
||||
|
||||
if (advancement == null) {
|
||||
// CraftBukkit start
|
||||
if (((MinecraftKey) entry.getKey()).b().equals("minecraft")) {
|
||||
AdvancementDataPlayer.a.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry.getKey(), this.e);
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
this.a(advancement, (AdvancementProgress) entry.getValue());
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (jsonreader != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
jsonreader.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
jsonreader.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (JsonParseException jsonparseexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't parse player advancements in {}", this.e, jsonparseexception);
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't access player advancements in {}", this.e, ioexception);
|
||||
}
|
||||
}
|
||||
|
||||
this.f();
|
||||
this.e();
|
||||
this.d();
|
||||
}
|
||||
|
||||
public void c() {
|
||||
if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return;
|
||||
Map<MinecraftKey, AdvancementProgress> map = Maps.newHashMap();
|
||||
Iterator iterator = this.data.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Advancement, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
AdvancementProgress advancementprogress = (AdvancementProgress) entry.getValue();
|
||||
|
||||
if (advancementprogress.b()) {
|
||||
map.put(((Advancement) entry.getKey()).getName(), advancementprogress);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e.getParentFile() != null) {
|
||||
this.e.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
Files.write(AdvancementDataPlayer.b.toJson(map), this.e, StandardCharsets.UTF_8);
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't save player advancements to {}", this.e, ioexception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean grantCriteria(Advancement advancement, String s) {
|
||||
boolean flag = false;
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
boolean flag1 = advancementprogress.isDone();
|
||||
|
||||
if (advancementprogress.a(s)) {
|
||||
// Paper start
|
||||
if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.bukkit, s).callEvent()) {
|
||||
advancementprogress.b(s);
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
this.d(advancement);
|
||||
this.i.add(advancement);
|
||||
flag = true;
|
||||
if (!flag1 && advancementprogress.isDone()) {
|
||||
this.player.world.getServer().getPluginManager().callEvent(new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.bukkit)); // CraftBukkit
|
||||
advancement.d().a(this.player);
|
||||
if (advancement.c() != null && advancement.c().i() && this.player.world.getGameRules().getBoolean("announceAdvancements")) {
|
||||
this.d.getPlayerList().sendMessage(new ChatMessage("chat.type.advancement." + advancement.c().e().a(), new Object[] { this.player.getScoreboardDisplayName(), advancement.j()}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
public boolean revokeCritera(Advancement advancement, String s) {
|
||||
boolean flag = false;
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.b(s)) {
|
||||
this.c(advancement);
|
||||
this.i.add(advancement);
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!advancementprogress.b()) {
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private void c(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (!advancementprogress.isDone()) {
|
||||
Iterator iterator = advancement.getCriteria().entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey());
|
||||
|
||||
if (criterionprogress != null && !criterionprogress.a()) {
|
||||
CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a();
|
||||
|
||||
if (criterioninstance != null) {
|
||||
CriterionTrigger<CriterionInstance> criteriontrigger = CriterionTriggers.a(criterioninstance.a());
|
||||
|
||||
if (criteriontrigger != null) {
|
||||
criteriontrigger.a(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void d(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
Iterator iterator = advancement.getCriteria().entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey());
|
||||
|
||||
if (criterionprogress != null && (criterionprogress.a() || advancementprogress.isDone())) {
|
||||
CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a();
|
||||
|
||||
if (criterioninstance != null) {
|
||||
CriterionTrigger<CriterionInstance> criteriontrigger = CriterionTriggers.a(criterioninstance.a());
|
||||
|
||||
if (criteriontrigger != null) {
|
||||
criteriontrigger.b(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b(EntityPlayer entityplayer) {
|
||||
if (this.l || !this.h.isEmpty() || !this.i.isEmpty()) {
|
||||
Map<MinecraftKey, AdvancementProgress> map = Maps.newHashMap();
|
||||
Set<Advancement> set = Sets.newLinkedHashSet();
|
||||
Set<MinecraftKey> set1 = Sets.newLinkedHashSet();
|
||||
Iterator iterator = this.i.iterator();
|
||||
|
||||
Advancement advancement;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
advancement = (Advancement) iterator.next();
|
||||
if (this.g.contains(advancement)) {
|
||||
map.put(advancement.getName(), this.data.get(advancement));
|
||||
}
|
||||
}
|
||||
|
||||
iterator = this.h.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
advancement = (Advancement) iterator.next();
|
||||
if (this.g.contains(advancement)) {
|
||||
set.add(advancement);
|
||||
} else {
|
||||
set1.add(advancement.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.l || !map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutAdvancements(this.l, set, set1, map));
|
||||
this.h.clear();
|
||||
this.i.clear();
|
||||
}
|
||||
}
|
||||
|
||||
this.l = false;
|
||||
}
|
||||
|
||||
public void a(@Nullable Advancement advancement) {
|
||||
Advancement advancement1 = this.k;
|
||||
|
||||
if (advancement != null && advancement.b() == null && advancement.c() != null) {
|
||||
this.k = advancement;
|
||||
} else {
|
||||
this.k = null;
|
||||
}
|
||||
|
||||
if (advancement1 != this.k) {
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutSelectAdvancementTab(this.k == null ? null : this.k.getName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AdvancementProgress getProgress(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = (AdvancementProgress) this.data.get(advancement);
|
||||
|
||||
if (advancementprogress == null) {
|
||||
advancementprogress = new AdvancementProgress();
|
||||
this.a(advancement, advancementprogress);
|
||||
}
|
||||
|
||||
return advancementprogress;
|
||||
}
|
||||
|
||||
private void a(Advancement advancement, AdvancementProgress advancementprogress) {
|
||||
advancementprogress.a(advancement.getCriteria(), advancement.i());
|
||||
this.data.put(advancement, advancementprogress);
|
||||
}
|
||||
|
||||
private void e(Advancement advancement) {
|
||||
boolean flag = this.f(advancement);
|
||||
boolean flag1 = this.g.contains(advancement);
|
||||
|
||||
if (flag && !flag1) {
|
||||
this.g.add(advancement);
|
||||
this.h.add(advancement);
|
||||
if (this.data.containsKey(advancement)) {
|
||||
this.i.add(advancement);
|
||||
}
|
||||
} else if (!flag && flag1) {
|
||||
this.g.remove(advancement);
|
||||
this.h.add(advancement);
|
||||
}
|
||||
|
||||
if (flag != flag1 && advancement.b() != null) {
|
||||
this.e(advancement.b());
|
||||
}
|
||||
|
||||
Iterator iterator = advancement.e().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement1 = (Advancement) iterator.next();
|
||||
|
||||
this.e(advancement1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean f(Advancement advancement) {
|
||||
for (int i = 0; advancement != null && i <= 2; ++i) {
|
||||
if (i == 0 && this.g(advancement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (advancement.c() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (advancement.c().j()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
advancement = advancement.b();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean g(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
return true;
|
||||
} else {
|
||||
Iterator iterator = advancement.e().iterator();
|
||||
|
||||
Advancement advancement1;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
advancement1 = (Advancement) iterator.next();
|
||||
} while (!this.g(advancement1));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/main/java/net/minecraft/server/AdvancementDataWorld.java
Normal file
119
src/main/java/net/minecraft/server/AdvancementDataWorld.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class AdvancementDataWorld implements IResourcePackListener {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
public static final Gson DESERIALIZER = (new GsonBuilder()).registerTypeHierarchyAdapter(Advancement.SerializedAdvancement.class, (com.google.gson.JsonDeserializer) (jsonelement, type, jsondeserializationcontext) -> {
|
||||
JsonObject jsonobject = ChatDeserializer.m(jsonelement, "advancement");
|
||||
|
||||
return Advancement.SerializedAdvancement.a(jsonobject, jsondeserializationcontext);
|
||||
}).registerTypeAdapter(AdvancementRewards.class, new AdvancementRewards.b()).registerTypeHierarchyAdapter(IChatBaseComponent.class, new IChatBaseComponent.ChatSerializer()).registerTypeHierarchyAdapter(ChatModifier.class, new ChatModifier.ChatModifierSerializer()).registerTypeAdapterFactory(new ChatTypeAdapterFactory()).create();
|
||||
public static final Advancements REGISTRY = new Advancements();
|
||||
public static final int a = "advancements/".length();
|
||||
public static final int b = ".json".length();
|
||||
private boolean f;
|
||||
|
||||
public AdvancementDataWorld() {}
|
||||
|
||||
private Map<MinecraftKey, Advancement.SerializedAdvancement> b(IResourceManager iresourcemanager) {
|
||||
Map<MinecraftKey, Advancement.SerializedAdvancement> map = Maps.newHashMap();
|
||||
Iterator iterator = iresourcemanager.a("advancements", (s) -> {
|
||||
return s.endsWith(".json");
|
||||
}).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
MinecraftKey minecraftkey = (MinecraftKey) iterator.next();
|
||||
String s = minecraftkey.getKey();
|
||||
MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), s.substring(AdvancementDataWorld.a, s.length() - AdvancementDataWorld.b));
|
||||
|
||||
try {
|
||||
IResource iresource = iresourcemanager.a(minecraftkey);
|
||||
Throwable throwable = null;
|
||||
// Spigot start
|
||||
if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(minecraftkey.toString()))) {
|
||||
continue;
|
||||
}
|
||||
// Spigot end
|
||||
|
||||
try {
|
||||
Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) ChatDeserializer.a(AdvancementDataWorld.DESERIALIZER, IOUtils.toString(iresource.b(), StandardCharsets.UTF_8), Advancement.SerializedAdvancement.class);
|
||||
|
||||
if (advancement_serializedadvancement == null) {
|
||||
AdvancementDataWorld.c.error("Couldn't load custom advancement {} from {} as it's empty or null", minecraftkey1, minecraftkey);
|
||||
} else {
|
||||
map.put(minecraftkey1, advancement_serializedadvancement);
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (iresource != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
iresource.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
iresource.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IllegalArgumentException | JsonParseException jsonparseexception) {
|
||||
AdvancementDataWorld.c.error("Parsing error loading custom advancement {}: {}", minecraftkey1, jsonparseexception.getMessage());
|
||||
this.f = true;
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataWorld.c.error("Couldn't read custom advancement {} from {}", minecraftkey1, minecraftkey, ioexception);
|
||||
this.f = true;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement a(MinecraftKey minecraftkey) {
|
||||
return AdvancementDataWorld.REGISTRY.a(minecraftkey);
|
||||
}
|
||||
|
||||
public Collection<Advancement> b() {
|
||||
return AdvancementDataWorld.REGISTRY.c();
|
||||
}
|
||||
|
||||
public void a(IResourceManager iresourcemanager) {
|
||||
this.f = false;
|
||||
AdvancementDataWorld.REGISTRY.a();
|
||||
Map<MinecraftKey, Advancement.SerializedAdvancement> map = this.b(iresourcemanager);
|
||||
|
||||
AdvancementDataWorld.REGISTRY.a(map);
|
||||
Iterator iterator = AdvancementDataWorld.REGISTRY.b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
if (advancement.c() != null) {
|
||||
AdvancementTree.a(advancement);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
109
src/main/java/net/minecraft/server/Advancements.java
Normal file
109
src/main/java/net/minecraft/server/Advancements.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class Advancements {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
public final Map<MinecraftKey, Advancement> advancements = Maps.newHashMap();
|
||||
private final Set<Advancement> c = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> d = Sets.newLinkedHashSet();
|
||||
private Advancements.a e;
|
||||
|
||||
public Advancements() {}
|
||||
|
||||
public void a(Map<MinecraftKey, Advancement.SerializedAdvancement> map) {
|
||||
Function function = Functions.forMap(this.advancements, (Object) null);
|
||||
|
||||
label42:
|
||||
while (!map.isEmpty()) {
|
||||
boolean flag = false;
|
||||
Iterator iterator = map.entrySet().iterator();
|
||||
|
||||
Entry entry;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
entry = (Entry) iterator.next();
|
||||
MinecraftKey minecraftkey = (MinecraftKey) entry.getKey();
|
||||
Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) entry.getValue();
|
||||
|
||||
if (advancement_serializedadvancement.a((java.util.function.Function) function)) {
|
||||
Advancement advancement = advancement_serializedadvancement.b(minecraftkey);
|
||||
|
||||
this.advancements.put(minecraftkey, advancement);
|
||||
flag = true;
|
||||
iterator.remove();
|
||||
if (advancement.b() == null) {
|
||||
this.c.add(advancement);
|
||||
if (this.e != null) {
|
||||
this.e.a(advancement);
|
||||
}
|
||||
} else {
|
||||
this.d.add(advancement);
|
||||
if (this.e != null) {
|
||||
this.e.c(advancement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
iterator = map.entrySet().iterator();
|
||||
|
||||
while (true) {
|
||||
if (!iterator.hasNext()) {
|
||||
break label42;
|
||||
}
|
||||
|
||||
entry = (Entry) iterator.next();
|
||||
Advancements.a.error("Couldn't load advancement {}: {}", entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advancements.a.info("Loaded {} advancements", this.advancements.size()); // CraftBukkit - moved to AdvancementDataWorld#reload
|
||||
}
|
||||
|
||||
public void a() {
|
||||
this.advancements.clear();
|
||||
this.c.clear();
|
||||
this.d.clear();
|
||||
if (this.e != null) {
|
||||
this.e.a();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Iterable<Advancement> b() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public Collection<Advancement> c() {
|
||||
return this.advancements.values();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement a(MinecraftKey minecraftkey) {
|
||||
return (Advancement) this.advancements.get(minecraftkey);
|
||||
}
|
||||
|
||||
public interface a {
|
||||
|
||||
void a(Advancement advancement);
|
||||
|
||||
void c(Advancement advancement);
|
||||
|
||||
void a();
|
||||
}
|
||||
}
|
||||
541
src/main/java/net/minecraft/server/ArgumentBlock.java
Normal file
541
src/main/java/net/minecraft/server/ArgumentBlock.java
Normal file
@@ -0,0 +1,541 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.Dynamic3CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ArgumentBlock {
|
||||
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.block.tag.disallowed", new Object[0]));
|
||||
public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.block.id.invalid", new Object[] { object});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType c = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.unknown", new Object[] { object, object1});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType d = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.duplicate", new Object[] { object1, object});
|
||||
});
|
||||
public static final Dynamic3CommandExceptionType e = new Dynamic3CommandExceptionType((object, object1, object2) -> {
|
||||
return new ChatMessage("argument.block.property.invalid", new Object[] { object, object2, object1});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType f = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.novalue", new Object[] { object, object1});
|
||||
});
|
||||
public static final SimpleCommandExceptionType g = new SimpleCommandExceptionType(new ChatMessage("argument.block.property.unclosed", new Object[0]));
|
||||
private static final Function<SuggestionsBuilder, CompletableFuture<Suggestions>> h = SuggestionsBuilder::buildFuture;
|
||||
private final StringReader i;
|
||||
private final boolean j;
|
||||
private final Map<IBlockState<?>, Comparable<?>> k = Maps.newLinkedHashMap(); // CraftBukkit - stable
|
||||
private final Map<String, String> l = Maps.newHashMap();
|
||||
private MinecraftKey m = new MinecraftKey(""); public MinecraftKey getBlockKey() { return this.m; } // Paper - OBFHELPER
|
||||
private BlockStateList<Block, IBlockData> n;
|
||||
private IBlockData o;
|
||||
@Nullable
|
||||
private NBTTagCompound p;
|
||||
private MinecraftKey q = new MinecraftKey("");
|
||||
private int r;
|
||||
private Function<SuggestionsBuilder, CompletableFuture<Suggestions>> s;
|
||||
|
||||
public ArgumentBlock(StringReader stringreader, boolean flag) {
|
||||
this.s = ArgumentBlock.h;
|
||||
this.i = stringreader;
|
||||
this.j = flag;
|
||||
}
|
||||
|
||||
public Map<IBlockState<?>, Comparable<?>> getStateMap() {
|
||||
return this.k;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getBlockData() {
|
||||
return this.o;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public NBTTagCompound c() {
|
||||
return this.p;
|
||||
}
|
||||
|
||||
public @Nullable MinecraftKey getTagKey() { return d(); } // Paper - OBFHELPER
|
||||
@Nullable
|
||||
public MinecraftKey d() {
|
||||
return this.q;
|
||||
}
|
||||
|
||||
public ArgumentBlock parse(boolean parseTile) throws CommandSyntaxException { return this.a(parseTile); } // Paper - OBFHELPER
|
||||
public ArgumentBlock a(boolean flag) throws CommandSyntaxException {
|
||||
this.s = this::l;
|
||||
if (this.i.canRead() && this.i.peek() == '#') {
|
||||
this.f();
|
||||
this.s = this::i;
|
||||
if (this.i.canRead() && this.i.peek() == '[') {
|
||||
this.h();
|
||||
this.s = this::f;
|
||||
}
|
||||
} else {
|
||||
this.e();
|
||||
this.s = this::j;
|
||||
if (this.i.canRead() && this.i.peek() == '[') {
|
||||
this.g();
|
||||
this.s = this::f;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag && this.i.canRead() && this.i.peek() == '{') {
|
||||
this.s = ArgumentBlock.h;
|
||||
this.i();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> b(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
return this.d(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> c(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
return this.e(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> d(SuggestionsBuilder suggestionsbuilder) {
|
||||
String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
Iterator iterator = this.o.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
IBlockState<?> iblockstate = (IBlockState) iterator.next();
|
||||
|
||||
if (!this.k.containsKey(iblockstate) && iblockstate.a().startsWith(s)) {
|
||||
suggestionsbuilder.suggest(iblockstate.a() + '=');
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> e(SuggestionsBuilder suggestionsbuilder) {
|
||||
String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (this.q != null && !this.q.getKey().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
Iterator iterator1 = block.getStates().d().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
IBlockState<?> iblockstate = (IBlockState) iterator1.next();
|
||||
|
||||
if (!this.l.containsKey(iblockstate.a()) && iblockstate.a().startsWith(s)) {
|
||||
suggestionsbuilder.suggest(iblockstate.a() + '=');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> f(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty() && this.k()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private boolean k() {
|
||||
if (this.o != null) {
|
||||
return this.o.getBlock().isTileEntity();
|
||||
} else {
|
||||
if (this.q != null) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
|
||||
if (block.isTileEntity()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> g(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('='));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> h(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
if (suggestionsbuilder.getRemaining().isEmpty() && this.k.size() < this.o.a().size()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> SuggestionsBuilder a(SuggestionsBuilder suggestionsbuilder, IBlockState<T> iblockstate) {
|
||||
Iterator iterator = iblockstate.d().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T t0 = (T) iterator.next(); // CraftBukkit - decompile error
|
||||
|
||||
if (t0 instanceof Integer) {
|
||||
suggestionsbuilder.suggest((Integer) t0);
|
||||
} else {
|
||||
suggestionsbuilder.suggest(iblockstate.a(t0));
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder;
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder, String s) {
|
||||
boolean flag = false;
|
||||
|
||||
if (this.q != null && !this.q.getKey().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
IBlockState<?> iblockstate = block.getStates().a(s);
|
||||
|
||||
if (iblockstate != null) {
|
||||
a(suggestionsbuilder, iblockstate);
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
Iterator iterator1 = block.getStates().d().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
IBlockState<?> iblockstate1 = (IBlockState) iterator1.next();
|
||||
|
||||
if (!this.l.containsKey(iblockstate1.a())) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
}
|
||||
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> i(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
|
||||
flag |= !block.getStates().d().isEmpty();
|
||||
flag1 |= block.isTileEntity();
|
||||
if (flag && flag1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.k(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> j(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
if (!this.o.getBlock().getStates().d().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
}
|
||||
|
||||
if (this.o.getBlock().isTileEntity()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> k(SuggestionsBuilder suggestionsbuilder) {
|
||||
return ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder.createOffset(this.r).add(suggestionsbuilder));
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> l(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (this.j) {
|
||||
ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder, String.valueOf('#'));
|
||||
}
|
||||
|
||||
ICompletionProvider.a((Iterable) IRegistry.BLOCK.keySet(), suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
public void e() throws CommandSyntaxException {
|
||||
int i = this.i.getCursor();
|
||||
|
||||
this.m = MinecraftKey.a(this.i);
|
||||
if (IRegistry.BLOCK.c(this.m)) {
|
||||
Block block = (Block) IRegistry.BLOCK.getOrDefault(this.m);
|
||||
|
||||
this.n = block.getStates();
|
||||
this.o = block.getBlockData();
|
||||
} else {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.b.createWithContext(this.i, this.m.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void f() throws CommandSyntaxException {
|
||||
if (!this.j) {
|
||||
throw ArgumentBlock.a.create();
|
||||
} else {
|
||||
this.s = this::k;
|
||||
this.i.expect('#');
|
||||
this.r = this.i.getCursor();
|
||||
this.q = MinecraftKey.a(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void g() throws CommandSyntaxException {
|
||||
this.i.skip();
|
||||
this.s = this::b;
|
||||
this.i.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.i.canRead() && this.i.peek() != ']') {
|
||||
this.i.skipWhitespace();
|
||||
int i = this.i.getCursor();
|
||||
String s = this.i.readString();
|
||||
IBlockState<?> iblockstate = this.n.a(s);
|
||||
|
||||
if (iblockstate == null) {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.c.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
if (this.k.containsKey(iblockstate)) {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skipWhitespace();
|
||||
this.s = this::g;
|
||||
if (!this.i.canRead() || this.i.peek() != '=') {
|
||||
throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skip();
|
||||
this.i.skipWhitespace();
|
||||
this.s = (suggestionsbuilder) -> {
|
||||
return a(suggestionsbuilder, iblockstate).buildFuture();
|
||||
};
|
||||
int j = this.i.getCursor();
|
||||
|
||||
this.a(iblockstate, this.i.readString(), j);
|
||||
this.s = this::h;
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() == ',') {
|
||||
this.i.skip();
|
||||
this.s = this::d;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() != ']') {
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.i.canRead()) {
|
||||
this.i.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void h() throws CommandSyntaxException {
|
||||
this.i.skip();
|
||||
this.s = this::c;
|
||||
int i = -1;
|
||||
|
||||
this.i.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.i.canRead() && this.i.peek() != ']') {
|
||||
this.i.skipWhitespace();
|
||||
int j = this.i.getCursor();
|
||||
String s = this.i.readString();
|
||||
|
||||
if (this.l.containsKey(s)) {
|
||||
this.i.setCursor(j);
|
||||
throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead() || this.i.peek() != '=') {
|
||||
this.i.setCursor(j);
|
||||
throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skip();
|
||||
this.i.skipWhitespace();
|
||||
this.s = (suggestionsbuilder) -> {
|
||||
return this.a(suggestionsbuilder, s);
|
||||
};
|
||||
i = this.i.getCursor();
|
||||
String s1 = this.i.readString();
|
||||
|
||||
this.l.put(s, s1);
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i = -1;
|
||||
if (this.i.peek() == ',') {
|
||||
this.i.skip();
|
||||
this.s = this::e;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() != ']') {
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.i.canRead()) {
|
||||
this.i.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.i.setCursor(i);
|
||||
}
|
||||
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void i() throws CommandSyntaxException {
|
||||
this.p = (new MojangsonParser(this.i)).f();
|
||||
}
|
||||
|
||||
private <T extends Comparable<T>> void a(IBlockState<T> iblockstate, String s, int i) throws CommandSyntaxException {
|
||||
Optional<T> optional = iblockstate.b(s);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
this.o = (IBlockData) this.o.set(iblockstate, (T) optional.get()); // CraftBukkit - decompile error
|
||||
this.k.put(iblockstate, optional.get());
|
||||
} else {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.e.createWithContext(this.i, this.m.toString(), iblockstate.a(), s);
|
||||
}
|
||||
}
|
||||
|
||||
public static String a(IBlockData iblockdata, @Nullable NBTTagCompound nbttagcompound) {
|
||||
StringBuilder stringbuilder = new StringBuilder(IRegistry.BLOCK.getKey(iblockdata.getBlock()).toString());
|
||||
|
||||
if (!iblockdata.a().isEmpty()) {
|
||||
stringbuilder.append('[');
|
||||
boolean flag = false;
|
||||
|
||||
for (UnmodifiableIterator unmodifiableiterator = iblockdata.getStateMap().entrySet().iterator(); unmodifiableiterator.hasNext(); flag = true) {
|
||||
Entry<IBlockState<?>, Comparable<?>> entry = (Entry) unmodifiableiterator.next();
|
||||
|
||||
if (flag) {
|
||||
stringbuilder.append(',');
|
||||
}
|
||||
|
||||
a(stringbuilder, (IBlockState) entry.getKey(), (Comparable) entry.getValue());
|
||||
}
|
||||
|
||||
stringbuilder.append(']');
|
||||
}
|
||||
|
||||
if (nbttagcompound != null) {
|
||||
stringbuilder.append(nbttagcompound);
|
||||
}
|
||||
|
||||
return stringbuilder.toString();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> void a(StringBuilder stringbuilder, IBlockState<T> iblockstate, Comparable<?> comparable) {
|
||||
stringbuilder.append(iblockstate.a());
|
||||
stringbuilder.append('=');
|
||||
stringbuilder.append(iblockstate.a((T) comparable)); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder) {
|
||||
return (CompletableFuture) this.s.apply(suggestionsbuilder.createOffset(this.i.getCursor()));
|
||||
}
|
||||
|
||||
public Map<String, String> j() {
|
||||
return this.l;
|
||||
}
|
||||
}
|
||||
171
src/main/java/net/minecraft/server/ArgumentEntity.java
Normal file
171
src/main/java/net/minecraft/server/ArgumentEntity.java
Normal file
@@ -0,0 +1,171 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ArgumentEntity implements ArgumentType<EntitySelector> {
|
||||
|
||||
private static final Collection<String> g = Arrays.asList("Player", "0123", "@e", "@e[type=foo]", "dd12be42-52a9-4a91-a8a1-11c01849e498");
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.toomany", new Object[0]));
|
||||
public static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("argument.player.toomany", new Object[0]));
|
||||
public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.player.entities", new Object[0]));
|
||||
public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.entity", new Object[0]));
|
||||
public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.player", new Object[0]));
|
||||
public static final SimpleCommandExceptionType f = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0]));
|
||||
private final boolean h;
|
||||
private final boolean i;
|
||||
|
||||
protected ArgumentEntity(boolean flag, boolean flag1) {
|
||||
this.h = flag;
|
||||
this.i = flag1;
|
||||
}
|
||||
|
||||
public static ArgumentEntity a() {
|
||||
return new ArgumentEntity(true, false);
|
||||
}
|
||||
|
||||
public static Entity a(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).a((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity b() {
|
||||
return new ArgumentEntity(false, false);
|
||||
}
|
||||
|
||||
public static Collection<? extends Entity> b(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> collection = c(commandcontext, s);
|
||||
|
||||
if (collection.isEmpty()) {
|
||||
throw ArgumentEntity.d.create();
|
||||
} else {
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<? extends Entity> c(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).b((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static Collection<EntityPlayer> d(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity c() {
|
||||
return new ArgumentEntity(true, true);
|
||||
}
|
||||
|
||||
public static EntityPlayer e(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).c((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity d() {
|
||||
return new ArgumentEntity(false, true);
|
||||
}
|
||||
|
||||
public static Collection<EntityPlayer> f(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
List<EntityPlayer> list = ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource());
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw ArgumentEntity.e.create();
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public EntitySelector parse(StringReader stringreader) throws CommandSyntaxException {
|
||||
// CraftBukkit start
|
||||
return parse(stringreader, false);
|
||||
}
|
||||
|
||||
public EntitySelector parse(StringReader stringreader, boolean overridePermissions) throws CommandSyntaxException {
|
||||
// CraftBukkit end
|
||||
boolean flag = false;
|
||||
ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader);
|
||||
EntitySelector entityselector = argumentparserselector.s(overridePermissions); // CraftBukkit
|
||||
|
||||
if (entityselector.a() > 1 && this.h) {
|
||||
if (this.i) {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.b.createWithContext(stringreader);
|
||||
} else {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.a.createWithContext(stringreader);
|
||||
}
|
||||
} else if (entityselector.b() && this.i && !entityselector.c()) {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.c.createWithContext(stringreader);
|
||||
} else {
|
||||
return entityselector;
|
||||
}
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> commandcontext, SuggestionsBuilder suggestionsbuilder) {
|
||||
if (commandcontext.getSource() instanceof ICompletionProvider) {
|
||||
StringReader stringreader = new StringReader(suggestionsbuilder.getInput());
|
||||
|
||||
stringreader.setCursor(suggestionsbuilder.getStart());
|
||||
ICompletionProvider icompletionprovider = (ICompletionProvider) commandcontext.getSource();
|
||||
ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader, icompletionprovider.hasPermission(2));
|
||||
|
||||
try {
|
||||
argumentparserselector.s();
|
||||
} catch (CommandSyntaxException commandsyntaxexception) {
|
||||
;
|
||||
}
|
||||
|
||||
return argumentparserselector.a(suggestionsbuilder, (suggestionsbuilder1) -> {
|
||||
Collection<String> collection = icompletionprovider.l();
|
||||
Iterable<String> iterable = this.i ? collection : Iterables.concat(collection, icompletionprovider.p());
|
||||
|
||||
ICompletionProvider.b((Iterable) iterable, suggestionsbuilder1);
|
||||
});
|
||||
} else {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return ArgumentEntity.g;
|
||||
}
|
||||
|
||||
public static class a implements ArgumentSerializer<ArgumentEntity> {
|
||||
|
||||
public a() {}
|
||||
|
||||
public void a(ArgumentEntity argumententity, PacketDataSerializer packetdataserializer) {
|
||||
byte b0 = 0;
|
||||
|
||||
if (argumententity.h) {
|
||||
b0 = (byte) (b0 | 1);
|
||||
}
|
||||
|
||||
if (argumententity.i) {
|
||||
b0 = (byte) (b0 | 2);
|
||||
}
|
||||
|
||||
packetdataserializer.writeByte(b0);
|
||||
}
|
||||
|
||||
public ArgumentEntity b(PacketDataSerializer packetdataserializer) {
|
||||
byte b0 = packetdataserializer.readByte();
|
||||
|
||||
return new ArgumentEntity((b0 & 1) != 0, (b0 & 2) != 0);
|
||||
}
|
||||
|
||||
public void a(ArgumentEntity argumententity, JsonObject jsonobject) {
|
||||
jsonobject.addProperty("amount", argumententity.h ? "single" : "multiple");
|
||||
jsonobject.addProperty("type", argumententity.i ? "players" : "entities");
|
||||
}
|
||||
}
|
||||
}
|
||||
630
src/main/java/net/minecraft/server/ArgumentParserSelector.java
Normal file
630
src/main/java/net/minecraft/server/ArgumentParserSelector.java
Normal file
@@ -0,0 +1,630 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ArgumentParserSelector {
|
||||
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.invalid", new Object[0]));
|
||||
public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.entity.selector.unknown", new Object[] { object});
|
||||
});
|
||||
public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0]));
|
||||
public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.missing", new Object[0]));
|
||||
public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.options.unterminated", new Object[0]));
|
||||
public static final DynamicCommandExceptionType f = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.entity.options.valueless", new Object[] { object});
|
||||
});
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> g = (vec3d, list) -> {
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> h = (vec3d, list) -> {
|
||||
list.sort((entity, entity1) -> {
|
||||
return Doubles.compare(entity.a(vec3d), entity1.a(vec3d));
|
||||
});
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> i = (vec3d, list) -> {
|
||||
list.sort((entity, entity1) -> {
|
||||
return Doubles.compare(entity1.a(vec3d), entity.a(vec3d));
|
||||
});
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> j = (vec3d, list) -> {
|
||||
Collections.shuffle(list);
|
||||
};
|
||||
public static final BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> k = (suggestionsbuilder, consumer) -> {
|
||||
return suggestionsbuilder.buildFuture();
|
||||
};
|
||||
private final StringReader l;
|
||||
private final boolean m;
|
||||
private int n;
|
||||
private boolean o;
|
||||
private boolean p;
|
||||
private CriterionConditionValue.c q;
|
||||
private CriterionConditionValue.d r;
|
||||
@Nullable
|
||||
private Double s;
|
||||
@Nullable
|
||||
private Double t;
|
||||
@Nullable
|
||||
private Double u;
|
||||
@Nullable
|
||||
private Double v;
|
||||
@Nullable
|
||||
private Double w;
|
||||
@Nullable
|
||||
private Double x;
|
||||
private CriterionConditionRange y;
|
||||
private CriterionConditionRange z;
|
||||
private Predicate<Entity> A;
|
||||
private BiConsumer<Vec3D, List<? extends Entity>> B;
|
||||
private boolean C;
|
||||
@Nullable
|
||||
private String D;
|
||||
private int E;
|
||||
@Nullable
|
||||
private UUID F;
|
||||
private BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> G;
|
||||
private boolean H;
|
||||
private boolean I;
|
||||
private boolean J;
|
||||
private boolean K;
|
||||
private boolean L;
|
||||
private boolean M;
|
||||
private boolean N;
|
||||
private boolean O;
|
||||
private Class<? extends Entity> P;
|
||||
private boolean Q;
|
||||
private boolean R;
|
||||
private boolean S;
|
||||
private boolean T;
|
||||
|
||||
public ArgumentParserSelector(StringReader stringreader) {
|
||||
this(stringreader, true);
|
||||
}
|
||||
|
||||
// CraftBukkit start - decompile error
|
||||
private static final CriterionConditionValue.c DEFAULT_q;
|
||||
private static final CriterionConditionValue.d DEFAULT_r;
|
||||
|
||||
static {
|
||||
try {
|
||||
DEFAULT_q = (CriterionConditionValue.c) Class.forName("net.minecraft.server.CriterionConditionValue$c").getDeclaredField("e").get(null);
|
||||
DEFAULT_r = (CriterionConditionValue.d) Class.forName("net.minecraft.server.CriterionConditionValue$d").getDeclaredField("e").get(null);
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ArgumentParserSelector(StringReader stringreader, boolean flag) {
|
||||
this.q = DEFAULT_q;
|
||||
this.r = DEFAULT_r;
|
||||
// CraftBukkit end
|
||||
this.y = CriterionConditionRange.a;
|
||||
this.z = CriterionConditionRange.a;
|
||||
this.A = (entity) -> {
|
||||
return true;
|
||||
};
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.G = ArgumentParserSelector.k;
|
||||
this.l = stringreader;
|
||||
this.m = flag;
|
||||
}
|
||||
|
||||
public EntitySelector a() {
|
||||
AxisAlignedBB axisalignedbb;
|
||||
|
||||
if (this.v == null && this.w == null && this.x == null) {
|
||||
if (this.q.b() != null) {
|
||||
float f = (Float) this.q.b();
|
||||
|
||||
axisalignedbb = new AxisAlignedBB((double) (-f), (double) (-f), (double) (-f), (double) (f + 1.0F), (double) (f + 1.0F), (double) (f + 1.0F));
|
||||
} else {
|
||||
axisalignedbb = null;
|
||||
}
|
||||
} else {
|
||||
axisalignedbb = this.a(this.v == null ? 0.0D : this.v, this.w == null ? 0.0D : this.w, this.x == null ? 0.0D : this.x);
|
||||
}
|
||||
|
||||
Function<Vec3D, Vec3D> function; // CraftBukkit - decompile error
|
||||
|
||||
if (this.s == null && this.t == null && this.u == null) {
|
||||
function = (vec3d) -> {
|
||||
return vec3d;
|
||||
};
|
||||
} else {
|
||||
function = (vec3d) -> {
|
||||
return new Vec3D(this.s == null ? vec3d.x : this.s, this.t == null ? vec3d.y : this.t, this.u == null ? vec3d.z : this.u);
|
||||
};
|
||||
}
|
||||
|
||||
return new EntitySelector(this.n, this.o, this.p, this.A, this.q, function, axisalignedbb, this.B, this.C, this.D, this.F, this.P == null ? Entity.class : this.P, this.T);
|
||||
}
|
||||
|
||||
private AxisAlignedBB a(double d0, double d1, double d2) {
|
||||
boolean flag = d0 < 0.0D;
|
||||
boolean flag1 = d1 < 0.0D;
|
||||
boolean flag2 = d2 < 0.0D;
|
||||
double d3 = flag ? d0 : 0.0D;
|
||||
double d4 = flag1 ? d1 : 0.0D;
|
||||
double d5 = flag2 ? d2 : 0.0D;
|
||||
double d6 = (flag ? 0.0D : d0) + 1.0D;
|
||||
double d7 = (flag1 ? 0.0D : d1) + 1.0D;
|
||||
double d8 = (flag2 ? 0.0D : d2) + 1.0D;
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
private void I() {
|
||||
if (this.y != CriterionConditionRange.a) {
|
||||
this.A = this.A.and(this.a(this.y, (entity) -> {
|
||||
return (double) entity.pitch;
|
||||
}));
|
||||
}
|
||||
|
||||
if (this.z != CriterionConditionRange.a) {
|
||||
this.A = this.A.and(this.a(this.z, (entity) -> {
|
||||
return (double) entity.yaw;
|
||||
}));
|
||||
}
|
||||
|
||||
if (!this.r.c()) {
|
||||
this.A = this.A.and((entity) -> {
|
||||
return !(entity instanceof EntityPlayer) ? false : this.r.d(((EntityPlayer) entity).expLevel);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Predicate<Entity> a(CriterionConditionRange criterionconditionrange, ToDoubleFunction<Entity> todoublefunction) {
|
||||
double d0 = (double) MathHelper.g(criterionconditionrange.a() == null ? 0.0F : criterionconditionrange.a());
|
||||
double d1 = (double) MathHelper.g(criterionconditionrange.b() == null ? 359.0F : criterionconditionrange.b());
|
||||
|
||||
return (entity) -> {
|
||||
double d2 = MathHelper.g(todoublefunction.applyAsDouble(entity));
|
||||
|
||||
return d0 > d1 ? d2 >= d0 || d2 <= d1 : d2 >= d0 && d2 <= d1;
|
||||
};
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
protected void b(boolean overridePermissions) throws CommandSyntaxException {
|
||||
this.T = !overridePermissions;
|
||||
// CraftBukkit end
|
||||
this.G = this::d;
|
||||
if (!this.l.canRead()) {
|
||||
throw ArgumentParserSelector.d.createWithContext(this.l);
|
||||
} else {
|
||||
int i = this.l.getCursor();
|
||||
char c0 = this.l.read();
|
||||
|
||||
if (c0 == 'p') {
|
||||
this.n = 1;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.h;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 'a') {
|
||||
this.n = Integer.MAX_VALUE;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 'r') {
|
||||
this.n = 1;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.j;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 's') {
|
||||
this.n = 1;
|
||||
this.o = true;
|
||||
this.C = true;
|
||||
} else {
|
||||
if (c0 != 'e') {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.b.createWithContext(this.l, '@' + String.valueOf(c0));
|
||||
}
|
||||
|
||||
this.n = Integer.MAX_VALUE;
|
||||
this.o = true;
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.A = Entity::isAlive;
|
||||
}
|
||||
|
||||
this.G = this::e;
|
||||
if (this.l.canRead() && this.l.peek() == '[') {
|
||||
this.l.skip();
|
||||
this.G = this::f;
|
||||
this.d();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void c() throws CommandSyntaxException {
|
||||
if (this.l.canRead()) {
|
||||
this.G = this::c;
|
||||
}
|
||||
|
||||
int i = this.l.getCursor();
|
||||
String s = this.l.readString();
|
||||
|
||||
try {
|
||||
this.F = UUID.fromString(s);
|
||||
this.o = true;
|
||||
} catch (IllegalArgumentException illegalargumentexception) {
|
||||
if (s.isEmpty() || s.length() > 16) {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.a.createWithContext(this.l);
|
||||
}
|
||||
|
||||
this.o = false;
|
||||
this.D = s;
|
||||
}
|
||||
|
||||
this.n = 1;
|
||||
}
|
||||
|
||||
protected void d() throws CommandSyntaxException {
|
||||
this.G = this::g;
|
||||
this.l.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.l.canRead() && this.l.peek() != ']') {
|
||||
this.l.skipWhitespace();
|
||||
int i = this.l.getCursor();
|
||||
String s = this.l.readString();
|
||||
PlayerSelector.a playerselector_a = PlayerSelector.a(this, s, i);
|
||||
|
||||
this.l.skipWhitespace();
|
||||
if (!this.l.canRead() || this.l.peek() != '=') {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.f.createWithContext(this.l, s);
|
||||
}
|
||||
|
||||
this.l.skip();
|
||||
this.l.skipWhitespace();
|
||||
this.G = ArgumentParserSelector.k;
|
||||
playerselector_a.handle(this);
|
||||
this.l.skipWhitespace();
|
||||
this.G = this::h;
|
||||
if (!this.l.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.l.peek() == ',') {
|
||||
this.l.skip();
|
||||
this.G = this::g;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.l.peek() != ']') {
|
||||
throw ArgumentParserSelector.e.createWithContext(this.l);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.l.canRead()) {
|
||||
this.l.skip();
|
||||
this.G = ArgumentParserSelector.k;
|
||||
return;
|
||||
}
|
||||
|
||||
throw ArgumentParserSelector.e.createWithContext(this.l);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean e() {
|
||||
this.l.skipWhitespace();
|
||||
if (this.l.canRead() && this.l.peek() == '!') {
|
||||
this.l.skip();
|
||||
this.l.skipWhitespace();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public StringReader f() {
|
||||
return this.l;
|
||||
}
|
||||
|
||||
public void a(Predicate<Entity> predicate) {
|
||||
this.A = this.A.and(predicate);
|
||||
}
|
||||
|
||||
public void g() {
|
||||
this.p = true;
|
||||
}
|
||||
|
||||
public CriterionConditionValue.c h() {
|
||||
return this.q;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionValue.c criterionconditionvalue_c) {
|
||||
this.q = criterionconditionvalue_c;
|
||||
}
|
||||
|
||||
public CriterionConditionValue.d i() {
|
||||
return this.r;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionValue.d criterionconditionvalue_d) {
|
||||
this.r = criterionconditionvalue_d;
|
||||
}
|
||||
|
||||
public CriterionConditionRange j() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionRange criterionconditionrange) {
|
||||
this.y = criterionconditionrange;
|
||||
}
|
||||
|
||||
public CriterionConditionRange k() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public void b(CriterionConditionRange criterionconditionrange) {
|
||||
this.z = criterionconditionrange;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double l() {
|
||||
return this.s;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double m() {
|
||||
return this.t;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double n() {
|
||||
return this.u;
|
||||
}
|
||||
|
||||
public void a(double d0) {
|
||||
this.s = d0;
|
||||
}
|
||||
|
||||
public void b(double d0) {
|
||||
this.t = d0;
|
||||
}
|
||||
|
||||
public void c(double d0) {
|
||||
this.u = d0;
|
||||
}
|
||||
|
||||
public void d(double d0) {
|
||||
this.v = d0;
|
||||
}
|
||||
|
||||
public void e(double d0) {
|
||||
this.w = d0;
|
||||
}
|
||||
|
||||
public void f(double d0) {
|
||||
this.x = d0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double o() {
|
||||
return this.v;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double p() {
|
||||
return this.w;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double q() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.n = i;
|
||||
}
|
||||
|
||||
public void a(boolean flag) {
|
||||
this.o = flag;
|
||||
}
|
||||
|
||||
public void a(BiConsumer<Vec3D, List<? extends Entity>> biconsumer) {
|
||||
this.B = biconsumer;
|
||||
}
|
||||
|
||||
public EntitySelector s() throws CommandSyntaxException {
|
||||
// CraftBukkit start
|
||||
return s(false);
|
||||
}
|
||||
|
||||
public EntitySelector s(boolean overridePermissions) throws CommandSyntaxException {
|
||||
// CraftBukkit end
|
||||
this.E = this.l.getCursor();
|
||||
this.G = this::b;
|
||||
if (this.l.canRead() && this.l.peek() == '@') {
|
||||
if (!this.m) {
|
||||
throw ArgumentParserSelector.c.createWithContext(this.l);
|
||||
}
|
||||
|
||||
this.l.skip();
|
||||
this.b(overridePermissions); // CraftBukkit
|
||||
} else {
|
||||
this.c();
|
||||
}
|
||||
|
||||
this.I();
|
||||
return this.a();
|
||||
}
|
||||
|
||||
private static void a(SuggestionsBuilder suggestionsbuilder) {
|
||||
suggestionsbuilder.suggest("@p", new ChatMessage("argument.entity.selector.nearestPlayer", new Object[0]));
|
||||
suggestionsbuilder.suggest("@a", new ChatMessage("argument.entity.selector.allPlayers", new Object[0]));
|
||||
suggestionsbuilder.suggest("@r", new ChatMessage("argument.entity.selector.randomPlayer", new Object[0]));
|
||||
suggestionsbuilder.suggest("@s", new ChatMessage("argument.entity.selector.self", new Object[0]));
|
||||
suggestionsbuilder.suggest("@e", new ChatMessage("argument.entity.selector.allEntities", new Object[0]));
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> b(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
consumer.accept(suggestionsbuilder);
|
||||
if (this.m) {
|
||||
a(suggestionsbuilder);
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> c(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(this.E);
|
||||
|
||||
consumer.accept(suggestionsbuilder1);
|
||||
return suggestionsbuilder.add(suggestionsbuilder1).buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> d(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(suggestionsbuilder.getStart() - 1);
|
||||
|
||||
a(suggestionsbuilder1);
|
||||
suggestionsbuilder.add(suggestionsbuilder1);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> e(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> f(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
PlayerSelector.a(this, suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> g(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
PlayerSelector.a(this, suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> h(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
public boolean t() {
|
||||
return this.C;
|
||||
}
|
||||
|
||||
public void a(BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> bifunction) {
|
||||
this.G = bifunction;
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
return (CompletableFuture) this.G.apply(suggestionsbuilder.createOffset(this.l.getCursor()), consumer);
|
||||
}
|
||||
|
||||
public boolean u() {
|
||||
return this.H;
|
||||
}
|
||||
|
||||
public void c(boolean flag) {
|
||||
this.H = flag;
|
||||
}
|
||||
|
||||
public boolean v() {
|
||||
return this.I;
|
||||
}
|
||||
|
||||
public void d(boolean flag) {
|
||||
this.I = flag;
|
||||
}
|
||||
|
||||
public boolean w() {
|
||||
return this.J;
|
||||
}
|
||||
|
||||
public void e(boolean flag) {
|
||||
this.J = flag;
|
||||
}
|
||||
|
||||
public boolean x() {
|
||||
return this.K;
|
||||
}
|
||||
|
||||
public void f(boolean flag) {
|
||||
this.K = flag;
|
||||
}
|
||||
|
||||
public boolean y() {
|
||||
return this.L;
|
||||
}
|
||||
|
||||
public void g(boolean flag) {
|
||||
this.L = flag;
|
||||
}
|
||||
|
||||
public boolean z() {
|
||||
return this.M;
|
||||
}
|
||||
|
||||
public void h(boolean flag) {
|
||||
this.M = flag;
|
||||
}
|
||||
|
||||
public boolean A() {
|
||||
return this.N;
|
||||
}
|
||||
|
||||
public void i(boolean flag) {
|
||||
this.N = flag;
|
||||
}
|
||||
|
||||
public void j(boolean flag) {
|
||||
this.O = flag;
|
||||
}
|
||||
|
||||
public void a(Class<? extends Entity> oclass) {
|
||||
this.P = oclass;
|
||||
}
|
||||
|
||||
public void C() {
|
||||
this.Q = true;
|
||||
}
|
||||
|
||||
public boolean E() {
|
||||
return this.P != null;
|
||||
}
|
||||
|
||||
public boolean F() {
|
||||
return this.Q;
|
||||
}
|
||||
|
||||
public boolean G() {
|
||||
return this.R;
|
||||
}
|
||||
|
||||
public void k(boolean flag) {
|
||||
this.R = flag;
|
||||
}
|
||||
|
||||
public boolean H() {
|
||||
return this.S;
|
||||
}
|
||||
|
||||
public void l(boolean flag) {
|
||||
this.S = flag;
|
||||
}
|
||||
}
|
||||
33
src/main/java/net/minecraft/server/AttributeInstance.java
Normal file
33
src/main/java/net/minecraft/server/AttributeInstance.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface AttributeInstance {
|
||||
|
||||
IAttribute getAttribute();
|
||||
|
||||
double b();
|
||||
|
||||
void setValue(double d0);
|
||||
|
||||
Collection<AttributeModifier> a(int i);
|
||||
|
||||
Collection<AttributeModifier> c();
|
||||
|
||||
boolean a(AttributeModifier attributemodifier);
|
||||
|
||||
@Nullable
|
||||
AttributeModifier a(UUID uuid);
|
||||
|
||||
default void addModifier(AttributeModifier modifier) { b(modifier); } // Paper - OBFHELPER
|
||||
void b(AttributeModifier attributemodifier);
|
||||
|
||||
default void removeModifier(AttributeModifier modifier) { c(modifier); } // Paper - OBFHELPER
|
||||
void c(AttributeModifier attributemodifier);
|
||||
|
||||
void b(UUID uuid);
|
||||
|
||||
double getValue();
|
||||
}
|
||||
39
src/main/java/net/minecraft/server/AttributeRanged.java
Normal file
39
src/main/java/net/minecraft/server/AttributeRanged.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AttributeRanged extends AttributeBase {
|
||||
|
||||
private final double a;
|
||||
public double maximum; // Spigot
|
||||
private String c;
|
||||
|
||||
public AttributeRanged(@Nullable IAttribute iattribute, String s, double d0, double d1, double d2) {
|
||||
super(iattribute, s, d0);
|
||||
this.a = d1;
|
||||
this.maximum = d2;
|
||||
if (d1 > d2) {
|
||||
throw new IllegalArgumentException("Minimum value cannot be bigger than maximum value!");
|
||||
} else if (d0 < d1) {
|
||||
throw new IllegalArgumentException("Default value cannot be lower than minimum value!");
|
||||
} else if (d0 > d2) {
|
||||
throw new IllegalArgumentException("Default value cannot be bigger than maximum value!");
|
||||
}
|
||||
}
|
||||
|
||||
public AttributeRanged a(String s) {
|
||||
this.c = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String g() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public double a(double d0) {
|
||||
if (d0 != d0) return getDefault(); // CraftBukkit
|
||||
|
||||
d0 = MathHelper.a(d0, this.a, this.maximum);
|
||||
return d0;
|
||||
}
|
||||
}
|
||||
303
src/main/java/net/minecraft/server/AxisAlignedBB.java
Normal file
303
src/main/java/net/minecraft/server/AxisAlignedBB.java
Normal file
@@ -0,0 +1,303 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AxisAlignedBB {
|
||||
|
||||
public final double minX; public double getMinX() { return this.minX; } // Paper - OBFHELPER
|
||||
public final double minY; public double getMinY() { return this.minY; } // Paper - OBFHELPER
|
||||
public final double minZ; public double getMinZ() { return this.minZ; } // Paper - OBFHELPER
|
||||
public final double maxX; public double getMaxX() { return this.maxX; } // Paper - OBFHELPER
|
||||
public final double maxY; public double getMaxY() { return this.maxY; } // Paper - OBFHELPER
|
||||
public final double maxZ; public double getMaxZ() { return this.maxZ; } // Paper - OBFHELPER
|
||||
|
||||
public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5) {
|
||||
this.minX = Math.min(d0, d3);
|
||||
this.minY = Math.min(d1, d4);
|
||||
this.minZ = Math.min(d2, d5);
|
||||
this.maxX = Math.max(d0, d3);
|
||||
this.maxY = Math.max(d1, d4);
|
||||
this.maxZ = Math.max(d2, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB(BlockPosition blockposition) {
|
||||
this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 1), (double) (blockposition.getZ() + 1));
|
||||
}
|
||||
|
||||
public AxisAlignedBB(BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ());
|
||||
}
|
||||
|
||||
public double a(EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||
return enumdirection_enumaxis.a(this.minX, this.minY, this.minZ);
|
||||
}
|
||||
|
||||
public double b(EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||
return enumdirection_enumaxis.a(this.maxX, this.maxY, this.maxZ);
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof AxisAlignedBB)) {
|
||||
return false;
|
||||
} else {
|
||||
AxisAlignedBB axisalignedbb = (AxisAlignedBB) object;
|
||||
|
||||
return Double.compare(axisalignedbb.minX, this.minX) != 0 ? false : (Double.compare(axisalignedbb.minY, this.minY) != 0 ? false : (Double.compare(axisalignedbb.minZ, this.minZ) != 0 ? false : (Double.compare(axisalignedbb.maxX, this.maxX) != 0 ? false : (Double.compare(axisalignedbb.maxY, this.maxY) != 0 ? false : Double.compare(axisalignedbb.maxZ, this.maxZ) == 0))));
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
long i = Double.doubleToLongBits(this.minX);
|
||||
int j = (int) (i ^ i >>> 32);
|
||||
|
||||
i = Double.doubleToLongBits(this.minY);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.minZ);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxX);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxY);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxZ);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
return j;
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(double d0, double d1, double d2) {
|
||||
double d3 = this.minX;
|
||||
double d4 = this.minY;
|
||||
double d5 = this.minZ;
|
||||
double d6 = this.maxX;
|
||||
double d7 = this.maxY;
|
||||
double d8 = this.maxZ;
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d3 -= d0;
|
||||
} else if (d0 > 0.0D) {
|
||||
d6 -= d0;
|
||||
}
|
||||
|
||||
if (d1 < 0.0D) {
|
||||
d4 -= d1;
|
||||
} else if (d1 > 0.0D) {
|
||||
d7 -= d1;
|
||||
}
|
||||
|
||||
if (d2 < 0.0D) {
|
||||
d5 -= d2;
|
||||
} else if (d2 > 0.0D) {
|
||||
d8 -= d2;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
public AxisAlignedBB expand(double x, double y, double z) { return b(x, y, z); } // Paper - OBFHELPER
|
||||
public AxisAlignedBB b(double d0, double d1, double d2) {
|
||||
double d3 = this.minX;
|
||||
double d4 = this.minY;
|
||||
double d5 = this.minZ;
|
||||
double d6 = this.maxX;
|
||||
double d7 = this.maxY;
|
||||
double d8 = this.maxZ;
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d3 += d0;
|
||||
} else if (d0 > 0.0D) {
|
||||
d6 += d0;
|
||||
}
|
||||
|
||||
if (d1 < 0.0D) {
|
||||
d4 += d1;
|
||||
} else if (d1 > 0.0D) {
|
||||
d7 += d1;
|
||||
}
|
||||
|
||||
if (d2 < 0.0D) {
|
||||
d5 += d2;
|
||||
} else if (d2 > 0.0D) {
|
||||
d8 += d2;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public AxisAlignedBB grow(double d0) {
|
||||
return grow(d0, d0, d0);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public AxisAlignedBB grow(double d0, double d1, double d2) {
|
||||
double d3 = this.minX - d0;
|
||||
double d4 = this.minY - d1;
|
||||
double d5 = this.minZ - d2;
|
||||
double d6 = this.maxX + d0;
|
||||
double d7 = this.maxY + d1;
|
||||
double d8 = this.maxZ + d2;
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
public AxisAlignedBB g(double d0) {
|
||||
return this.grow(d0, d0, d0);
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(AxisAlignedBB axisalignedbb) {
|
||||
double d0 = Math.max(this.minX, axisalignedbb.minX);
|
||||
double d1 = Math.max(this.minY, axisalignedbb.minY);
|
||||
double d2 = Math.max(this.minZ, axisalignedbb.minZ);
|
||||
double d3 = Math.min(this.maxX, axisalignedbb.maxX);
|
||||
double d4 = Math.min(this.maxY, axisalignedbb.maxY);
|
||||
double d5 = Math.min(this.maxZ, axisalignedbb.maxZ);
|
||||
|
||||
return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB b(AxisAlignedBB axisalignedbb) {
|
||||
double d0 = Math.min(this.minX, axisalignedbb.minX);
|
||||
double d1 = Math.min(this.minY, axisalignedbb.minY);
|
||||
double d2 = Math.min(this.minZ, axisalignedbb.minZ);
|
||||
double d3 = Math.max(this.maxX, axisalignedbb.maxX);
|
||||
double d4 = Math.max(this.maxY, axisalignedbb.maxY);
|
||||
double d5 = Math.max(this.maxZ, axisalignedbb.maxZ);
|
||||
|
||||
return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB d(double d0, double d1, double d2) {
|
||||
return new AxisAlignedBB(this.minX + d0, this.minY + d1, this.minZ + d2, this.maxX + d0, this.maxY + d1, this.maxZ + d2);
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(BlockPosition blockposition) {
|
||||
return new AxisAlignedBB(this.minX + (double) blockposition.getX(), this.minY + (double) blockposition.getY(), this.minZ + (double) blockposition.getZ(), this.maxX + (double) blockposition.getX(), this.maxY + (double) blockposition.getY(), this.maxZ + (double) blockposition.getZ());
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(Vec3D vec3d) {
|
||||
return this.d(vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
public boolean c(AxisAlignedBB axisalignedbb) {
|
||||
return this.a(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.maxY, axisalignedbb.maxZ);
|
||||
}
|
||||
|
||||
public boolean a(double d0, double d1, double d2, double d3, double d4, double d5) {
|
||||
return this.minX < d3 && this.maxX > d0 && this.minY < d4 && this.maxY > d1 && this.minZ < d5 && this.maxZ > d2;
|
||||
}
|
||||
|
||||
public boolean contains(Vec3D vec3d) { return b(vec3d); } // Paper - OBFHELPER
|
||||
public boolean b(Vec3D vec3d) {
|
||||
return this.e(vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
public boolean e(double d0, double d1, double d2) {
|
||||
return d0 >= this.minX && d0 < this.maxX && d1 >= this.minY && d1 < this.maxY && d2 >= this.minZ && d2 < this.maxZ;
|
||||
}
|
||||
|
||||
public double a() {
|
||||
double d0 = this.maxX - this.minX;
|
||||
double d1 = this.maxY - this.minY;
|
||||
double d2 = this.maxZ - this.minZ;
|
||||
|
||||
return (d0 + d1 + d2) / 3.0D;
|
||||
}
|
||||
|
||||
public AxisAlignedBB f(double d0, double d1, double d2) {
|
||||
return this.grow(-d0, -d1, -d2);
|
||||
}
|
||||
|
||||
public AxisAlignedBB shrink(double d0) {
|
||||
return this.g(-d0);
|
||||
}
|
||||
|
||||
public MovingObjectPosition calculateIntercept(Vec3D vec3d, Vec3D vec3d1) { return b(vec3d, vec3d1); } // Paper - OBFHELPER
|
||||
@Nullable
|
||||
public MovingObjectPosition b(Vec3D vec3d, Vec3D vec3d1) {
|
||||
return this.a(vec3d, vec3d1, (BlockPosition) null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MovingObjectPosition a(Vec3D vec3d, Vec3D vec3d1, @Nullable BlockPosition blockposition) {
|
||||
double[] adouble = new double[] { 1.0D};
|
||||
EnumDirection enumdirection = null;
|
||||
double d0 = vec3d1.x - vec3d.x;
|
||||
double d1 = vec3d1.y - vec3d.y;
|
||||
double d2 = vec3d1.z - vec3d.z;
|
||||
|
||||
enumdirection = a(blockposition == null ? this : this.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2);
|
||||
if (enumdirection == null) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = adouble[0];
|
||||
|
||||
return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition == null ? BlockPosition.ZERO : blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MovingObjectPosition a(Iterable<AxisAlignedBB> iterable, Vec3D vec3d, Vec3D vec3d1, BlockPosition blockposition) {
|
||||
double[] adouble = new double[] { 1.0D};
|
||||
EnumDirection enumdirection = null;
|
||||
double d0 = vec3d1.x - vec3d.x;
|
||||
double d1 = vec3d1.y - vec3d.y;
|
||||
double d2 = vec3d1.z - vec3d.z;
|
||||
|
||||
AxisAlignedBB axisalignedbb;
|
||||
|
||||
for (Iterator iterator = iterable.iterator(); iterator.hasNext(); enumdirection = a(axisalignedbb.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2)) {
|
||||
axisalignedbb = (AxisAlignedBB) iterator.next();
|
||||
}
|
||||
|
||||
if (enumdirection == null) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = adouble[0];
|
||||
|
||||
return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EnumDirection a(AxisAlignedBB axisalignedbb, Vec3D vec3d, double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2) {
|
||||
if (d0 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.WEST, vec3d.x, vec3d.y, vec3d.z);
|
||||
} else if (d0 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.EAST, vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
if (d1 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.DOWN, vec3d.y, vec3d.z, vec3d.x);
|
||||
} else if (d1 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.UP, vec3d.y, vec3d.z, vec3d.x);
|
||||
}
|
||||
|
||||
if (d2 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.minZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.NORTH, vec3d.z, vec3d.x, vec3d.y);
|
||||
} else if (d2 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.SOUTH, vec3d.z, vec3d.x, vec3d.y);
|
||||
}
|
||||
|
||||
return enumdirection;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EnumDirection a(double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2, double d3, double d4, double d5, double d6, double d7, EnumDirection enumdirection1, double d8, double d9, double d10) {
|
||||
double d11 = (d3 - d8) / d0;
|
||||
double d12 = d9 + d11 * d1;
|
||||
double d13 = d10 + d11 * d2;
|
||||
|
||||
if (0.0D < d11 && d11 < adouble[0] && d4 - 1.0E-7D < d12 && d12 < d5 + 1.0E-7D && d6 - 1.0E-7D < d13 && d13 < d7 + 1.0E-7D) {
|
||||
adouble[0] = d11;
|
||||
return enumdirection1;
|
||||
} else {
|
||||
return enumdirection;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "box[" + this.minX + ", " + this.minY + ", " + this.minZ + " -> " + this.maxX + ", " + this.maxY + ", " + this.maxZ + "]";
|
||||
}
|
||||
}
|
||||
105
src/main/java/net/minecraft/server/BaseBlockPosition.java
Normal file
105
src/main/java/net/minecraft/server/BaseBlockPosition.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
public class BaseBlockPosition implements Comparable<BaseBlockPosition> {
|
||||
|
||||
public static final BaseBlockPosition ZERO = new BaseBlockPosition(0, 0, 0);
|
||||
// Paper start
|
||||
protected int x;
|
||||
protected int y;
|
||||
protected int z;
|
||||
public boolean isValidLocation() {
|
||||
return x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000 && y >= 0 && y < 256;
|
||||
}
|
||||
public boolean isInvalidYLocation() {
|
||||
return y < 0 || y >= 256;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public BaseBlockPosition(int i, int j, int k) {
|
||||
this.x = i;
|
||||
this.y = j;
|
||||
this.z = k;
|
||||
}
|
||||
|
||||
public BaseBlockPosition(double d0, double d1, double d2) {
|
||||
this(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2));
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof BaseBlockPosition)) {
|
||||
return false;
|
||||
} else {
|
||||
BaseBlockPosition baseblockposition = (BaseBlockPosition) object;
|
||||
|
||||
return this.getX() != baseblockposition.getX() ? false : (this.getY() != baseblockposition.getY() ? false : this.getZ() == baseblockposition.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
}
|
||||
|
||||
public int compareTo(BaseBlockPosition baseblockposition) {
|
||||
return this.getY() == baseblockposition.getY() ? (this.getZ() == baseblockposition.getZ() ? this.getX() - baseblockposition.getX() : this.getZ() - baseblockposition.getZ()) : this.getY() - baseblockposition.getY();
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public final int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public BaseBlockPosition d(BaseBlockPosition baseblockposition) {
|
||||
return new BaseBlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX());
|
||||
}
|
||||
|
||||
public double h(int i, int j, int k) {
|
||||
double d0 = (double) (this.getX() - i);
|
||||
double d1 = (double) (this.getY() - j);
|
||||
double d2 = (double) (this.getZ() - k);
|
||||
|
||||
return Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
}
|
||||
|
||||
public double m(BaseBlockPosition baseblockposition) {
|
||||
return this.h(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public double distanceSquared(double d0, double d1, double d2) {
|
||||
double d3 = (double) this.getX() - d0;
|
||||
double d4 = (double) this.getY() - d1;
|
||||
double d5 = (double) this.getZ() - d2;
|
||||
|
||||
return d3 * d3 + d4 * d4 + d5 * d5;
|
||||
}
|
||||
|
||||
public double g(double d0, double d1, double d2) {
|
||||
double d3 = (double) this.getX() + 0.5D - d0;
|
||||
double d4 = (double) this.getY() + 0.5D - d1;
|
||||
double d5 = (double) this.getZ() + 0.5D - d2;
|
||||
|
||||
return d3 * d3 + d4 * d4 + d5 * d5;
|
||||
}
|
||||
|
||||
public double n(BaseBlockPosition baseblockposition) {
|
||||
return this.distanceSquared((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this).add("x", this.getX()).add("y", this.getY()).add("z", this.getZ()).toString();
|
||||
}
|
||||
}
|
||||
626
src/main/java/net/minecraft/server/BiomeBase.java
Normal file
626
src/main/java/net/minecraft/server/BiomeBase.java
Normal file
@@ -0,0 +1,626 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public abstract class BiomeBase {
|
||||
|
||||
public static final Logger a = LogManager.getLogger();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> b = new WorldGenCaves();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> c = new WorldGenCavesHell();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> d = new WorldGenCanyon();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> e = new WorldGenCanyonOcean();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> f = new WorldGenCavesOcean();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> g = new WorldGenDecoratorHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> h = new WorldGenDecoratorSkyVisible();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> i = new WorldGenDecoratorHeight32();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> j = new WorldGenDecoratorHeightDouble();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> k = new WorldGenDecoratorHeight64();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorNoiseConfiguration> l = new WorldGenDecoratorNoiseHeight32();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorNoiseConfiguration> m = new WorldGenDecoratorNoiseHeightDouble();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> n = new WorldGenDecoratorEmpty();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> o = new WorldGenDecoratorChance();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> p = new WorldGenDecoratorChanceHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> q = new WorldGenDecoratorChancePass();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> r = new WorldGenDecoratorSkyVisibleBiased();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyExtraChanceConfiguration> s = new WorldGenDecoratorHeightExtraChance();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> t = new WorldGenDecoratorNetherHeight();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> u = new WorldGenDecoratorHeightBiased();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> v = new WorldGenDecoratorHeightBiased2();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> w = new WorldGenDecoratorNetherRandomCount();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorRangeConfiguration> x = new WorldGenDecoratorNetherChance();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyChanceConfiguration> y = new WorldGenFeatureChanceDecorator();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyChanceConfiguration> z = new WorldGenFeatureChanceDecoratorHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorHeightAverageConfiguration> A = new WorldGenDecoratorHeightAverage();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> B = new WorldGenDecoratorSolidTop();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorRangeConfiguration> C = new WorldGenDecoratorSolidTopHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorNoiseConfiguration> D = new WorldGenDecoratorSolidTopNoise();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorCarveMaskConfiguration> E = new WorldGenDecoratorCarveMask();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> F = new WorldGenDecoratorForestRock();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> G = new WorldGenDecoratorNetherFire();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> H = new WorldGenDecoratorNetherMagma();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> I = new WorldGenDecoratorEmerald();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorLakeChanceConfiguration> J = new WorldGenDecoratorLakeLava();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorLakeChanceConfiguration> K = new WorldGenDecoratorLakeWater();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorDungeonConfiguration> L = new WorldGenDecoratorDungeon();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> M = new WorldGenDecoratorRoofedTree();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> N = new WorldGenDecoratorIceburg();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> O = new WorldGenDecoratorNetherGlowstone();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> P = new WorldGenDecoratorSpike();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> Q = new WorldGenDecoratorEndIsland();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> R = new WorldGenDecoratorChorusPlant();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> S = new WorldGenDecoratorEndGateway();
|
||||
protected static final IBlockData T = Blocks.AIR.getBlockData();
|
||||
protected static final IBlockData U = Blocks.DIRT.getBlockData();
|
||||
protected static final IBlockData V = Blocks.GRASS_BLOCK.getBlockData();
|
||||
protected static final IBlockData W = Blocks.PODZOL.getBlockData();
|
||||
protected static final IBlockData X = Blocks.GRAVEL.getBlockData();
|
||||
protected static final IBlockData Y = Blocks.STONE.getBlockData();
|
||||
protected static final IBlockData Z = Blocks.COARSE_DIRT.getBlockData();
|
||||
protected static final IBlockData aa = Blocks.SAND.getBlockData();
|
||||
protected static final IBlockData ab = Blocks.RED_SAND.getBlockData();
|
||||
protected static final IBlockData ac = Blocks.WHITE_TERRACOTTA.getBlockData();
|
||||
protected static final IBlockData ad = Blocks.MYCELIUM.getBlockData();
|
||||
protected static final IBlockData ae = Blocks.NETHERRACK.getBlockData();
|
||||
protected static final IBlockData af = Blocks.END_STONE.getBlockData();
|
||||
public static final WorldGenSurfaceConfigurationBase ag = new WorldGenSurfaceConfigurationBase(BiomeBase.T, BiomeBase.T, BiomeBase.T);
|
||||
public static final WorldGenSurfaceConfigurationBase ah = new WorldGenSurfaceConfigurationBase(BiomeBase.U, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ai = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase aj = new WorldGenSurfaceConfigurationBase(BiomeBase.Y, BiomeBase.Y, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ak = new WorldGenSurfaceConfigurationBase(BiomeBase.X, BiomeBase.X, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase al = new WorldGenSurfaceConfigurationBase(BiomeBase.Z, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase am = new WorldGenSurfaceConfigurationBase(BiomeBase.W, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase an = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.aa);
|
||||
public static final WorldGenSurfaceConfigurationBase ao = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.aa);
|
||||
public static final WorldGenSurfaceConfigurationBase ap = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase aq = new WorldGenSurfaceConfigurationBase(BiomeBase.ab, BiomeBase.ac, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ar = new WorldGenSurfaceConfigurationBase(BiomeBase.ad, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase as = new WorldGenSurfaceConfigurationBase(BiomeBase.ae, BiomeBase.ae, BiomeBase.ae);
|
||||
public static final WorldGenSurfaceConfigurationBase at = new WorldGenSurfaceConfigurationBase(BiomeBase.af, BiomeBase.af, BiomeBase.af);
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> au = new WorldGenSurfaceDefaultBlock();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> av = new WorldGenSurfaceExtremeHills();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aw = new WorldGenSurfaceSavannaMutated();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> ax = new WorldGenSurfaceExtremeHillMutated();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> ay = new WorldGenSurfaceTaigaMega();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> az = new WorldGenSurfaceSwamp();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aA = new WorldGenSurfaceMesa();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aB = new WorldGenSurfaceMesaForest();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aC = new WorldGenSurfaceMesaBryce();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aD = new WorldGenSurfaceFrozenOcean();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aE = new WorldGenSurfaceNether();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aF = new WorldGenSurfaceEmpty();
|
||||
public static final Set<BiomeBase> aG = Sets.newHashSet();
|
||||
public static final RegistryBlockID<BiomeBase> aH = new RegistryBlockID<>();
|
||||
protected static final NoiseGenerator3 aI = new NoiseGenerator3(new Random(1234L), 1);
|
||||
public static final NoiseGenerator3 aJ = new NoiseGenerator3(new Random(2345L), 1);
|
||||
@Nullable
|
||||
protected String aK;
|
||||
protected final float aL;
|
||||
protected final float aM;
|
||||
protected final float aN;
|
||||
protected final float aO;
|
||||
protected final int aP;
|
||||
protected final int aQ;
|
||||
@Nullable
|
||||
protected final String aR;
|
||||
protected final WorldGenSurfaceComposite<?> aS;
|
||||
protected final BiomeBase.Geography aT;
|
||||
protected final BiomeBase.Precipitation aU;
|
||||
protected final Map<WorldGenStage.Features, List<WorldGenCarverWrapper<?>>> aV = Maps.newHashMap();
|
||||
protected final Map<WorldGenStage.Decoration, List<WorldGenFeatureComposite<?, ?>>> aW = Maps.newHashMap();
|
||||
protected final List<WorldGenFeatureCompositeFlower<?>> aX = Lists.newArrayList();
|
||||
protected final Map<StructureGenerator<?>, WorldGenFeatureConfiguration> aY = Maps.newHashMap();
|
||||
private final java.util.EnumMap<EnumCreatureType, List<BiomeBase.BiomeMeta>> aZ = Maps.newEnumMap(EnumCreatureType.class); // Paper
|
||||
|
||||
@Nullable
|
||||
public static BiomeBase a(BiomeBase biomebase) {
|
||||
return (BiomeBase) BiomeBase.aH.fromId(IRegistry.BIOME.a(biomebase)); // Paper - decompile fix
|
||||
}
|
||||
|
||||
public static <C extends WorldGenFeatureConfiguration> WorldGenCarverWrapper<C> a(WorldGenCarver<C> worldgencarver, C c0) {
|
||||
return new WorldGenCarverWrapper<>(worldgencarver, c0);
|
||||
}
|
||||
|
||||
public static <F extends WorldGenFeatureConfiguration, D extends WorldGenFeatureDecoratorConfiguration> WorldGenFeatureComposite<F, D> a(WorldGenerator<F> worldgenerator, F f0, WorldGenDecorator<D> worldgendecorator, D d0) {
|
||||
return new WorldGenFeatureComposite<>(worldgenerator, f0, worldgendecorator, d0);
|
||||
}
|
||||
|
||||
public static <D extends WorldGenFeatureDecoratorConfiguration> WorldGenFeatureCompositeFlower<D> a(WorldGenFlowers worldgenflowers, WorldGenDecorator<D> worldgendecorator, D d0) {
|
||||
return new WorldGenFeatureCompositeFlower<>(worldgenflowers, worldgendecorator, d0);
|
||||
}
|
||||
|
||||
protected BiomeBase(BiomeBase.a biomebase_a) {
|
||||
if (biomebase_a.a != null && biomebase_a.b != null && biomebase_a.c != null && biomebase_a.d != null && biomebase_a.e != null && biomebase_a.f != null && biomebase_a.g != null && biomebase_a.h != null && biomebase_a.i != null) {
|
||||
this.aS = biomebase_a.a;
|
||||
this.aU = biomebase_a.b;
|
||||
this.aT = biomebase_a.c;
|
||||
this.aL = biomebase_a.d;
|
||||
this.aM = biomebase_a.e;
|
||||
this.aN = biomebase_a.f;
|
||||
this.aO = biomebase_a.g;
|
||||
this.aP = biomebase_a.h;
|
||||
this.aQ = biomebase_a.i;
|
||||
this.aR = biomebase_a.j;
|
||||
WorldGenStage.Decoration[] aworldgenstage_decoration = WorldGenStage.Decoration.values();
|
||||
int i = aworldgenstage_decoration.length;
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
WorldGenStage.Decoration worldgenstage_decoration = aworldgenstage_decoration[j];
|
||||
|
||||
this.aW.put(worldgenstage_decoration, Lists.newArrayList());
|
||||
}
|
||||
|
||||
EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values();
|
||||
|
||||
i = aenumcreaturetype.length;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
EnumCreatureType enumcreaturetype = aenumcreaturetype[j];
|
||||
|
||||
this.aZ.put(enumcreaturetype, new MobList()); // Paper
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("You are missing parameters to build a proper biome for " + this.getClass().getSimpleName() + "\n" + biomebase_a);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a() {
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.f, new WorldGenMineshaftConfiguration(0.004000000189989805D, WorldGenMineshaft.Type.NORMAL), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.e, new WorldGenFeatureVillageConfiguration(0, WorldGenVillagePieces.Material.OAK), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.m, new WorldGenFeatureStrongholdConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.l, new WorldGenFeatureSwampHutConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.i, new WorldGenFeatureDesertPyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.h, new WorldGenFeatureJunglePyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.j, new WorldGenFeatureIglooConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.k, new WorldGenFeatureShipwreckConfiguration(false), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.n, new WorldGenMonumentConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.g, new WorldGenMansionConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.o, new WorldGenFeatureOceanRuinConfiguration(WorldGenFeatureOceanRuin.Temperature.COLD, 0.3F, 0.9F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.r, new WorldGenBuriedTreasureConfiguration(0.01F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
}
|
||||
|
||||
public boolean b() {
|
||||
return this.aR != null;
|
||||
}
|
||||
|
||||
protected void a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta) {
|
||||
((List) this.aZ.get(enumcreaturetype)).add(biomebase_biomemeta);
|
||||
}
|
||||
|
||||
public List<BiomeBase.BiomeMeta> getMobs(EnumCreatureType enumcreaturetype) {
|
||||
return (List) this.aZ.get(enumcreaturetype);
|
||||
}
|
||||
|
||||
public BiomeBase.Precipitation c() {
|
||||
return this.aU;
|
||||
}
|
||||
|
||||
public boolean d() {
|
||||
return this.getHumidity() > 0.85F;
|
||||
}
|
||||
|
||||
public float e() {
|
||||
return 0.1F;
|
||||
}
|
||||
|
||||
public float getAdjustedTemperature(BlockPosition blockposition) {
|
||||
if (blockposition.getY() > 64) {
|
||||
float f = (float) (BiomeBase.aI.a((double) ((float) blockposition.getX() / 8.0F), (double) ((float) blockposition.getZ() / 8.0F)) * 4.0D);
|
||||
|
||||
return this.getTemperature() - (f + (float) blockposition.getY() - 64.0F) * 0.05F / 30.0F;
|
||||
} else {
|
||||
return this.getTemperature();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return this.a(iworldreader, blockposition, true);
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition, boolean flag) {
|
||||
if (this.getAdjustedTemperature(blockposition) >= 0.15F) {
|
||||
return false;
|
||||
} else {
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
Fluid fluid = iworldreader.getFluid(blockposition);
|
||||
|
||||
if (fluid.c() == FluidTypes.WATER && iblockdata.getBlock() instanceof BlockFluids) {
|
||||
if (!flag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean flag1 = iworldreader.B(blockposition.west()) && iworldreader.B(blockposition.east()) && iworldreader.B(blockposition.north()) && iworldreader.B(blockposition.south());
|
||||
|
||||
if (!flag1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean b(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
if (this.getAdjustedTemperature(blockposition) >= 0.15F) {
|
||||
return false;
|
||||
} else {
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
|
||||
if (iblockdata.isAir() && Blocks.SNOW.getBlockData().canPlace(iworldreader, blockposition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(WorldGenStage.Decoration worldgenstage_decoration, WorldGenFeatureComposite<?, ?> worldgenfeaturecomposite) {
|
||||
if (worldgenfeaturecomposite instanceof WorldGenFeatureCompositeFlower) {
|
||||
this.aX.add((WorldGenFeatureCompositeFlower) worldgenfeaturecomposite);
|
||||
}
|
||||
|
||||
((List) this.aW.get(worldgenstage_decoration)).add(worldgenfeaturecomposite);
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> void a(WorldGenStage.Features worldgenstage_features, WorldGenCarverWrapper<C> worldgencarverwrapper) {
|
||||
((List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> {
|
||||
return Lists.newArrayList();
|
||||
})).add(worldgencarverwrapper);
|
||||
}
|
||||
|
||||
public List<WorldGenCarverWrapper<?>> a(WorldGenStage.Features worldgenstage_features) {
|
||||
return (List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> {
|
||||
return Lists.newArrayList();
|
||||
});
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> void a(StructureGenerator<C> structuregenerator, C c0) {
|
||||
this.aY.put(structuregenerator, c0);
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> boolean a(StructureGenerator<C> structuregenerator) {
|
||||
return this.aY.containsKey(structuregenerator);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <C extends WorldGenFeatureConfiguration> WorldGenFeatureConfiguration b(StructureGenerator<C> structuregenerator) {
|
||||
return (WorldGenFeatureConfiguration) this.aY.get(structuregenerator);
|
||||
}
|
||||
|
||||
public List<WorldGenFeatureCompositeFlower<?>> f() {
|
||||
return this.aX;
|
||||
}
|
||||
|
||||
public List<WorldGenFeatureComposite<?, ?>> a(WorldGenStage.Decoration worldgenstage_decoration) {
|
||||
return (List) this.aW.get(worldgenstage_decoration);
|
||||
}
|
||||
|
||||
public void a(WorldGenStage.Decoration worldgenstage_decoration, ChunkGenerator<? extends GeneratorSettings> chunkgenerator, GeneratorAccess generatoraccess, long i, SeededRandom seededrandom, BlockPosition blockposition) {
|
||||
int j = 0;
|
||||
|
||||
for (Iterator iterator = ((List) this.aW.get(worldgenstage_decoration)).iterator(); iterator.hasNext(); ++j) {
|
||||
WorldGenFeatureComposite<?, ?> worldgenfeaturecomposite = (WorldGenFeatureComposite) iterator.next();
|
||||
|
||||
seededrandom.b(i, j, worldgenstage_decoration.ordinal());
|
||||
worldgenfeaturecomposite.a(generatoraccess, chunkgenerator, seededrandom, blockposition, WorldGenFeatureConfiguration.e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(Random random, IChunkAccess ichunkaccess, int i, int j, int k, double d0, IBlockData iblockdata, IBlockData iblockdata1, int l, long i1) {
|
||||
this.aS.a(i1);
|
||||
this.aS.a(random, ichunkaccess, this, i, j, k, d0, iblockdata, iblockdata1, l, i1, BiomeBase.ag);
|
||||
}
|
||||
|
||||
public BiomeBase.EnumTemperature g() {
|
||||
return this.aT == BiomeBase.Geography.OCEAN ? BiomeBase.EnumTemperature.OCEAN : ((double) this.getTemperature() < 0.2D ? BiomeBase.EnumTemperature.COLD : ((double) this.getTemperature() < 1.0D ? BiomeBase.EnumTemperature.MEDIUM : BiomeBase.EnumTemperature.WARM));
|
||||
}
|
||||
|
||||
public static BiomeBase getBiome(int i, BiomeBase biomebase) {
|
||||
BiomeBase biomebase1 = (BiomeBase) IRegistry.BIOME.fromId(i);
|
||||
|
||||
return biomebase1 == null ? biomebase : biomebase1;
|
||||
}
|
||||
|
||||
public final float h() {
|
||||
return this.aL;
|
||||
}
|
||||
|
||||
public final float getHumidity() {
|
||||
return this.aO;
|
||||
}
|
||||
|
||||
public String k() {
|
||||
if (this.aK == null) {
|
||||
this.aK = SystemUtils.a("biome", IRegistry.BIOME.getKey(this));
|
||||
}
|
||||
|
||||
return this.aK;
|
||||
}
|
||||
|
||||
public final float l() {
|
||||
return this.aM;
|
||||
}
|
||||
|
||||
public final float getTemperature() {
|
||||
return this.aN;
|
||||
}
|
||||
|
||||
public final int n() {
|
||||
return this.aP;
|
||||
}
|
||||
|
||||
public final int o() {
|
||||
return this.aQ;
|
||||
}
|
||||
|
||||
public final BiomeBase.Geography p() {
|
||||
return this.aT;
|
||||
}
|
||||
|
||||
public WorldGenSurfaceComposite<?> q() {
|
||||
return this.aS;
|
||||
}
|
||||
|
||||
public WorldGenSurfaceConfiguration r() {
|
||||
return this.aS.a();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String s() {
|
||||
return this.aR;
|
||||
}
|
||||
|
||||
public static void t() {
|
||||
a(0, "ocean", new BiomeOcean());
|
||||
a(1, "plains", new BiomePlains());
|
||||
a(2, "desert", new BiomeDesert());
|
||||
a(3, "mountains", new BiomeBigHills());
|
||||
a(4, "forest", new BiomeForest());
|
||||
a(5, "taiga", new BiomeTaiga());
|
||||
a(6, "swamp", new BiomeSwamp());
|
||||
a(7, "river", new BiomeRiver());
|
||||
a(8, "nether", new BiomeHell());
|
||||
a(9, "the_end", new BiomeTheEnd());
|
||||
a(10, "frozen_ocean", new BiomeFrozenOcean());
|
||||
a(11, "frozen_river", new BiomeFrozenRiver());
|
||||
a(12, "snowy_tundra", new BiomeIcePlains());
|
||||
a(13, "snowy_mountains", new BiomeIceMountains());
|
||||
a(14, "mushroom_fields", new BiomeMushrooms());
|
||||
a(15, "mushroom_field_shore", new BiomeMushroomIslandShore());
|
||||
a(16, "beach", new BiomeBeach());
|
||||
a(17, "desert_hills", new BiomeDesertHills());
|
||||
a(18, "wooded_hills", new BiomeForestHills());
|
||||
a(19, "taiga_hills", new BiomeTaigaHills());
|
||||
a(20, "mountain_edge", new BiomeExtremeHillsEdge());
|
||||
a(21, "jungle", new BiomeJungle());
|
||||
a(22, "jungle_hills", new BiomeJungleHills());
|
||||
a(23, "jungle_edge", new BiomeJungleEdge());
|
||||
a(24, "deep_ocean", new BiomeDeepOcean());
|
||||
a(25, "stone_shore", new BiomeStoneBeach());
|
||||
a(26, "snowy_beach", new BiomeColdBeach());
|
||||
a(27, "birch_forest", new BiomeBirchForest());
|
||||
a(28, "birch_forest_hills", new BiomeBirchForestHills());
|
||||
a(29, "dark_forest", new BiomeRoofedForest());
|
||||
a(30, "snowy_taiga", new BiomeColdTaiga());
|
||||
a(31, "snowy_taiga_hills", new BiomeColdTaigaHills());
|
||||
a(32, "giant_tree_taiga", new BiomeMegaTaiga());
|
||||
a(33, "giant_tree_taiga_hills", new BiomeMegaTaigaHills());
|
||||
a(34, "wooded_mountains", new BiomeExtremeHillsWithTrees());
|
||||
a(35, "savanna", new BiomeSavanna());
|
||||
a(36, "savanna_plateau", new BiomeSavannaPlateau());
|
||||
a(37, "badlands", new BiomeMesa());
|
||||
a(38, "wooded_badlands_plateau", new BiomeMesaPlataeu());
|
||||
a(39, "badlands_plateau", new BiomeMesaPlataeuClear());
|
||||
a(40, "small_end_islands", new BiomeTheEndFloatingIslands());
|
||||
a(41, "end_midlands", new BiomeTheEndMediumIsland());
|
||||
a(42, "end_highlands", new BiomeTheEndHighIsland());
|
||||
a(43, "end_barrens", new BiomeTheEndBarrenIsland());
|
||||
a(44, "warm_ocean", new BiomeWarmOcean());
|
||||
a(45, "lukewarm_ocean", new BiomeLukewarmOcean());
|
||||
a(46, "cold_ocean", new BiomeColdOcean());
|
||||
a(47, "deep_warm_ocean", new BiomeWarmDeepOcean());
|
||||
a(48, "deep_lukewarm_ocean", new BiomeLukewarmDeepOcean());
|
||||
a(49, "deep_cold_ocean", new BiomeColdDeepOcean());
|
||||
a(50, "deep_frozen_ocean", new BiomeFrozenDeepOcean());
|
||||
a(127, "the_void", new BiomeVoid());
|
||||
a(129, "sunflower_plains", new BiomeSunflowerPlains());
|
||||
a(130, "desert_lakes", new BiomeDesertMutated());
|
||||
a(131, "gravelly_mountains", new BiomeExtremeHillsMutated());
|
||||
a(132, "flower_forest", new BiomeFlowerForest());
|
||||
a(133, "taiga_mountains", new BiomeTaigaMutated());
|
||||
a(134, "swamp_hills", new BiomeSwamplandMutated());
|
||||
a(140, "ice_spikes", new BiomeIcePlainsSpikes());
|
||||
a(149, "modified_jungle", new BiomeJungleMutated());
|
||||
a(151, "modified_jungle_edge", new BiomeJungleEdgeMutated());
|
||||
a(155, "tall_birch_forest", new BiomeBirchForestMutated());
|
||||
a(156, "tall_birch_hills", new BiomeBirchForestHillsMutated());
|
||||
a(157, "dark_forest_hills", new BiomeRoofedForestMutated());
|
||||
a(158, "snowy_taiga_mountains", new BiomeColdTaigaMutated());
|
||||
a(160, "giant_spruce_taiga", new BiomeMegaSpruceTaiga());
|
||||
a(161, "giant_spruce_taiga_hills", new BiomeRedwoodTaigaHillsMutated());
|
||||
a(162, "modified_gravelly_mountains", new BiomeExtremeHillsWithTreesMutated());
|
||||
a(163, "shattered_savanna", new BiomeSavannaMutated());
|
||||
a(164, "shattered_savanna_plateau", new BiomeSavannaPlateauMutated());
|
||||
a(165, "eroded_badlands", new BiomeMesaBryce());
|
||||
a(166, "modified_wooded_badlands_plateau", new BiomeMesaPlateauMutated());
|
||||
a(167, "modified_badlands_plateau", new BiomeMesaPlateauClearMutated());
|
||||
Collections.addAll(BiomeBase.aG, new BiomeBase[] { Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU});
|
||||
}
|
||||
|
||||
private static void a(int i, String s, BiomeBase biomebase) {
|
||||
IRegistry.BIOME.a(i, new MinecraftKey(s), biomebase);
|
||||
if (biomebase.b()) {
|
||||
BiomeBase.aH.a(biomebase, IRegistry.BIOME.a(IRegistry.BIOME.get(new MinecraftKey(biomebase.aR))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start - keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it
|
||||
public static class MobList extends java.util.ArrayList<BiomeMeta> {
|
||||
java.util.Set<BiomeMeta> biomes = new java.util.HashSet<>();
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return biomes.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(BiomeMeta biomeMeta) {
|
||||
biomes.add(biomeMeta);
|
||||
return super.add(biomeMeta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeMeta remove(int index) {
|
||||
BiomeMeta removed = super.remove(index);
|
||||
if (removed != null) {
|
||||
biomes.remove(removed);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
biomes.clear();
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public static class a {
|
||||
|
||||
@Nullable
|
||||
private WorldGenSurfaceComposite<?> a;
|
||||
@Nullable
|
||||
private BiomeBase.Precipitation b;
|
||||
@Nullable
|
||||
private BiomeBase.Geography c;
|
||||
@Nullable
|
||||
private Float d;
|
||||
@Nullable
|
||||
private Float e;
|
||||
@Nullable
|
||||
private Float f;
|
||||
@Nullable
|
||||
private Float g;
|
||||
@Nullable
|
||||
private Integer h;
|
||||
@Nullable
|
||||
private Integer i;
|
||||
@Nullable
|
||||
private String j;
|
||||
|
||||
public a() {}
|
||||
|
||||
public BiomeBase.a a(WorldGenSurfaceComposite<?> worldgensurfacecomposite) {
|
||||
this.a = worldgensurfacecomposite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(BiomeBase.Precipitation biomebase_precipitation) {
|
||||
this.b = biomebase_precipitation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(BiomeBase.Geography biomebase_geography) {
|
||||
this.c = biomebase_geography;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(float f) {
|
||||
this.d = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a b(float f) {
|
||||
this.e = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a c(float f) {
|
||||
this.f = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a d(float f) {
|
||||
this.g = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(int i) {
|
||||
this.h = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a b(int i) {
|
||||
this.i = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(@Nullable String s) {
|
||||
this.j = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BiomeBuilder{\nsurfaceBuilder=" + this.a + ",\nprecipitation=" + this.b + ",\nbiomeCategory=" + this.c + ",\ndepth=" + this.d + ",\nscale=" + this.e + ",\ntemperature=" + this.f + ",\ndownfall=" + this.g + ",\nwaterColor=" + this.h + ",\nwaterFogColor=" + this.i + ",\nparent='" + this.j + '\'' + "\n" + '}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class BiomeMeta extends WeightedRandom.WeightedRandomChoice {
|
||||
|
||||
public EntityTypes<? extends EntityInsentient> b;
|
||||
public int c;
|
||||
public int d;
|
||||
|
||||
public BiomeMeta(EntityTypes<? extends EntityInsentient> entitytypes, int i, int j, int k) {
|
||||
super(i);
|
||||
this.b = entitytypes;
|
||||
this.c = j;
|
||||
this.d = k;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return EntityTypes.getName(this.b) + "*(" + this.c + "-" + this.d + "):" + this.a;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Precipitation {
|
||||
|
||||
NONE, RAIN, SNOW;
|
||||
|
||||
private Precipitation() {}
|
||||
}
|
||||
|
||||
public static enum Geography {
|
||||
|
||||
NONE, TAIGA, EXTREME_HILLS, JUNGLE, MESA, PLAINS, SAVANNA, ICY, THEEND, BEACH, FOREST, OCEAN, DESERT, RIVER, SWAMP, MUSHROOM, NETHER;
|
||||
|
||||
private Geography() {}
|
||||
}
|
||||
|
||||
public static enum EnumTemperature {
|
||||
|
||||
OCEAN, COLD, MEDIUM, WARM;
|
||||
|
||||
private EnumTemperature() {}
|
||||
}
|
||||
}
|
||||
1574
src/main/java/net/minecraft/server/Block.java
Normal file
1574
src/main/java/net/minecraft/server/Block.java
Normal file
File diff suppressed because it is too large
Load Diff
78
src/main/java/net/minecraft/server/BlockBeacon.java
Normal file
78
src/main/java/net/minecraft/server/BlockBeacon.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockBeacon extends BlockTileEntity {
|
||||
|
||||
public BlockBeacon(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityBeacon();
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
entityhuman.openContainer((TileEntityBeacon) tileentity);
|
||||
entityhuman.a(StatisticList.INTERACT_WITH_BEACON);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
((TileEntityBeacon) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public static void a(World world, BlockPosition blockposition) {
|
||||
//HttpUtilities.a.submit(() -> { // Paper
|
||||
Chunk chunk = world.getChunkAtWorldCoords(blockposition);
|
||||
|
||||
for (int i = blockposition.getY() - 1; i >= 0; --i) {
|
||||
BlockPosition blockposition1 = new BlockPosition(blockposition.getX(), i, blockposition.getZ());
|
||||
|
||||
if (!chunk.c(blockposition1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
IBlockData iblockdata = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata.getBlock() == Blocks.BEACON) {
|
||||
((WorldServer) world).postToMainThread(() -> {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
((TileEntityBeacon) tileentity).p();
|
||||
world.playBlockAction(blockposition1, Blocks.BEACON, 1, 0);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
// }); // Paper
|
||||
}
|
||||
}
|
||||
310
src/main/java/net/minecraft/server/BlockBed.java
Normal file
310
src/main/java/net/minecraft/server/BlockBed.java
Normal file
@@ -0,0 +1,310 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockBed extends BlockFacingHorizontal implements ITileEntity {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyBedPart> PART = BlockProperties.ao;
|
||||
public static final BlockStateBoolean OCCUPIED = BlockProperties.q;
|
||||
protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
|
||||
private final EnumColor color;
|
||||
|
||||
public BlockBed(EnumColor enumcolor, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.color = enumcolor;
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockBed.PART, BlockPropertyBedPart.FOOT)).set(BlockBed.OCCUPIED, false));
|
||||
}
|
||||
|
||||
public MaterialMapColor c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? this.color.e() : MaterialMapColor.e;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
if (iblockdata.get(BlockBed.PART) != BlockPropertyBedPart.HEAD) {
|
||||
blockposition = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING));
|
||||
iblockdata = world.getType(blockposition);
|
||||
if (iblockdata.getBlock() != this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit - moved world and biome check into EntityHuman
|
||||
if (true || world.worldProvider.canRespawn() && world.getBiome(blockposition) != Biomes.NETHER) {
|
||||
if ((Boolean) iblockdata.get(BlockBed.OCCUPIED)) {
|
||||
EntityHuman entityhuman1 = this.a(world, blockposition);
|
||||
|
||||
if (entityhuman1 != null) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.occupied", new Object[0])), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, false);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
}
|
||||
|
||||
EntityHuman.EnumBedResult entityhuman_enumbedresult = entityhuman.a(blockposition);
|
||||
|
||||
if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.OK) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, true);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
return true;
|
||||
} else {
|
||||
if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_NOW) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.no_sleep", new Object[0])), true);
|
||||
} else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_SAFE) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.not_safe", new Object[0])), true);
|
||||
} else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.TOO_FAR_AWAY) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.too_far_away", new Object[0])), true);
|
||||
}
|
||||
// CraftBukkit start - handling bed explosion from below here
|
||||
else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_HERE) {
|
||||
this.explodeBed(iblockdata, world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit start - moved bed explosion into separate method
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean explodeBed(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.setAir(blockposition);
|
||||
BlockPosition blockposition1 = blockposition.shift(((EnumDirection) iblockdata.get(BlockBed.FACING)).opposite());
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == this) {
|
||||
world.setAir(blockposition1);
|
||||
}
|
||||
|
||||
world.createExplosion((Entity) null, DamageSource.a(), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, 5.0F, true, true);
|
||||
return true;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private EntityHuman a(World world, BlockPosition blockposition) {
|
||||
Iterator iterator = world.players.iterator();
|
||||
|
||||
EntityHuman entityhuman;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
entityhuman = (EntityHuman) iterator.next();
|
||||
} while (!entityhuman.isSleeping() || !entityhuman.bedPosition.equals(blockposition));
|
||||
|
||||
return entityhuman;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) {
|
||||
super.fallOn(world, blockposition, entity, f * 0.5F);
|
||||
}
|
||||
|
||||
public void a(IBlockAccess iblockaccess, Entity entity) {
|
||||
if (entity.isSneaking()) {
|
||||
super.a(iblockaccess, entity);
|
||||
} else if (entity.motY < 0.0D) {
|
||||
entity.motY = -entity.motY * 0.6600000262260437D;
|
||||
if (!(entity instanceof EntityLiving)) {
|
||||
entity.motY *= 0.8D;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == a((BlockPropertyBedPart) iblockdata.get(BlockBed.PART), (EnumDirection) iblockdata.get(BlockBed.FACING)) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != iblockdata.get(BlockBed.PART) ? (IBlockData) iblockdata.set(BlockBed.OCCUPIED, iblockdata1.get(BlockBed.OCCUPIED)) : Blocks.AIR.getBlockData()) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
private static EnumDirection a(BlockPropertyBedPart blockpropertybedpart, EnumDirection enumdirection) {
|
||||
return blockpropertybedpart == BlockPropertyBedPart.FOOT ? enumdirection : enumdirection.opposite();
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
world.n(blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
BlockPropertyBedPart blockpropertybedpart = (BlockPropertyBedPart) iblockdata.get(BlockBed.PART);
|
||||
boolean flag = blockpropertybedpart == BlockPropertyBedPart.HEAD;
|
||||
BlockPosition blockposition1 = blockposition.shift(a(blockpropertybedpart, (EnumDirection) iblockdata.get(BlockBed.FACING)));
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != blockpropertybedpart) {
|
||||
world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35);
|
||||
world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1));
|
||||
if (!world.isClientSide && !entityhuman.u()) {
|
||||
if (flag) {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
} else {
|
||||
iblockdata1.a(world, blockposition1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
}
|
||||
|
||||
super.a(world, blockposition, iblockdata, entityhuman);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
EnumDirection enumdirection = blockactioncontext.f();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
|
||||
return blockactioncontext.getWorld().getType(blockposition1).a(blockactioncontext) ? (IBlockData) this.getBlockData().set(BlockBed.FACING, enumdirection) : null;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return (IMaterial) (iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockBed.c;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BlockPosition a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockaccess.getType(blockposition).get(BlockBed.FACING);
|
||||
// Paper - replace whole method
|
||||
World world = (World) iblockaccess;
|
||||
int radius = world.paperConfig.bedSearchRadius;
|
||||
for (int r = 1; r <= radius; r++) {
|
||||
int x = -r;
|
||||
int z = r;
|
||||
|
||||
// Iterates the edge of half of the box; then negates for other half.
|
||||
while (x <= r && z > -r) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
BlockPosition pos = blockposition.add(x, y, z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
pos = blockposition.add(-x, y, -z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos = blockposition.add(enumdirection.getAdjacentX() + x, y, enumdirection.getAdjacentZ() + z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos = blockposition.add(enumdirection.getAdjacentX() - x, y, enumdirection.getAdjacentZ() - z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x < r) {
|
||||
x++;
|
||||
} else {
|
||||
z--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; /* // Paper comment out
|
||||
int j = blockposition.getX();
|
||||
int k = blockposition.getY();
|
||||
int l = blockposition.getZ();
|
||||
|
||||
for (int i1 = 0; i1 <= 1; ++i1) {
|
||||
int j1 = j - enumdirection.getAdjacentX() * i1 - 1;
|
||||
int k1 = l - enumdirection.getAdjacentZ() * i1 - 1;
|
||||
int l1 = j1 + 2;
|
||||
int i2 = k1 + 2;
|
||||
|
||||
for (int j2 = j1; j2 <= l1; ++j2) {
|
||||
for (int k2 = k1; k2 <= i2; ++k2) {
|
||||
BlockPosition blockposition1 = new BlockPosition(j2, k, k2);
|
||||
|
||||
if (a(iblockaccess, blockposition1)) {
|
||||
if (i <= 0) {
|
||||
return blockposition1;
|
||||
}
|
||||
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;*/ // Paper
|
||||
}
|
||||
|
||||
protected static boolean isSafeRespawn(IBlockAccess iblockaccess, BlockPosition blockposition) { // Paper - OBFHELPER + behavior improvement
|
||||
return a(iblockaccess, blockposition) && iblockaccess.getType(blockposition.down()).getMaterial().isBuildable(); // Paper - ensure solid block
|
||||
}
|
||||
protected static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockaccess.getType(blockposition.down()).q() && !iblockaccess.getType(blockposition).getMaterial().isBuildable() && !iblockaccess.getType(blockposition.up()).getMaterial().isBuildable();
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockBed.FACING, BlockBed.PART, BlockBed.OCCUPIED);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityBed(this.color);
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, @Nullable EntityLiving entityliving, ItemStack itemstack) {
|
||||
super.postPlace(world, blockposition, iblockdata, entityliving, itemstack);
|
||||
if (!world.isClientSide) {
|
||||
BlockPosition blockposition1 = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING));
|
||||
|
||||
world.setTypeAndData(blockposition1, (IBlockData) iblockdata.set(BlockBed.PART, BlockPropertyBedPart.HEAD), 3);
|
||||
world.update(blockposition, Blocks.AIR);
|
||||
iblockdata.a((GeneratorAccess) world, blockposition, 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
224
src/main/java/net/minecraft/server/BlockButtonAbstract.java
Normal file
224
src/main/java/net/minecraft/server/BlockButtonAbstract.java
Normal file
@@ -0,0 +1,224 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||
import org.bukkit.event.entity.EntityInteractEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public abstract class BlockButtonAbstract extends BlockAttachable {
|
||||
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
protected static final VoxelShape b = Block.a(6.0D, 14.0D, 5.0D, 10.0D, 16.0D, 11.0D);
|
||||
protected static final VoxelShape c = Block.a(5.0D, 14.0D, 6.0D, 11.0D, 16.0D, 10.0D);
|
||||
protected static final VoxelShape o = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 2.0D, 11.0D);
|
||||
protected static final VoxelShape p = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 2.0D, 10.0D);
|
||||
protected static final VoxelShape q = Block.a(5.0D, 6.0D, 14.0D, 11.0D, 10.0D, 16.0D);
|
||||
protected static final VoxelShape r = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 2.0D);
|
||||
protected static final VoxelShape s = Block.a(14.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape t = Block.a(0.0D, 6.0D, 5.0D, 2.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape u = Block.a(6.0D, 15.0D, 5.0D, 10.0D, 16.0D, 11.0D);
|
||||
protected static final VoxelShape v = Block.a(5.0D, 15.0D, 6.0D, 11.0D, 16.0D, 10.0D);
|
||||
protected static final VoxelShape w = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 1.0D, 11.0D);
|
||||
protected static final VoxelShape x = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 1.0D, 10.0D);
|
||||
protected static final VoxelShape y = Block.a(5.0D, 6.0D, 15.0D, 11.0D, 10.0D, 16.0D);
|
||||
protected static final VoxelShape z = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 1.0D);
|
||||
protected static final VoxelShape A = Block.a(15.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape B = Block.a(0.0D, 6.0D, 5.0D, 1.0D, 10.0D, 11.0D);
|
||||
private final boolean E;
|
||||
|
||||
protected BlockButtonAbstract(boolean flag, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockButtonAbstract.FACING, EnumDirection.NORTH)).set(BlockButtonAbstract.POWERED, false)).set(BlockButtonAbstract.FACE, BlockPropertyAttachPosition.WALL));
|
||||
this.E = flag;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return this.E ? 30 : 20;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED);
|
||||
|
||||
switch ((BlockPropertyAttachPosition) iblockdata.get(BlockButtonAbstract.FACE)) {
|
||||
case FLOOR:
|
||||
if (enumdirection.k() == EnumDirection.EnumAxis.X) {
|
||||
return flag ? BlockButtonAbstract.w : BlockButtonAbstract.o;
|
||||
}
|
||||
|
||||
return flag ? BlockButtonAbstract.x : BlockButtonAbstract.p;
|
||||
case WALL:
|
||||
switch (enumdirection) {
|
||||
case EAST:
|
||||
return flag ? BlockButtonAbstract.B : BlockButtonAbstract.t;
|
||||
case WEST:
|
||||
return flag ? BlockButtonAbstract.A : BlockButtonAbstract.s;
|
||||
case SOUTH:
|
||||
return flag ? BlockButtonAbstract.z : BlockButtonAbstract.r;
|
||||
case NORTH:
|
||||
default:
|
||||
return flag ? BlockButtonAbstract.y : BlockButtonAbstract.q;
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
return enumdirection.k() == EnumDirection.EnumAxis.X ? (flag ? BlockButtonAbstract.u : BlockButtonAbstract.b) : (flag ? BlockButtonAbstract.v : BlockButtonAbstract.c);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
return true;
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
boolean powered = ((Boolean) iblockdata.get(POWERED));
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, true), 3);
|
||||
this.a(entityhuman, world, blockposition, true);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(@Nullable EntityHuman entityhuman, GeneratorAccess generatoraccess, BlockPosition blockposition, boolean flag) {
|
||||
generatoraccess.a(flag ? entityhuman : null, blockposition, this.a(flag), SoundCategory.BLOCKS, 0.3F, flag ? 0.6F : 0.5F);
|
||||
}
|
||||
|
||||
protected abstract SoundEffect a(boolean flag);
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
this.c(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) && k(iblockdata) == enumdirection ? 15 : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide && (Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
if (this.E) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if (eventRedstone.getNewCurrent() > 0) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, false), 3);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
this.a((EntityHuman) null, world, blockposition, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide && this.E && !(Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
List<? extends Entity> list = world.a(EntityArrow.class, iblockdata.getShape(world, blockposition).getBoundingBox().a(blockposition));
|
||||
boolean flag = !list.isEmpty();
|
||||
boolean flag1 = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED);
|
||||
|
||||
// CraftBukkit start - Call interact event when arrows turn on wooden buttons
|
||||
if (flag1 != flag && flag) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
boolean allowed = false;
|
||||
|
||||
// If all of the events are cancelled block the button press, else allow
|
||||
for (Object object : list) {
|
||||
if (object != null) {
|
||||
EntityInteractEvent event = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (flag != flag1) {
|
||||
// CraftBukkit start
|
||||
boolean powered = flag1;
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((flag && eventRedstone.getNewCurrent() <= 0) || (!flag && eventRedstone.getNewCurrent() > 0)) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, flag), 3);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
this.a((EntityHuman) null, world, blockposition, flag);
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
world.getBlockTickList().a(new BlockPosition(blockposition), this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void c(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockButtonAbstract.FACING, BlockButtonAbstract.POWERED, BlockButtonAbstract.FACE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
117
src/main/java/net/minecraft/server/BlockCactus.java
Normal file
117
src/main/java/net/minecraft/server/BlockCactus.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCactus extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.X;
|
||||
protected static final VoxelShape b = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D);
|
||||
protected static final VoxelShape c = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
|
||||
protected BlockCactus(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCactus.AGE, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.isEmpty(blockposition1)) {
|
||||
int i;
|
||||
|
||||
for (i = 1; world.getType(blockposition.down(i)).getBlock() == this; ++i) {
|
||||
;
|
||||
}
|
||||
|
||||
if (i < world.paperConfig.cactusMaxHeight) { // Paper - Configurable growth height
|
||||
int j = (Integer) iblockdata.get(BlockCactus.AGE);
|
||||
|
||||
if (j >= (byte) range(3, ((100.0F / world.spigotConfig.cactusModifier) * 15) + 0.5F, 15)) { // Spigot
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.getBlockData()); // CraftBukkit
|
||||
IBlockData iblockdata1 = (IBlockData) iblockdata.set(BlockCactus.AGE, 0);
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata1, 4);
|
||||
iblockdata1.doPhysics(world, blockposition1, this, blockposition);
|
||||
} else {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCactus.AGE, j + 1), 4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelShape f(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCactus.b;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCactus.c;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
EnumDirection enumdirection;
|
||||
Material material;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
Block block = iworldreader.getType(blockposition.down()).getBlock();
|
||||
|
||||
return (block == Blocks.CACTUS || block == Blocks.SAND || block == Blocks.RED_SAND) && !iworldreader.getType(blockposition.up()).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
enumdirection = (EnumDirection) iterator.next();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.shift(enumdirection));
|
||||
|
||||
material = iblockdata1.getMaterial();
|
||||
} while (!material.isBuildable() && !iworldreader.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.LAVA));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit
|
||||
entity.damageEntity(DamageSource.CACTUS, 1.0F);
|
||||
CraftEventFactory.blockDamage = null; // CraftBukkit
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCactus.AGE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
91
src/main/java/net/minecraft/server/BlockCake.java
Normal file
91
src/main/java/net/minecraft/server/BlockCake.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockCake extends Block {
|
||||
|
||||
public static final BlockStateInteger BITES = BlockProperties.Z;
|
||||
protected static final VoxelShape[] b = new VoxelShape[] { Block.a(1.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(3.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(5.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(7.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(9.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(11.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(13.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D)};
|
||||
|
||||
protected BlockCake(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCake.BITES, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCake.b[(Integer) iblockdata.get(BlockCake.BITES)];
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (!world.isClientSide) {
|
||||
return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman);
|
||||
} else {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman) || itemstack.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
if (!entityhuman.q(false)) {
|
||||
return false;
|
||||
} else {
|
||||
entityhuman.a(StatisticList.EAT_CAKE_SLICE);
|
||||
// CraftBukkit start
|
||||
// entityhuman.getFoodData().eat(2, 0.1F);
|
||||
int oldFoodLevel = entityhuman.getFoodData().foodLevel;
|
||||
|
||||
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, 2 + oldFoodLevel);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F);
|
||||
}
|
||||
|
||||
((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate();
|
||||
// CraftBukkit end
|
||||
int i = (Integer) iblockdata.get(BlockCake.BITES);
|
||||
|
||||
if (i < 6) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCake.BITES, i + 1), 3);
|
||||
} else {
|
||||
generatoraccess.setAir(blockposition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).getMaterial().isBuildable();
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCake.BITES);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return (7 - (Integer) iblockdata.get(BlockCake.BITES)) * 2;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
267
src/main/java/net/minecraft/server/BlockCauldron.java
Normal file
267
src/main/java/net/minecraft/server/BlockCauldron.java
Normal file
@@ -0,0 +1,267 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.CauldronLevelChangeEvent; // CraftBukkit
|
||||
|
||||
public class BlockCauldron extends Block {
|
||||
|
||||
public static final BlockStateInteger LEVEL = BlockProperties.af;
|
||||
protected static final VoxelShape b = Block.a(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);
|
||||
protected static final VoxelShape c = VoxelShapes.a(VoxelShapes.b(), BlockCauldron.b, OperatorBoolean.ONLY_FIRST);
|
||||
|
||||
public BlockCauldron(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCauldron.LEVEL, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCauldron.c;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape h(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCauldron.b;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
int i = (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
float f = (float) blockposition.getY() + (6.0F + (float) (3 * i)) / 16.0F;
|
||||
|
||||
if (!world.isClientSide && entity.isBurning() && i > 0 && entity.getBoundingBox().minY <= (double) f) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH)) {
|
||||
return;
|
||||
}
|
||||
entity.extinguish();
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
int i = (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if (item == Items.WATER_BUCKET) {
|
||||
if (i < 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, 3, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.BUCKET));
|
||||
}
|
||||
|
||||
entityhuman.a(StatisticList.FILL_CAULDRON);
|
||||
// this.a(world, blockposition, iblockdata, 3);
|
||||
// CraftBukkit end
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (item == Items.BUCKET) {
|
||||
if (i == 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, 0, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.WATER_BUCKET));
|
||||
} else if (!entityhuman.inventory.pickup(new ItemStack(Items.WATER_BUCKET))) {
|
||||
entityhuman.drop(new ItemStack(Items.WATER_BUCKET), false);
|
||||
}
|
||||
}
|
||||
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
// this.a(world, blockposition, iblockdata, 0);
|
||||
// CraftBukkit end
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ItemStack itemstack1;
|
||||
|
||||
if (item == Items.GLASS_BOTTLE) {
|
||||
if (i > 0 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack1 = PotionUtil.a(new ItemStack(Items.POTION), Potions.b);
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
} else if (!entityhuman.inventory.pickup(itemstack1)) {
|
||||
entityhuman.drop(itemstack1, false);
|
||||
} else if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (item == Items.POTION && PotionUtil.d(itemstack) == Potions.b) {
|
||||
if (i < 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i + 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack1 = new ItemStack(Items.GLASS_BOTTLE);
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// this.a(world, blockposition, iblockdata, i + 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (i > 0 && item instanceof ItemArmorColorable) {
|
||||
ItemArmorColorable itemarmorcolorable = (ItemArmorColorable) item;
|
||||
|
||||
if (itemarmorcolorable.e(itemstack) && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
|
||||
return true;
|
||||
}
|
||||
itemarmorcolorable.g(itemstack);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
entityhuman.a(StatisticList.CLEAN_ARMOR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 && item instanceof ItemBanner) {
|
||||
if (TileEntityBanner.a(itemstack) > 0 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
|
||||
return true;
|
||||
}
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
itemstack1.setCount(1);
|
||||
TileEntityBanner.b(itemstack1);
|
||||
entityhuman.a(StatisticList.CLEAN_BANNER);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
} else if (!entityhuman.inventory.pickup(itemstack1)) {
|
||||
entityhuman.drop(itemstack1, false);
|
||||
} else if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (i > 0 && item instanceof ItemBlock) {
|
||||
Block block = ((ItemBlock) item).getBlock();
|
||||
|
||||
if (block instanceof BlockShulkerBox && !world.e()) {
|
||||
ItemStack itemstack2 = new ItemStack(Blocks.SHULKER_BOX, 1);
|
||||
|
||||
if (itemstack.hasTag()) {
|
||||
itemstack2.setTag(itemstack.getTag().clone());
|
||||
}
|
||||
|
||||
entityhuman.a(enumhand, itemstack2);
|
||||
this.a(world, blockposition, iblockdata, i - 1);
|
||||
entityhuman.a(StatisticList.CLEAN_SHULKER_BOX);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) {
|
||||
this.changeLevel(world, blockposition, iblockdata, i, null, CauldronLevelChangeEvent.ChangeReason.UNKNOWN);
|
||||
}
|
||||
|
||||
private boolean changeLevel(World world, BlockPosition blockposition, IBlockData iblockdata, int i, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) {
|
||||
int newLevel = Integer.valueOf(MathHelper.clamp(i, 0, 3));
|
||||
CauldronLevelChangeEvent event = new CauldronLevelChangeEvent(
|
||||
world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()),
|
||||
(entity == null) ? null : entity.getBukkitEntity(), reason, iblockdata.get(BlockCauldron.LEVEL), newLevel
|
||||
);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCauldron.LEVEL, event.getNewLevel()), 2);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
return true;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public void c(World world, BlockPosition blockposition) {
|
||||
if (world.random.nextInt(20) == 1) {
|
||||
float f = world.getBiome(blockposition).getAdjustedTemperature(blockposition);
|
||||
|
||||
if (f >= 0.15F) {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
if ((Integer) iblockdata.get(BlockCauldron.LEVEL) < 3) {
|
||||
this.a(world, blockposition, (IBlockData) iblockdata.a((IBlockState) BlockCauldron.LEVEL), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCauldron.LEVEL);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.UP ? EnumBlockFaceShape.BOWL : (enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
289
src/main/java/net/minecraft/server/BlockChest.java
Normal file
289
src/main/java/net/minecraft/server/BlockChest.java
Normal file
@@ -0,0 +1,289 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockChest extends BlockTileEntity implements IFluidSource, IFluidContainer {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
|
||||
public static final BlockStateEnum<BlockPropertyChestType> b = BlockProperties.ap;
|
||||
public static final BlockStateBoolean c = BlockProperties.y;
|
||||
protected static final VoxelShape o = Block.a(1.0D, 0.0D, 0.0D, 15.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape p = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 16.0D);
|
||||
protected static final VoxelShape q = Block.a(0.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape r = Block.a(1.0D, 0.0D, 1.0D, 16.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape s = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D);
|
||||
|
||||
protected BlockChest(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChest.FACING, EnumDirection.NORTH)).set(BlockChest.b, BlockPropertyChestType.SINGLE)).set(BlockChest.c, false));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if ((Boolean) iblockdata.get(BlockChest.c)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
if (iblockdata1.getBlock() == this && enumdirection.k().c()) {
|
||||
BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata1.get(BlockChest.b);
|
||||
|
||||
if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE && blockpropertychesttype != BlockPropertyChestType.SINGLE && iblockdata.get(BlockChest.FACING) == iblockdata1.get(BlockChest.FACING) && k(iblockdata1) == enumdirection.opposite()) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.b, blockpropertychesttype.a());
|
||||
}
|
||||
} else if (k(iblockdata) == enumdirection) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.b, BlockPropertyChestType.SINGLE);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE) {
|
||||
return BlockChest.s;
|
||||
} else {
|
||||
switch (k(iblockdata)) {
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockChest.o;
|
||||
case SOUTH:
|
||||
return BlockChest.p;
|
||||
case WEST:
|
||||
return BlockChest.q;
|
||||
case EAST:
|
||||
return BlockChest.r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EnumDirection k(IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING);
|
||||
|
||||
return iblockdata.get(BlockChest.b) == BlockPropertyChestType.LEFT ? enumdirection.e() : enumdirection.f();
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
BlockPropertyChestType blockpropertychesttype = BlockPropertyChestType.SINGLE;
|
||||
EnumDirection enumdirection = blockactioncontext.f().opposite();
|
||||
Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition());
|
||||
boolean flag = blockactioncontext.isSneaking();
|
||||
EnumDirection enumdirection1 = blockactioncontext.getClickedFace();
|
||||
|
||||
if (enumdirection1.k().c() && flag) {
|
||||
EnumDirection enumdirection2 = this.a(blockactioncontext, enumdirection1.opposite());
|
||||
|
||||
if (enumdirection2 != null && enumdirection2.k() != enumdirection1.k()) {
|
||||
enumdirection = enumdirection2;
|
||||
blockpropertychesttype = enumdirection2.f() == enumdirection1.opposite() ? BlockPropertyChestType.RIGHT : BlockPropertyChestType.LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockpropertychesttype == BlockPropertyChestType.SINGLE && !flag) {
|
||||
if (enumdirection == this.a(blockactioncontext, enumdirection.e())) {
|
||||
blockpropertychesttype = BlockPropertyChestType.LEFT;
|
||||
} else if (enumdirection == this.a(blockactioncontext, enumdirection.f())) {
|
||||
blockpropertychesttype = BlockPropertyChestType.RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
return (IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockChest.FACING, enumdirection)).set(BlockChest.b, blockpropertychesttype)).set(BlockChest.c, fluid.c() == FluidTypes.WATER);
|
||||
}
|
||||
|
||||
public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if ((Boolean) iblockdata.get(BlockChest.c)) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, false), 3);
|
||||
return FluidTypes.WATER;
|
||||
} else {
|
||||
return FluidTypes.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
return (Boolean) iblockdata.get(BlockChest.c) ? FluidTypes.WATER.a(false) : super.h(iblockdata);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) {
|
||||
return !(Boolean) iblockdata.get(BlockChest.c) && fluidtype == FluidTypes.WATER;
|
||||
}
|
||||
|
||||
public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) {
|
||||
if (!(Boolean) iblockdata.get(BlockChest.c) && fluid.c() == FluidTypes.WATER) {
|
||||
if (!generatoraccess.e()) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, true), 3);
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private EnumDirection a(BlockActionContext blockactioncontext, EnumDirection enumdirection) {
|
||||
IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().shift(enumdirection));
|
||||
|
||||
return iblockdata.getBlock() == this && iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE ? (EnumDirection) iblockdata.get(BlockChest.FACING) : null;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityChest) {
|
||||
((TileEntityChest) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof IInventory) {
|
||||
InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
ITileInventory itileinventory = this.getInventory(iblockdata, world, blockposition, false);
|
||||
|
||||
if (itileinventory != null) {
|
||||
entityhuman.openContainer(itileinventory);
|
||||
entityhuman.b(this.d());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Statistic<MinecraftKey> d() {
|
||||
return StatisticList.CUSTOM.b(StatisticList.OPEN_CHEST);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ITileInventory getInventory(IBlockData iblockdata, World world, BlockPosition blockposition, boolean flag) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (!(tileentity instanceof TileEntityChest)) {
|
||||
return null;
|
||||
} else if (!flag && this.a(world, blockposition)) {
|
||||
return null;
|
||||
} else {
|
||||
Object object = (TileEntityChest) tileentity;
|
||||
BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata.get(BlockChest.b);
|
||||
|
||||
if (blockpropertychesttype == BlockPropertyChestType.SINGLE) {
|
||||
return (ITileInventory) object;
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.shift(k(iblockdata));
|
||||
// Paper start - don't load chunks if the other side of the chest is in unloaded chunk
|
||||
final IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition1); // Paper
|
||||
if (iblockdata1 == null) {
|
||||
return null;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
BlockPropertyChestType blockpropertychesttype1 = (BlockPropertyChestType) iblockdata1.get(BlockChest.b);
|
||||
|
||||
if (blockpropertychesttype1 != BlockPropertyChestType.SINGLE && blockpropertychesttype != blockpropertychesttype1 && iblockdata1.get(BlockChest.FACING) == iblockdata.get(BlockChest.FACING)) {
|
||||
if (!flag && this.a(world, blockposition1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TileEntity tileentity1 = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity1 instanceof TileEntityChest) {
|
||||
Object object1 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? object : (ITileInventory) tileentity1;
|
||||
Object object2 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? (ITileInventory) tileentity1 : object;
|
||||
|
||||
object = new InventoryLargeChest(new ChatMessage("container.chestDouble", new Object[0]), (ITileInventory) object1, (ITileInventory) object2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ITileInventory) object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityChest();
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition) {
|
||||
return this.a((IBlockAccess) world, blockposition) || this.b(world, blockposition);
|
||||
}
|
||||
|
||||
private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockaccess.getType(blockposition.up()).isOccluding();
|
||||
}
|
||||
|
||||
private boolean b(World world, BlockPosition blockposition) {
|
||||
// Paper start - Option to disable chest cat detection
|
||||
if (world.paperConfig.disableChestCatDetection) {
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
List<EntityOcelot> list = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1)));
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityOcelot entityocelot = (EntityOcelot) iterator.next();
|
||||
|
||||
if (entityocelot.isSitting()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return Container.b((IInventory) this.getInventory(iblockdata, world, blockposition, false));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockChest.FACING, BlockChest.b, BlockChest.c);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
259
src/main/java/net/minecraft/server/BlockChorusFlower.java
Normal file
259
src/main/java/net/minecraft/server/BlockChorusFlower.java
Normal file
@@ -0,0 +1,259 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockChorusFlower extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.V;
|
||||
private final BlockChorusFruit b;
|
||||
|
||||
protected BlockChorusFlower(BlockChorusFruit blockchorusfruit, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.b = blockchorusfruit;
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChorusFlower.AGE, 0));
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.isEmpty(blockposition1) && blockposition1.getY() < 256) {
|
||||
int i = (Integer) iblockdata.get(BlockChorusFlower.AGE);
|
||||
|
||||
if (i < 5) {
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
IBlockData iblockdata1 = world.getType(blockposition.down());
|
||||
Block block = iblockdata1.getBlock();
|
||||
int j;
|
||||
|
||||
if (block == Blocks.END_STONE) {
|
||||
flag = true;
|
||||
} else if (block == this.b) {
|
||||
j = 1;
|
||||
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
Block block1 = world.getType(blockposition.down(j + 1)).getBlock();
|
||||
|
||||
if (block1 != this.b) {
|
||||
if (block1 == Blocks.END_STONE) {
|
||||
flag1 = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
if (j < 2 || j <= random.nextInt(flag1 ? 5 : 4)) {
|
||||
flag = true;
|
||||
}
|
||||
} else if (iblockdata1.isAir()) {
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (flag && a((IWorldReader) world, blockposition1, (EnumDirection) null) && world.isEmpty(blockposition.up(2))) {
|
||||
// CraftBukkit start - add event
|
||||
if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i)), 2)) {
|
||||
world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2);
|
||||
this.b(world, blockposition1, i);
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else if (i < 4) {
|
||||
j = random.nextInt(4);
|
||||
if (flag1) {
|
||||
++j;
|
||||
}
|
||||
|
||||
boolean flag2 = false;
|
||||
|
||||
for (int l = 0; l < j; ++l) {
|
||||
EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random);
|
||||
BlockPosition blockposition2 = blockposition.shift(enumdirection);
|
||||
|
||||
if (world.isEmpty(blockposition2) && world.isEmpty(blockposition2.down()) && a((IWorldReader) world, blockposition2, enumdirection.opposite())) {
|
||||
// CraftBukkit start - add event
|
||||
if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i + 1)), 2)) {
|
||||
this.b(world, blockposition2, i + 1);
|
||||
flag2 = true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2);
|
||||
} else {
|
||||
// CraftBukkit - add event
|
||||
if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) {
|
||||
this.a(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
} else {
|
||||
// CraftBukkit - add event
|
||||
if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) {
|
||||
this.a(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void b(World world, BlockPosition blockposition, int i) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, i), 2);
|
||||
world.triggerEffect(1033, blockposition, 0);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, 5), 2);
|
||||
world.triggerEffect(1034, blockposition, 0);
|
||||
}
|
||||
|
||||
private static boolean a(IWorldReader iworldreader, BlockPosition blockposition, @Nullable EnumDirection enumdirection) {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
EnumDirection enumdirection1;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
enumdirection1 = (EnumDirection) iterator.next();
|
||||
} while (enumdirection1 == enumdirection || iworldreader.isEmpty(blockposition.shift(enumdirection1)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection != EnumDirection.UP && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.down());
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
if (block != this.b && block != Blocks.END_STONE) {
|
||||
if (!iblockdata1.isAir()) {
|
||||
return false;
|
||||
} else {
|
||||
boolean flag = false;
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection = (EnumDirection) iterator.next();
|
||||
IBlockData iblockdata2 = iworldreader.getType(blockposition.shift(enumdirection));
|
||||
|
||||
if (iblockdata2.getBlock() == this.b) {
|
||||
if (flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
} else if (!iblockdata2.isAir()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack);
|
||||
a(world, blockposition, new ItemStack(this));
|
||||
}
|
||||
|
||||
protected ItemStack t(IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockChorusFlower.AGE);
|
||||
}
|
||||
|
||||
public static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, int i) {
|
||||
generatoraccess.setTypeAndData(blockposition, ((BlockChorusFruit) Blocks.CHORUS_PLANT).a((IBlockAccess) generatoraccess, blockposition), 2);
|
||||
a(generatoraccess, blockposition, random, blockposition, i, 0);
|
||||
}
|
||||
|
||||
private static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, BlockPosition blockposition1, int i, int j) {
|
||||
BlockChorusFruit blockchorusfruit = (BlockChorusFruit) Blocks.CHORUS_PLANT;
|
||||
int k = random.nextInt(4) + 1;
|
||||
|
||||
if (j == 0) {
|
||||
++k;
|
||||
}
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
BlockPosition blockposition2 = blockposition.up(l + 1);
|
||||
|
||||
if (!a((IWorldReader) generatoraccess, blockposition2, (EnumDirection) null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
generatoraccess.setTypeAndData(blockposition2, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2), 2);
|
||||
generatoraccess.setTypeAndData(blockposition2.down(), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2.down()), 2);
|
||||
}
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
if (j < 4) {
|
||||
int i1 = random.nextInt(4);
|
||||
|
||||
if (j == 0) {
|
||||
++i1;
|
||||
}
|
||||
|
||||
for (int j1 = 0; j1 < i1; ++j1) {
|
||||
EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random);
|
||||
BlockPosition blockposition3 = blockposition.up(k).shift(enumdirection);
|
||||
|
||||
if (Math.abs(blockposition3.getX() - blockposition1.getX()) < i && Math.abs(blockposition3.getZ() - blockposition1.getZ()) < i && generatoraccess.isEmpty(blockposition3) && generatoraccess.isEmpty(blockposition3.down()) && a((IWorldReader) generatoraccess, blockposition3, enumdirection.opposite())) {
|
||||
flag = true;
|
||||
generatoraccess.setTypeAndData(blockposition3, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3), 2);
|
||||
generatoraccess.setTypeAndData(blockposition3.shift(enumdirection.opposite()), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3.shift(enumdirection.opposite())), 2);
|
||||
a(generatoraccess, blockposition3, random, blockposition1, i, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
generatoraccess.setTypeAndData(blockposition.up(k), (IBlockData) Blocks.CHORUS_FLOWER.getBlockData().set(BlockChorusFlower.AGE, 5), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
125
src/main/java/net/minecraft/server/BlockCocoa.java
Normal file
125
src/main/java/net/minecraft/server/BlockCocoa.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCocoa extends BlockFacingHorizontal implements IBlockFragilePlantElement {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.T;
|
||||
protected static final VoxelShape[] b = new VoxelShape[] { Block.a(11.0D, 7.0D, 6.0D, 15.0D, 12.0D, 10.0D), Block.a(9.0D, 5.0D, 5.0D, 15.0D, 12.0D, 11.0D), Block.a(7.0D, 3.0D, 4.0D, 15.0D, 12.0D, 12.0D)};
|
||||
protected static final VoxelShape[] c = new VoxelShape[] { Block.a(1.0D, 7.0D, 6.0D, 5.0D, 12.0D, 10.0D), Block.a(1.0D, 5.0D, 5.0D, 7.0D, 12.0D, 11.0D), Block.a(1.0D, 3.0D, 4.0D, 9.0D, 12.0D, 12.0D)};
|
||||
protected static final VoxelShape[] o = new VoxelShape[] { Block.a(6.0D, 7.0D, 1.0D, 10.0D, 12.0D, 5.0D), Block.a(5.0D, 5.0D, 1.0D, 11.0D, 12.0D, 7.0D), Block.a(4.0D, 3.0D, 1.0D, 12.0D, 12.0D, 9.0D)};
|
||||
protected static final VoxelShape[] p = new VoxelShape[] { Block.a(6.0D, 7.0D, 11.0D, 10.0D, 12.0D, 15.0D), Block.a(5.0D, 5.0D, 9.0D, 11.0D, 12.0D, 15.0D), Block.a(4.0D, 3.0D, 7.0D, 12.0D, 12.0D, 15.0D)};
|
||||
|
||||
public BlockCocoa(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCocoa.FACING, EnumDirection.NORTH)).set(BlockCocoa.AGE, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.cocoaModifier) * 5)) == 0) { // Spigot
|
||||
int i = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
|
||||
if (i < 2) {
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, i + 1), 2); // CraftBukkkit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
Block block = iworldreader.getType(blockposition.shift((EnumDirection) iblockdata.get(BlockCocoa.FACING))).getBlock();
|
||||
|
||||
return block.a(TagsBlock.JUNGLE_LOGS);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
int i = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
|
||||
switch ((EnumDirection) iblockdata.get(BlockCocoa.FACING)) {
|
||||
case SOUTH:
|
||||
return BlockCocoa.p[i];
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockCocoa.o[i];
|
||||
case WEST:
|
||||
return BlockCocoa.c[i];
|
||||
case EAST:
|
||||
return BlockCocoa.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
IBlockData iblockdata = this.getBlockData();
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
EnumDirection[] aenumdirection = blockactioncontext.e();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (enumdirection.k().c()) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockCocoa.FACING, enumdirection);
|
||||
if (iblockdata.canPlace(world, blockposition)) {
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == iblockdata.get(BlockCocoa.FACING) && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
int j = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
byte b0 = 1;
|
||||
|
||||
if (j >= 2) {
|
||||
b0 = 3;
|
||||
}
|
||||
|
||||
for (int k = 0; k < b0; ++k) {
|
||||
a(world, blockposition, new ItemStack(Items.COCOA_BEANS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(Items.COCOA_BEANS);
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return (Integer) iblockdata.get(BlockCocoa.AGE) < 2;
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, (Integer) iblockdata.get(BlockCocoa.AGE) + 1), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCocoa.FACING, BlockCocoa.AGE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
229
src/main/java/net/minecraft/server/BlockCommand.java
Normal file
229
src/main/java/net/minecraft/server/BlockCommand.java
Normal file
@@ -0,0 +1,229 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockCommand extends BlockTileEntity {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
public static final BlockStateDirection a = BlockDirectional.FACING;
|
||||
public static final BlockStateBoolean b = BlockProperties.b;
|
||||
|
||||
public BlockCommand(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCommand.a, EnumDirection.NORTH)).set(BlockCommand.b, false));
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
TileEntityCommand tileentitycommand = new TileEntityCommand();
|
||||
|
||||
tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK);
|
||||
return tileentitycommand;
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
boolean flag1 = tileentitycommand.d();
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = flag1 ? 15 : 0;
|
||||
int current = flag ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
flag = eventRedstone.getNewCurrent() > 0;
|
||||
// CraftBukkit end
|
||||
|
||||
tileentitycommand.a(flag);
|
||||
if (!flag1 && !tileentitycommand.e() && tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) {
|
||||
if (flag) {
|
||||
tileentitycommand.h();
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
boolean flag = !UtilColor.b(commandblocklistenerabstract.getCommand());
|
||||
TileEntityCommand.Type tileentitycommand_type = tileentitycommand.j();
|
||||
boolean flag1 = tileentitycommand.f();
|
||||
|
||||
if (tileentitycommand_type == TileEntityCommand.Type.AUTO) {
|
||||
tileentitycommand.h();
|
||||
if (flag1) {
|
||||
this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
|
||||
if (tileentitycommand.d() || tileentitycommand.e()) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
} else if (tileentitycommand_type == TileEntityCommand.Type.REDSTONE) {
|
||||
if (flag1) {
|
||||
this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void a(IBlockData iblockdata, World world, BlockPosition blockposition, CommandBlockListenerAbstract commandblocklistenerabstract, boolean flag) {
|
||||
if (flag) {
|
||||
commandblocklistenerabstract.a(world);
|
||||
} else {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
|
||||
a(world, blockposition, (EnumDirection) iblockdata.get(BlockCommand.a));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand && entityhuman.isCreativeAndOp()) {
|
||||
entityhuman.a((TileEntityCommand) tileentity);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
return tileentity instanceof TileEntityCommand ? ((TileEntityCommand) tileentity).getCommandBlock().i() : 0;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
|
||||
if (itemstack.hasName()) {
|
||||
commandblocklistenerabstract.setName(itemstack.getName());
|
||||
}
|
||||
|
||||
if (!world.isClientSide) {
|
||||
if (itemstack.b("BlockEntityTag") == null) {
|
||||
commandblocklistenerabstract.a(world.getGameRules().getBoolean("sendCommandFeedback"));
|
||||
tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK);
|
||||
}
|
||||
|
||||
if (tileentitycommand.j() == TileEntityCommand.Type.SEQUENCE) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
|
||||
tileentitycommand.a(flag);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockCommand.a, enumblockrotation.a((EnumDirection) iblockdata.get(BlockCommand.a)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockCommand.a)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCommand.a, BlockCommand.b);
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockCommand.a, blockactioncontext.d().opposite());
|
||||
}
|
||||
|
||||
private static void a(World world, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition);
|
||||
GameRules gamerules = world.getGameRules();
|
||||
|
||||
IBlockData iblockdata;
|
||||
int i;
|
||||
|
||||
for (i = gamerules.c("maxCommandChainLength"); i-- > 0; enumdirection = (EnumDirection) iblockdata.get(BlockCommand.a)) {
|
||||
blockposition_mutableblockposition.c(enumdirection);
|
||||
iblockdata = world.getType(blockposition_mutableblockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block != Blocks.CHAIN_COMMAND_BLOCK) {
|
||||
break;
|
||||
}
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(blockposition_mutableblockposition);
|
||||
|
||||
if (!(tileentity instanceof TileEntityCommand)) {
|
||||
break;
|
||||
}
|
||||
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
|
||||
if (tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tileentitycommand.d() || tileentitycommand.e()) {
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
|
||||
if (tileentitycommand.h()) {
|
||||
if (!commandblocklistenerabstract.a(world)) {
|
||||
break;
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition_mutableblockposition, block);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i <= 0) {
|
||||
int j = Math.max(gamerules.c("maxCommandChainLength"), 0);
|
||||
|
||||
BlockCommand.c.warn("Command Block chain tried to execute more than {} steps!", j);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
92
src/main/java/net/minecraft/server/BlockConcretePowder.java
Normal file
92
src/main/java/net/minecraft/server/BlockConcretePowder.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
import org.bukkit.event.block.BlockFormEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockConcretePowder extends BlockFalling {
|
||||
|
||||
private final IBlockData a;
|
||||
|
||||
public BlockConcretePowder(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block.getBlockData();
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {
|
||||
if (x(iblockdata1)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, this.a, 3); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
|
||||
// CraftBukkit start
|
||||
if (!x(world.getType(blockposition)) && !a((IBlockAccess) world, blockposition)) {
|
||||
return super.getPlacedState(blockactioncontext);
|
||||
}
|
||||
|
||||
// TODO: An event factory call for methods like this
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(world, blockposition);
|
||||
blockState.setData(this.a);
|
||||
|
||||
BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
|
||||
world.getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
|
||||
return super.getPlacedState(blockactioncontext);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
private static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
boolean flag = false;
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition);
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
|
||||
|
||||
if (enumdirection != EnumDirection.DOWN || x(iblockdata)) {
|
||||
blockposition_mutableblockposition.g(blockposition).c(enumdirection);
|
||||
iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
|
||||
if (x(iblockdata) && !Block.a(iblockdata.getCollisionShape(iblockaccess, blockposition), enumdirection.opposite())) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private static boolean x(IBlockData iblockdata) {
|
||||
return iblockdata.s().a(TagsFluid.WATER);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
if (a((IBlockAccess) generatoraccess, blockposition)) {
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
|
||||
blockState.setData(this.a);
|
||||
|
||||
BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
|
||||
generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
67
src/main/java/net/minecraft/server/BlockCoral.java
Normal file
67
src/main/java/net/minecraft/server/BlockCoral.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockCoral extends Block {
|
||||
|
||||
private final Block a;
|
||||
|
||||
public BlockCoral(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!this.a((IBlockAccess) world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, this.a.getBlockData(), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!this.a((IBlockAccess) generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 60 + generatoraccess.m().nextInt(40));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
protected boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
Fluid fluid = iblockaccess.getFluid(blockposition.shift(enumdirection));
|
||||
|
||||
if (fluid.a(TagsFluid.WATER)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
if (!this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition())) {
|
||||
blockactioncontext.getWorld().getBlockTickList().a(blockactioncontext.getClickPosition(), this, 60 + blockactioncontext.getWorld().m().nextInt(40));
|
||||
}
|
||||
|
||||
return this.getBlockData();
|
||||
}
|
||||
|
||||
protected boolean X_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return this.a;
|
||||
}
|
||||
}
|
||||
42
src/main/java/net/minecraft/server/BlockCoralFan.java
Normal file
42
src/main/java/net/minecraft/server/BlockCoralFan.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralFan extends BlockCoralFanAbstract {
|
||||
|
||||
private final Block a;
|
||||
|
||||
protected BlockCoralFan(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData().set(BlockCoralFan.b, false)).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.a.getBlockData().set(BlockCoralFan.b, false), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
if ((Boolean) iblockdata.get(BlockCoralFan.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/main/java/net/minecraft/server/BlockCoralFanWall.java
Normal file
42
src/main/java/net/minecraft/server/BlockCoralFanWall.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralFanWall extends BlockCoralFanWallAbstract {
|
||||
|
||||
private final Block c;
|
||||
|
||||
protected BlockCoralFanWall(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.c = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralFanWall.b, false).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a))).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) this.c.getBlockData().set(BlockCoralFanWall.b, false)).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a)), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection.opposite() == iblockdata.get(BlockCoralFanWall.a) && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
if ((Boolean) iblockdata.get(BlockCoralFanWall.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/main/java/net/minecraft/server/BlockCoralPlant.java
Normal file
47
src/main/java/net/minecraft/server/BlockCoralPlant.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralPlant extends BlockCoralBase {
|
||||
|
||||
private final Block c;
|
||||
protected static final VoxelShape a = Block.a(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
|
||||
|
||||
protected BlockCoralPlant(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.c = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralPlant.b, false)).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.c.getBlockData().set(BlockCoralPlant.b, false), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
if ((Boolean) iblockdata.get(BlockCoralPlant.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCoralPlant.a;
|
||||
}
|
||||
}
|
||||
186
src/main/java/net/minecraft/server/BlockCrops.java
Normal file
186
src/main/java/net/minecraft/server/BlockCrops.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCrops extends BlockPlant implements IBlockFragilePlantElement {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.W;
|
||||
private static final VoxelShape[] a = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D)};
|
||||
|
||||
protected BlockCrops(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(this.d(), 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCrops.a[(Integer) iblockdata.get(this.d())];
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.getBlock() == Blocks.FARMLAND;
|
||||
}
|
||||
|
||||
public BlockStateInteger d() {
|
||||
return BlockCrops.AGE;
|
||||
}
|
||||
|
||||
public int e() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
protected int k(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(this.d());
|
||||
}
|
||||
|
||||
public IBlockData setAge(int i) {
|
||||
return (IBlockData) this.getBlockData().set(this.d(), i);
|
||||
}
|
||||
|
||||
public boolean w(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(this.d()) >= this.e();
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
super.a(iblockdata, world, blockposition, random);
|
||||
if (world.isLightLevel(blockposition.up(), 9)) { // Paper
|
||||
int i = this.k(iblockdata);
|
||||
|
||||
if (i < this.e()) {
|
||||
float f = a((Block) this, (IBlockAccess) world, blockposition);
|
||||
|
||||
// Spigot start
|
||||
int modifier;
|
||||
if (this == Blocks.BEETROOTS) {
|
||||
modifier = world.spigotConfig.beetrootModifier;
|
||||
} else if (this == Blocks.CARROTS) {
|
||||
modifier = world.spigotConfig.carrotModifier;
|
||||
} else if (this == Blocks.POTATOES) {
|
||||
modifier = world.spigotConfig.potatoModifier;
|
||||
} else {
|
||||
modifier = world.spigotConfig.wheatModifier;
|
||||
}
|
||||
|
||||
if (random.nextInt((int) ((100.0F / modifier) * (25.0F / f)) + 1) == 0) {
|
||||
// Spigot end
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i + 1), 2); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
int i = this.k(iblockdata) + this.a(world);
|
||||
int j = this.e();
|
||||
|
||||
if (i > j) {
|
||||
i = j;
|
||||
}
|
||||
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
protected int a(World world) {
|
||||
return MathHelper.nextInt(world.random, 2, 5);
|
||||
}
|
||||
|
||||
protected static float a(Block block, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
float f = 1.0F;
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
|
||||
for (int i = -1; i <= 1; ++i) {
|
||||
for (int j = -1; j <= 1; ++j) {
|
||||
float f1 = 0.0F;
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition1.a(i, 0, j));
|
||||
|
||||
if (iblockdata.getBlock() == Blocks.FARMLAND) {
|
||||
f1 = 1.0F;
|
||||
if ((Integer) iblockdata.get(BlockSoil.MOISTURE) > 0) {
|
||||
f1 = 3.0F;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0 || j != 0) {
|
||||
f1 /= 4.0F;
|
||||
}
|
||||
|
||||
f += f1;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition2 = blockposition.north();
|
||||
BlockPosition blockposition3 = blockposition.south();
|
||||
BlockPosition blockposition4 = blockposition.west();
|
||||
BlockPosition blockposition5 = blockposition.east();
|
||||
boolean flag = block == iblockaccess.getType(blockposition4).getBlock() || block == iblockaccess.getType(blockposition5).getBlock();
|
||||
boolean flag1 = block == iblockaccess.getType(blockposition2).getBlock() || block == iblockaccess.getType(blockposition3).getBlock();
|
||||
|
||||
if (flag && flag1) {
|
||||
f /= 2.0F;
|
||||
} else {
|
||||
boolean flag2 = block == iblockaccess.getType(blockposition4.north()).getBlock() || block == iblockaccess.getType(blockposition5.north()).getBlock() || block == iblockaccess.getType(blockposition5.south()).getBlock() || block == iblockaccess.getType(blockposition4.south()).getBlock();
|
||||
|
||||
if (flag2) {
|
||||
f /= 2.0F;
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return (iworldreader.getLightLevel(blockposition, 0) >= 8 || iworldreader.e(blockposition)) && super.canPlace(iblockdata, iworldreader, blockposition);
|
||||
}
|
||||
|
||||
protected IMaterial f() {
|
||||
return Items.WHEAT_SEEDS;
|
||||
}
|
||||
|
||||
protected IMaterial g() {
|
||||
return Items.WHEAT;
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, 0);
|
||||
if (!world.isClientSide) {
|
||||
int j = this.k(iblockdata);
|
||||
|
||||
if (j >= this.e()) {
|
||||
int k = 3 + i;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
if (world.random.nextInt(2 * this.e()) <= j) {
|
||||
a(world, blockposition, new ItemStack(this.f()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return this.w(iblockdata) ? this.g() : this.f();
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(this.f());
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return !this.w(iblockdata);
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCrops.AGE);
|
||||
}
|
||||
}
|
||||
25
src/main/java/net/minecraft/server/BlockData.java
Normal file
25
src/main/java/net/minecraft/server/BlockData.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
|
||||
public class BlockData extends BlockDataAbstract<Block, IBlockData> implements IBlockData {
|
||||
|
||||
public BlockData(Block block, ImmutableMap<IBlockState<?>, Comparable<?>> immutablemap) {
|
||||
super(block, immutablemap);
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return (Block) this.e_;
|
||||
}
|
||||
|
||||
// Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
|
||||
private CraftBlockData cachedCraftBlockData;
|
||||
|
||||
@Override
|
||||
public CraftBlockData createCraftBlockData() {
|
||||
if(cachedCraftBlockData == null) cachedCraftBlockData = CraftBlockData.createData(this);
|
||||
return (CraftBlockData) cachedCraftBlockData.clone();
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockDaylightDetector extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateInteger POWER = BlockProperties.al;
|
||||
public static final BlockStateBoolean b = BlockProperties.m;
|
||||
protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D);
|
||||
|
||||
public BlockDaylightDetector(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDaylightDetector.POWER, 0)).set(BlockDaylightDetector.b, false));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDaylightDetector.c;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Integer) iblockdata.get(BlockDaylightDetector.POWER);
|
||||
}
|
||||
|
||||
public static void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
if (world.worldProvider.g()) {
|
||||
int i = world.getBrightness(EnumSkyBlock.SKY, blockposition) - world.c();
|
||||
float f = world.c(1.0F);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDaylightDetector.b);
|
||||
|
||||
if (flag) {
|
||||
i = 15 - i;
|
||||
} else if (i > 0) {
|
||||
float f1 = f < 3.1415927F ? 0.0F : 6.2831855F;
|
||||
|
||||
f += (f1 - f) * 0.2F;
|
||||
i = Math.round((float) i * MathHelper.cos(f));
|
||||
}
|
||||
|
||||
i = MathHelper.clamp(i, 0, 15);
|
||||
if ((Integer) iblockdata.get(BlockDaylightDetector.POWER) != i) {
|
||||
i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, blockposition, ((Integer) iblockdata.get(POWER)), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDaylightDetector.POWER, i), 3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (entityhuman.dy()) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
IBlockData iblockdata1 = (IBlockData) iblockdata.a((IBlockState) BlockDaylightDetector.b);
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata1, 4);
|
||||
b(iblockdata1, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return super.interact(iblockdata, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityLightDetector();
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDaylightDetector.POWER, BlockDaylightDetector.b);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
207
src/main/java/net/minecraft/server/BlockDiodeAbstract.java
Normal file
207
src/main/java/net/minecraft/server/BlockDiodeAbstract.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public abstract class BlockDiodeAbstract extends BlockFacingHorizontal {
|
||||
|
||||
protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
public static final BlockStateBoolean c = BlockProperties.t;
|
||||
|
||||
protected BlockDiodeAbstract(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDiodeAbstract.b;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).q();
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!this.a((IWorldReader) world, blockposition, iblockdata)) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c);
|
||||
boolean flag1 = this.a(world, blockposition, iblockdata);
|
||||
|
||||
if (flag && !flag1) {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, false), 2);
|
||||
} else if (!flag) {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, true), 2);
|
||||
if (!flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.k(iblockdata), TickListPriority.HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return iblockdata.a(iblockaccess, blockposition, enumdirection);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return !(Boolean) iblockdata.get(BlockDiodeAbstract.c) ? 0 : (iblockdata.get(BlockDiodeAbstract.FACING) == enumdirection ? this.b(iblockaccess, blockposition, iblockdata) : 0);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (iblockdata.canPlace(world, blockposition)) {
|
||||
this.c(world, blockposition, iblockdata);
|
||||
} else {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setAir(blockposition);
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
world.applyPhysics(blockposition.shift(enumdirection), this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void c(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if (!this.a((IWorldReader) world, blockposition, iblockdata)) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c);
|
||||
boolean flag1 = this.a(world, blockposition, iblockdata);
|
||||
|
||||
if (flag != flag1 && !world.getBlockTickList().b(blockposition, this)) {
|
||||
TickListPriority ticklistpriority = TickListPriority.HIGH;
|
||||
|
||||
if (this.c((IBlockAccess) world, blockposition, iblockdata)) {
|
||||
ticklistpriority = TickListPriority.EXTREMELY_HIGH;
|
||||
} else if (flag) {
|
||||
ticklistpriority = TickListPriority.VERY_HIGH;
|
||||
}
|
||||
|
||||
world.getBlockTickList().a(blockposition, this, this.k(iblockdata), ticklistpriority);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return this.b(world, blockposition, iblockdata) > 0;
|
||||
}
|
||||
|
||||
protected int b(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
int i = world.getBlockFacePower(blockposition1, enumdirection);
|
||||
|
||||
if (i >= 15) {
|
||||
return i;
|
||||
} else {
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
return Math.max(i, iblockdata1.getBlock() == Blocks.REDSTONE_WIRE ? (Integer) iblockdata1.get(BlockRedstoneWire.POWER) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected int b(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
EnumDirection enumdirection1 = enumdirection.e();
|
||||
EnumDirection enumdirection2 = enumdirection.f();
|
||||
|
||||
return Math.max(this.a(iworldreader, blockposition.shift(enumdirection1), enumdirection1), this.a(iworldreader, blockposition.shift(enumdirection2), enumdirection2));
|
||||
}
|
||||
|
||||
protected int a(IWorldReader iworldreader, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return this.w(iblockdata) ? (block == Blocks.REDSTONE_BLOCK ? 15 : (block == Blocks.REDSTONE_WIRE ? (Integer) iblockdata.get(BlockRedstoneWire.POWER) : iworldreader.a(blockposition, enumdirection))) : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockDiodeAbstract.FACING, blockactioncontext.f().opposite());
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.d(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
this.a(world, blockposition);
|
||||
this.d(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(World world, BlockPosition blockposition) {}
|
||||
|
||||
protected void d(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite());
|
||||
|
||||
world.a(blockposition1, (Block) this, blockposition);
|
||||
world.a(blockposition1, (Block) this, enumdirection);
|
||||
}
|
||||
|
||||
protected boolean w(IBlockData iblockdata) {
|
||||
return iblockdata.isPowerSource();
|
||||
}
|
||||
|
||||
protected int b(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
public static boolean isDiode(IBlockData iblockdata) {
|
||||
return iblockdata.getBlock() instanceof BlockDiodeAbstract;
|
||||
}
|
||||
|
||||
public boolean c(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING)).opposite();
|
||||
IBlockData iblockdata1 = iblockaccess.getType(blockposition.shift(enumdirection));
|
||||
|
||||
return isDiode(iblockdata1) && iblockdata1.get(BlockDiodeAbstract.FACING) != enumdirection;
|
||||
}
|
||||
|
||||
protected abstract int k(IBlockData iblockdata);
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public abstract class BlockDirtSnowSpreadable extends BlockDirtSnow {
|
||||
|
||||
protected BlockDirtSnowSpreadable(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
private static boolean a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
return iworldreader.getLightLevel(blockposition1) >= 4 || iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K();
|
||||
}
|
||||
|
||||
private static boolean b(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
return iworldreader.getLightLevel(blockposition1) >= 4 && iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K() && !iworldreader.getFluid(blockposition1).a(TagsFluid.WATER);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (this instanceof BlockGrass && world.paperConfig.grassUpdateRate != 1 && (world.paperConfig.grassUpdateRate < 1 || (MinecraftServer.currentTick + blockposition.hashCode()) % world.paperConfig.grassUpdateRate != 0)) { return; } // Paper
|
||||
if (!world.isClientSide) {
|
||||
if (!a((IWorldReader) world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.DIRT.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData());
|
||||
} else {
|
||||
if (world.getLightLevel(blockposition.up()) >= 9) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
BlockPosition blockposition1 = blockposition.a(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
|
||||
|
||||
if (!world.p(blockposition1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == Blocks.DIRT && b(world, blockposition1)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData()); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
src/main/java/net/minecraft/server/BlockDispenser.java
Normal file
155
src/main/java/net/minecraft/server/BlockDispenser.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockDispenser extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockDirectional.FACING;
|
||||
public static final BlockStateBoolean TRIGGERED = BlockProperties.w;
|
||||
public static final Map<Item, IDispenseBehavior> REGISTRY = (Map) SystemUtils.a((new Object2ObjectOpenHashMap()), (object2objectopenhashmap) -> { // CraftBukkit - decompile error
|
||||
object2objectopenhashmap.defaultReturnValue(new DispenseBehaviorItem());
|
||||
});
|
||||
public static boolean eventFired = false; // CraftBukkit
|
||||
|
||||
public static void a(IMaterial imaterial, IDispenseBehavior idispensebehavior) {
|
||||
BlockDispenser.REGISTRY.put(imaterial.getItem(), idispensebehavior);
|
||||
}
|
||||
|
||||
protected BlockDispenser(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDispenser.FACING, EnumDirection.NORTH)).set(BlockDispenser.TRIGGERED, false));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
entityhuman.openContainer((TileEntityDispenser) tileentity);
|
||||
if (tileentity instanceof TileEntityDropper) {
|
||||
entityhuman.a(StatisticList.INSPECT_DROPPER);
|
||||
} else {
|
||||
entityhuman.a(StatisticList.INSPECT_DISPENSER);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispense(World world, BlockPosition blockposition) {
|
||||
SourceBlock sourceblock = new SourceBlock(world, blockposition);
|
||||
TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity();
|
||||
int i = tileentitydispenser.p();
|
||||
|
||||
if (i < 0) {
|
||||
world.triggerEffect(1001, blockposition, 0);
|
||||
} else {
|
||||
ItemStack itemstack = tileentitydispenser.getItem(i);
|
||||
IDispenseBehavior idispensebehavior = this.a(itemstack);
|
||||
|
||||
if (idispensebehavior != IDispenseBehavior.NONE) {
|
||||
eventFired = false; // CraftBukkit - reset event status
|
||||
tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected IDispenseBehavior a(ItemStack itemstack) {
|
||||
return (IDispenseBehavior) BlockDispenser.REGISTRY.get(itemstack.getItem());
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up());
|
||||
boolean flag1 = (Boolean) iblockdata.get(BlockDispenser.TRIGGERED);
|
||||
|
||||
if (flag && !flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, true), 4);
|
||||
} else if (!flag && flag1) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, false), 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide) {
|
||||
this.dispense(world, blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityDispenser();
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockDispenser.FACING, blockactioncontext.d().opposite());
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
((TileEntityDispenser) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
InventoryUtils.dropInventory(world, blockposition, (TileEntityDispenser) tileentity);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public static IPosition a(ISourceBlock isourceblock) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + 0.7D * (double) enumdirection.getAdjacentX();
|
||||
double d1 = isourceblock.getY() + 0.7D * (double) enumdirection.getAdjacentY();
|
||||
double d2 = isourceblock.getZ() + 0.7D * (double) enumdirection.getAdjacentZ();
|
||||
|
||||
return new Position(d0, d1, d2);
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return Container.a(world.getTileEntity(blockposition));
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockDispenser.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDispenser.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDispenser.FACING)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDispenser.FACING, BlockDispenser.TRIGGERED);
|
||||
}
|
||||
}
|
||||
232
src/main/java/net/minecraft/server/BlockDoor.java
Normal file
232
src/main/java/net/minecraft/server/BlockDoor.java
Normal file
@@ -0,0 +1,232 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockDoor extends Block {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
|
||||
public static final BlockStateBoolean OPEN = BlockProperties.r;
|
||||
public static final BlockStateEnum<BlockPropertyDoorHinge> HINGE = BlockProperties.ar;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
public static final BlockStateEnum<BlockPropertyDoubleBlockHalf> HALF = BlockProperties.P;
|
||||
protected static final VoxelShape q = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D);
|
||||
protected static final VoxelShape r = Block.a(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape s = Block.a(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape t = Block.a(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D);
|
||||
|
||||
protected BlockDoor(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDoor.FACING, EnumDirection.NORTH)).set(BlockDoor.OPEN, false)).set(BlockDoor.HINGE, BlockPropertyDoorHinge.LEFT)).set(BlockDoor.POWERED, false)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDoor.FACING);
|
||||
boolean flag = !(Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
boolean flag1 = iblockdata.get(BlockDoor.HINGE) == BlockPropertyDoorHinge.RIGHT;
|
||||
|
||||
switch (enumdirection) {
|
||||
case EAST:
|
||||
default:
|
||||
return flag ? BlockDoor.t : (flag1 ? BlockDoor.r : BlockDoor.q);
|
||||
case SOUTH:
|
||||
return flag ? BlockDoor.q : (flag1 ? BlockDoor.t : BlockDoor.s);
|
||||
case WEST:
|
||||
return flag ? BlockDoor.s : (flag1 ? BlockDoor.q : BlockDoor.r);
|
||||
case NORTH:
|
||||
return flag ? BlockDoor.r : (flag1 ? BlockDoor.s : BlockDoor.t);
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF);
|
||||
|
||||
return enumdirection.k() == EnumDirection.EnumAxis.Y && blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER == (enumdirection == EnumDirection.UP) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf ? (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockDoor.FACING, iblockdata1.get(BlockDoor.FACING))).set(BlockDoor.OPEN, iblockdata1.get(BlockDoor.OPEN))).set(BlockDoor.HINGE, iblockdata1.get(BlockDoor.HINGE))).set(BlockDoor.POWERED, iblockdata1.get(BlockDoor.POWERED)) : Blocks.AIR.getBlockData()) : (blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER && enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1));
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack);
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF);
|
||||
boolean flag = blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
BlockPosition blockposition1 = flag ? blockposition.up() : blockposition.down();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf) {
|
||||
world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35);
|
||||
world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1));
|
||||
if (!world.isClientSide && !entityhuman.u()) {
|
||||
if (flag) {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
} else {
|
||||
iblockdata1.a(world, blockposition1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.a(world, blockposition, iblockdata, entityhuman);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
switch (pathmode) {
|
||||
case LAND:
|
||||
return (Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
case WATER:
|
||||
return false;
|
||||
case AIR:
|
||||
return (Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private int d() {
|
||||
return this.material == Material.ORE ? 1011 : 1012;
|
||||
}
|
||||
|
||||
private int e() {
|
||||
return this.material == Material.ORE ? 1005 : 1006;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
|
||||
if (blockposition.getY() < 255 && blockactioncontext.getWorld().getType(blockposition.up()).a(blockactioncontext)) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up());
|
||||
|
||||
return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockDoor.FACING, blockactioncontext.f())).set(BlockDoor.HINGE, this.b(blockactioncontext))).set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
world.setTypeAndData(blockposition.up(), (IBlockData) iblockdata.set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.UPPER), 3);
|
||||
}
|
||||
|
||||
private BlockPropertyDoorHinge b(BlockActionContext blockactioncontext) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
EnumDirection enumdirection = blockactioncontext.f();
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
EnumDirection enumdirection1 = enumdirection.f();
|
||||
IBlockData iblockdata = world.getType(blockposition.shift(enumdirection1));
|
||||
IBlockData iblockdata1 = world.getType(blockposition1.shift(enumdirection1));
|
||||
EnumDirection enumdirection2 = enumdirection.e();
|
||||
IBlockData iblockdata2 = world.getType(blockposition.shift(enumdirection2));
|
||||
IBlockData iblockdata3 = world.getType(blockposition1.shift(enumdirection2));
|
||||
int i = (iblockdata.k() ? -1 : 0) + (iblockdata1.k() ? -1 : 0) + (iblockdata2.k() ? 1 : 0) + (iblockdata3.k() ? 1 : 0);
|
||||
boolean flag = iblockdata.getBlock() == this && iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
boolean flag1 = iblockdata2.getBlock() == this && iblockdata2.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
|
||||
if ((!flag || flag1) && i <= 0) {
|
||||
if ((!flag1 || flag) && i >= 0) {
|
||||
int j = enumdirection.getAdjacentX();
|
||||
int k = enumdirection.getAdjacentZ();
|
||||
float f = blockactioncontext.m();
|
||||
float f1 = blockactioncontext.o();
|
||||
|
||||
return (j >= 0 || f1 >= 0.5F) && (j <= 0 || f1 <= 0.5F) && (k >= 0 || f <= 0.5F) && (k <= 0 || f >= 0.5F) ? BlockPropertyDoorHinge.LEFT : BlockPropertyDoorHinge.RIGHT;
|
||||
} else {
|
||||
return BlockPropertyDoorHinge.LEFT;
|
||||
}
|
||||
} else {
|
||||
return BlockPropertyDoorHinge.RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (this.material == Material.ORE) {
|
||||
return false;
|
||||
} else {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockDoor.OPEN);
|
||||
world.setTypeAndData(blockposition, iblockdata, 10);
|
||||
world.a(entityhuman, (Boolean) iblockdata.get(BlockDoor.OPEN) ? this.e() : this.d(), blockposition, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDoor(World world, BlockPosition blockposition, boolean flag) {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
if (iblockdata.getBlock() == this && (Boolean) iblockdata.get(BlockDoor.OPEN) != flag) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDoor.OPEN, flag), 10);
|
||||
this.b(world, blockposition, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
BlockPosition otherHalf = blockposition.shift(iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? EnumDirection.UP : EnumDirection.DOWN);
|
||||
|
||||
org.bukkit.World bworld = world.getWorld();
|
||||
org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block blockTop = bworld.getBlockAt(otherHalf.getX(), otherHalf.getY(), otherHalf.getZ());
|
||||
|
||||
int power = bukkitBlock.getBlockPower();
|
||||
int powerTop = blockTop.getBlockPower();
|
||||
if (powerTop > power) power = powerTop;
|
||||
int oldPower = (Boolean) iblockdata.get(BlockDoor.POWERED) ? 15 : 0;
|
||||
|
||||
if (oldPower == 0 ^ power == 0) {
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
boolean flag = eventRedstone.getNewCurrent() > 0;
|
||||
// CraftBukkit end
|
||||
if (flag != (Boolean) iblockdata.get(BlockDoor.OPEN)) {
|
||||
this.b(world, blockposition, flag);
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.down());
|
||||
|
||||
return iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? iblockdata1.q() : iblockdata1.getBlock() == this;
|
||||
}
|
||||
|
||||
private void b(World world, BlockPosition blockposition, boolean flag) {
|
||||
world.a((EntityHuman) null, flag ? this.e() : this.d(), blockposition, 0);
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return (IMaterial) (iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.UPPER ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i));
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockDoor.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDoor.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return enumblockmirror == EnumBlockMirror.NONE ? iblockdata : (IBlockData) iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDoor.FACING))).a((IBlockState) BlockDoor.HINGE);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDoor.HALF, BlockDoor.FACING, BlockDoor.OPEN, BlockDoor.HINGE, BlockDoor.POWERED);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
81
src/main/java/net/minecraft/server/BlockDragonEgg.java
Normal file
81
src/main/java/net/minecraft/server/BlockDragonEgg.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit
|
||||
|
||||
public class BlockDragonEgg extends BlockFalling {
|
||||
|
||||
protected static final VoxelShape a = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
|
||||
public BlockDragonEgg(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDragonEgg.a;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
BlockPosition blockposition1 = blockposition.a(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16));
|
||||
|
||||
if (world.getType(blockposition1).isAir()) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block from = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block to = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ());
|
||||
BlockFromToEvent event = new BlockFromToEvent(from, to);
|
||||
org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockposition1 = new BlockPosition(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ());
|
||||
// CraftBukkit end
|
||||
if (world.isClientSide) {
|
||||
for (int j = 0; j < 128; ++j) {
|
||||
double d0 = world.random.nextDouble();
|
||||
float f = (world.random.nextFloat() - 0.5F) * 0.2F;
|
||||
float f1 = (world.random.nextFloat() - 0.5F) * 0.2F;
|
||||
float f2 = (world.random.nextFloat() - 0.5F) * 0.2F;
|
||||
double d1 = (double) blockposition1.getX() + (double) (blockposition.getX() - blockposition1.getX()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D;
|
||||
double d2 = (double) blockposition1.getY() + (double) (blockposition.getY() - blockposition1.getY()) * d0 + world.random.nextDouble() - 0.5D;
|
||||
double d3 = (double) blockposition1.getZ() + (double) (blockposition.getZ() - blockposition1.getZ()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D;
|
||||
|
||||
world.addParticle(Particles.K, d1, d2, d3, (double) f, (double) f1, (double) f2);
|
||||
}
|
||||
} else {
|
||||
world.setTypeAndData(blockposition1, iblockdata, 2);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
72
src/main/java/net/minecraft/server/BlockDropper.java
Normal file
72
src/main/java/net/minecraft/server/BlockDropper.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockDropper extends BlockDispenser {
|
||||
|
||||
private static final IDispenseBehavior c = new DispenseBehaviorItem();
|
||||
|
||||
public BlockDropper(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
protected IDispenseBehavior a(ItemStack itemstack) {
|
||||
return BlockDropper.c;
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityDropper();
|
||||
}
|
||||
|
||||
public void dispense(World world, BlockPosition blockposition) {
|
||||
SourceBlock sourceblock = new SourceBlock(world, blockposition);
|
||||
TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity();
|
||||
int i = tileentitydispenser.p();
|
||||
|
||||
if (i < 0) {
|
||||
world.triggerEffect(1001, blockposition, 0);
|
||||
} else {
|
||||
ItemStack itemstack = tileentitydispenser.getItem(i);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
EnumDirection enumdirection = (EnumDirection) world.getType(blockposition).get(BlockDropper.FACING);
|
||||
IInventory iinventory = TileEntityHopper.a(world, blockposition.shift(enumdirection));
|
||||
ItemStack itemstack1;
|
||||
|
||||
if (iinventory == null) {
|
||||
itemstack1 = BlockDropper.c.dispense(sourceblock, itemstack);
|
||||
} else {
|
||||
// CraftBukkit start - Fire event when pushing items into other inventories
|
||||
CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.cloneItemStack().cloneAndSubtract(1));
|
||||
|
||||
org.bukkit.inventory.Inventory destinationInventory;
|
||||
// Have to special case large chests as they work oddly
|
||||
if (iinventory instanceof InventoryLargeChest) {
|
||||
destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
|
||||
} else {
|
||||
destinationInventory = iinventory.getOwner().getInventory();
|
||||
}
|
||||
|
||||
InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentitydispenser.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
itemstack1 = TileEntityHopper.addItem(tileentitydispenser, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection.opposite());
|
||||
if (event.getItem().equals(oitemstack) && itemstack1.isEmpty()) {
|
||||
// CraftBukkit end
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
itemstack1.subtract(1);
|
||||
} else {
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
}
|
||||
}
|
||||
|
||||
tileentitydispenser.setItem(i, itemstack1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/main/java/net/minecraft/server/BlockEnderPortal.java
Normal file
49
src/main/java/net/minecraft/server/BlockEnderPortal.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit
|
||||
|
||||
public class BlockEnderPortal extends BlockTileEntity {
|
||||
|
||||
protected static final VoxelShape a = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
|
||||
protected BlockEnderPortal(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityEnderPortal();
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockEnderPortal.a;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide && !entity.isPassenger() && !entity.isVehicle() && entity.bm() && VoxelShapes.c(VoxelShapes.a(entity.getBoundingBox().d((double) (-blockposition.getX()), (double) (-blockposition.getY()), (double) (-blockposition.getZ()))), iblockdata.getShape(world, blockposition), OperatorBoolean.AND)) {
|
||||
// CraftBukkit start - Entity in portal
|
||||
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
entity.a(DimensionManager.THE_END);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
459
src/main/java/net/minecraft/server/BlockFire.java
Normal file
459
src/main/java/net/minecraft/server/BlockFire.java
Normal file
@@ -0,0 +1,459 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import com.destroystokyo.paper.event.block.TNTPrimeEvent; // Paper - TNTPrimeEvent
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import org.bukkit.event.block.BlockFadeEvent;
|
||||
import org.bukkit.event.block.BlockSpreadEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockFire extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.X;
|
||||
public static final BlockStateBoolean NORTH = BlockSprawling.a;
|
||||
public static final BlockStateBoolean EAST = BlockSprawling.b;
|
||||
public static final BlockStateBoolean SOUTH = BlockSprawling.c;
|
||||
public static final BlockStateBoolean WEST = BlockSprawling.o;
|
||||
public static final BlockStateBoolean UPPER = BlockSprawling.p;
|
||||
private static final Map<EnumDirection, BlockStateBoolean> r = (Map) BlockSprawling.r.entrySet().stream().filter((entry) -> {
|
||||
return entry.getKey() != EnumDirection.DOWN;
|
||||
}).collect(SystemUtils.a());
|
||||
private final Object2IntMap<Block> flameChances = new Object2IntOpenHashMap();
|
||||
private final Object2IntMap<Block> t = new Object2IntOpenHashMap();
|
||||
|
||||
protected BlockFire(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFire.AGE, 0)).set(BlockFire.NORTH, false)).set(BlockFire.EAST, false)).set(BlockFire.SOUTH, false)).set(BlockFire.WEST, false)).set(BlockFire.UPPER, false));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return VoxelShapes.a();
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
|
||||
blockState.setData(Blocks.AIR.getBlockData());
|
||||
|
||||
BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState);
|
||||
generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
}
|
||||
return this.a((IBlockAccess) generatoraccess, blockposition).set(BlockFire.AGE, iblockdata.get(BlockFire.AGE));
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition());
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition.down());
|
||||
|
||||
if (!iblockdata.q() && !this.k(iblockdata)) {
|
||||
IBlockData iblockdata1 = this.getBlockData();
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
BlockStateBoolean blockstateboolean = (BlockStateBoolean) BlockFire.r.get(enumdirection);
|
||||
|
||||
if (blockstateboolean != null) {
|
||||
iblockdata1 = (IBlockData) iblockdata1.set(blockstateboolean, this.k(iblockaccess.getType(blockposition.shift(enumdirection))));
|
||||
}
|
||||
}
|
||||
|
||||
return iblockdata1;
|
||||
} else {
|
||||
return this.getBlockData();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).q() || this.d(iworldreader, blockposition);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.getGameRules().getBoolean("doFireTick")) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - invalid place location
|
||||
}
|
||||
|
||||
Block block = world.getType(blockposition.down()).getBlock();
|
||||
boolean flag = world.worldProvider instanceof WorldProviderTheEnd && block == Blocks.BEDROCK || block == Blocks.NETHERRACK || block == Blocks.MAGMA_BLOCK;
|
||||
int i = (Integer) iblockdata.get(BlockFire.AGE);
|
||||
|
||||
if (!flag && world.isRaining() && this.a(world, blockposition) && random.nextFloat() < 0.2F + (float) i * 0.03F) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - extinguished by rain
|
||||
} else {
|
||||
int j = Math.min(15, i + random.nextInt(3) / 2);
|
||||
|
||||
if (i != j) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockFire.AGE, j);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + random.nextInt(10));
|
||||
if (!this.d(world, blockposition)) {
|
||||
if (!world.getType(blockposition.down()).q() || i > 3) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (i == 15 && random.nextInt(4) == 0 && !this.k(world.getType(blockposition.down()))) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag1 = world.x(blockposition);
|
||||
int k = flag1 ? -50 : 0;
|
||||
|
||||
// CraftBukkit start - add source blockposition to burn calls
|
||||
this.a(world, blockposition.east(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.west(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.down(), 250 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.up(), 250 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.north(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.south(), 300 + k, random, i, blockposition);
|
||||
// CraftBukkit end
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
|
||||
for (int l = -1; l <= 1; ++l) {
|
||||
for (int i1 = -1; i1 <= 1; ++i1) {
|
||||
for (int j1 = -1; j1 <= 4; ++j1) {
|
||||
if (l != 0 || j1 != 0 || i1 != 0) {
|
||||
int k1 = 100;
|
||||
|
||||
if (j1 > 1) {
|
||||
k1 += (j1 - 1) * 100;
|
||||
}
|
||||
|
||||
blockposition_mutableblockposition.g(blockposition).d(l, j1, i1);
|
||||
if (!world.isLoaded(blockposition_mutableblockposition)) continue; // Paper
|
||||
int l1 = this.a((IWorldReader) world, (BlockPosition) blockposition_mutableblockposition);
|
||||
|
||||
if (l1 > 0) {
|
||||
int i2 = (l1 + 40 + world.getDifficulty().a() * 7) / (i + 30);
|
||||
|
||||
if (flag1) {
|
||||
i2 /= 2;
|
||||
}
|
||||
|
||||
if (i2 > 0 && random.nextInt(k1) <= i2 && (!world.isRaining() || !this.a(world, (BlockPosition) blockposition_mutableblockposition))) {
|
||||
int j2 = Math.min(15, i + random.nextInt(5) / 4);
|
||||
|
||||
// CraftBukkit start - Call to stop spread of fire
|
||||
if (world.getType(blockposition_mutableblockposition) != Blocks.FIRE) {
|
||||
if (CraftEventFactory.callBlockIgniteEvent(world, blockposition_mutableblockposition, blockposition).isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition_mutableblockposition, (IBlockData) this.a((IBlockAccess) world, (BlockPosition) blockposition_mutableblockposition).set(BlockFire.AGE, j2), 3); // CraftBukkit
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean a(World world, BlockPosition blockposition) {
|
||||
return world.isRainingAt(blockposition) || world.isRainingAt(blockposition.west()) || world.isRainingAt(blockposition.east()) || world.isRainingAt(blockposition.north()) || world.isRainingAt(blockposition.south());
|
||||
}
|
||||
|
||||
private int f(Block block) {
|
||||
return this.t.getInt(block);
|
||||
}
|
||||
|
||||
private int g(Block block) {
|
||||
return this.flameChances.getInt(block);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition, int i, Random random, int j, BlockPosition sourceposition) { // CraftBukkit add sourceposition
|
||||
// Paper start
|
||||
final IBlockData iblockdata = world.getTypeIfLoaded(blockposition);
|
||||
if (iblockdata == null) return;
|
||||
int k = this.f(iblockdata.getBlock());
|
||||
// Paper end
|
||||
|
||||
if (random.nextInt(i) < k) {
|
||||
//IBlockData iblockdata = world.getType(blockposition); // Paper
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block sourceBlock = world.getWorld().getBlockAt(sourceposition.getX(), sourceposition.getY(), sourceposition.getZ());
|
||||
|
||||
BlockBurnEvent event = new BlockBurnEvent(theBlock, sourceBlock);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (random.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) {
|
||||
int l = Math.min(j + random.nextInt(5) / 4, 15);
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.a((IBlockAccess) world, blockposition).set(BlockFire.AGE, l), 3);
|
||||
} else {
|
||||
if(iblockdata.getBlock() != Blocks.TNT) world.setAir(blockposition); // Paper - TNTPrimeEvent - We might be cancelling it below, move the setAir down
|
||||
}
|
||||
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block instanceof BlockTNT) {
|
||||
// Paper start - TNTPrimeEvent
|
||||
org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition);
|
||||
if (!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.FIRE, null).callEvent()) {
|
||||
return;
|
||||
}
|
||||
world.setAir(blockposition); // setair after non cancelled event, it would usually be air by now
|
||||
// Paper end
|
||||
((BlockTNT) block).a(world, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean d(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (this.k(iblockaccess.getType(blockposition.shift(enumdirection)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
if (!iworldreader.isEmpty(blockposition)) {
|
||||
return 0;
|
||||
} else {
|
||||
int i = 0;
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int j = aenumdirection.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnumDirection enumdirection = aenumdirection[k];
|
||||
|
||||
// Paper start
|
||||
final IBlockData type = ((World)iworldreader).getTypeIfLoaded(blockposition.shift(enumdirection));
|
||||
if (type == null) continue;
|
||||
i = Math.max(this.g(type.getBlock()), i);
|
||||
// Paper end
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean j() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean k(IBlockData iblockdata) {
|
||||
return this.g(iblockdata.getBlock()) > 0;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata1.getBlock() != iblockdata.getBlock()) {
|
||||
if (world.worldProvider.getDimensionManager() != DimensionManager.OVERWORLD && world.worldProvider.getDimensionManager() != DimensionManager.NETHER || !((BlockPortal) Blocks.NETHER_PORTAL).a((GeneratorAccess) world, blockposition)) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke
|
||||
} else {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + world.random.nextInt(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockFire.AGE, BlockFire.NORTH, BlockFire.EAST, BlockFire.SOUTH, BlockFire.WEST, BlockFire.UPPER);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public void a(Block block, int i, int j) {
|
||||
this.flameChances.put(block, i);
|
||||
this.t.put(block, j);
|
||||
}
|
||||
|
||||
public static void d() {
|
||||
BlockFire blockfire = (BlockFire) Blocks.FIRE;
|
||||
|
||||
blockfire.a(Blocks.OAK_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.OAK_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.OAK_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.OAK_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.OAK_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.SPRUCE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.BIRCH_LOG, 5, 5);
|
||||
blockfire.a(Blocks.JUNGLE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.ACACIA_LOG, 5, 5);
|
||||
blockfire.a(Blocks.DARK_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_SPRUCE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_BIRCH_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_JUNGLE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_ACACIA_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_DARK_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_SPRUCE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_BIRCH_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_JUNGLE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_ACACIA_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_DARK_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.SPRUCE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.BIRCH_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.JUNGLE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.ACACIA_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.DARK_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.OAK_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.SPRUCE_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.BIRCH_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.JUNGLE_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.ACACIA_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.DARK_OAK_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.BOOKSHELF, 30, 20);
|
||||
blockfire.a(Blocks.TNT, 15, 100);
|
||||
blockfire.a(Blocks.GRASS, 60, 100);
|
||||
blockfire.a(Blocks.FERN, 60, 100);
|
||||
blockfire.a(Blocks.DEAD_BUSH, 60, 100);
|
||||
blockfire.a(Blocks.SUNFLOWER, 60, 100);
|
||||
blockfire.a(Blocks.LILAC, 60, 100);
|
||||
blockfire.a(Blocks.ROSE_BUSH, 60, 100);
|
||||
blockfire.a(Blocks.PEONY, 60, 100);
|
||||
blockfire.a(Blocks.TALL_GRASS, 60, 100);
|
||||
blockfire.a(Blocks.LARGE_FERN, 60, 100);
|
||||
blockfire.a(Blocks.DANDELION, 60, 100);
|
||||
blockfire.a(Blocks.POPPY, 60, 100);
|
||||
blockfire.a(Blocks.BLUE_ORCHID, 60, 100);
|
||||
blockfire.a(Blocks.ALLIUM, 60, 100);
|
||||
blockfire.a(Blocks.AZURE_BLUET, 60, 100);
|
||||
blockfire.a(Blocks.RED_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.ORANGE_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.WHITE_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.PINK_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.OXEYE_DAISY, 60, 100);
|
||||
blockfire.a(Blocks.WHITE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.ORANGE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.MAGENTA_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIGHT_BLUE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.YELLOW_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIME_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.PINK_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.GRAY_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIGHT_GRAY_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.CYAN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.PURPLE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BLUE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BROWN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.GREEN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.RED_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BLACK_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.VINE, 15, 100);
|
||||
blockfire.a(Blocks.COAL_BLOCK, 5, 5);
|
||||
blockfire.a(Blocks.HAY_BLOCK, 60, 20);
|
||||
blockfire.a(Blocks.WHITE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.ORANGE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.MAGENTA_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIGHT_BLUE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.YELLOW_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIME_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.PINK_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.GRAY_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIGHT_GRAY_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.CYAN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.PURPLE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BLUE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BROWN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.GREEN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.RED_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BLACK_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.DRIED_KELP_BLOCK, 30, 60);
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
private void fireExtinguished(GeneratorAccess world, BlockPosition position) {
|
||||
if (!CraftEventFactory.callBlockFadeEvent(world, position, Blocks.AIR.getBlockData()).isCancelled()) {
|
||||
world.setAir(position);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
186
src/main/java/net/minecraft/server/BlockFluids.java
Normal file
186
src/main/java/net/minecraft/server/BlockFluids.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockFluids extends Block implements IFluidSource {
|
||||
|
||||
public static final BlockStateInteger LEVEL = BlockProperties.ah;
|
||||
protected final FluidTypeFlowing b;
|
||||
private final List<Fluid> c;
|
||||
private final Map<IBlockData, VoxelShape> o = Maps.newIdentityHashMap();
|
||||
|
||||
protected BlockFluids(FluidTypeFlowing fluidtypeflowing, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.b = fluidtypeflowing;
|
||||
this.c = Lists.newArrayList();
|
||||
this.c.add(fluidtypeflowing.a(false));
|
||||
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
this.c.add(fluidtypeflowing.a(8 - i, false));
|
||||
}
|
||||
|
||||
this.c.add(fluidtypeflowing.a(8, true));
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFluids.LEVEL, 0));
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
world.getFluid(blockposition).b(world, blockposition, random);
|
||||
}
|
||||
|
||||
public boolean a_(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return !this.b.a(TagsFluid.LAVA);
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
int i = (Integer) iblockdata.get(BlockFluids.LEVEL);
|
||||
|
||||
return (Fluid) this.c.get(Math.min(i, 8));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCollidable(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
Fluid fluid = iblockaccess.getFluid(blockposition.up());
|
||||
|
||||
return fluid.c().a((FluidType) this.b) ? VoxelShapes.b() : (VoxelShape) this.o.computeIfAbsent(iblockdata, (iblockdata1) -> {
|
||||
Fluid fluid1 = iblockdata1.s();
|
||||
|
||||
return VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid1.getHeight(), 1.0D);
|
||||
});
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.INVISIBLE;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return this.b.a(iworldreader);
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start - Get flow speed. Throttle if its water and flowing adjacent to lava
|
||||
public int getFlowSpeed(World world, BlockPosition blockposition) {
|
||||
if (this.material == Material.WATER) {
|
||||
if (
|
||||
world.getMaterialIfLoaded(blockposition.north(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.south(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.west(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.east(1)) == Material.LAVA
|
||||
) {
|
||||
return world.paperConfig.waterOverLavaFlowSpeed;
|
||||
}
|
||||
}
|
||||
return this.a(world);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (iblockdata.s().d() || iblockdata1.s().d()) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, iblockdata.s().c(), this.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if (this.b.a(TagsFluid.LAVA)) {
|
||||
boolean flag = false;
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (enumdirection != EnumDirection.DOWN && world.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.WATER)) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
Fluid fluid = world.getFluid(blockposition);
|
||||
|
||||
if (fluid.d()) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.OBSIDIAN.getBlockData())) {
|
||||
this.fizz(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fluid.getHeight() >= 0.44444445F) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.COBBLESTONE.getBlockData())) {
|
||||
this.fizz(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void fizz(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
double d0 = (double) blockposition.getX();
|
||||
double d1 = (double) blockposition.getY();
|
||||
double d2 = (double) blockposition.getZ();
|
||||
|
||||
generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (generatoraccess.m().nextFloat() - generatoraccess.m().nextFloat()) * 0.8F);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
generatoraccess.addParticle(Particles.F, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockFluids.LEVEL);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if ((Integer) iblockdata.get(BlockFluids.LEVEL) == 0) {
|
||||
generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 11);
|
||||
return this.b;
|
||||
} else {
|
||||
return FluidTypes.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/main/java/net/minecraft/server/BlockGrass.java
Normal file
81
src/main/java/net/minecraft/server/BlockGrass.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockGrass extends BlockDirtSnowSpreadable implements IBlockFragilePlantElement {
|
||||
|
||||
public BlockGrass(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return iblockaccess.getType(blockposition.up()).isAir();
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
IBlockData iblockdata1 = Blocks.GRASS.getBlockData();
|
||||
int i = 0;
|
||||
|
||||
while (i < 128) {
|
||||
BlockPosition blockposition2 = blockposition1;
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
if (j < i / 16) {
|
||||
blockposition2 = blockposition2.a(random.nextInt(3) - 1, (random.nextInt(3) - 1) * random.nextInt(3) / 2, random.nextInt(3) - 1);
|
||||
if (world.getType(blockposition2.down()).getBlock() == this && !world.getType(blockposition2).k()) {
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
IBlockData iblockdata2 = world.getType(blockposition2);
|
||||
|
||||
if (iblockdata2.getBlock() == iblockdata1.getBlock() && random.nextInt(10) == 0) {
|
||||
((IBlockFragilePlantElement) iblockdata1.getBlock()).b(world, random, blockposition2, iblockdata2);
|
||||
}
|
||||
|
||||
if (iblockdata2.isAir()) {
|
||||
label38:
|
||||
{
|
||||
IBlockData iblockdata3;
|
||||
|
||||
if (random.nextInt(8) == 0) {
|
||||
List<WorldGenFeatureCompositeFlower<?>> list = world.getBiome(blockposition2).f();
|
||||
|
||||
if (list.isEmpty()) {
|
||||
break label38;
|
||||
}
|
||||
|
||||
iblockdata3 = ((WorldGenFeatureCompositeFlower) list.get(0)).a(random, blockposition2);
|
||||
} else {
|
||||
iblockdata3 = iblockdata1;
|
||||
}
|
||||
|
||||
if (iblockdata3.canPlace(world, blockposition2)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition2, iblockdata3, 3); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT_MIPPED;
|
||||
}
|
||||
}
|
||||
72
src/main/java/net/minecraft/server/BlockIce.java
Normal file
72
src/main/java/net/minecraft/server/BlockIce.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockIce extends BlockHalfTransparent {
|
||||
|
||||
public BlockIce(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return Blocks.WATER.getBlockData().b(iblockaccess, blockposition);
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.TRANSLUCENT;
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
entityhuman.applyExhaustion(0.005F);
|
||||
if (this.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0) {
|
||||
a(world, blockposition, this.t(iblockdata));
|
||||
} else {
|
||||
if (world.worldProvider.isNether()) {
|
||||
world.setAir(blockposition);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack);
|
||||
|
||||
iblockdata.a(world, blockposition, i);
|
||||
Material material = world.getType(blockposition.down()).getMaterial();
|
||||
|
||||
if (material.isSolid() || material.isLiquid()) {
|
||||
world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 11 - iblockdata.b(world, blockposition)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, world.worldProvider.isNether() ? Blocks.AIR.getBlockData() : Blocks.WATER.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (world.worldProvider.isNether()) {
|
||||
world.setAir(blockposition);
|
||||
} else {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData());
|
||||
world.a(blockposition, Blocks.WATER, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.NORMAL;
|
||||
}
|
||||
}
|
||||
128
src/main/java/net/minecraft/server/BlockIceFrost.java
Normal file
128
src/main/java/net/minecraft/server/BlockIceFrost.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockIceFrost extends BlockIce {
|
||||
|
||||
public static final BlockStateInteger a = BlockProperties.U;
|
||||
|
||||
public BlockIceFrost(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockIceFrost.a, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.paperConfig.frostedIceEnabled) return; // Paper - add ability to disable frosted ice
|
||||
if ((random.nextInt(3) == 0 || this.a(world, blockposition, 4)) && world.getLightLevel(blockposition) > 11 - (Integer) iblockdata.get(BlockIceFrost.a) - iblockdata.b(world, blockposition) && this.c(iblockdata, world, blockposition)) {
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition_b); // Paper - don't load chunks
|
||||
if (iblockdata1 == null) continue; // Paper
|
||||
|
||||
if (iblockdata1.getBlock() == this && !this.c(iblockdata1, world, blockposition_b)) {
|
||||
world.getBlockTickList().a(blockposition_b, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
world.getBlockTickList().a(blockposition, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay
|
||||
}
|
||||
}
|
||||
|
||||
private boolean c(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
int i = (Integer) iblockdata.get(BlockIceFrost.a);
|
||||
|
||||
if (i < 3) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockIceFrost.a, i + 1), 2);
|
||||
return false;
|
||||
} else {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (block == this && this.a(world, blockposition, 2)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.doPhysics(iblockdata, world, blockposition, block, blockposition1);
|
||||
}
|
||||
|
||||
private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) {
|
||||
int j = 0;
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int k = aenumdirection.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
EnumDirection enumdirection = aenumdirection[l];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
if (((World) iblockaccess).getBlockIfLoaded(blockposition_b) == this) { // Paper - don't load chunks
|
||||
++j;
|
||||
if (j >= i) {
|
||||
boolean flag = false;
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockIceFrost.a);
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
}
|
||||
106
src/main/java/net/minecraft/server/BlockJukeBox.java
Normal file
106
src/main/java/net/minecraft/server/BlockJukeBox.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockJukeBox extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateBoolean HAS_RECORD = BlockProperties.l;
|
||||
|
||||
protected BlockJukeBox(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockJukeBox.HAS_RECORD, false));
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if ((Boolean) iblockdata.get(BlockJukeBox.HAS_RECORD)) {
|
||||
this.dropRecord(world, blockposition);
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, false);
|
||||
world.setTypeAndData(blockposition, iblockdata, 2);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, ItemStack itemstack) {
|
||||
TileEntity tileentity = generatoraccess.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
// CraftBukkit start - There can only be one
|
||||
itemstack = itemstack.cloneItemStack();
|
||||
if (!itemstack.isEmpty()) {
|
||||
itemstack.setCount(1);
|
||||
}
|
||||
((TileEntityJukeBox) tileentity).setRecord(itemstack);
|
||||
// CraftBukkit end
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, true), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void dropRecord(World world, BlockPosition blockposition) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
TileEntityJukeBox tileentityjukebox = (TileEntityJukeBox) tileentity;
|
||||
ItemStack itemstack = tileentityjukebox.getRecord();
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
world.triggerEffect(1010, blockposition, 0);
|
||||
world.a(blockposition, (SoundEffect) null);
|
||||
tileentityjukebox.setRecord(ItemStack.a);
|
||||
float f = 0.7F;
|
||||
double d0 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D;
|
||||
double d1 = (double) (world.random.nextFloat() * 0.7F) + 0.06000000238418579D + 0.6D;
|
||||
double d2 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D;
|
||||
ItemStack itemstack1 = itemstack.cloneItemStack();
|
||||
EntityItem entityitem = new EntityItem(world, (double) blockposition.getX() + d0, (double) blockposition.getY() + d1, (double) blockposition.getZ() + d2, itemstack1);
|
||||
|
||||
entityitem.n();
|
||||
world.addEntity(entityitem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
this.dropRecord(world, blockposition);
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityJukeBox();
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
Item item = ((TileEntityJukeBox) tileentity).getRecord().getItem();
|
||||
|
||||
if (item instanceof ItemRecord) {
|
||||
return ((ItemRecord) item).d();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockJukeBox.HAS_RECORD);
|
||||
}
|
||||
}
|
||||
97
src/main/java/net/minecraft/server/BlockKelp.java
Normal file
97
src/main/java/net/minecraft/server/BlockKelp.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockKelp extends Block implements IFluidContainer {
|
||||
|
||||
public static final BlockStateInteger a = BlockProperties.Y;
|
||||
protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
|
||||
|
||||
protected BlockKelp(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockKelp.a, 0));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockKelp.b;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition());
|
||||
|
||||
return fluid.a(TagsFluid.WATER) && fluid.g() == 8 ? this.a((GeneratorAccess) blockactioncontext.getWorld()) : null;
|
||||
}
|
||||
|
||||
public IBlockData a(GeneratorAccess generatoraccess) {
|
||||
return (IBlockData) this.getBlockData().set(BlockKelp.a, generatoraccess.m().nextInt(25));
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
return FluidTypes.WATER.a(false);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == Blocks.WATER && (Integer) iblockdata.get(BlockKelp.a) < 25 && random.nextDouble() < 0.14D) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, (IBlockData) iblockdata.a((IBlockState) BlockKelp.a)); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition1);
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
return block == Blocks.MAGMA_BLOCK ? false : block == this || block == Blocks.KELP_PLANT || Block.a(iblockdata1.getCollisionShape(iworldreader, blockposition1), EnumDirection.UP);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
if (enumdirection == EnumDirection.DOWN) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
}
|
||||
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == this) {
|
||||
return Blocks.KELP_PLANT.getBlockData();
|
||||
} else {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockKelp.a);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
174
src/main/java/net/minecraft/server/BlockLeaves.java
Normal file
174
src/main/java/net/minecraft/server/BlockLeaves.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit
|
||||
|
||||
public class BlockLeaves extends Block {
|
||||
|
||||
public static final BlockStateInteger DISTANCE = BlockProperties.ab;
|
||||
public static final BlockStateBoolean PERSISTENT = BlockProperties.s;
|
||||
protected static boolean c;
|
||||
|
||||
public BlockLeaves(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLeaves.DISTANCE, 7)).set(BlockLeaves.PERSISTENT, false));
|
||||
}
|
||||
|
||||
public boolean isTicking(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7 && !(Boolean) iblockdata.get(BlockLeaves.PERSISTENT);
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!(Boolean) iblockdata.get(BlockLeaves.PERSISTENT) && (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7) {
|
||||
// CraftBukkit start
|
||||
LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled() || world.getType(blockposition).getBlock() != this) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
world.setTypeAndData(blockposition, a(iblockdata, (GeneratorAccess) world, blockposition), 3);
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
int i = w(iblockdata1) + 1;
|
||||
|
||||
if (i != 1 || (Integer) iblockdata.get(BlockLeaves.DISTANCE) != i) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return iblockdata;
|
||||
}
|
||||
|
||||
private static IBlockData a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
int i = 7;
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int j = aenumdirection.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnumDirection enumdirection = aenumdirection[k];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
i = Math.min(i, w(generatoraccess.getType(blockposition_b)) + 1);
|
||||
if (i == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (IBlockData) iblockdata.set(BlockLeaves.DISTANCE, i);
|
||||
}
|
||||
|
||||
private static int w(IBlockData iblockdata) {
|
||||
return TagsBlock.LOGS.isTagged(iblockdata.getBlock()) ? 0 : (iblockdata.getBlock() instanceof BlockLeaves ? (Integer) iblockdata.get(BlockLeaves.DISTANCE) : 7);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return random.nextInt(20) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return block == Blocks.OAK_LEAVES ? Blocks.OAK_SAPLING : (block == Blocks.SPRUCE_LEAVES ? Blocks.SPRUCE_SAPLING : (block == Blocks.BIRCH_LEAVES ? Blocks.BIRCH_SAPLING : (block == Blocks.JUNGLE_LEAVES ? Blocks.JUNGLE_SAPLING : (block == Blocks.ACACIA_LEAVES ? Blocks.ACACIA_SAPLING : (block == Blocks.DARK_OAK_LEAVES ? Blocks.DARK_OAK_SAPLING : Blocks.OAK_SAPLING)))));
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
int j = this.k(iblockdata);
|
||||
|
||||
if (i > 0) {
|
||||
j -= 2 << i;
|
||||
if (j < 10) {
|
||||
j = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (world.random.nextInt(j) == 0) {
|
||||
a(world, blockposition, new ItemStack(this.getDropType(iblockdata, world, blockposition, i)));
|
||||
}
|
||||
|
||||
j = 200;
|
||||
if (i > 0) {
|
||||
j -= 10 << i;
|
||||
if (j < 40) {
|
||||
j = 40;
|
||||
}
|
||||
}
|
||||
|
||||
this.a(world, blockposition, iblockdata, j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) {
|
||||
if ((iblockdata.getBlock() == Blocks.OAK_LEAVES || iblockdata.getBlock() == Blocks.DARK_OAK_LEAVES) && world.random.nextInt(i) == 0) {
|
||||
a(world, blockposition, new ItemStack(Items.APPLE));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected int k(IBlockData iblockdata) {
|
||||
return iblockdata.getBlock() == Blocks.JUNGLE_LEAVES ? 40 : 20;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return BlockLeaves.c ? TextureType.CUTOUT_MIPPED : TextureType.SOLID;
|
||||
}
|
||||
|
||||
public boolean q(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
if (!world.isClientSide && itemstack.getItem() == Items.SHEARS) {
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
entityhuman.applyExhaustion(0.005F);
|
||||
a(world, blockposition, new ItemStack(this));
|
||||
} else {
|
||||
super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockLeaves.DISTANCE, BlockLeaves.PERSISTENT);
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return a((IBlockData) this.getBlockData().set(BlockLeaves.PERSISTENT, true), (GeneratorAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition());
|
||||
}
|
||||
}
|
||||
138
src/main/java/net/minecraft/server/BlockLever.java
Normal file
138
src/main/java/net/minecraft/server/BlockLever.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockLever extends BlockAttachable {
|
||||
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
protected static final VoxelShape b = Block.a(5.0D, 4.0D, 10.0D, 11.0D, 12.0D, 16.0D);
|
||||
protected static final VoxelShape c = Block.a(5.0D, 4.0D, 0.0D, 11.0D, 12.0D, 6.0D);
|
||||
protected static final VoxelShape o = Block.a(10.0D, 4.0D, 5.0D, 16.0D, 12.0D, 11.0D);
|
||||
protected static final VoxelShape p = Block.a(0.0D, 4.0D, 5.0D, 6.0D, 12.0D, 11.0D);
|
||||
protected static final VoxelShape q = Block.a(5.0D, 0.0D, 4.0D, 11.0D, 6.0D, 12.0D);
|
||||
protected static final VoxelShape r = Block.a(4.0D, 0.0D, 5.0D, 12.0D, 6.0D, 11.0D);
|
||||
protected static final VoxelShape s = Block.a(5.0D, 10.0D, 4.0D, 11.0D, 16.0D, 12.0D);
|
||||
protected static final VoxelShape t = Block.a(4.0D, 10.0D, 5.0D, 12.0D, 16.0D, 11.0D);
|
||||
|
||||
protected BlockLever(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLever.FACING, EnumDirection.NORTH)).set(BlockLever.POWERED, false)).set(BlockLever.FACE, BlockPropertyAttachPosition.WALL));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
switch ((BlockPropertyAttachPosition) iblockdata.get(BlockLever.FACE)) {
|
||||
case FLOOR:
|
||||
switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) {
|
||||
case X:
|
||||
return BlockLever.r;
|
||||
case Z:
|
||||
default:
|
||||
return BlockLever.q;
|
||||
}
|
||||
case WALL:
|
||||
switch ((EnumDirection) iblockdata.get(BlockLever.FACING)) {
|
||||
case EAST:
|
||||
return BlockLever.p;
|
||||
case WEST:
|
||||
return BlockLever.o;
|
||||
case SOUTH:
|
||||
return BlockLever.c;
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockLever.b;
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) {
|
||||
case X:
|
||||
return BlockLever.t;
|
||||
case Z:
|
||||
default:
|
||||
return BlockLever.s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockLever.POWERED);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockLever.POWERED);
|
||||
|
||||
if (world.isClientSide) {
|
||||
if (flag) {
|
||||
a(iblockdata, world, blockposition, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// CraftBukkit start - Interact Lever
|
||||
boolean powered = !flag; // Old powered state
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
float f3 = flag ? 0.6F : 0.5F;
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 0.3F, f3);
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, float f) {
|
||||
EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockLever.FACING)).opposite();
|
||||
EnumDirection enumdirection1 = k(iblockdata).opposite();
|
||||
double d0 = (double) blockposition.getX() + 0.5D + 0.1D * (double) enumdirection.getAdjacentX() + 0.2D * (double) enumdirection1.getAdjacentX();
|
||||
double d1 = (double) blockposition.getY() + 0.5D + 0.1D * (double) enumdirection.getAdjacentY() + 0.2D * (double) enumdirection1.getAdjacentY();
|
||||
double d2 = (double) blockposition.getZ() + 0.5D + 0.1D * (double) enumdirection.getAdjacentZ() + 0.2D * (double) enumdirection1.getAdjacentZ();
|
||||
|
||||
generatoraccess.addParticle(new ParticleParamRedstone(1.0F, 0.0F, 0.0F, f), d0, d1, d2, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if ((Boolean) iblockdata.get(BlockLever.POWERED)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockLever.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockLever.POWERED) && k(iblockdata) == enumdirection ? 15 : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockLever.FACE, BlockLever.FACING, BlockLever.POWERED);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
60
src/main/java/net/minecraft/server/BlockMagma.java
Normal file
60
src/main/java/net/minecraft/server/BlockMagma.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockMagma extends Block {
|
||||
|
||||
public BlockMagma(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public void stepOn(World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!entity.isFireProof() && entity instanceof EntityLiving && !EnchantmentManager.i((EntityLiving) entity)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit
|
||||
entity.damageEntity(DamageSource.HOT_FLOOR, 1.0F);
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; // CraftBukkit
|
||||
}
|
||||
|
||||
super.stepOn(world, blockposition, entity);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
BlockBubbleColumn.a(world, blockposition.up(), true);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == Blocks.WATER) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, this.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.getFluid(blockposition).a(TagsFluid.WATER)) {
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
|
||||
if (world instanceof WorldServer) {
|
||||
((WorldServer) world).a(Particles.F, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.25D, (double) blockposition1.getZ() + 0.5D, 8, 0.5D, 0.25D, 0.5D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, Entity entity) {
|
||||
return entity.isFireProof();
|
||||
}
|
||||
|
||||
public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
271
src/main/java/net/minecraft/server/BlockMinecartDetector.java
Normal file
271
src/main/java/net/minecraft/server/BlockMinecartDetector.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockMinecartDetector extends BlockMinecartTrackAbstract {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyTrackPosition> SHAPE = BlockProperties.S;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
|
||||
public BlockMinecartDetector(Block.Info block_info) {
|
||||
super(true, block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockMinecartDetector.POWERED, false)).set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide) {
|
||||
if (!(Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide && (Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return !(Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 0 : (enumdirection == EnumDirection.UP ? 15 : 0);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockMinecartDetector.POWERED);
|
||||
boolean flag1 = false;
|
||||
List<EntityMinecartAbstract> list = this.a(world, blockposition, EntityMinecartAbstract.class, (Predicate) null);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
flag1 = true;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
if (flag != flag1) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
flag1 = eventRedstone.getNewCurrent() > 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (flag1 && !flag) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, true), 3);
|
||||
this.b(world, blockposition, iblockdata, true);
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.down(), this);
|
||||
world.a(blockposition, blockposition);
|
||||
}
|
||||
|
||||
if (!flag1 && flag) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, false), 3);
|
||||
this.b(world, blockposition, iblockdata, false);
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.down(), this);
|
||||
world.a(blockposition, blockposition);
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
protected void b(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
MinecartTrackLogic minecarttracklogic = new MinecartTrackLogic(world, blockposition, iblockdata);
|
||||
List<BlockPosition> list = minecarttracklogic.a();
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
BlockPosition blockposition1 = (BlockPosition) iterator.next();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
iblockdata1.doPhysics(world, blockposition1, iblockdata1.getBlock(), blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata1.getBlock() != iblockdata.getBlock()) {
|
||||
super.onPlace(iblockdata, world, blockposition, iblockdata1);
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockState<BlockPropertyTrackPosition> e() {
|
||||
return BlockMinecartDetector.SHAPE;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
if ((Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
List<EntityMinecartCommandBlock> list = this.a(world, blockposition, EntityMinecartCommandBlock.class, (Predicate) null);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
return ((EntityMinecartCommandBlock) list.get(0)).getCommandBlock().i();
|
||||
}
|
||||
|
||||
List<EntityMinecartAbstract> list1 = this.a(world, blockposition, EntityMinecartAbstract.class, IEntitySelector.d);
|
||||
|
||||
if (!list1.isEmpty()) {
|
||||
return Container.b((IInventory) list1.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected <T extends EntityMinecartAbstract> List<T> a(World world, BlockPosition blockposition, Class<T> oclass, @Nullable Predicate<Entity> predicate) {
|
||||
return world.a(oclass, this.a(blockposition), predicate);
|
||||
}
|
||||
|
||||
private AxisAlignedBB a(BlockPosition blockposition) {
|
||||
float f = 0.2F;
|
||||
|
||||
return new AxisAlignedBB((double) ((float) blockposition.getX() + 0.2F), (double) blockposition.getY(), (double) ((float) blockposition.getZ() + 0.2F), (double) ((float) (blockposition.getX() + 1) - 0.2F), (double) ((float) (blockposition.getY() + 1) - 0.2F), (double) ((float) (blockposition.getZ() + 1) - 0.2F));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
switch (enumblockrotation) {
|
||||
case CLOCKWISE_180:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
}
|
||||
case COUNTERCLOCKWISE_90:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST);
|
||||
case EAST_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH);
|
||||
}
|
||||
case CLOCKWISE_90:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST);
|
||||
case EAST_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH);
|
||||
}
|
||||
default:
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE);
|
||||
|
||||
switch (enumblockmirror) {
|
||||
case LEFT_RIGHT:
|
||||
switch (blockpropertytrackposition) {
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
default:
|
||||
return super.a(iblockdata, enumblockmirror);
|
||||
}
|
||||
case FRONT_BACK:
|
||||
switch (blockpropertytrackposition) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_NORTH:
|
||||
case ASCENDING_SOUTH:
|
||||
default:
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
}
|
||||
}
|
||||
|
||||
return super.a(iblockdata, enumblockmirror);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockMinecartDetector.SHAPE, BlockMinecartDetector.POWERED);
|
||||
}
|
||||
}
|
||||
45
src/main/java/net/minecraft/server/BlockMobSpawner.java
Normal file
45
src/main/java/net/minecraft/server/BlockMobSpawner.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockMobSpawner extends BlockTileEntity {
|
||||
|
||||
protected BlockMobSpawner(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityMobSpawner();
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, i);
|
||||
/* CraftBukkit start - Delegate to getExpDrop
|
||||
int j = 15 + world.random.nextInt(15) + world.random.nextInt(15);
|
||||
|
||||
this.dropExperience(world, blockposition, j);
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) {
|
||||
int j = 15 + world.random.nextInt(15) + world.random.nextInt(15);
|
||||
|
||||
return j;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
}
|
||||
50
src/main/java/net/minecraft/server/BlockMonsterEggs.java
Normal file
50
src/main/java/net/minecraft/server/BlockMonsterEggs.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit
|
||||
|
||||
public class BlockMonsterEggs extends Block {
|
||||
|
||||
private final Block a;
|
||||
private static final Map<Block, Block> b = Maps.newIdentityHashMap();
|
||||
|
||||
public BlockMonsterEggs(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
BlockMonsterEggs.b.put(block, this);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Block d() {
|
||||
return this.a;
|
||||
}
|
||||
|
||||
public static boolean k(IBlockData iblockdata) {
|
||||
return BlockMonsterEggs.b.containsKey(iblockdata.getBlock());
|
||||
}
|
||||
|
||||
protected ItemStack t(IBlockData iblockdata) {
|
||||
return new ItemStack(this.a);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) {
|
||||
EntitySilverfish entitysilverfish = EntityTypes.SILVERFISH.create(world); // Paper
|
||||
|
||||
entitysilverfish.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F);
|
||||
world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason
|
||||
entitysilverfish.doSpawnEffect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static IBlockData f(Block block) {
|
||||
return ((Block) BlockMonsterEggs.b.get(block)).getBlockData();
|
||||
}
|
||||
}
|
||||
103
src/main/java/net/minecraft/server/BlockMushroom.java
Normal file
103
src/main/java/net/minecraft/server/BlockMushroom.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.TreeType;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockMushroom extends BlockPlant implements IBlockFragilePlantElement {
|
||||
|
||||
protected static final VoxelShape a = Block.a(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
|
||||
|
||||
public BlockMushroom(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockMushroom.a;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.mushroomModifier) * 25)) == 0) { // Spigot
|
||||
int i = 5;
|
||||
boolean flag = true;
|
||||
Iterator iterator = BlockPosition.b(blockposition.a(-4, -1, -4), blockposition.a(4, 1, 4)).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
BlockPosition blockposition1 = (BlockPosition) iterator.next();
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == this) {
|
||||
--i;
|
||||
if (i <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1);
|
||||
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) {
|
||||
blockposition = blockposition2;
|
||||
}
|
||||
|
||||
blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1);
|
||||
}
|
||||
|
||||
if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, iblockdata, 2); // CraftBukkit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.f(iblockaccess, blockposition);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition1);
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
return block != Blocks.MYCELIUM && block != Blocks.PODZOL ? iworldreader.getLightLevel(blockposition, 0) < 13 && this.b(iblockdata1, (IBlockAccess) iworldreader, blockposition1) : true;
|
||||
}
|
||||
|
||||
public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) {
|
||||
generatoraccess.setAir(blockposition);
|
||||
WorldGenerator<WorldGenFeatureEmptyConfiguration> worldgenerator = null;
|
||||
|
||||
if (this == Blocks.BROWN_MUSHROOM) {
|
||||
BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit
|
||||
worldgenerator = WorldGenerator.U;
|
||||
} else if (this == Blocks.RED_MUSHROOM) {
|
||||
BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit
|
||||
worldgenerator = WorldGenerator.T;
|
||||
}
|
||||
|
||||
if (worldgenerator != null && worldgenerator.generate(generatoraccess, generatoraccess.getChunkProvider().getChunkGenerator(), random, blockposition, WorldGenFeatureConfiguration.e)) {
|
||||
return true;
|
||||
} else {
|
||||
generatoraccess.setTypeAndData(blockposition, iblockdata, 3);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return (double) random.nextFloat() < 0.4D;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
this.a((GeneratorAccess) world, blockposition, iblockdata, random);
|
||||
}
|
||||
|
||||
public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
63
src/main/java/net/minecraft/server/BlockNetherWart.java
Normal file
63
src/main/java/net/minecraft/server/BlockNetherWart.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockNetherWart extends BlockPlant {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.U;
|
||||
private static final VoxelShape[] b = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 5.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 11.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D)};
|
||||
|
||||
protected BlockNetherWart(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNetherWart.AGE, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockNetherWart.b[(Integer) iblockdata.get(BlockNetherWart.AGE)];
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.getBlock() == Blocks.SOUL_SAND;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
int i = (Integer) iblockdata.get(BlockNetherWart.AGE);
|
||||
|
||||
if (i < 3 && random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.wartModifier) * 10)) == 0) { // Spigot
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockNetherWart.AGE, i + 1);
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata, 2); // CraftBukkit
|
||||
}
|
||||
|
||||
super.a(iblockdata, world, blockposition, random);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
int j = 1;
|
||||
|
||||
if ((Integer) iblockdata.get(BlockNetherWart.AGE) >= 3) {
|
||||
j = 2 + world.random.nextInt(3);
|
||||
if (i > 0) {
|
||||
j += world.random.nextInt(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
a(world, blockposition, new ItemStack(Items.NETHER_WART));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(Items.NETHER_WART);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockNetherWart.AGE);
|
||||
}
|
||||
}
|
||||
78
src/main/java/net/minecraft/server/BlockNote.java
Normal file
78
src/main/java/net/minecraft/server/BlockNote.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockNote extends Block {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyInstrument> INSTRUMENT = BlockProperties.as;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
public static final BlockStateInteger NOTE = BlockProperties.aj;
|
||||
|
||||
public BlockNote(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNote.INSTRUMENT, BlockPropertyInstrument.HARP)).set(BlockNote.NOTE, 0)).set(BlockNote.POWERED, false));
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().down())));
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == EnumDirection.DOWN ? (IBlockData) iblockdata.set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(iblockdata1)) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
|
||||
if (flag != (Boolean) iblockdata.get(BlockNote.POWERED)) {
|
||||
if (flag) {
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockNote.POWERED, flag), 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void play(World world, BlockPosition blockposition, IBlockData data) { // CraftBukkit
|
||||
if (world.getType(blockposition.up()).isAir()) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, blockposition, data.get(BlockNote.INSTRUMENT), data.get(BlockNote.NOTE));
|
||||
if (!event.isCancelled()) {
|
||||
world.playBlockAction(blockposition, this, 0, 0);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockNote.NOTE);
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
entityhuman.a(StatisticList.TUNE_NOTEBLOCK);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) {
|
||||
if (!world.isClientSide) {
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
entityhuman.a(StatisticList.PLAY_NOTEBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) {
|
||||
int k = (Integer) iblockdata.get(BlockNote.NOTE);
|
||||
float f = (float) Math.pow(2.0D, (double) (k - 12) / 12.0D);
|
||||
|
||||
world.a((EntityHuman) null, blockposition, ((BlockPropertyInstrument) iblockdata.get(BlockNote.INSTRUMENT)).a(), SoundCategory.RECORDS, 3.0F, f);
|
||||
world.addParticle(Particles.I, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 1.2D, (double) blockposition.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockNote.INSTRUMENT, BlockNote.POWERED, BlockNote.NOTE);
|
||||
}
|
||||
}
|
||||
108
src/main/java/net/minecraft/server/BlockObserver.java
Normal file
108
src/main/java/net/minecraft/server/BlockObserver.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockObserver extends BlockDirectional {
|
||||
|
||||
public static final BlockStateBoolean b = BlockProperties.t;
|
||||
|
||||
public BlockObserver(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockObserver.FACING, EnumDirection.SOUTH)).set(BlockObserver.b, false));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockObserver.FACING, BlockObserver.b);
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockObserver.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockObserver.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockObserver.FACING)));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if ((Boolean) iblockdata.get(BlockObserver.b)) {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockObserver.b, false), 2);
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockObserver.b, true), 2);
|
||||
world.getBlockTickList().a(blockposition, this, 2);
|
||||
}
|
||||
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (iblockdata.get(BlockObserver.FACING) == enumdirection && !(Boolean) iblockdata.get(BlockObserver.b)) {
|
||||
this.a(generatoraccess, blockposition);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
private void a(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
if (!generatoraccess.e() && !generatoraccess.getBlockTickList().a(blockposition, this)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockObserver.FACING);
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite());
|
||||
|
||||
world.a(blockposition1, (Block) this, blockposition);
|
||||
world.a(blockposition1, (Block) this, enumdirection);
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return iblockdata.a(iblockaccess, blockposition, enumdirection);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockObserver.b) && iblockdata.get(BlockObserver.FACING) == enumdirection ? 15 : 0;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if (!world.e() && (Boolean) iblockdata.get(BlockObserver.b) && !world.getBlockTickList().a(blockposition, this)) {
|
||||
IBlockData iblockdata2 = (IBlockData) iblockdata.set(BlockObserver.b, false);
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata2, 18);
|
||||
this.a(world, blockposition, iblockdata2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if (!world.isClientSide && (Boolean) iblockdata.get(BlockObserver.b) && world.getBlockTickList().a(blockposition, this)) {
|
||||
this.a(world, blockposition, (IBlockData) iblockdata.set(BlockObserver.b, false));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockObserver.FACING, blockactioncontext.d().opposite().opposite());
|
||||
}
|
||||
}
|
||||
84
src/main/java/net/minecraft/server/BlockOre.java
Normal file
84
src/main/java/net/minecraft/server/BlockOre.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockOre extends Block {
|
||||
|
||||
public BlockOre(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return (IMaterial) (this == Blocks.COAL_ORE ? Items.COAL : (this == Blocks.DIAMOND_ORE ? Items.DIAMOND : (this == Blocks.LAPIS_ORE ? Items.LAPIS_LAZULI : (this == Blocks.EMERALD_ORE ? Items.EMERALD : (this == Blocks.NETHER_QUARTZ_ORE ? Items.QUARTZ : this)))));
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return this == Blocks.LAPIS_ORE ? 4 + random.nextInt(5) : 1;
|
||||
}
|
||||
|
||||
public int getDropCount(IBlockData iblockdata, int i, World world, BlockPosition blockposition, Random random) {
|
||||
if (i > 0 && this != this.getDropType((IBlockData) this.getStates().a().iterator().next(), world, blockposition, i)) {
|
||||
int j = random.nextInt(i + 2) - 1;
|
||||
|
||||
if (j < 0) {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
return this.a(iblockdata, random) * (j + 1);
|
||||
} else {
|
||||
return this.a(iblockdata, random);
|
||||
}
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, i);
|
||||
/* CraftBukkit start - Delegated to getExpDrop
|
||||
if (this.getDropType(iblockdata, world, blockposition, i) != this) {
|
||||
int j = 0;
|
||||
|
||||
if (this == Blocks.COAL_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 0, 2);
|
||||
} else if (this == Blocks.DIAMOND_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 3, 7);
|
||||
} else if (this == Blocks.EMERALD_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 3, 7);
|
||||
} else if (this == Blocks.LAPIS_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 2, 5);
|
||||
} else if (this == Blocks.NETHER_QUARTZ_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 2, 5);
|
||||
}
|
||||
|
||||
this.dropExperience(world, blockposition, j);
|
||||
}
|
||||
// */
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) {
|
||||
if (this.getDropType(iblockdata, world, blockposition, enchantmentLevel) != this) {
|
||||
int j = 0;
|
||||
|
||||
if (this == Blocks.COAL_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 0, 2);
|
||||
} else if (this == Blocks.DIAMOND_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 3, 7);
|
||||
} else if (this == Blocks.EMERALD_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 3, 7);
|
||||
} else if (this == Blocks.LAPIS_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 2, 5);
|
||||
} else if (this == Blocks.NETHER_QUARTZ_ORE) {
|
||||
j = MathHelper.nextInt(world.random, 2, 5);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(this);
|
||||
}
|
||||
}
|
||||
433
src/main/java/net/minecraft/server/BlockPiston.java
Normal file
433
src/main/java/net/minecraft/server/BlockPiston.java
Normal file
@@ -0,0 +1,433 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
// CraftBukkit start
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockPiston extends BlockDirectional {
|
||||
|
||||
public static final BlockStateBoolean EXTENDED = BlockProperties.f;
|
||||
protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 12.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape o = Block.a(4.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape p = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 12.0D);
|
||||
protected static final VoxelShape q = Block.a(0.0D, 0.0D, 4.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape r = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
protected static final VoxelShape s = Block.a(0.0D, 4.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
private final boolean sticky;
|
||||
|
||||
public BlockPiston(boolean flag, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPiston.FACING, EnumDirection.NORTH)).set(BlockPiston.EXTENDED, false));
|
||||
this.sticky = flag;
|
||||
}
|
||||
|
||||
public boolean q(IBlockData iblockdata) {
|
||||
return !(Boolean) iblockdata.get(BlockPiston.EXTENDED);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
if ((Boolean) iblockdata.get(BlockPiston.EXTENDED)) {
|
||||
switch ((EnumDirection) iblockdata.get(BlockPiston.FACING)) {
|
||||
case DOWN:
|
||||
return BlockPiston.s;
|
||||
case UP:
|
||||
default:
|
||||
return BlockPiston.r;
|
||||
case NORTH:
|
||||
return BlockPiston.q;
|
||||
case SOUTH:
|
||||
return BlockPiston.p;
|
||||
case WEST:
|
||||
return BlockPiston.o;
|
||||
case EAST:
|
||||
return BlockPiston.c;
|
||||
}
|
||||
} else {
|
||||
return VoxelShapes.b();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean r(IBlockData iblockdata) {
|
||||
return !(Boolean) iblockdata.get(BlockPiston.EXTENDED) || iblockdata.get(BlockPiston.FACING) == EnumDirection.DOWN;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (!world.isClientSide) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (!world.isClientSide) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata1.getBlock() != iblockdata.getBlock()) {
|
||||
if (!world.isClientSide && world.getTileEntity(blockposition) == null) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) ((IBlockData) this.getBlockData().set(BlockPiston.FACING, blockactioncontext.d().opposite())).set(BlockPiston.EXTENDED, false);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING);
|
||||
boolean flag = this.a(world, blockposition, enumdirection);
|
||||
|
||||
if (flag && !(Boolean) iblockdata.get(BlockPiston.EXTENDED)) {
|
||||
if ((new PistonExtendsChecker(world, blockposition, enumdirection, true)).a()) {
|
||||
world.playBlockAction(blockposition, this, 0, enumdirection.a());
|
||||
}
|
||||
} else if (!flag && (Boolean) iblockdata.get(BlockPiston.EXTENDED)) {
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection, 2);
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
byte b0 = 1;
|
||||
|
||||
if (iblockdata1.getBlock() == Blocks.MOVING_PISTON && iblockdata1.get(BlockPiston.FACING) == enumdirection) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity instanceof TileEntityPiston) {
|
||||
TileEntityPiston tileentitypiston = (TileEntityPiston) tileentity;
|
||||
|
||||
if (tileentitypiston.c() && (tileentitypiston.a(0.0F) < 0.5F || world.getTime() == tileentitypiston.k() || ((WorldServer) world).j_())) {
|
||||
b0 = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
//if (!this.sticky) { // Paper - Prevents empty sticky pistons from firing retract - history behind is odd
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
//} // Paper
|
||||
// PAIL: checkME - what happened to setTypeAndData?
|
||||
// CraftBukkit end
|
||||
world.playBlockAction(blockposition, this, b0, enumdirection.a());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection1 = aenumdirection[j];
|
||||
|
||||
if (enumdirection1 != enumdirection && world.isBlockFacePowered(blockposition.shift(enumdirection1), enumdirection1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (world.isBlockFacePowered(blockposition, EnumDirection.DOWN)) {
|
||||
return true;
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
EnumDirection[] aenumdirection1 = EnumDirection.values();
|
||||
|
||||
j = aenumdirection1.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnumDirection enumdirection2 = aenumdirection1[k];
|
||||
|
||||
if (enumdirection2 != EnumDirection.DOWN && world.isBlockFacePowered(blockposition1.shift(enumdirection2), enumdirection2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING);
|
||||
|
||||
if (!world.isClientSide) {
|
||||
boolean flag = this.a(world, blockposition, enumdirection);
|
||||
|
||||
if (flag && (i == 1 || i == 2)) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockPiston.EXTENDED, true), 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!flag && i == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
if (!this.a(world, blockposition, enumdirection, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockPiston.EXTENDED, true), 67);
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.25F + 0.6F);
|
||||
} else if (i == 1 || i == 2) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition.shift(enumdirection));
|
||||
|
||||
if (tileentity instanceof TileEntityPiston) {
|
||||
((TileEntityPiston) tileentity).j();
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT), 3);
|
||||
world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true));
|
||||
if (this.sticky) {
|
||||
BlockPosition blockposition1 = blockposition.a(enumdirection.getAdjacentX() * 2, enumdirection.getAdjacentY() * 2, enumdirection.getAdjacentZ() * 2);
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
Block block = iblockdata1.getBlock();
|
||||
boolean flag1 = false;
|
||||
|
||||
if (block == Blocks.MOVING_PISTON) {
|
||||
TileEntity tileentity1 = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity1 instanceof TileEntityPiston) {
|
||||
TileEntityPiston tileentitypiston = (TileEntityPiston) tileentity1;
|
||||
|
||||
if (tileentitypiston.d() == enumdirection && tileentitypiston.c()) {
|
||||
tileentitypiston.j();
|
||||
flag1 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag1) {
|
||||
if (i == 1 && !iblockdata1.isAir() && a(iblockdata1, world, blockposition1, enumdirection.opposite(), false, enumdirection) && (iblockdata1.getPushReaction() == EnumPistonReaction.NORMAL || block == Blocks.PISTON || block == Blocks.STICKY_PISTON)) {
|
||||
this.a(world, blockposition, enumdirection, false);
|
||||
} else {
|
||||
world.setAir(blockposition.shift(enumdirection));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
world.setAir(blockposition.shift(enumdirection));
|
||||
}
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, EnumDirection enumdirection, boolean flag, EnumDirection enumdirection1) {
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block == Blocks.OBSIDIAN) {
|
||||
return false;
|
||||
} else if (!world.getWorldBorder().a(blockposition)) {
|
||||
return false;
|
||||
} else if (blockposition.getY() >= 0 && (enumdirection != EnumDirection.DOWN || blockposition.getY() != 0)) {
|
||||
if (blockposition.getY() <= world.getHeight() - 1 && (enumdirection != EnumDirection.UP || blockposition.getY() != world.getHeight() - 1)) {
|
||||
if (block != Blocks.PISTON && block != Blocks.STICKY_PISTON) {
|
||||
if (iblockdata.e(world, blockposition) == -1.0F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (iblockdata.getPushReaction()) {
|
||||
case BLOCK:
|
||||
return false;
|
||||
case DESTROY:
|
||||
return flag;
|
||||
case PUSH_ONLY:
|
||||
return enumdirection == enumdirection1;
|
||||
}
|
||||
} else if ((Boolean) iblockdata.get(BlockPiston.EXTENDED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !block.isTileEntity();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition, EnumDirection enumdirection, boolean flag) {
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
|
||||
if (!flag && world.getType(blockposition1).getBlock() == Blocks.PISTON_HEAD) {
|
||||
world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 20);
|
||||
}
|
||||
|
||||
PistonExtendsChecker pistonextendschecker = new PistonExtendsChecker(world, blockposition, enumdirection, flag);
|
||||
|
||||
if (!pistonextendschecker.a()) {
|
||||
return false;
|
||||
} else {
|
||||
List<BlockPosition> list = pistonextendschecker.getMovedBlocks();
|
||||
List<IBlockData> list1 = Lists.newArrayList();
|
||||
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
BlockPosition blockposition2 = (BlockPosition) list.get(i);
|
||||
|
||||
list1.add(world.getType(blockposition2));
|
||||
}
|
||||
|
||||
List<BlockPosition> list2 = pistonextendschecker.getBrokenBlocks();
|
||||
int j = list.size() + list2.size();
|
||||
IBlockData[] aiblockdata = new IBlockData[j];
|
||||
EnumDirection enumdirection1 = flag ? enumdirection : enumdirection.opposite();
|
||||
Set<BlockPosition> set = Sets.newHashSet(list);
|
||||
// CraftBukkit start
|
||||
final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
final List<BlockPosition> moved = pistonextendschecker.getMovedBlocks();
|
||||
final List<BlockPosition> broken = pistonextendschecker.getBrokenBlocks();
|
||||
|
||||
List<org.bukkit.block.Block> blocks = new AbstractList<org.bukkit.block.Block>() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return moved.size() + broken.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.block.Block get(int index) {
|
||||
if (index >= size() || index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
BlockPosition pos = (BlockPosition) (index < moved.size() ? moved.get(index) : broken.get(index - moved.size()));
|
||||
return bblock.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
};
|
||||
org.bukkit.event.block.BlockPistonEvent event;
|
||||
if (flag) {
|
||||
event = new BlockPistonExtendEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1));
|
||||
} else {
|
||||
event = new BlockPistonRetractEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1));
|
||||
}
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
for (BlockPosition b : broken) {
|
||||
world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3);
|
||||
}
|
||||
for (BlockPosition b : moved) {
|
||||
world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3);
|
||||
b = b.shift(enumdirection1);
|
||||
world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
BlockPosition blockposition3;
|
||||
int k;
|
||||
IBlockData iblockdata;
|
||||
|
||||
for (k = list2.size() - 1; k >= 0; --k) {
|
||||
blockposition3 = (BlockPosition) list2.get(k);
|
||||
iblockdata = world.getType(blockposition3);
|
||||
iblockdata.a(world, blockposition3, 0);
|
||||
world.setTypeAndData(blockposition3, Blocks.AIR.getBlockData(), 18);
|
||||
--j;
|
||||
aiblockdata[j] = iblockdata;
|
||||
}
|
||||
|
||||
for (k = list.size() - 1; k >= 0; --k) {
|
||||
blockposition3 = (BlockPosition) list.get(k);
|
||||
iblockdata = world.getType(blockposition3);
|
||||
blockposition3 = blockposition3.shift(enumdirection1);
|
||||
set.remove(blockposition3);
|
||||
world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68);
|
||||
world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
|
||||
--j;
|
||||
aiblockdata[j] = iblockdata;
|
||||
}
|
||||
|
||||
IBlockData iblockdata1;
|
||||
|
||||
if (flag) {
|
||||
BlockPropertyPistonType blockpropertypistontype = this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT;
|
||||
|
||||
iblockdata1 = (IBlockData) ((IBlockData) Blocks.PISTON_HEAD.getBlockData().set(BlockPistonExtension.FACING, enumdirection)).set(BlockPistonExtension.TYPE, blockpropertypistontype);
|
||||
iblockdata = (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT);
|
||||
set.remove(blockposition1);
|
||||
world.setTypeAndData(blockposition1, iblockdata, 68);
|
||||
world.setTileEntity(blockposition1, BlockPistonMoving.a(iblockdata1, enumdirection, true, true));
|
||||
}
|
||||
|
||||
Iterator iterator = set.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
blockposition3 = (BlockPosition) iterator.next();
|
||||
world.setTypeAndData(blockposition3, Blocks.AIR.getBlockData(), 66);
|
||||
}
|
||||
|
||||
for (k = list2.size() - 1; k >= 0; --k) {
|
||||
iblockdata1 = aiblockdata[j++];
|
||||
BlockPosition blockposition4 = (BlockPosition) list2.get(k);
|
||||
|
||||
iblockdata1.b(world, blockposition4, 2);
|
||||
world.applyPhysics(blockposition4, iblockdata1.getBlock());
|
||||
}
|
||||
|
||||
for (k = list.size() - 1; k >= 0; --k) {
|
||||
world.applyPhysics((BlockPosition) list.get(k), aiblockdata[j++].getBlock());
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
world.applyPhysics(blockposition1, Blocks.PISTON_HEAD);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockPiston.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockPiston.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockPiston.FACING)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockPiston.FACING, BlockPiston.EXTENDED);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return iblockdata.get(BlockPiston.FACING) != enumdirection.opposite() && (Boolean) iblockdata.get(BlockPiston.EXTENDED) ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID;
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
47
src/main/java/net/minecraft/server/BlockPlant.java
Normal file
47
src/main/java/net/minecraft/server/BlockPlant.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockPlant extends Block {
|
||||
|
||||
protected BlockPlant(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return block == Blocks.GRASS_BLOCK || block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.PODZOL || block == Blocks.FARMLAND;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
if (!(generatoraccess instanceof WorldServer && ((WorldServer) generatoraccess).hasPhysicsEvent) || !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(generatoraccess, blockposition).isCancelled()) { // Paper
|
||||
return Blocks.AIR.getBlockData();
|
||||
}
|
||||
}
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
|
||||
return this.b(iworldreader.getType(blockposition1), (IBlockAccess) iworldreader, blockposition1);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
381
src/main/java/net/minecraft/server/BlockPortal.java
Normal file
381
src/main/java/net/minecraft/server/BlockPortal.java
Normal file
@@ -0,0 +1,381 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.event.entity.EntityPortalEnterEvent;
|
||||
import org.bukkit.event.world.PortalCreateEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockPortal extends Block {
|
||||
|
||||
public static final BlockStateEnum<EnumDirection.EnumAxis> AXIS = BlockProperties.z;
|
||||
protected static final VoxelShape b = Block.a(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
|
||||
protected static final VoxelShape c = Block.a(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
|
||||
|
||||
public BlockPortal(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPortal.AXIS, EnumDirection.EnumAxis.X));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
switch ((EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS)) {
|
||||
case Z:
|
||||
return BlockPortal.c;
|
||||
case X:
|
||||
default:
|
||||
return BlockPortal.b;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.worldProvider.isOverworld() && world.getGameRules().getBoolean("doMobSpawning") && random.nextInt(2000) < world.getDifficulty().a()) { // Spigot
|
||||
int i = blockposition.getY();
|
||||
|
||||
BlockPosition blockposition1;
|
||||
|
||||
for (blockposition1 = blockposition; !world.getType(blockposition1).q() && blockposition1.getY() > 0; blockposition1 = blockposition1.down()) {
|
||||
;
|
||||
}
|
||||
|
||||
if (i > 0 && !world.getType(blockposition1.up()).isOccluding()) {
|
||||
// CraftBukkit - set spawn reason to NETHER_PORTAL
|
||||
Entity entity = EntityTypes.ZOMBIE_PIGMAN.spawnCreature(world, (NBTTagCompound) null, (IChatBaseComponent) null, (EntityHuman) null, blockposition1.up(), false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NETHER_PORTAL);
|
||||
|
||||
if (entity != null) {
|
||||
entity.portalCooldown = entity.aQ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
BlockPortal.Shape blockportal_shape = this.b(generatoraccess, blockposition);
|
||||
|
||||
if (blockportal_shape != null) {
|
||||
// CraftBukkit start - return portalcreator
|
||||
return blockportal_shape.createPortal();
|
||||
// return true;
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPortal.Shape b(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
BlockPortal.Shape blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.X);
|
||||
|
||||
if (blockportal_shape.d() && blockportal_shape.e == 0) {
|
||||
return blockportal_shape;
|
||||
} else {
|
||||
BlockPortal.Shape blockportal_shape1 = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.Z);
|
||||
|
||||
return blockportal_shape1.d() && blockportal_shape1.e == 0 ? blockportal_shape1 : null;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
EnumDirection.EnumAxis enumdirection_enumaxis = enumdirection.k();
|
||||
EnumDirection.EnumAxis enumdirection_enumaxis1 = (EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS);
|
||||
boolean flag = enumdirection_enumaxis1 != enumdirection_enumaxis && enumdirection_enumaxis.c();
|
||||
|
||||
return !flag && iblockdata1.getBlock() != this && !(new BlockPortal.Shape(generatoraccess, blockposition, enumdirection_enumaxis1)).f() ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.TRANSLUCENT;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!entity.isPassenger() && !entity.isVehicle() && entity.bm()) {
|
||||
// CraftBukkit start - Entity in portal
|
||||
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
entity.e(blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
switch (enumblockrotation) {
|
||||
case COUNTERCLOCKWISE_90:
|
||||
case CLOCKWISE_90:
|
||||
switch ((EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS)) {
|
||||
case Z:
|
||||
return (IBlockData) iblockdata.set(BlockPortal.AXIS, EnumDirection.EnumAxis.X);
|
||||
case X:
|
||||
return (IBlockData) iblockdata.set(BlockPortal.AXIS, EnumDirection.EnumAxis.Z);
|
||||
default:
|
||||
return iblockdata;
|
||||
}
|
||||
default:
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockPortal.AXIS);
|
||||
}
|
||||
|
||||
public ShapeDetector.ShapeDetectorCollection c(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
EnumDirection.EnumAxis enumdirection_enumaxis = EnumDirection.EnumAxis.Z;
|
||||
BlockPortal.Shape blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.X);
|
||||
LoadingCache<BlockPosition, ShapeDetectorBlock> loadingcache = ShapeDetector.a(generatoraccess, true);
|
||||
|
||||
if (!blockportal_shape.d()) {
|
||||
enumdirection_enumaxis = EnumDirection.EnumAxis.X;
|
||||
blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.Z);
|
||||
}
|
||||
|
||||
if (!blockportal_shape.d()) {
|
||||
return new ShapeDetector.ShapeDetectorCollection(blockposition, EnumDirection.NORTH, EnumDirection.UP, loadingcache, 1, 1, 1);
|
||||
} else {
|
||||
int[] aint = new int[EnumDirection.EnumAxisDirection.values().length];
|
||||
EnumDirection enumdirection = blockportal_shape.c.f();
|
||||
BlockPosition blockposition1 = blockportal_shape.position.up(blockportal_shape.a() - 1);
|
||||
EnumDirection.EnumAxisDirection[] aenumdirection_enumaxisdirection = EnumDirection.EnumAxisDirection.values();
|
||||
int i = aenumdirection_enumaxisdirection.length;
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection = aenumdirection_enumaxisdirection[j];
|
||||
ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = new ShapeDetector.ShapeDetectorCollection(enumdirection.c() == enumdirection_enumaxisdirection ? blockposition1 : blockposition1.shift(blockportal_shape.c, blockportal_shape.b() - 1), EnumDirection.a(enumdirection_enumaxisdirection, enumdirection_enumaxis), EnumDirection.UP, loadingcache, blockportal_shape.b(), blockportal_shape.a(), 1);
|
||||
|
||||
for (int k = 0; k < blockportal_shape.b(); ++k) {
|
||||
for (int l = 0; l < blockportal_shape.a(); ++l) {
|
||||
ShapeDetectorBlock shapedetectorblock = shapedetector_shapedetectorcollection.a(k, l, 1);
|
||||
|
||||
if (!shapedetectorblock.a().isAir()) {
|
||||
++aint[enumdirection_enumaxisdirection.ordinal()];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection1 = EnumDirection.EnumAxisDirection.POSITIVE;
|
||||
EnumDirection.EnumAxisDirection[] aenumdirection_enumaxisdirection1 = EnumDirection.EnumAxisDirection.values();
|
||||
|
||||
j = aenumdirection_enumaxisdirection1.length;
|
||||
|
||||
for (int i1 = 0; i1 < j; ++i1) {
|
||||
EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection2 = aenumdirection_enumaxisdirection1[i1];
|
||||
|
||||
if (aint[enumdirection_enumaxisdirection2.ordinal()] < aint[enumdirection_enumaxisdirection1.ordinal()]) {
|
||||
enumdirection_enumaxisdirection1 = enumdirection_enumaxisdirection2;
|
||||
}
|
||||
}
|
||||
|
||||
return new ShapeDetector.ShapeDetectorCollection(enumdirection.c() == enumdirection_enumaxisdirection1 ? blockposition1 : blockposition1.shift(blockportal_shape.c, blockportal_shape.b() - 1), EnumDirection.a(enumdirection_enumaxisdirection1, enumdirection_enumaxis), EnumDirection.UP, loadingcache, blockportal_shape.b(), blockportal_shape.a(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public static class Shape {
|
||||
|
||||
private final GeneratorAccess a;
|
||||
private final EnumDirection.EnumAxis b;
|
||||
private final EnumDirection c;
|
||||
private final EnumDirection d;
|
||||
private int e;
|
||||
private BlockPosition position;
|
||||
private int height;
|
||||
private int width;
|
||||
java.util.Collection<org.bukkit.block.Block> blocks = new java.util.HashSet<org.bukkit.block.Block>(); // CraftBukkit - add field
|
||||
|
||||
public Shape(GeneratorAccess generatoraccess, BlockPosition blockposition, EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||
this.a = generatoraccess;
|
||||
this.b = enumdirection_enumaxis;
|
||||
if (enumdirection_enumaxis == EnumDirection.EnumAxis.X) {
|
||||
this.d = EnumDirection.EAST;
|
||||
this.c = EnumDirection.WEST;
|
||||
} else {
|
||||
this.d = EnumDirection.NORTH;
|
||||
this.c = EnumDirection.SOUTH;
|
||||
}
|
||||
|
||||
for (BlockPosition blockposition1 = blockposition; blockposition.getY() > blockposition1.getY() - 21 && blockposition.getY() > 0 && this.a(generatoraccess.getType(blockposition.down())); blockposition = blockposition.down()) {
|
||||
;
|
||||
}
|
||||
|
||||
int i = this.a(blockposition, this.d) - 1;
|
||||
|
||||
if (i >= 0) {
|
||||
this.position = blockposition.shift(this.d, i);
|
||||
this.width = this.a(this.position, this.c);
|
||||
if (this.width < 2 || this.width > 21) {
|
||||
this.position = null;
|
||||
this.width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.position != null) {
|
||||
this.height = this.c();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected int a(BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 22; ++i) {
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection, i);
|
||||
|
||||
if (!this.a(this.a.getType(blockposition1)) || this.a.getType(blockposition1.down()).getBlock() != Blocks.OBSIDIAN) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Block block = this.a.getType(blockposition.shift(enumdirection, i)).getBlock();
|
||||
|
||||
return block == Blocks.OBSIDIAN ? i : 0;
|
||||
}
|
||||
|
||||
public int a() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
public int b() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
protected int c() {
|
||||
// CraftBukkit start
|
||||
this.blocks.clear();
|
||||
// CraftBukkit end
|
||||
int i;
|
||||
|
||||
label56:
|
||||
for (this.height = 0; this.height < 21; ++this.height) {
|
||||
for (i = 0; i < this.width; ++i) {
|
||||
BlockPosition blockposition = this.position.shift(this.c, i).up(this.height);
|
||||
IBlockData iblockdata = this.a.getType(blockposition);
|
||||
|
||||
if (!this.a(iblockdata)) {
|
||||
break label56;
|
||||
}
|
||||
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block == Blocks.NETHER_PORTAL) {
|
||||
++this.e;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
block = this.a.getType(blockposition.shift(this.d)).getBlock();
|
||||
if (block != Blocks.OBSIDIAN) {
|
||||
break label56;
|
||||
// CraftBukkit start - add the block to our list
|
||||
} else {
|
||||
BlockPosition pos = blockposition.shift(this.d);
|
||||
blocks.add(CraftBlock.at(this.a, pos));
|
||||
// CraftBukkit end
|
||||
}
|
||||
} else if (i == this.width - 1) {
|
||||
block = this.a.getType(blockposition.shift(this.c)).getBlock();
|
||||
if (block != Blocks.OBSIDIAN) {
|
||||
break label56;
|
||||
// CraftBukkit start - add the block to our list
|
||||
} else {
|
||||
BlockPosition pos = blockposition.shift(this.c);
|
||||
blocks.add(CraftBlock.at(this.a, pos));
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this.width; ++i) {
|
||||
if (this.a.getType(this.position.shift(this.c, i).up(this.height)).getBlock() != Blocks.OBSIDIAN) {
|
||||
this.height = 0;
|
||||
break;
|
||||
// CraftBukkit start - add the block to our list
|
||||
} else {
|
||||
BlockPosition pos = this.position.shift(this.c, i).up(this.height);
|
||||
blocks.add(CraftBlock.at(this.a, pos));
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
if (this.height <= 21 && this.height >= 3) {
|
||||
return this.height;
|
||||
} else {
|
||||
this.position = null;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean a(IBlockData iblockdata) {
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return iblockdata.isAir() || block == Blocks.FIRE || block == Blocks.NETHER_PORTAL;
|
||||
}
|
||||
|
||||
public boolean d() {
|
||||
return this.position != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
|
||||
}
|
||||
|
||||
// CraftBukkit start - return boolean
|
||||
public boolean createPortal() {
|
||||
org.bukkit.World bworld = this.a.getMinecraftWorld().getWorld();
|
||||
|
||||
// Copy below for loop
|
||||
for (int i = 0; i < this.width; ++i) {
|
||||
BlockPosition blockposition = this.position.shift(this.c, i);
|
||||
|
||||
for (int j = 0; j < this.height; ++j) {
|
||||
BlockPosition pos = blockposition.up(j);
|
||||
blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()));
|
||||
}
|
||||
}
|
||||
|
||||
PortalCreateEvent event = new PortalCreateEvent(blocks, bworld, PortalCreateEvent.CreateReason.FIRE);
|
||||
this.a.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
for (int i = 0; i < this.width; ++i) {
|
||||
BlockPosition blockposition = this.position.shift(this.c, i);
|
||||
|
||||
for (int j = 0; j < this.height; ++j) {
|
||||
this.a.setTypeAndData(blockposition.up(j), (IBlockData) Blocks.NETHER_PORTAL.getBlockData().set(BlockPortal.AXIS, this.b), 18);
|
||||
}
|
||||
}
|
||||
|
||||
return true; // CraftBukkit
|
||||
}
|
||||
|
||||
private boolean g() {
|
||||
return this.e >= this.width * this.height;
|
||||
}
|
||||
|
||||
public boolean f() {
|
||||
return this.d() && this.g();
|
||||
}
|
||||
}
|
||||
}
|
||||
422
src/main/java/net/minecraft/server/BlockPosition.java
Normal file
422
src/main/java/net/minecraft/server/BlockPosition.java
Normal file
@@ -0,0 +1,422 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@Immutable
|
||||
public class BlockPosition extends BaseBlockPosition {
|
||||
|
||||
//private static final Logger b = LogManager.getLogger(); // Paper - variable name conflict, logger isn't used
|
||||
public static final BlockPosition ZERO = new BlockPosition(0, 0, 0);
|
||||
private static final int c = 1 + MathHelper.e(MathHelper.c(30000000));
|
||||
private static final int d = BlockPosition.c;
|
||||
private static final int f = 64 - BlockPosition.c - BlockPosition.d;
|
||||
private static final int g = 0 + BlockPosition.d;
|
||||
private static final int h = BlockPosition.g + BlockPosition.f;
|
||||
private static final long i = (1L << BlockPosition.c) - 1L;
|
||||
private static final long j = (1L << BlockPosition.f) - 1L;
|
||||
private static final long k = (1L << BlockPosition.d) - 1L;
|
||||
|
||||
public BlockPosition(int i, int j, int k) {
|
||||
super(i, j, k);
|
||||
}
|
||||
|
||||
public BlockPosition(double d0, double d1, double d2) {
|
||||
super(d0, d1, d2);
|
||||
}
|
||||
|
||||
public BlockPosition(Entity entity) {
|
||||
this(entity.locX, entity.locY, entity.locZ);
|
||||
}
|
||||
|
||||
public BlockPosition(Vec3D vec3d) {
|
||||
this(vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
public BlockPosition(BaseBlockPosition baseblockposition) {
|
||||
this(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public BlockPosition a(double d0, double d1, double d2) {
|
||||
return d0 == 0.0D && d1 == 0.0D && d2 == 0.0D ? this : new BlockPosition((double) this.getX() + d0, (double) this.getY() + d1, (double) this.getZ() + d2);
|
||||
}
|
||||
|
||||
public BlockPosition add(int i, int j, int k) {return a(i, j, k);} // Paper - OBFHELPER
|
||||
public BlockPosition a(int i, int j, int k) {
|
||||
return i == 0 && j == 0 && k == 0 ? this : new BlockPosition(this.getX() + i, this.getY() + j, this.getZ() + k);
|
||||
}
|
||||
|
||||
public BlockPosition a(BaseBlockPosition baseblockposition) {
|
||||
return this.a(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public BlockPosition b(BaseBlockPosition baseblockposition) {
|
||||
return this.a(-baseblockposition.getX(), -baseblockposition.getY(), -baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public BlockPosition up() {
|
||||
return new BlockPosition(this.getX(), this.getY() + 1, this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition up(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX(), this.getY() + i, this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition down() {
|
||||
return new BlockPosition(this.getX(), this.getY() - 1, this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition down(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX(), this.getY() - i, this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition north() {
|
||||
return new BlockPosition(this.getX(), this.getY(), this.getZ() - 1); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition north(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX(), this.getY(), this.getZ() - i); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition south() {
|
||||
return new BlockPosition(this.getX(), this.getY(), this.getZ() + 1); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition south(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX(), this.getY(), this.getZ() + i); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition west() {
|
||||
return new BlockPosition(this.getX() - 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition west(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX() - i, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition east() {
|
||||
return new BlockPosition(this.getX() + 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition east(int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX() + i, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
|
||||
}
|
||||
|
||||
public BlockPosition shift(EnumDirection enumdirection) {
|
||||
// Paper Start - Optimize BlockPosition
|
||||
switch(enumdirection) {
|
||||
case UP:
|
||||
return new BlockPosition(this.getX(), this.getY() + 1, this.getZ());
|
||||
case DOWN:
|
||||
return new BlockPosition(this.getX(), this.getY() - 1, this.getZ());
|
||||
case NORTH:
|
||||
return new BlockPosition(this.getX(), this.getY(), this.getZ() - 1);
|
||||
case SOUTH:
|
||||
return new BlockPosition(this.getX(), this.getY(), this.getZ() + 1);
|
||||
case WEST:
|
||||
return new BlockPosition(this.getX() - 1, this.getY(), this.getZ());
|
||||
case EAST:
|
||||
return new BlockPosition(this.getX() + 1, this.getY(), this.getZ());
|
||||
default:
|
||||
return new BlockPosition(this.getX() + enumdirection.getAdjacentX(), this.getY() + enumdirection.getAdjacentY(), this.getZ() + enumdirection.getAdjacentZ());
|
||||
}
|
||||
// Paper End
|
||||
}
|
||||
|
||||
public BlockPosition shift(EnumDirection enumdirection, int i) {
|
||||
return i == 0 ? this : new BlockPosition(this.getX() + enumdirection.getAdjacentX() * i, this.getY() + enumdirection.getAdjacentY() * i, this.getZ() + enumdirection.getAdjacentZ() * i);
|
||||
}
|
||||
|
||||
public BlockPosition a(EnumBlockRotation enumblockrotation) {
|
||||
switch (enumblockrotation) {
|
||||
case NONE:
|
||||
default:
|
||||
return this;
|
||||
case CLOCKWISE_90:
|
||||
return new BlockPosition(-this.getZ(), this.getY(), this.getX());
|
||||
case CLOCKWISE_180:
|
||||
return new BlockPosition(-this.getX(), this.getY(), -this.getZ());
|
||||
case COUNTERCLOCKWISE_90:
|
||||
return new BlockPosition(this.getZ(), this.getY(), -this.getX());
|
||||
}
|
||||
}
|
||||
|
||||
public BlockPosition d(BaseBlockPosition baseblockposition) {
|
||||
return new BlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX());
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
return ((long) this.getX() & BlockPosition.i) << BlockPosition.h | ((long) this.getY() & BlockPosition.j) << BlockPosition.g | ((long) this.getZ() & BlockPosition.k) << 0;
|
||||
}
|
||||
|
||||
public static BlockPosition fromLong(long i) {
|
||||
int j = (int) (i << 64 - BlockPosition.h - BlockPosition.c >> 64 - BlockPosition.c);
|
||||
int k = (int) (i << 64 - BlockPosition.g - BlockPosition.f >> 64 - BlockPosition.f);
|
||||
int l = (int) (i << 64 - BlockPosition.d >> 64 - BlockPosition.d);
|
||||
|
||||
return new BlockPosition(j, k, l);
|
||||
}
|
||||
|
||||
public static Iterable<BlockPosition> a(BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return a(Math.min(blockposition.getX(), blockposition1.getX()), Math.min(blockposition.getY(), blockposition1.getY()), Math.min(blockposition.getZ(), blockposition1.getZ()), Math.max(blockposition.getX(), blockposition1.getX()), Math.max(blockposition.getY(), blockposition1.getY()), Math.max(blockposition.getZ(), blockposition1.getZ()));
|
||||
}
|
||||
|
||||
public static Iterable<BlockPosition> a(int ix, int jx, int kx, int l, int i1, int j1) { // Paper - decompile fix
|
||||
return () -> {
|
||||
return new AbstractIterator<BlockPosition>() {
|
||||
private boolean g = true;
|
||||
private int h;
|
||||
private int i;
|
||||
private int j;
|
||||
|
||||
protected BlockPosition computeNext() {
|
||||
if (this.g) {
|
||||
this.g = false;
|
||||
this.h = ix; // Paper - decompile fix
|
||||
this.i = jx; // Paper - decompile fix
|
||||
this.j = kx; // Paper - decompile fix
|
||||
return new BlockPosition(ix, jx, kx);
|
||||
} else if (this.h == l && this.i == i1 && this.j == j1) {
|
||||
return (BlockPosition) this.endOfData();
|
||||
} else {
|
||||
if (this.h < l) {
|
||||
++this.h;
|
||||
} else if (this.i < i1) {
|
||||
this.h = ix; // Paper - decompile fix
|
||||
++this.i;
|
||||
} else if (this.j < j1) {
|
||||
this.h = ix; // Paper - decompile fix
|
||||
this.i = jx; // Paper - decompile fix
|
||||
++this.j;
|
||||
}
|
||||
|
||||
return new BlockPosition(this.h, this.i, this.j);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public BlockPosition asImmutable() { return h(); } // Paper - OBFHELPER
|
||||
public BlockPosition h() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Iterable<BlockPosition.MutableBlockPosition> b(BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return b(Math.min(blockposition.getX(), blockposition1.getX()), Math.min(blockposition.getY(), blockposition1.getY()), Math.min(blockposition.getZ(), blockposition1.getZ()), Math.max(blockposition.getX(), blockposition1.getX()), Math.max(blockposition.getY(), blockposition1.getY()), Math.max(blockposition.getZ(), blockposition1.getZ()));
|
||||
}
|
||||
|
||||
public static Iterable<BlockPosition.MutableBlockPosition> b(int i, int j, int k, int l, int i1, int j1) {
|
||||
return () -> {
|
||||
return new AbstractIterator<BlockPosition.MutableBlockPosition>() {
|
||||
private BlockPosition.MutableBlockPosition g;
|
||||
|
||||
protected BlockPosition.MutableBlockPosition computeNext() {
|
||||
if (this.g == null) {
|
||||
this.g = new BlockPosition.MutableBlockPosition(i, j, k);
|
||||
return this.g;
|
||||
} else if (this.g.x == l && this.g.y == i1 && this.g.z == j1) {
|
||||
return (BlockPosition.MutableBlockPosition) this.endOfData();
|
||||
} else {
|
||||
if (this.g.x < l) {
|
||||
++this.g.x;
|
||||
} else if (this.g.y < i1) {
|
||||
this.g.x = i; // Paper - decompile fix Readd line removed by the decompiler
|
||||
++this.g.y;
|
||||
} else if (this.g.z < j1) {
|
||||
this.g.x = i; // Paper - decompile fix Readd line removed by the decompiler
|
||||
this.g.y = j; // Paper - decompile fix Readd line removed by the decompiler
|
||||
++this.g.z;
|
||||
}
|
||||
|
||||
return this.g;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public static final class b extends BlockPosition.MutableBlockPosition implements AutoCloseable {
|
||||
|
||||
private boolean f;
|
||||
private static final List<BlockPosition.b> g = Lists.newArrayList();
|
||||
|
||||
private b(int i, int j, int k) {
|
||||
super(i, j, k);
|
||||
}
|
||||
|
||||
public static BlockPosition.b r() {
|
||||
return e(0, 0, 0);
|
||||
}
|
||||
|
||||
public static BlockPosition.b b(Entity entity) {
|
||||
return d(entity.locX, entity.locY, entity.locZ);
|
||||
}
|
||||
|
||||
public static BlockPosition.b d(double d0, double d1, double d2) {
|
||||
return e(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2));
|
||||
}
|
||||
|
||||
public static BlockPosition.b e(int i, int j, int k) {
|
||||
synchronized (BlockPosition.b.g) {
|
||||
if (!BlockPosition.b.g.isEmpty()) {
|
||||
BlockPosition.b blockposition_b = (BlockPosition.b) BlockPosition.b.g.remove(BlockPosition.b.g.size() - 1);
|
||||
|
||||
if (blockposition_b != null && blockposition_b.f) {
|
||||
blockposition_b.f = false;
|
||||
blockposition_b.c(i, j, k);
|
||||
return blockposition_b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BlockPosition.b(i, j, k);
|
||||
}
|
||||
|
||||
public BlockPosition.b c(int i, int j, int k) {
|
||||
return (BlockPosition.b) super.c(i, j, k);
|
||||
}
|
||||
|
||||
public BlockPosition.b c(double d0, double d1, double d2) {
|
||||
return (BlockPosition.b) super.c(d0, d1, d2);
|
||||
}
|
||||
|
||||
public BlockPosition.b g(BaseBlockPosition baseblockposition) {
|
||||
return (BlockPosition.b) super.g(baseblockposition);
|
||||
}
|
||||
|
||||
public BlockPosition.b c(EnumDirection enumdirection) {
|
||||
return (BlockPosition.b) super.c(enumdirection);
|
||||
}
|
||||
|
||||
public BlockPosition.b c(EnumDirection enumdirection, int i) {
|
||||
return (BlockPosition.b) super.c(enumdirection, i);
|
||||
}
|
||||
|
||||
public BlockPosition.b d(int i, int j, int k) {
|
||||
return (BlockPosition.b) super.d(i, j, k);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
synchronized (BlockPosition.b.g) {
|
||||
if (BlockPosition.b.g.size() < 100) {
|
||||
BlockPosition.b.g.add(this);
|
||||
}
|
||||
|
||||
this.f = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MutableBlockPosition extends BlockPosition {
|
||||
// Paper start - comment out
|
||||
/*
|
||||
protected int b;
|
||||
protected int c;
|
||||
protected int d;
|
||||
|
||||
@Override
|
||||
public boolean isValidLocation() {
|
||||
return b >= -30000000 && d >= -30000000 && b < 30000000 && d < 30000000 && c >= 0 && c < 256;
|
||||
}
|
||||
@Override
|
||||
public boolean isInvalidYLocation() {
|
||||
return c < 0 || c >= 256;
|
||||
}
|
||||
*/
|
||||
// Paper end
|
||||
|
||||
public MutableBlockPosition() {
|
||||
this(0, 0, 0);
|
||||
}
|
||||
|
||||
public MutableBlockPosition(BlockPosition blockposition) {
|
||||
this(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
}
|
||||
|
||||
public MutableBlockPosition(int i, int j, int k) {
|
||||
// Paper start
|
||||
super(i, j, k);
|
||||
/*
|
||||
this.b = i;
|
||||
this.c = j;
|
||||
this.d = k;*/
|
||||
// Paper end
|
||||
}
|
||||
|
||||
public BlockPosition a(double d0, double d1, double d2) {
|
||||
return super.a(d0, d1, d2).h();
|
||||
}
|
||||
|
||||
public BlockPosition a(int i, int j, int k) {
|
||||
return super.a(i, j, k).h();
|
||||
}
|
||||
|
||||
public BlockPosition shift(EnumDirection enumdirection, int i) {
|
||||
return super.shift(enumdirection, i).h();
|
||||
}
|
||||
|
||||
public BlockPosition a(EnumBlockRotation enumblockrotation) {
|
||||
return super.a(enumblockrotation).h();
|
||||
}
|
||||
|
||||
/*
|
||||
// Paper start - use parent getters
|
||||
public int getX() {
|
||||
return this.b;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.d;
|
||||
}*/
|
||||
// Paper end
|
||||
|
||||
public BlockPosition.MutableBlockPosition setValues(int i, int j, int k) { return c(i, j, k);} // Paper - OBFHELPER
|
||||
public BlockPosition.MutableBlockPosition c(int i, int j, int k) {
|
||||
// Paper start - use xyz
|
||||
this.x = i;
|
||||
this.y = j;
|
||||
this.z = k;
|
||||
// Paper end
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockPosition.MutableBlockPosition setValues(double d0, double d1, double d2) { return c(d0, d1, d2);} // Paper - OBFHELPER
|
||||
public BlockPosition.MutableBlockPosition c(double d0, double d1, double d2) {
|
||||
return this.c(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2));
|
||||
}
|
||||
|
||||
public BlockPosition.MutableBlockPosition g(BaseBlockPosition baseblockposition) {
|
||||
return this.c(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public BlockPosition.MutableBlockPosition c(EnumDirection enumdirection) {
|
||||
return this.c(enumdirection, 1);
|
||||
}
|
||||
|
||||
public BlockPosition.MutableBlockPosition c(EnumDirection enumdirection, int i) {
|
||||
return this.c(x + enumdirection.getAdjacentX() * i, y + enumdirection.getAdjacentY() * i, z + enumdirection.getAdjacentZ() * i); // Paper - use xyz
|
||||
}
|
||||
|
||||
public BlockPosition.MutableBlockPosition d(int i, int j, int k) {
|
||||
return this.c(x + i, y + j, z + k); // Paper - use xyz
|
||||
}
|
||||
|
||||
public void p(int i) {
|
||||
this.y = i; // Paper change to y
|
||||
}
|
||||
|
||||
public BlockPosition toBlockPosition() { return h(); } // Paper - OBFHELPER
|
||||
public BlockPosition h() {
|
||||
return new BlockPosition(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user