Copy paste/merge FAWE classes to this WorldEdit fork

- so certain people can look at the diff and complain about my sloppy code :(

Signed-off-by: Jesse Boyd <jessepaleg@gmail.com>
This commit is contained in:
Jesse Boyd 2018-08-13 00:03:07 +10:00
parent a920c77cb8
commit a629d15c74
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
994 changed files with 117583 additions and 10745 deletions

7
.gitignore vendored
View File

@ -17,4 +17,9 @@ out
run
/dependency-reduced-pom.xml
*-private.sh
*-private.sh
logs/
lib/
*.bat
worldedit-bukkit/src/main/java/ignore/*
todo.txt

View File

@ -1,16 +1,4 @@
println """
*******************************************
You are building WorldEdit!
If you encounter trouble:
1) Read COMPILING.md if you haven't yet
2) Try running 'build' in a separate Gradle run
3) Use gradlew and not gradle
4) If you still need help, ask on IRC! irc.esper.net #sk89q
Output files will be in [subproject]/build/libs
*******************************************
"""
print new File('splash.txt').text
buildscript {
repositories {
@ -38,73 +26,62 @@ allprojects {
version = '7.0.0-SNAPSHOT'
}
if (!project.hasProperty("artifactory_contextUrl")) ext.artifactory_contextUrl = "http://localhost"
if (!project.hasProperty("artifactory_user")) ext.artifactory_user = "guest"
if (!project.hasProperty("artifactory_password")) ext.artifactory_password = ""
apply plugin: 'java'
clean { delete "target" }
if (!project.hasProperty("gitCommitHash") && !JavaVersion.current().isJava6()) {
group = 'com.boydti.fawe'
def revision = ""
def buildNumber = ""
def date = ""
ext {
try {
def repo = org.ajoberstar.grgit.Grgit.open(dir: '.')
ext.gitCommitHash = repo.head().abbreviatedId
} catch (Exception e) {
println "Error getting commit hash: " + e.getMessage()
git = org.ajoberstar.grgit.Grgit.open(file(".git"))
date = git.head().date.format("yy.MM.dd")
revision = "-${git.head().abbreviatedId}"
index = 0; // Offset to match CI
buildNumber = "-${index}"
} catch (Throwable ignore) {
revision = "unknown";
}
}
if (!project.hasProperty("gitCommitHash")) {
ext.gitCommitHash = "no_git_id"
}
apply plugin: 'com.jfrog.artifactory'
artifactory {
contextUrl = "${artifactory_contextUrl}"
publish {
repository {
repoKey = project.version.contains("SNAPSHOT") ? 'libs-snapshot-local' : 'libs-release-local'
username = "${artifactory_user}"
password = "${artifactory_password}"
maven = true
ivy = false
}
}
resolve {
repository {
repoKey = 'repo'
username = "${artifactory_user}"
password = "${artifactory_password}"
maven = true
}
}
version = date + revision + buildNumber
if ( project.hasProperty("lzNoVersion") ) { // gradle build -PlzNoVersion
version = "unknown";
}
artifactoryPublish.skip = true
description = """FastAsyncWorldEdit"""
subprojects {
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'checkstyle'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.jfrog.artifactory'
ext.internalVersion = version + ";" + gitCommitHash
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext.internalVersion = version
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
checkstyle.configFile = new File(rootProject.projectDir, "config/checkstyle/checkstyle.xml")
checkstyle.toolVersion = '7.6.1'
repositories {
mavenCentral()
maven { url "http://repo.bukkit.org/content/groups/public" }
maven { url "http://maven.sk89q.com/repo/" }
maven { url "http://repo.maven.apache.org/maven2" }
}
if (JavaVersion.current().isJava8Compatible()) {
// Java 8 turns on doclint which we fail
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
// Fawe
maven {url "https://mvnrepository.com/artifact/"}
maven {url "http://repo.dmulloy2.net/content/groups/public/"}
maven {url "https://repo.destroystokyo.com/repository/maven-public//"}
maven {url "http://ci.emc.gs/nexus/content/groups/aikar/" }
maven {url "http://ci.athion.net/job/PlotSquared/ws/mvn/"}
maven {url "http://empcraft.com/maven2"}
maven {url "https://hub.spigotmc.org/nexus/content/groups/public/"}
maven {url "http://ci.frostcast.net/plugin/repository/everything"}
maven {url "http://maven.sk89q.com/artifactory/repo"}
maven {url "http://repo.spongepowered.org/maven"}
maven {url "https://repo.inventivetalent.org/content/groups/public/"}
maven {url "http://dl.bintray.com/tastybento/maven-repo"}
}
task javadocJar(type: Jar, dependsOn: javadoc) {
@ -114,7 +91,6 @@ subprojects {
artifacts {
archives jar
archives javadocJar
}
if (!(name.equals('worldedit-forge') || name.equals('worldedit-sponge'))) {
@ -129,23 +105,17 @@ subprojects {
build.dependsOn(sourcesJar)
}
build.dependsOn(checkstyleMain)
build.dependsOn(checkstyleTest)
build.dependsOn(javadocJar)
shadowJar {
classifier 'dist'
dependencies {
include(dependency('com.sk89q:jchronic:0.2.4a'))
include(dependency('com.thoughtworks.paranamer:paranamer:2.6'))
include(dependency('com.sk89q.lib:jlibnoise:1.0.0'))
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
}
exclude 'GradleStart**'
exclude '.cache'
exclude 'LICENSE*'
}
artifactoryPublish {
publishConfigs('archives')
}
}

View File

@ -1,5 +1,6 @@
#Thu Jul 26 14:29:48 AEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip

View File

@ -1,3 +1,3 @@
rootProject.name = 'worldedit'
include 'worldedit-core', 'worldedit-bukkit', 'worldedit-forge', 'worldedit-sponge'
include 'worldedit-core', 'worldedit-bukkit'

View File

@ -9,8 +9,27 @@ repositories {
dependencies {
compile project(':worldedit-core')
compile 'com.sk89q:dummypermscompat:1.8'
compile 'org.bukkit:bukkit:1.13-R0.1-SNAPSHOT' // zzz
compile 'com.destroystokyo.paper:paper-api:1.13-R0.1-SNAPSHOT'
compile "org.bukkit:craftbukkit:1.13-R0.1-SNAPSHOT"
testCompile 'org.mockito:mockito-core:1.9.0-rc1'
compile 'net.milkbowl.vault:VaultAPI:1.5.6'
compile 'com.massivecraft:factions:2.8.0'
compile 'com.drtshock:factions:1.6.9.5'
compile 'com.factionsone:FactionsOne:1.2.2'
compile 'me.ryanhamshire:GriefPrevention:11.5.2'
compile 'com.massivecraft:mcore:7.0.1'
compile 'net.sacredlabyrinth.Phaed:PreciousStones:10.0.4-SNAPSHOT'
compile 'net.jzx7:regios:5.9.9'
compile 'com.bekvon.bukkit.residence:Residence:4.5._13.1'
compile 'com.palmergames.bukkit:towny:0.84.0.9'
compile 'com.worldcretornica:plotme_core:0.16.3'
compile 'com.thevoxelbox.voxelsniper:voxelsniper:5.171.0'
compile 'com.comphenix.protocol:ProtocolLib-API:4.4.0-SNAPSHOT'
compile 'com.wasteofplastic:askyblock:3.0.8.2'
compile 'com.sk89q:worldguard:6.0.0-SNAPSHOT'
compile('org.inventivetalent:mapmanager:1.4.0-SNAPSHOT') {
transitive = false
}
}
processResources {
@ -35,6 +54,8 @@ shadowJar {
dependencies {
include(dependency(':worldedit-core'))
}
archiveName = "${parent.name}-${project.name}-${parent.version}.jar"
destinationDir = file '../target'
}
build.dependsOn(shadowJar)

View File

@ -0,0 +1,29 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FawePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class BukkitCommand implements CommandExecutor {
private final FaweCommand cmd;
public BukkitCommand(final FaweCommand cmd) {
this.cmd = cmd;
}
@Override
public boolean onCommand(final CommandSender sender, final Command cmd, final String label, final String[] args) {
final FawePlayer plr = Fawe.imp().wrap(sender);
if (!sender.hasPermission(this.cmd.getPerm()) && !sender.isOp()) {
BBC.NO_PERM.send(plr, this.cmd.getPerm());
return true;
}
this.cmd.executeSafe(plr, args);
return true;
}
}

View File

@ -0,0 +1,114 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.wrappers.PlayerWrapper;
import java.lang.reflect.Method;
import java.util.UUID;
import com.intellectualcrafters.plot.config.C;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
public class BukkitPlayer extends FawePlayer<Player> {
private static ConsoleCommandSender console;
public BukkitPlayer(final Player parent) {
super(parent);
}
@Override
public String getName() {
return this.parent.getName();
}
@Override
public UUID getUUID() {
return this.parent.getUniqueId();
}
@Override
public boolean hasPermission(final String perm) {
return this.parent.hasPermission(perm);
}
@Override
public boolean isSneaking() {
return parent.isSneaking();
}
@Override
public void setPermission(final String perm, final boolean flag) {
/*
* Permissions are used to managing WorldEdit region restrictions
* - The `/wea` command will give/remove the required bypass permission
*/
if (Fawe.<FaweBukkit> imp().getVault() == null || Fawe.<FaweBukkit> imp().getVault().permission == null) {
this.parent.addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(perm, flag);
} else if (flag) {
if (!Fawe.<FaweBukkit> imp().getVault().permission.playerAdd(this.parent, perm)) {
this.parent.addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(perm, flag);
}
} else {
if (!Fawe.<FaweBukkit> imp().getVault().permission.playerRemove(this.parent, perm)) {
this.parent.addAttachment(Fawe.<FaweBukkit> imp().getPlugin()).setPermission(perm, flag);
}
}
}
@Override
public void resetTitle() {
sendTitle("","");
}
public void sendTitle(String title, String sub) {
try {
Method methodSendTitle = Player.class.getDeclaredMethod("sendTitle", String.class, String.class, int.class, int.class, int.class);
methodSendTitle.invoke(parent, ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20);
return;
} catch (Throwable ignore) {
try {
Method methodSendTitle = Player.class.getDeclaredMethod("sendTitle", String.class, String.class);
methodSendTitle.invoke(parent, ChatColor.GOLD + title, ChatColor.GOLD + sub);
return;
} catch (Throwable ignore2) {}
}
if (console == null) {
console = Bukkit.getConsoleSender();
Bukkit.getServer().dispatchCommand(console, "gamerule sendCommandFeedback false");
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " times 0 60 20");
}
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " subtitle [{\"text\":\"" + sub + "\",\"color\":\"gold\"}]");
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " title [{\"text\":\"" + title + "\",\"color\":\"gold\"}]");
}
@Override
public void sendMessage(final String message) {
this.parent.sendMessage(C.color(message));
}
@Override
public void executeCommand(final String cmd) {
Bukkit.getServer().dispatchCommand(this.parent, cmd);
}
@Override
public FaweLocation getLocation() {
final Location loc = this.parent.getLocation();
return new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
@Override
public com.sk89q.worldedit.entity.Player toWorldEditPlayer() {
return WorldEditPlugin.getInstance().wrapPlayer(this.parent);
}
}

View File

@ -0,0 +1,665 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.IFawe;
import com.boydti.fawe.bukkit.chat.BukkitChatManager;
import com.boydti.fawe.bukkit.listener.BrushListener;
import com.boydti.fawe.bukkit.listener.BukkitImageListener;
import com.boydti.fawe.bukkit.listener.CFIPacketListener;
import com.boydti.fawe.bukkit.listener.RenderListener;
import com.boydti.fawe.bukkit.regions.*;
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
import com.boydti.fawe.bukkit.util.BukkitTaskMan;
import com.boydti.fawe.bukkit.util.ItemUtil;
import com.boydti.fawe.bukkit.util.VaultUtil;
import com.boydti.fawe.bukkit.util.cui.CUIListener;
import com.boydti.fawe.bukkit.util.cui.StructureCUI;
import com.boydti.fawe.bukkit.util.image.BukkitImageViewer;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.bukkit.v0.BukkitQueue_All;
import com.boydti.fawe.bukkit.v0.ChunkListener_8;
import com.boydti.fawe.bukkit.v0.ChunkListener_9;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.Jars;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.cui.CUI;
import com.boydti.fawe.util.image.ImageViewer;
import com.boydti.fawe.util.metrics.BStats;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
public class FaweBukkit implements IFawe, Listener {
// private final WorldEditPlugin plugin;
private final Plugin plugin;
private VaultUtil vault;
private ItemUtil itemUtil;
private boolean listeningImages;
private BukkitImageListener imageListener;
private CFIPacketListener packetListener;
private boolean listeningCui;
private CUIListener cuiListener;
public VaultUtil getVault() {
return this.vault;
}
public FaweBukkit(Plugin plugin) {
this.plugin = plugin;
try {
Settings.IMP.TICK_LIMITER.ENABLED = !Bukkit.hasWhitelist();
Fawe.set(this);
Fawe.setupInjector();
try {
new BrushListener(plugin);
} catch (Throwable e) {
debug("====== BRUSH LISTENER FAILED ======");
e.printStackTrace();
debug("===================================");
}
if (Bukkit.getVersion().contains("git-Spigot")) {
debug("====== USE PAPER ======");
debug("DOWNLOAD: https://ci.destroystokyo.com/job/PaperSpigot/");
debug("GUIDE: https://www.spigotmc.org/threads/21726/");
debug(" - This is only a recommendation");
debug("==============================");
}
if (Bukkit.getVersion().contains("git-Paper") && Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) {
new RenderListener(plugin);
}
try {
Fawe.get().setChatManager(new BukkitChatManager());
} catch (Throwable ignore) {
ignore.printStackTrace();
}
} catch (final Throwable e) {
MainUtil.handleError(e);
Bukkit.getServer().shutdown();
}
// Registered delayed Event Listeners
TaskManager.IMP.task(new Runnable() {
@Override
public void run() {
// This class
Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin);
// The tick limiter
try {
Class.forName("sun.misc.SharedSecrets");
new ChunkListener_8();
} catch (ClassNotFoundException e) {
new ChunkListener_9();
}
}
});
}
@Override
public CUI getCUI(FawePlayer player) {
if (Settings.IMP.EXPERIMENTAL.VANILLA_CUI) {
if (listeningCui && cuiListener == null) return null;
listeningCui = true;
if (cuiListener == null) {
Plugin protocolLib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
if (protocolLib != null && protocolLib.isEnabled()) {
cuiListener = new CUIListener(plugin);
} else {
return null;
}
}
return new StructureCUI(player);
}
return null;
}
@Override
public void registerPacketListener() {
PluginManager manager = Bukkit.getPluginManager();
if (packetListener == null && manager.getPlugin("ProtocolLib") != null) {
packetListener = new CFIPacketListener(plugin);
}
}
@Override
public synchronized ImageViewer getImageViewer(FawePlayer fp) {
if (listeningImages && imageListener == null) return null;
try {
listeningImages = true;
registerPacketListener();
PluginManager manager = Bukkit.getPluginManager();
if (manager.getPlugin("PacketListenerApi") == null) {
File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar");
byte[] jarData = Jars.PL_v3_6_0.download();
try (FileOutputStream fos = new FileOutputStream(output)) {
fos.write(jarData);
}
}
if (manager.getPlugin("MapManager") == null) {
File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.4.0-SNAPSHOT.jar");
byte[] jarData = Jars.MM_v1_4_0.download();
try (FileOutputStream fos = new FileOutputStream(output)) {
fos.write(jarData);
}
}
BukkitImageViewer viewer = new BukkitImageViewer((Player) fp.parent);
if (imageListener == null) {
this.imageListener = new BukkitImageListener(plugin);
}
return viewer;
} catch (Throwable ignore) {}
return null;
}
@Override
public int getPlayerCount() {
return plugin.getServer().getOnlinePlayers().size();
}
@Override
public boolean isOnlineMode() {
return Bukkit.getOnlineMode();
}
@Override
public String getPlatformVersion() {
String bukkitVersion = Bukkit.getVersion();
int index = bukkitVersion.indexOf("MC: ");
return index == -1 ? bukkitVersion : bukkitVersion.substring(index + 4, bukkitVersion.length() - 1);
}
@Override
public void debug(final String s) {
ConsoleCommandSender console = Bukkit.getConsoleSender();
if (console != null) {
console.sendMessage(BBC.color(s));
} else {
Bukkit.getLogger().info(BBC.color(s));
}
}
@Override
public File getDirectory() {
return plugin.getDataFolder();
}
@Override
public void setupCommand(final String label, final FaweCommand cmd) {
if (plugin instanceof JavaPlugin) {
PluginCommand registered = ((JavaPlugin) plugin).getCommand(label);
if (registered == null) {
debug("Command not registered in plugin.yml: " + label);
return;
}
registered.setExecutor(new BukkitCommand(cmd));
}
}
@Override
public FawePlayer<Player> wrap(final Object obj) {
if (obj.getClass() == String.class) {
String name = (String) obj;
FawePlayer existing = Fawe.get().getCachedPlayer(name);
if (existing != null) {
return existing;
}
Player player = Bukkit.getPlayer(name);
return player != null ? new BukkitPlayer(player) : null;
} else if (obj instanceof Player) {
Player player = (Player) obj;
FawePlayer existing = Fawe.get().getCachedPlayer(player.getName());
return existing != null ? existing : new BukkitPlayer(player);
} else if (obj != null && obj.getClass().getName().contains("EntityPlayer")) {
try {
Method method = obj.getClass().getDeclaredMethod("getBukkitEntity");
return wrap(method.invoke(obj));
} catch (Throwable e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
}
@Override
public void startMetrics() {
Metrics metrics = new Metrics(plugin);
metrics.start();
TaskManager.IMP.task(new Runnable() {
@Override
public void run() {
ArrayList<Class<?>> services = new ArrayList(Bukkit.getServicesManager().getKnownServices());
services.forEach(service -> {
try {
service.getField("B_STATS_VERSION");
ArrayList<RegisteredServiceProvider<?>> providers = new ArrayList(Bukkit.getServicesManager().getRegistrations(service));
for (RegisteredServiceProvider<?> provider : providers) {
Object instance = provider.getProvider();
// Link it to FAWE's metrics instead
BStats.linkMetrics(instance);
// Disable the other metrics
Bukkit.getServicesManager().unregister(service, instance);
try {
Class<? extends Object> clazz = instance.getClass();
Field logFailedRequests = ReflectionUtils.findField(clazz, boolean.class);
logFailedRequests.set(null, false);
Field url = null;
try { url = clazz.getDeclaredField("URL"); } catch (NoSuchFieldException ignore) {
for (Field field : clazz.getDeclaredFields()) if (ReflectionUtils.setAccessible(field).get(null).toString().startsWith("http")) { url = field; break; }
}
if (url != null) ReflectionUtils.setFailsafeFieldValue(url, null, null);
} catch (NoSuchFieldError | IllegalAccessException ignore) {}
catch (Throwable e) {}
}
} catch (NoSuchFieldException ignored) { }
});
}
});
}
public ItemUtil getItemUtil() {
ItemUtil tmp = itemUtil;
if (tmp == null) {
try {
this.itemUtil = tmp = new ItemUtil();
} catch (Throwable e) {
Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES = false;
debug("===== PERSISTENT BRUSH FAILED =====");
e.printStackTrace();
debug("===================================");
}
}
return tmp;
}
/**
* Vault isn't required, but used for setting player permissions (WorldEdit bypass)
* @return
*/
@Override
public void setupVault() {
try {
this.vault = new VaultUtil();
} catch (final Throwable e) {
this.debug("&dVault is used for persistent `/wea` toggles.");
}
}
@Override
public String getDebugInfo() {
StringBuilder msg = new StringBuilder();
List<String> pl = new ArrayList<>();
msg.append("server.plugins: \n");
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
msg.append(" - " + p.getName() + ": " + p.getDescription().getVersion() + "\n");
}
msg.append("server.version: " + Bukkit.getVersion() + " / " + Bukkit.getBukkitVersion() + "\n");
return msg.toString();
}
/**
* The task manager handles sync/async tasks
*/
@Override
public TaskManager getTaskManager() {
return new BukkitTaskMan(plugin);
}
private boolean hasNMS = true;
private boolean playerChunk = false;
@Override
public FaweQueue getNewQueue(String world, boolean fast) {
if (playerChunk != (playerChunk = true)) {
try {
Field fieldDirtyCount = BukkitReflectionUtils.getRefClass("{nms}.PlayerChunk").getField("dirtyCount").getRealField();
fieldDirtyCount.setAccessible(true);
int mod = fieldDirtyCount.getModifiers();
if ((mod & Modifier.VOLATILE) == 0) {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(fieldDirtyCount, mod + Modifier.VOLATILE);
}
} catch (Throwable ignore) {}
}
try {
return getQueue(world);
} catch (Throwable ignore) {
// Disable incompatible settings
Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level
Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI
if (hasNMS) {
debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion());
debug("NMS label: " + plugin.getClass().getSimpleName().split("_")[1]);
debug("Fallback placer: " + BukkitQueue_All.class);
debug("=======================================");
debug("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("=======================================");
ignore.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {
MainUtil.sendAdmin("&cNo NMS placer found, see console!");
}
}, 1);
hasNMS = false;
}
return new BukkitQueue_All(world);
}
}
/**
* The FaweQueue is a core part of block placement<br>
* - The queue returned here is used in the SetQueue class (SetQueue handles the implementation specific queue)<br>
* - Block changes are grouped by chunk (as it's more efficient for lighting/packet sending)<br>
* - The FaweQueue returned here will provide the wrapper around the chunk object (FaweChunk)<br>
* - When a block change is requested, the SetQueue will first check if the chunk exists in the queue, or it will create and add it<br>
*/
@Override
public FaweQueue getNewQueue(World world, boolean fast) {
if (fast) {
if (playerChunk != (playerChunk = true)) {
try {
Field fieldDirtyCount = BukkitReflectionUtils.getRefClass("{nms}.PlayerChunk").getField("dirtyCount").getRealField();
fieldDirtyCount.setAccessible(true);
int mod = fieldDirtyCount.getModifiers();
if ((mod & Modifier.VOLATILE) == 0) {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(fieldDirtyCount, mod + Modifier.VOLATILE);
}
} catch (Throwable ignore) {
}
}
Throwable error = null;
try {
return getQueue(world);
} catch (Throwable ignore) {
error = ignore;
}
// Disable incompatible settings
Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level
Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI
if (hasNMS) {
debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion());
debug("NMS label: " + plugin.getClass().getSimpleName());
debug("Fallback placer: " + BukkitQueue_All.class);
debug("=======================================");
debug("Download the version of FAWE for your platform");
debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target");
debug("=======================================");
error.printStackTrace();
debug("=======================================");
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {
MainUtil.sendAdmin("&cNo NMS placer found, see console!");
}
}, 1);
hasNMS = false;
}
}
return new BukkitQueue_All(world);
}
public Plugin getPlugin() {
return plugin;
}
@Override
public String getWorldName(World world) {
return world.getName();
}
/**
* A mask manager handles region restrictions e.g. PlotSquared plots / WorldGuard regions
*/
@Override
public Collection<FaweMaskManager> getMaskManagers() {
final Plugin worldguardPlugin = Bukkit.getServer().getPluginManager().getPlugin("WorldGuard");
final ArrayList<FaweMaskManager> managers = new ArrayList<>();
if ((worldguardPlugin != null) && worldguardPlugin.isEnabled()) {
try {
managers.add(new Worldguard(worldguardPlugin, this));
Fawe.debug("Plugin 'WorldGuard' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin plotmePlugin = Bukkit.getServer().getPluginManager().getPlugin("PlotMe");
if ((plotmePlugin != null) && plotmePlugin.isEnabled()) {
try {
managers.add(new PlotMeFeature(plotmePlugin, this));
Fawe.debug("Plugin 'PlotMe' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin townyPlugin = Bukkit.getServer().getPluginManager().getPlugin("Towny");
if ((townyPlugin != null) && townyPlugin.isEnabled()) {
try {
managers.add(new TownyFeature(townyPlugin, this));
Fawe.debug("Plugin 'Towny' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin factionsPlugin = Bukkit.getServer().getPluginManager().getPlugin("Factions");
if ((factionsPlugin != null) && factionsPlugin.isEnabled()) {
try {
managers.add(new FactionsFeature(factionsPlugin, this));
Fawe.debug("Plugin 'Factions' found. Using it now.");
} catch (final Throwable e) {
try {
managers.add(new FactionsUUIDFeature(factionsPlugin, this));
Fawe.debug("Plugin 'FactionsUUID' found. Using it now.");
} catch (Throwable e2) {
try {
managers.add(new FactionsOneFeature(factionsPlugin, this));
Fawe.debug("Plugin 'FactionsUUID' found. Using it now.");
} catch (Throwable e3) {
MainUtil.handleError(e);
}
}
}
}
final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence");
if ((residencePlugin != null) && residencePlugin.isEnabled()) {
try {
managers.add(new ResidenceFeature(residencePlugin, this));
Fawe.debug("Plugin 'Residence' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin griefpreventionPlugin = Bukkit.getServer().getPluginManager().getPlugin("GriefPrevention");
if ((griefpreventionPlugin != null) && griefpreventionPlugin.isEnabled()) {
try {
managers.add(new GriefPreventionFeature(griefpreventionPlugin, this));
Fawe.debug("Plugin 'GriefPrevention' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin preciousstonesPlugin = Bukkit.getServer().getPluginManager().getPlugin("PreciousStones");
if ((preciousstonesPlugin != null) && preciousstonesPlugin.isEnabled()) {
try {
managers.add(new PreciousStonesFeature(preciousstonesPlugin, this));
Fawe.debug("Plugin 'PreciousStones' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
final Plugin aSkyBlock = Bukkit.getServer().getPluginManager().getPlugin("ASkyBlock");
if ((aSkyBlock != null) && aSkyBlock.isEnabled()) {
try {
managers.add(new ASkyBlockHook(aSkyBlock, this));
Fawe.debug("Plugin 'ASkyBlock' found. Using it now.");
} catch (final Throwable e) {
MainUtil.handleError(e);
}
}
return managers;
}
//
// @EventHandler
// public void onWorldLoad(WorldLoadEvent event) {
// org.bukkit.World world = event.getWorld();
// world.setKeepSpawnInMemory(false);
// WorldServer nmsWorld = ((CraftWorld) world).getHandle();
// ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
// try {
// Field fieldChunkLoader = provider.getClass().getDeclaredField("chunkLoader");
// ReflectionUtils.setFailsafeFieldValue(fieldChunkLoader, provider, new FaweChunkLoader());
// } catch (Throwable e) {
// e.printStackTrace();
// }
// }
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
String name = player.getName();
FawePlayer fp = Fawe.get().getCachedPlayer(name);
if (fp != null) {
fp.unregister();
Fawe.get().unregister(name);
}
}
@Override
public String getPlatform() {
return "bukkit";
}
@Override
public UUID getUUID(String name) {
return Bukkit.getOfflinePlayer(name).getUniqueId();
}
@Override
public String getName(UUID uuid) {
return Bukkit.getOfflinePlayer(uuid).getName();
}
private boolean enabledBlocksHub = true;
@Override
public Object getBlocksHubApi() {
if (!enabledBlocksHub) {
return null;
}
Plugin blocksHubPlugin = Bukkit.getPluginManager().getPlugin("BlocksHub");
if (blocksHubPlugin == null) {
enabledBlocksHub = false;
return null;
}
return null;
// return ((BlocksHubBukkit) blocksHubPlugin).getApi();
}
private Version version = null;
public Version getVersion() {
Version tmp = this.version;
if (tmp == null) {
tmp = Version.NONE;
for (Version v : Version.values()) {
try {
BukkitQueue_0.checkVersion(v.name());
this.version = tmp = v;
if (tmp == Version.v1_13_R1) {
try {
Fawe.debug("Running 1.13 registry dumper!");
// TODO FIXME
// NMSRegistryDumper dumper = new NMSRegistryDumper(MainUtil.getFile(plugin.getDataFolder(), "extrablocks.json"));
// dumper.run();
} catch (Throwable e) {
e.printStackTrace();
}
}
break;
} catch (IllegalStateException e) {}
}
}
return tmp;
}
public enum Version {
// v1_7_R4,
// v1_8_R3,
// v1_9_R2,
// v1_10_R1,
// v1_11_R1,
// v1_12_R2,
v1_13_R1,
NONE,
}
private FaweQueue getQueue(World world) {
switch (getVersion()) {
case v1_13_R1:
// return new BukkitQueue_1_13(world);
default:
case NONE:
return new BukkitQueue_All(world);
}
}
private FaweQueue getQueue(String world) {
switch (getVersion()) {
case v1_13_R1:
// return new BukkitQueue_1_13(world);
default:
case NONE:
return new BukkitQueue_All(world);
}
}
}

View File

@ -0,0 +1,584 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.object.io.PGZIPOutputStream;
import com.boydti.fawe.util.MainUtil;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.*;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.scheduler.BukkitTask;
public class Metrics {
/**
* The current revision number.
*/
private static final int REVISION = 7;
/**
* The base url of the metrics domain.
*/
private static final String BASE_URL = "http://report.mcstats.org";
/**
* The url used to report a server's status.
*/
private static final String REPORT_URL = "/plugin/%s";
/**
* Interval of time to ping (in minutes).
*/
private static final int PING_INTERVAL = 15;
/**
* The plugin this metrics submits for.
*/
private final Plugin plugin;
/**
* All of the custom graphs to submit to metrics.
*/
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
/**
* Unique server id.
*/
private final String guid;
/**
* Debug mode.
*/
private final boolean debug;
/**
* The scheduled task.
*/
private volatile BukkitTask task = null;
public Metrics(Plugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null");
}
this.plugin = plugin;
this.guid = UUID.randomUUID().toString();
this.debug = false;
}
/**
* GZip compress a string of bytes.
*
* @param input
*
* @return byte[] the file as a byte array
*/
public static byte[] gzip(String input) {
FastByteArrayOutputStream baos = new FastByteArrayOutputStream();
PGZIPOutputStream gzos = null;
try {
gzos = new PGZIPOutputStream(baos);
gzos.write(input.getBytes("UTF-8"));
} catch (IOException e) {
MainUtil.handleError(e);
} finally {
if (gzos != null) {
try {
gzos.close();
} catch (IOException ignore) {
}
}
}
return baos.toByteArray();
}
/**
* Appends a json encoded key/value pair to the given string builder.
*
* @param json
* @param key
* @param value
*
*/
private static void appendJSONPair(StringBuilder json, String key, String value) {
boolean isValueNumeric = false;
try {
if (value.equals("0") || !value.endsWith("0")) {
Double.parseDouble(value);
isValueNumeric = true;
}
} catch (NumberFormatException e) {
isValueNumeric = false;
}
if (json.charAt(json.length() - 1) != '{') {
json.append(',');
}
json.append(escapeJSON(key));
json.append(':');
if (isValueNumeric) {
json.append(value);
} else {
json.append(escapeJSON(value));
}
}
/**
* Escape a string to create a valid JSON string
*
* @param text
*
* @return String
*/
private static String escapeJSON(String text) {
StringBuilder builder = new StringBuilder();
builder.append('"');
for (int index = 0; index < text.length(); index++) {
char chr = text.charAt(index);
switch (chr) {
case '"':
case '\\':
builder.append('\\');
builder.append(chr);
break;
case '\b':
builder.append("\\b");
break;
case '\t':
builder.append("\\t");
break;
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
default:
if (chr < ' ') {
String t = "000" + Integer.toHexString(chr);
builder.append("\\u" + t.substring(t.length() - 4));
} else {
builder.append(chr);
}
break;
}
}
builder.append('"');
return builder.toString();
}
/**
* Encode text as UTF-8
*
* @param text the text to encode
*
* @return the encoded text, as UTF-8
*/
private static String urlEncode(String text) throws UnsupportedEncodingException {
return URLEncoder.encode(text, "UTF-8");
}
/**
* Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
* website. Plotters can be added to the graph object returned.
*
* @param name The name of the graph
*
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
*/
public Graph createGraph(String name) {
if (name == null) {
throw new IllegalArgumentException("Graph name cannot be null");
}
// Construct the graph object
Graph graph = new Graph(name);
// Now we can add our graph
this.graphs.add(graph);
// and return back
return graph;
}
/**
* Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
*
* @param graph The name of the graph
*/
public void addGraph(Graph graph) {
if (graph == null) {
throw new IllegalArgumentException("Graph cannot be null");
}
this.graphs.add(graph);
}
/**
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
* initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
* ticks.
*
* @return True if statistics measuring is running, otherwise false.
*/
public boolean start() {
// Is metrics already running?
if (this.task != null) {
return true;
}
// Begin hitting the server with glorious data
this.task = this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, new Runnable() {
private boolean firstPost = true;
@Override
public void run() {
try {
postPlugin(!this.firstPost);
// After the first post we set firstPost to
// false
// Each post thereafter will be a ping
this.firstPost = false;
} catch (IOException e) {
MainUtil.handleError(e);
if (Metrics.this.debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
}
}
}
}, 0, PING_INTERVAL * 1200);
return true;
}
/**
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
*
* @throws IOException
*/
public void enable() {
// Enable Task, if it is not running
if (this.task == null) {
start();
}
}
/**
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
*
*/
public void disable() {
// Disable Task, if it is running
if (this.task != null) {
this.task.cancel();
this.task = null;
}
}
/**
* Gets the File object of the config file that should be used to store
* data such as the GUID and opt-out status.
*
* @return the File object for the config file
*/
public File getConfigFile() {
// I believe the easiest way to get the base folder (e.g craftbukkit set
// via -P) for plugins to use
// is to abuse the plugin object we already have
// plugin.getDataFolder() => base/plugins/PluginA/
// pluginsFolder => base/plugins/
// The base is not necessarily relative to the startup directory.
File pluginsFolder = this.plugin.getDataFolder().getParentFile();
// return => base/plugins/PluginMetrics/config.yml
return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml");
}
/**
* Generic method that posts a plugin to the metrics website.
*/
private void postPlugin(boolean isPing) throws IOException {
// Server software specific section
PluginDescriptionFile description = this.plugin.getDescription();
String pluginName = description.getName();
boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled
String pluginVersion = description.getVersion();
String serverVersion = Bukkit.getVersion();
int playersOnline = 0;
try {
if (Bukkit.class.getMethod("getOnlinePlayers", new Class<?>[0]).getReturnType() == Collection.class) {
playersOnline = ((Collection<?>) Bukkit.class.getMethod("getOnlinePlayers", new Class<?>[0]).invoke(null)).size();
} else {
playersOnline = ((Player[]) Bukkit.class.getMethod("getOnlinePlayers", new Class<?>[0]).invoke(null)).length;
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
ex.printStackTrace();
}
// END server software specific section -- all code below does not use
// any code outside of this class / Java
// Construct the post data
StringBuilder json = new StringBuilder(1024);
json.append('{');
// The plugin's description file containing all of the plugin data such as name, version, author, etc
appendJSONPair(json, "guid", this.guid);
appendJSONPair(json, "plugin_version", pluginVersion);
appendJSONPair(json, "server_version", serverVersion);
appendJSONPair(json, "players_online", Integer.toString(playersOnline));
// New data as of R6
String osname = System.getProperty("os.name");
String osarch = System.getProperty("os.arch");
String osversion = System.getProperty("os.version");
String java_version = System.getProperty("java.version");
int coreCount = Runtime.getRuntime().availableProcessors();
// normalize os arch .. amd64 -> x86_64
if (osarch.equals("amd64")) {
osarch = "x86_64";
}
appendJSONPair(json, "osname", osname);
appendJSONPair(json, "osarch", osarch);
appendJSONPair(json, "osversion", osversion);
appendJSONPair(json, "cores", Integer.toString(coreCount));
appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0");
appendJSONPair(json, "java_version", java_version);
// If we're pinging, append it
if (isPing) {
appendJSONPair(json, "ping", "1");
}
if (!this.graphs.isEmpty()) {
synchronized (this.graphs) {
json.append(',');
json.append('"');
json.append("graphs");
json.append('"');
json.append(':');
json.append('{');
boolean firstGraph = true;
for (Graph graph : this.graphs) {
StringBuilder graphJson = new StringBuilder();
graphJson.append('{');
for (Plotter plotter : graph.getPlotters()) {
appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue()));
}
graphJson.append('}');
if (!firstGraph) {
json.append(',');
}
json.append(escapeJSON(graph.getName()));
json.append(':');
json.append(graphJson);
firstGraph = false;
}
json.append('}');
}
}
// close json
json.append('}');
// Create the url
URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName)));
// Connect to the website
URLConnection connection;
// Mineshafter creates a socks proxy, so we can safely bypass it
// It does not reroute POST requests so we need to go around it
if (isMineshafterPresent()) {
connection = url.openConnection(Proxy.NO_PROXY);
} else {
connection = url.openConnection();
}
byte[] uncompressed = json.toString().getBytes();
byte[] compressed = gzip(json.toString());
// Headers
connection.addRequestProperty("User-Agent", "MCStats/" + REVISION);
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Content-Encoding", "gzip");
connection.addRequestProperty("Content-Length", Integer.toString(compressed.length));
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.setDoOutput(true);
if (this.debug) {
Fawe.debug("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length);
}
try {
try (OutputStream os = connection.getOutputStream()) {
os.write(compressed);
os.flush();
}
String response;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
response = reader.readLine();
if (this.debug) {
Fawe.debug("[Metrics] Response for " + pluginName + ": " + response);
}
}
if (response == null || response.startsWith("ERR") || response.startsWith("7")) {
if (response == null) {
response = "null";
} else if (response.startsWith("7")) {
response = response.substring(response.startsWith("7,") ? 2 : 1);
}
throw new IOException(response);
} else {
// Is this the first update this hour?
if ("1".equals(response) || response.contains("This is your first update this hour")) {
synchronized (this.graphs) {
for (Graph graph : this.graphs) {
for (Plotter plotter : graph.getPlotters()) {
plotter.reset();
}
}
}
}
}
} catch (Exception e) {
if (this.debug) {
MainUtil.handleError(e);
}
}
}
/**
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
*
* @return true if mineshafter is installed on the server
*/
private boolean isMineshafterPresent() {
try {
Class.forName("mineshafter.MineServer");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Represents a custom graph on the website
*/
public static class Graph {
/**
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
* rejected
*/
private final String name;
/**
* The set of plotters that are contained within this graph
*/
private final Set<Plotter> plotters = new LinkedHashSet<>();
private Graph(String name) {
this.name = name;
}
/**
* Gets the graph's name
*
* @return the Graph's name
*/
public String getName() {
return this.name;
}
/**
* Add a plotter to the graph, which will be used to plot entries
*
* @param plotter the plotter to add to the graph
*/
public void addPlotter(Plotter plotter) {
this.plotters.add(plotter);
}
/**
* Remove a plotter from the graph
*
* @param plotter the plotter to remove from the graph
*/
public void removePlotter(Plotter plotter) {
this.plotters.remove(plotter);
}
/**
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
*
* @return an unmodifiable {@link Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(this.plotters);
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Graph)) {
return false;
}
Graph graph = (Graph) object;
return graph.name.equals(this.name);
}
/**
* Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
*/
protected void onOptOut() {
}
}
/**
* Interface used to collect custom data for a plugin
*/
public abstract static class Plotter {
/**
* The plot's name
*/
private final String name;
/**
* Construct a plotter with the default plot name
*/
public Plotter() {
this("Default");
}
/**
* Construct a plotter with a specific plot name
*
* @param name the name of the plotter to use, which will show up on the website
*/
public Plotter(String name) {
this.name = name;
}
/**
* Get the current value for the plotted point. Since this function defers to an external function it may or may
* not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
* from any thread so care should be taken when accessing resources that need to be synchronized.
*
* @return the current value for the point to be plotted.
*/
public abstract int getValue();
/**
* Get the column name for the plotted point
*
* @return the plotted point's column name
*/
public String getColumnName() {
return this.name;
}
/**
* Called after the website graphs have been updated
*/
public void reset() {
}
@Override
public int hashCode() {
return getColumnName().hashCode();
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Plotter)) {
return false;
}
Plotter plotter = (Plotter) object;
return plotter.name.equals(this.name) && plotter.getValue() == getValue();
}
}
}

View File

@ -0,0 +1,482 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.boydti.fawe.bukkit.adapter;
import static com.google.common.base.Preconditions.checkNotNull;
import ignore.test.DummyServer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.EndTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.registry.state.BooleanProperty;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.EnumProperty;
import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import net.minecraft.server.v1_13_R1.BiomeBase;
import net.minecraft.server.v1_13_R1.Block;
import net.minecraft.server.v1_13_R1.BlockPosition;
import net.minecraft.server.v1_13_R1.BlockStateBoolean;
import net.minecraft.server.v1_13_R1.BlockStateDirection;
import net.minecraft.server.v1_13_R1.BlockStateEnum;
import net.minecraft.server.v1_13_R1.BlockStateInteger;
import net.minecraft.server.v1_13_R1.BlockStateList;
import net.minecraft.server.v1_13_R1.Entity;
import net.minecraft.server.v1_13_R1.EntityTypes;
import net.minecraft.server.v1_13_R1.IBlockData;
import net.minecraft.server.v1_13_R1.IBlockState;
import net.minecraft.server.v1_13_R1.INamable;
import net.minecraft.server.v1_13_R1.MinecraftKey;
import net.minecraft.server.v1_13_R1.NBTBase;
import net.minecraft.server.v1_13_R1.NBTTagByte;
import net.minecraft.server.v1_13_R1.NBTTagByteArray;
import net.minecraft.server.v1_13_R1.NBTTagCompound;
import net.minecraft.server.v1_13_R1.NBTTagDouble;
import net.minecraft.server.v1_13_R1.NBTTagEnd;
import net.minecraft.server.v1_13_R1.NBTTagFloat;
import net.minecraft.server.v1_13_R1.NBTTagInt;
import net.minecraft.server.v1_13_R1.NBTTagIntArray;
import net.minecraft.server.v1_13_R1.NBTTagList;
import net.minecraft.server.v1_13_R1.NBTTagLong;
import net.minecraft.server.v1_13_R1.NBTTagShort;
import net.minecraft.server.v1_13_R1.NBTTagString;
import net.minecraft.server.v1_13_R1.TileEntity;
import net.minecraft.server.v1_13_R1.World;
import net.minecraft.server.v1_13_R1.WorldServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.v1_13_R1.CraftServer;
import org.bukkit.craftbukkit.v1_13_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_13_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_13_R1.entity.CraftEntity;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
public final class Spigot_v1_13_R1 implements BukkitImplAdapter<NBTBase> {
private final Logger logger = Logger.getLogger(getClass().getCanonicalName());
private final Field nbtListTagListField;
private final Method nbtCreateTagMethod;
// ------------------------------------------------------------------------
// Code that may break between versions of Minecraft
// ------------------------------------------------------------------------
public Spigot_v1_13_R1() throws NoSuchFieldException, NoSuchMethodException {
// A simple test
if (Bukkit.getServer().getClass() != DummyServer.class) CraftServer.class.cast(Bukkit.getServer());
// test between 1.12 and 1.12.1 since md_5 didn't update revision numbers
TileEntity.class.getDeclaredMethod("load", NBTTagCompound.class);
// The list of tags on an NBTTagList
nbtListTagListField = NBTTagList.class.getDeclaredField("list");
nbtListTagListField.setAccessible(true);
// The method to create an NBTBase tag given its type ID
nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", byte.class);
nbtCreateTagMethod.setAccessible(true);
}
/**
* Read the given NBT data into the given tile entity.
*
* @param tileEntity the tile entity
* @param tag the tag
*/
private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity) {
tileEntity.load(tag);
}
/**
* Write the tile entity's NBT data to the given tag.
*
* @param tileEntity the tile entity
* @param tag the tag
*/
private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag) {
tileEntity.save(tag);
}
/**
* Get the ID string of the given entity.
*
* @param entity the entity
* @return the entity ID or null if one is not known
*/
@Nullable
private static String getEntityId(Entity entity) {
MinecraftKey minecraftkey = EntityTypes.getName(entity.getBukkitEntity().getHandle().P());
return minecraftkey == null ? null : minecraftkey.toString();
}
/**
* Create an entity using the given entity ID.
*
* @param id the entity ID
* @param world the world
* @return an entity or null
*/
@Nullable
private static Entity createEntityFromId(String id, World world) {
return EntityTypes.a(world, new MinecraftKey(id));
}
/**
* Write the given NBT data into the given entity.
*
* @param entity the entity
* @param tag the tag
*/
private static void readTagIntoEntity(NBTTagCompound tag, Entity entity) {
entity.f(tag);
}
/**
* Write the entity's NBT data to the given tag.
*
* @param entity the entity
* @param tag the tag
*/
private static void readEntityIntoTag(Entity entity, NBTTagCompound tag) {
entity.save(tag);
}
// ------------------------------------------------------------------------
// Code that is less likely to break
// ------------------------------------------------------------------------
@Override
public int getBiomeId(Biome biome) {
BiomeBase mcBiome = CraftBlock.biomeToBiomeBase(biome);
return mcBiome != null ? BiomeBase.a(mcBiome) : 0;
}
@Override
public Biome getBiome(int id) {
BiomeBase mcBiome = BiomeBase.getBiome(id);
return CraftBlock.biomeBaseToBiome(mcBiome); // Defaults to ocean if it's an invalid ID
}
@SuppressWarnings("deprecation")
@Override
public BlockState getBlock(Location location) {
checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
org.bukkit.block.Block bukkitBlock = location.getBlock();
BlockState state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
if (state.getBlockType().getMaterial().hasContainer()) {
//Read the NBT data
TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
if (te != null) {
NBTTagCompound tag = new NBTTagCompound();
readTileEntityIntoTag(te, tag); // Load data
return new BaseBlock(state, (CompoundTag) toNative(tag));
}
}
return state;
}
@Override
public boolean setBlock(Location location, BlockStateHolder state, boolean notifyAndLight) {
checkNotNull(location);
checkNotNull(state);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
// Two pass update:
// Note, this will notify blocks BEFORE the tile entity is set
location.getBlock().setBlockData(BukkitAdapter.adapt(state), false);
// Copy NBT data for the block
CompoundTag nativeTag = state.getNbtData();
if (nativeTag != null) {
// We will assume that the tile entity was created for us,
// though we do not do this on the Forge version
TileEntity tileEntity = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
if (tileEntity != null) {
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
tag.set("x", new NBTTagInt(x));
tag.set("y", new NBTTagInt(y));
tag.set("z", new NBTTagInt(z));
readTagIntoTileEntity(tag, tileEntity); // Load data
}
}
return true;
}
@Override
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
checkNotNull(entity);
CraftEntity craftEntity = ((CraftEntity) entity);
Entity mcEntity = craftEntity.getHandle();
String id = getEntityId(mcEntity);
if (id != null) {
NBTTagCompound tag = new NBTTagCompound();
readEntityIntoTag(mcEntity, tag);
return new BaseEntity(com.sk89q.worldedit.world.entity.EntityTypes.get(id), (CompoundTag) toNative(tag));
} else {
return null;
}
}
@Nullable
@Override
public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) {
checkNotNull(location);
checkNotNull(state);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
WorldServer worldServer = craftWorld.getHandle();
Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
if (createdEntity != null) {
CompoundTag nativeTag = state.getNbtData();
if (nativeTag != null) {
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
readTagIntoEntity(tag, createdEntity);
}
createdEntity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
worldServer.addEntity(createdEntity, SpawnReason.CUSTOM);
return createdEntity.getBukkitEntity();
} else {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
public Map<String, ? extends Property> getProperties(BlockType blockType) {
Block block;
try {
block = Block.getByName(blockType.getId());
} catch (Throwable e) {
return Collections.emptyMap();
}
if (block == null) {
logger.warning("Failed to find properties for " + blockType.getId());
return Collections.emptyMap();
}
Map<String, Property> properties = Maps.newLinkedHashMap();
BlockStateList<Block, IBlockData> blockStateList = block.getStates();
for (IBlockState state : blockStateList.d()) {
Property property;
if (state instanceof BlockStateBoolean) {
property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.d()));
} else if (state instanceof BlockStateDirection) {
property = new DirectionalProperty(state.a(),
(List<Direction>) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList()));
} else if (state instanceof BlockStateEnum) {
property = new EnumProperty(state.a(),
(List<String>) state.d().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList()));
} else if (state instanceof BlockStateInteger) {
property = new IntegerProperty(state.a(), ImmutableList.copyOf(state.d()));
} else {
throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName());
}
properties.put(property.getName(), property);
}
return properties;
}
/**
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
* structure.
*
* @param foreign non-native NMS NBT structure
* @return native WorldEdit NBT structure
*/
@SuppressWarnings("unchecked")
@Override
public Tag toNative(NBTBase foreign) {
if (foreign == null) {
return null;
}
if (foreign instanceof NBTTagCompound) {
Map<String, Tag> values = new HashMap<>();
Set<String> foreignKeys = ((NBTTagCompound) foreign).getKeys(); // map.keySet
for (String str : foreignKeys) {
NBTBase base = ((NBTTagCompound) foreign).get(str);
values.put(str, toNative(base));
}
return new CompoundTag(values);
} else if (foreign instanceof NBTTagByte) {
return new ByteTag(((NBTTagByte) foreign).g()); // getByte
} else if (foreign instanceof NBTTagByteArray) {
return new ByteArrayTag(((NBTTagByteArray) foreign).c()); // data
} else if (foreign instanceof NBTTagDouble) {
return new DoubleTag(((NBTTagDouble) foreign).asDouble()); // getDouble
} else if (foreign instanceof NBTTagFloat) {
return new FloatTag(((NBTTagFloat) foreign).i()); // getFloat
} else if (foreign instanceof NBTTagInt) {
return new IntTag(((NBTTagInt) foreign).e()); // getInt
} else if (foreign instanceof NBTTagIntArray) {
return new IntArrayTag(((NBTTagIntArray) foreign).d()); // data
} else if (foreign instanceof NBTTagList) {
try {
return toNativeList((NBTTagList) foreign);
} catch (Throwable e) {
logger.log(Level.WARNING, "Failed to convert NBTTagList", e);
return new ListTag(ByteTag.class, new ArrayList<ByteTag>());
}
} else if (foreign instanceof NBTTagLong) {
return new LongTag(((NBTTagLong) foreign).d()); // getLong
} else if (foreign instanceof NBTTagShort) {
return new ShortTag(((NBTTagShort) foreign).f()); // getShort
} else if (foreign instanceof NBTTagString) {
return new StringTag(foreign.b_()); // data
} else if (foreign instanceof NBTTagEnd) {
return new EndTag();
} else {
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
}
}
/**
* Convert a foreign NBT list tag into a native WorldEdit one.
*
* @param foreign the foreign tag
* @return the converted tag
* @throws NoSuchFieldException on error
* @throws SecurityException on error
* @throws IllegalArgumentException on error
* @throws IllegalAccessException on error
*/
public ListTag toNativeList(NBTTagList foreign) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
List<Tag> values = new ArrayList<>();
int type = foreign.getTypeId();
List foreignList;
foreignList = (List) nbtListTagListField.get(foreign);
for (int i = 0; i < foreign.size(); i++) {
NBTBase element = (NBTBase) foreignList.get(i);
values.add(toNative(element)); // List elements shouldn't have names
}
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
return new ListTag(cls, values);
}
/**
* Converts a WorldEdit-native NBT structure to a NMS structure.
*
* @param foreign structure to convert
* @return non-native structure
*/
@Override
public NBTBase fromNative(Tag foreign) {
if (foreign == null) {
return null;
}
if (foreign instanceof CompoundTag) {
NBTTagCompound tag = new NBTTagCompound();
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
.getValue().entrySet()) {
tag.set(entry.getKey(), fromNative(entry.getValue()));
}
return tag;
} else if (foreign instanceof ByteTag) {
return new NBTTagByte(((ByteTag) foreign).getValue());
} else if (foreign instanceof ByteArrayTag) {
return new NBTTagByteArray(((ByteArrayTag) foreign).getValue());
} else if (foreign instanceof DoubleTag) {
return new NBTTagDouble(((DoubleTag) foreign).getValue());
} else if (foreign instanceof FloatTag) {
return new NBTTagFloat(((FloatTag) foreign).getValue());
} else if (foreign instanceof IntTag) {
return new NBTTagInt(((IntTag) foreign).getValue());
} else if (foreign instanceof IntArrayTag) {
return new NBTTagIntArray(((IntArrayTag) foreign).getValue());
} else if (foreign instanceof ListTag) {
NBTTagList tag = new NBTTagList();
ListTag<?> foreignList = (ListTag) foreign;
for (Tag t : foreignList.getValue()) {
tag.add(fromNative(t));
}
return tag;
} else if (foreign instanceof LongTag) {
return new NBTTagLong(((LongTag) foreign).getValue());
} else if (foreign instanceof ShortTag) {
return new NBTTagShort(((ShortTag) foreign).getValue());
} else if (foreign instanceof StringTag) {
return new NBTTagString(((StringTag) foreign).getValue());
} else if (foreign instanceof EndTag) {
try {
return (NBTBase) nbtCreateTagMethod.invoke(null, (byte) 0);
} catch (Exception e) {
return null;
}
} else {
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
}
}
}

View File

@ -0,0 +1,110 @@
package com.boydti.fawe.bukkit.chat;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import org.apache.commons.lang.Validate;
/**
* Represents a wrapper around an array class of an arbitrary reference type,
* which properly implements "value" hash code and equality functions.
* <p>
* This class is intended for use as a key to a map.
* </p>
*
* @param <E> The type of elements in the array.
* @author Glen Husman
* @see Arrays
*/
public final class ArrayWrapper<E> {
/**
* Creates an array wrapper with some elements.
*
* @param elements The elements of the array.
*/
public ArrayWrapper(E... elements) {
setArray(elements);
}
private E[] _array;
/**
* Retrieves a reference to the wrapped array instance.
*
* @return The array wrapped by this instance.
*/
public E[] getArray() {
return _array;
}
/**
* Set this wrapper to wrap a new array instance.
*
* @param array The new wrapped array.
*/
public void setArray(E[] array) {
Validate.notNull(array, "The array must not be null.");
_array = array;
}
/**
* Determines if this object has a value equivalent to another object.
*
* @see Arrays#equals(Object[], Object[])
*/
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object other) {
if (!(other instanceof ArrayWrapper)) {
return false;
}
return Arrays.equals(_array, ((ArrayWrapper) other)._array);
}
/**
* Gets the hash code represented by this objects value.
*
* @return This object's hash code.
* @see Arrays#hashCode(Object[])
*/
@Override
public int hashCode() {
return Arrays.hashCode(_array);
}
/**
* Converts an iterable element collection to an array of elements.
* The iteration order of the specified object will be used as the array element order.
*
* @param list The iterable of objects which will be converted to an array.
* @param c The type of the elements of the array.
* @return An array of elements in the specified iterable.
*/
@SuppressWarnings("unchecked")
public static <T> T[] toArray(Iterable<? extends T> list, Class<T> c) {
int size = -1;
if (list instanceof Collection<?>) {
@SuppressWarnings("rawtypes")
Collection coll = (Collection) list;
size = coll.size();
}
if (size < 0) {
size = 0;
// Ugly hack: Count it ourselves
for (@SuppressWarnings("unused") T element : list) {
size++;
}
}
T[] result = (T[]) Array.newInstance(c, size);
int i = 0;
for (T element : list) { // Assumes iteration order is consistent
result[i++] = element; // Assign array element at index THEN increment counter
}
return result;
}
}

View File

@ -0,0 +1,64 @@
package com.boydti.fawe.bukkit.chat;
import com.boydti.fawe.bukkit.BukkitPlayer;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.chat.ChatManager;
import com.boydti.fawe.util.chat.Message;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
public class BukkitChatManager implements ChatManager<FancyMessage> {
@Override
public FancyMessage builder() {
return new FancyMessage("");
}
@Override
public void color(Message message, String color) {
message.$(this).color(ChatColor.getByChar(BBC.color(color).substring(1)));
}
@Override
public void tooltip(Message message, Message... tooltips) {
List<FancyMessage> lines = new ArrayList<>();
for (Message tooltip : tooltips) {
lines.add(tooltip.$(this));
}
message.$(this).formattedTooltip(lines);
}
@Override
public void command(Message message, String command) {
message.$(this).command(command);
}
@Override
public void text(Message message, String text) {
message.$(this).color(BBC.color(text));
}
@Override
public void send(Message Message, FawePlayer player) {
if (!(player instanceof BukkitPlayer)) {
player.sendMessage(Message.$(this).toOldMessageFormat());
} else {
Message.$(this).send(((BukkitPlayer) player).parent);
}
}
@Override
public void suggest(Message Message, String command) {
Message.$(this).suggest(command);
}
@Override
public void link(Message Message, String url) {
Message.$(this).link(url);
}
}

View File

@ -0,0 +1,955 @@
package com.boydti.fawe.bukkit.chat;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import org.bukkit.Achievement;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.Statistic.Type;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import static com.boydti.fawe.bukkit.chat.TextualComponent.rawText;
/**
* Represents a formattable message. Such messages can use elements such as colors, formatting codes, hover and click data, and other features provided by the vanilla Minecraft <a href="http://minecraft.gamepedia.com/Tellraw#Raw_JSON_Text">JSON message formatter</a>.
* This class allows plugins to emulate the functionality of the vanilla Minecraft <a href="http://minecraft.gamepedia.com/Commands#tellraw">tellraw command</a>.
* <p>
* This class follows the builder pattern, allowing for method chaining.
* It is set up such that invocations of property-setting methods will affect the current editing component,
* and a call to {@link #then()} or {@link #then(String)} will append a new editing component to the end of the message,
* optionally initializing it with text. Further property-setting method calls will affect that editing component.
* </p>
*/
public class FancyMessage implements JsonRepresentedObject, Cloneable, Iterable<MessagePart>, ConfigurationSerializable {
static {
ConfigurationSerialization.registerClass(FancyMessage.class);
}
private List<MessagePart> messageParts;
private int index = 0;
private String jsonString;
private boolean dirty;
private static Constructor<?> nmsPacketPlayOutChatConstructor;
@Override
public FancyMessage clone() throws CloneNotSupportedException {
FancyMessage instance = (FancyMessage) super.clone();
instance.messageParts = new ArrayList<>(messageParts.size());
for (int i = 0; i < messageParts.size(); i++) {
instance.messageParts.add(i, messageParts.get(i).clone());
}
instance.index = index;
instance.dirty = false;
instance.jsonString = null;
return instance;
}
/**
* Creates a JSON message with text.
*
* @param firstPartText The existing text in the message.
*/
public FancyMessage(final String firstPartText) {
this(rawText(firstPartText));
}
private FancyMessage(final TextualComponent firstPartText) {
messageParts = new ArrayList<>();
messageParts.add(new MessagePart(firstPartText));
index = messageParts.size();
jsonString = null;
dirty = false;
if (nmsPacketPlayOutChatConstructor == null) {
try {
nmsPacketPlayOutChatConstructor = Reflection.getNMSClass("PacketPlayOutChat").getDeclaredConstructor(Reflection.getNMSClass("IChatBaseComponent"));
nmsPacketPlayOutChatConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
Bukkit.getLogger().log(Level.SEVERE, "Could not find Minecraft method or constructor.", e);
} catch (SecurityException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access constructor.", e);
}
}
}
/**
* Creates a JSON message without text.
*/
public FancyMessage() {
this((TextualComponent) null);
}
/**
* Sets the text of the current editing component to a value.
*
* @param text The new text of the current editing component.
* @return This builder instance.
*/
public FancyMessage text(String text) {
MessagePart latest = latest();
latest.text = rawText(text);
dirty = true;
return this;
}
/**
* Sets the text of the current editing component to a value.
*
* @param text The new text of the current editing component.
* @return This builder instance.
*/
public FancyMessage text(TextualComponent text) {
MessagePart latest = latest();
latest.text = text;
dirty = true;
return this;
}
/**
*
* @param text Text with coloring
* @return This builder instance.
* @throws IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value).
*/
public FancyMessage color(String text) {
index = messageParts.size();
boolean color = false;
ArrayDeque<ChatColor> colors = new ArrayDeque<>();
int last = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (color != (color = false)) {
ChatColor chatColor = ChatColor.getByChar(c);
if (chatColor != null) {
if (i - last > 1) {
append(text.substring(last, i - 1));
colors.forEach(this::color);
colors.clear();
}
colors.add(chatColor);
last = i + 1;
}
}
if (c == '\u00A7') {
color = true;
}
}
if (text.length() - last > 0) {
append(text.substring(last, text.length()));
colors.forEach(this::color);
}
index++;
return this;
}
/**
* Sets the color of the current editing component to a value.<br />
* Setting the color will clear current styles
*
* @param color The new color of the current editing component.
* @return This builder instance.
* @throws IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value).
*/
public FancyMessage color(ChatColor color) {
if (!color.isColor()) {
if (color.isFormat()) {
return style(color);
}
if (color == ChatColor.RESET) {
color = ChatColor.WHITE;
}
} else {
latest().styles.clear();
}
latest().color = color;
dirty = true;
return this;
}
/**
* Sets the stylization of the current editing component.
*
* @param styles The array of styles to apply to the editing component.
* @return This builder instance.
* @throws IllegalArgumentException If any of the enumeration values in the array do not represent formatters.
*/
public FancyMessage style(ChatColor... styles) {
for (final ChatColor style : styles) {
if (!style.isFormat()) {
color(style);
}
}
latest().styles.addAll(Arrays.asList(styles));
dirty = true;
return this;
}
/**
* Set the behavior of the current editing component to instruct the client to open a file on the client side filesystem when the currently edited part of the {@code FancyMessage} is clicked.
*
* @param path The path of the file on the client filesystem.
* @return This builder instance.
*/
public FancyMessage file(final String path) {
onClick("open_file", path);
return this;
}
/**
* Set the behavior of the current editing component to instruct the client to open a webpage in the client's web browser when the currently edited part of the {@code FancyMessage} is clicked.
*
* @param url The URL of the page to open when the link is clicked.
* @return This builder instance.
*/
public FancyMessage link(final String url) {
onClick("open_url", url);
return this;
}
/**
* Set the behavior of the current editing component to instruct the client to replace the chat input box content with the specified string when the currently edited part of the {@code FancyMessage} is clicked.
* The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key.
*
* @param command The text to display in the chat bar of the client.
* @return This builder instance.
*/
public FancyMessage suggest(final String command) {
onClick("suggest_command", command);
return this;
}
/**
* Set the behavior of the current editing component to instruct the client to append the chat input box content with the specified string when the currently edited part of the {@code FancyMessage} is SHIFT-CLICKED.
* The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key.
*
* @param command The text to append to the chat bar of the client.
* @return This builder instance.
*/
public FancyMessage insert(final String command) {
onCurrent(m -> m.insertionData = command);
dirty = true;
return this;
}
/**
* Set the behavior of the current editing component to instruct the client to send the specified string to the server as a chat message when the currently edited part of the {@code FancyMessage} is clicked.
* The client <b>will</b> immediately send the command to the server to be executed when the editing component is clicked.
*
* @param command The text to display in the chat bar of the client.
* @return This builder instance.
*/
public FancyMessage command(final String command) {
onClick("run_command", command);
return this;
}
/**
* Set the behavior of the current editing component to display information about an achievement when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param name The name of the achievement to display, excluding the "achievement." prefix.
* @return This builder instance.
*/
public FancyMessage achievementTooltip(final String name) {
onHover("show_achievement", new JsonString("achievement." + name));
return this;
}
/**
* Set the behavior of the current editing component to display information about an achievement when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param which The achievement to display.
* @return This builder instance.
*/
public FancyMessage achievementTooltip(final Achievement which) {
try {
Object achievement = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getNMSAchievement", Achievement.class).invoke(null, which);
return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Achievement"), "name").get(achievement));
} catch (IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
return this;
} catch (IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
return this;
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e);
return this;
}
}
/**
* Set the behavior of the current editing component to display information about a parameterless statistic when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param which The statistic to display.
* @return This builder instance.
* @throws IllegalArgumentException If the statistic requires a parameter which was not supplied.
*/
public FancyMessage statisticTooltip(final Statistic which) {
Type type = which.getType();
if (type != Type.UNTYPED) {
throw new IllegalArgumentException("That statistic requires an additional " + type + " parameter!");
}
try {
Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getNMSStatistic", Statistic.class).invoke(null, which);
return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic));
} catch (IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
return this;
} catch (IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
return this;
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e);
return this;
}
}
/**
* Set the behavior of the current editing component to display information about a statistic parameter with a material when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param which The statistic to display.
* @param item The sole material parameter to the statistic.
* @return This builder instance.
* @throws IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not required.
*/
public FancyMessage statisticTooltip(final Statistic which, Material item) {
Type type = which.getType();
if (type == Type.UNTYPED) {
throw new IllegalArgumentException("That statistic needs no additional parameter!");
}
if ((type == Type.BLOCK && item.isBlock()) || type == Type.ENTITY) {
throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!");
}
try {
Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getMaterialStatistic", Statistic.class, Material.class).invoke(null, which, item);
return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic));
} catch (IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
return this;
} catch (IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
return this;
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e);
return this;
}
}
/**
* Set the behavior of the current editing component to display information about a statistic parameter with an entity type when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param which The statistic to display.
* @param entity The sole entity type parameter to the statistic.
* @return This builder instance.
* @throws IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not required.
*/
public FancyMessage statisticTooltip(final Statistic which, EntityType entity) {
Type type = which.getType();
if (type == Type.UNTYPED) {
throw new IllegalArgumentException("That statistic needs no additional parameter!");
}
if (type != Type.ENTITY) {
throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!");
}
try {
Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getEntityStatistic", Statistic.class, EntityType.class).invoke(null, which, entity);
return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic));
} catch (IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
return this;
} catch (IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
return this;
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e);
return this;
}
}
/**
* Set the behavior of the current editing component to display information about an item when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param itemJSON A string representing the JSON-serialized NBT data tag of an {@link ItemStack}.
* @return This builder instance.
*/
public FancyMessage itemTooltip(final String itemJSON) {
onHover("show_item", new JsonString(itemJSON)); // Seems a bit hacky, considering we have a JSON object as a parameter
return this;
}
/**
* Set the behavior of the current editing component to display information about an item when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param itemStack The stack for which to display information.
* @return This builder instance.
*/
public FancyMessage itemTooltip(final ItemStack itemStack) {
try {
Object nmsItem = Reflection.getMethod(Reflection.getOBCClass("inventory.CraftItemStack"), "asNMSCopy", ItemStack.class).invoke(null, itemStack);
return itemTooltip(Reflection.getMethod(Reflection.getNMSClass("ItemStack"), "save", Reflection.getNMSClass("NBTTagCompound")).invoke(nmsItem, Reflection.getNMSClass("NBTTagCompound").newInstance()).toString());
} catch (Exception e) {
e.printStackTrace();
return this;
}
}
/**
* Set the behavior of the current editing component to display raw text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param text The text, which supports newlines, which will be displayed to the client upon hovering.
* @return This builder instance.
*/
public FancyMessage tooltip(final String text) {
onHover("show_text", new JsonString(text));
return this;
}
/**
* Set the behavior of the current editing component to display raw text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in which the lines of the tooltip are created.
* @return This builder instance.
*/
public FancyMessage tooltip(final Iterable<String> lines) {
tooltip(com.boydti.fawe.bukkit.chat.ArrayWrapper.toArray(lines, String.class));
return this;
}
/**
* Set the behavior of the current editing component to display raw text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param lines The lines of text which will be displayed to the client upon hovering.
* @return This builder instance.
*/
public FancyMessage tooltip(final String... lines) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
builder.append(lines[i]);
if (i != lines.length - 1) {
builder.append('\n');
}
}
tooltip(builder.toString());
return this;
}
/**
* Set the behavior of the current editing component to display formatted text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param text The formatted text which will be displayed to the client upon hovering.
* @return This builder instance.
*/
public FancyMessage formattedTooltip(FancyMessage text) {
for (MessagePart component : text.messageParts) {
if (component.clickActionData != null && component.clickActionName != null) {
throw new IllegalArgumentException("The tooltip text cannot have click data.");
} else if (component.hoverActionData != null && component.hoverActionName != null) {
throw new IllegalArgumentException("The tooltip text cannot have a tooltip.");
}
}
onHover("show_text", text);
return this;
}
/**
* Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param lines The lines of formatted text which will be displayed to the client upon hovering.
* @return This builder instance.
*/
public FancyMessage formattedTooltip(FancyMessage... lines) {
if (lines.length < 1) {
onHover(null, null); // Clear tooltip
return this;
}
FancyMessage result = new FancyMessage();
result.messageParts.clear(); // Remove the one existing text component that exists by default, which destabilizes the object
for (int i = 0; i < lines.length; i++) {
try {
for (MessagePart component : lines[i]) {
if (component.clickActionData != null && component.clickActionName != null) {
throw new IllegalArgumentException("The tooltip text cannot have click data.");
} else if (component.hoverActionData != null && component.hoverActionName != null) {
throw new IllegalArgumentException("The tooltip text cannot have a tooltip.");
}
if (component.hasText()) {
result.messageParts.add(component.clone());
result.index = result.messageParts.size();
}
}
if (i != lines.length - 1) {
result.messageParts.add(new MessagePart(rawText("\n")));
result.index = result.messageParts.size();
}
} catch (CloneNotSupportedException e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to clone object", e);
return this;
}
}
return formattedTooltip(result.messageParts.isEmpty() ? null : result); // Throws NPE if size is 0, intended
}
/**
* Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text.
* <p>Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.</p>
*
* @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in which the lines of the tooltip are created.
* @return This builder instance.
*/
public FancyMessage formattedTooltip(final Iterable<FancyMessage> lines) {
return formattedTooltip(com.boydti.fawe.bukkit.chat.ArrayWrapper.toArray(lines, FancyMessage.class));
}
/**
* If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message.
*
* @param replacements The replacements, in order, that will be used in the language-specific message.
* @return This builder instance.
*/
public FancyMessage translationReplacements(final String... replacements) {
for (String str : replacements) {
latest().translationReplacements.add(new JsonString(str));
}
dirty = true;
return this;
}
/*
/**
* If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message.
* @param replacements The replacements, in order, that will be used in the language-specific message.
* @return This builder instance.
*/ /* ------------
public FancyMessage translationReplacements(final Iterable<? extends CharSequence> replacements){
for(CharSequence str : replacements){
latest().translationReplacements.add(new JsonString(str));
}
return this;
}
*/
/**
* If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message.
*
* @param replacements The replacements, in order, that will be used in the language-specific message.
* @return This builder instance.
*/
public FancyMessage translationReplacements(final FancyMessage... replacements) {
for (FancyMessage str : replacements) {
latest().translationReplacements.add(str);
}
dirty = true;
return this;
}
/**
* If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message.
*
* @param replacements The replacements, in order, that will be used in the language-specific message.
* @return This builder instance.
*/
public FancyMessage translationReplacements(final Iterable<FancyMessage> replacements) {
return translationReplacements(com.boydti.fawe.bukkit.chat.ArrayWrapper.toArray(replacements, FancyMessage.class));
}
/**
* Terminate construction of the current editing component, and begin construction of a new message component.
* After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method.
*
* @param text The text which will populate the new message component.
* @return This builder instance.
*/
public FancyMessage then(final String text) {
return then(rawText(text));
}
private FancyMessage append(final String text) {
if (!latest().hasText()) {
throw new IllegalStateException("previous message part has no text");
}
MessagePart latest = latest();
messageParts.add(new MessagePart(rawText(text)));
latest().color = latest.color;
latest().styles.addAll(latest.styles);
dirty = true;
return this;
}
/**
* Terminate construction of the current editing component, and begin construction of a new message component.
* After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method.
*
* @param text The text which will populate the new message component.
* @return This builder instance.
*/
public FancyMessage then(final TextualComponent text) {
if (!latest().hasText()) {
throw new IllegalStateException("previous message part has no text");
}
messageParts.add(new MessagePart(text));
index = messageParts.size();
dirty = true;
return this;
}
/**
* Terminate construction of the current editing component, and begin construction of a new message component.
* After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method.
*
* @return This builder instance.
*/
public FancyMessage then() {
if (!latest().hasText()) {
throw new IllegalStateException("previous message part has no text");
}
messageParts.add(new MessagePart());
index = messageParts.size();
dirty = true;
return this;
}
@Override
public void writeJson(JsonWriter writer) throws IOException {
if (messageParts.size() == 1) {
latest().writeJson(writer);
} else {
writer.beginObject().name("text").value("").name("extra").beginArray();
for (final MessagePart part : this) {
part.writeJson(writer);
}
writer.endArray().endObject();
}
}
/**
* Serialize this fancy message, converting it into syntactically-valid JSON using a {@link JsonWriter}.
* This JSON should be compatible with vanilla formatter commands such as {@code /tellraw}.
*
* @return The JSON string representing this object.
*/
public String toJSONString() {
if (!dirty && jsonString != null) {
return jsonString;
}
StringWriter string = new StringWriter();
JsonWriter json = new JsonWriter(string);
try {
writeJson(json);
json.close();
} catch (IOException e) {
throw new RuntimeException("invalid message");
}
jsonString = string.toString();
dirty = false;
return jsonString;
}
/**
* Sends this message to a player. The player will receive the fully-fledged formatted display of this message.
*
* @param player The player who will receive the message.
*/
public void send(Player player) {
send(player, toJSONString());
}
private void send(CommandSender sender, String jsonString) {
if (!(sender instanceof Player)) {
sender.sendMessage(toOldMessageFormat());
return;
}
Player player = (Player) sender;
try {
Object handle = Reflection.getHandle(player);
Object connection = Reflection.getField(handle.getClass(), "playerConnection").get(handle);
Reflection.getMethod(connection.getClass(), "sendPacket", Reflection.getNMSClass("Packet")).invoke(connection, createChatPacket(jsonString));
} catch (IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
} catch (IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
} catch (InstantiationException e) {
Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e);
} catch (InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e);
} catch (NoSuchMethodException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e);
} catch (ClassNotFoundException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e);
}
}
// The ChatSerializer's instance of Gson
private static Object nmsChatSerializerGsonInstance;
private static Method fromJsonMethod;
private Object createChatPacket(String json) throws IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
if (nmsChatSerializerGsonInstance == null) {
// Find the field and its value, completely bypassing obfuscation
Class<?> chatSerializerClazz;
// Get the three parts of the version string (major version is currently unused)
// vX_Y_RZ
// X = major
// Y = minor
// Z = revision
final String version = Reflection.getVersion();
String[] split = version.substring(1, version.length() - 1).split("_"); // Remove trailing dot
//int majorVersion = Integer.parseInt(split[0]);
int minorVersion = Integer.parseInt(split[1]);
int revisionVersion = Integer.parseInt(split[2].substring(1)); // Substring to ignore R
if (minorVersion < 8 || (minorVersion == 8 && revisionVersion == 1)) {
chatSerializerClazz = Reflection.getNMSClass("ChatSerializer");
} else {
chatSerializerClazz = Reflection.getNMSClass("IChatBaseComponent$ChatSerializer");
}
if (chatSerializerClazz == null) {
throw new ClassNotFoundException("Can't find the ChatSerializer class");
}
for (Field declaredField : chatSerializerClazz.getDeclaredFields()) {
if (Modifier.isFinal(declaredField.getModifiers()) && Modifier.isStatic(declaredField.getModifiers()) && declaredField.getType().getName().endsWith("Gson")) {
// We've found our field
declaredField.setAccessible(true);
nmsChatSerializerGsonInstance = declaredField.get(null);
fromJsonMethod = nmsChatSerializerGsonInstance.getClass().getMethod("fromJson", String.class, Class.class);
break;
}
}
}
// Since the method is so simple, and all the obfuscated methods have the same name, it's easier to reimplement 'IChatBaseComponent a(String)' than to reflectively call it
// Of course, the implementation may change, but fuzzy matches might break with signature changes
Object serializedChatComponent = fromJsonMethod.invoke(nmsChatSerializerGsonInstance, json, Reflection.getNMSClass("IChatBaseComponent"));
return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent);
}
/**
* Sends this message to a command sender.
* If the sender is a player, they will receive the fully-fledged formatted display of this message.
* Otherwise, they will receive a version of this message with less formatting.
*
* @param sender The command sender who will receive the message.
* @see #toOldMessageFormat()
*/
public void send(CommandSender sender) {
send(sender, toJSONString());
}
/**
* Sends this message to multiple command senders.
*
* @param senders The command senders who will receive the message.
* @see #send(CommandSender)
*/
public void send(final Iterable<? extends CommandSender> senders) {
String string = toJSONString();
for (final CommandSender sender : senders) {
send(sender, string);
}
}
/**
* Convert this message to a human-readable string with limited formatting.
* This method is used to send this message to clients without JSON formatting support.
* <p>
* Serialization of this message by using this message will include (in this order for each message part):
* <ol>
* <li>The color of each message part.</li>
* <li>The applicable stylizations for each message part.</li>
* <li>The core text of the message part.</li>
* </ol>
* The primary omissions are tooltips and clickable actions. Consequently, this method should be used only as a last resort.
* </p>
* <p>
* Color and formatting can be removed from the returned string by using {@link ChatColor#stripColor(String)}.</p>
*
* @return A human-readable string representing limited formatting in addition to the core text of this message.
*/
public String toOldMessageFormat() {
StringBuilder result = new StringBuilder();
for (MessagePart part : this) {
result.append(part.color == null ? "" : part.color);
for (ChatColor formatSpecifier : part.styles) {
result.append(formatSpecifier);
}
result.append(part.text);
}
return result.toString();
}
private void onCurrent(Consumer<MessagePart> call) {
for (int i = index - 1; i < messageParts.size(); i++) {
call.accept(messageParts.get(i));
}
}
private MessagePart latest() {
return messageParts.get(messageParts.size() - 1);
}
private void onClick(final String name, final String data) {
onCurrent(m -> { m.clickActionName = name; m.clickActionData = data; });
dirty = true;
}
private void onHover(final String name, final JsonRepresentedObject data) {
onCurrent(m -> { m.hoverActionName = name; m.hoverActionData = data; });
dirty = true;
}
// Doc copied from interface
public Map<String, Object> serialize() {
HashMap<String, Object> map = new HashMap<>();
map.put("messageParts", messageParts);
// map.put("JSON", toJSONString());
return map;
}
/**
* Deserializes a JSON-represented message from a mapping of key-value pairs.
* This is called by the Bukkit serialization API.
* It is not intended for direct public API consumption.
*
* @param serialized The key-value mapping which represents a fancy message.
*/
@SuppressWarnings("unchecked")
public static FancyMessage deserialize(Map<String, Object> serialized) {
FancyMessage msg = new FancyMessage();
msg.messageParts = (List<MessagePart>) serialized.get("messageParts");
msg.jsonString = serialized.containsKey("JSON") ? serialized.get("JSON").toString() : null;
msg.dirty = !serialized.containsKey("JSON");
return msg;
}
/**
* <b>Internally called method. Not for API consumption.</b>
*/
public Iterator<MessagePart> iterator() {
return messageParts.iterator();
}
private static JsonParser _stringParser = new JsonParser();
/**
* Deserializes a fancy message from its JSON representation. This JSON representation is of the format of
* that returned by {@link #toJSONString()}, and is compatible with vanilla inputs.
*
* @param json The JSON string which represents a fancy message.
* @return A {@code FancyMessage} representing the parameterized JSON message.
*/
public static FancyMessage deserialize(String json) {
JsonObject serialized = _stringParser.parse(json).getAsJsonObject();
JsonArray extra = serialized.getAsJsonArray("extra"); // Get the extra component
FancyMessage returnVal = new FancyMessage();
returnVal.messageParts.clear();
for (JsonElement mPrt : extra) {
MessagePart component = new MessagePart();
JsonObject messagePart = mPrt.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : messagePart.entrySet()) {
// Deserialize text
if (TextualComponent.isTextKey(entry.getKey())) {
// The map mimics the YAML serialization, which has a "key" field and one or more "value" fields
Map<String, Object> serializedMapForm = new HashMap<>(); // Must be object due to Bukkit serializer API compliance
serializedMapForm.put("key", entry.getKey());
if (entry.getValue().isJsonPrimitive()) {
// Assume string
serializedMapForm.put("value", entry.getValue().getAsString());
} else {
// Composite object, but we assume each element is a string
for (Map.Entry<String, JsonElement> compositeNestedElement : entry.getValue().getAsJsonObject().entrySet()) {
serializedMapForm.put("value." + compositeNestedElement.getKey(), compositeNestedElement.getValue().getAsString());
}
}
component.text = TextualComponent.deserialize(serializedMapForm);
} else if (MessagePart.stylesToNames.inverse().containsKey(entry.getKey())) {
if (entry.getValue().getAsBoolean()) {
component.styles.add(MessagePart.stylesToNames.inverse().get(entry.getKey()));
}
} else if (entry.getKey().equals("color")) {
component.color = ChatColor.valueOf(entry.getValue().getAsString().toUpperCase());
} else if (entry.getKey().equals("clickEvent")) {
JsonObject object = entry.getValue().getAsJsonObject();
component.clickActionName = object.get("action").getAsString();
component.clickActionData = object.get("value").getAsString();
} else if (entry.getKey().equals("hoverEvent")) {
JsonObject object = entry.getValue().getAsJsonObject();
component.hoverActionName = object.get("action").getAsString();
if (object.get("value").isJsonPrimitive()) {
// Assume string
component.hoverActionData = new JsonString(object.get("value").getAsString());
} else {
// Assume composite type
// The only composite type we currently store is another FancyMessage
// Therefore, recursion time!
component.hoverActionData = deserialize(object.get("value").toString() /* This should properly serialize the JSON object as a JSON string */);
}
} else if (entry.getKey().equals("insertion")) {
component.insertionData = entry.getValue().getAsString();
} else if (entry.getKey().equals("with")) {
for (JsonElement object : entry.getValue().getAsJsonArray()) {
if (object.isJsonPrimitive()) {
component.translationReplacements.add(new JsonString(object.getAsString()));
} else {
// Only composite type stored in this array is - again - FancyMessages
// Recurse within this function to parse this as a translation replacement
component.translationReplacements.add(deserialize(object.toString()));
}
}
}
}
returnVal.messageParts.add(component);
returnVal.index = returnVal.messageParts.size();
}
return returnVal;
}
}

View File

@ -0,0 +1,18 @@
package com.boydti.fawe.bukkit.chat;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
/**
* Represents an object that can be serialized to a JSON writer instance.
*/
interface JsonRepresentedObject {
/**
* Writes the JSON representation of this object to the specified writer.
* @param writer The JSON writer which will receive the object.
* @throws IOException If an error occurs writing to the stream.
*/
public void writeJson(JsonWriter writer) throws IOException;
}

View File

@ -0,0 +1,46 @@
package com.boydti.fawe.bukkit.chat;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
/**
* Represents a JSON string value.
* Writes by this object will not write name values nor begin/end objects in the JSON stream.
* All writes merely write the represented string value.
*/
final class JsonString implements JsonRepresentedObject, ConfigurationSerializable {
private String _value;
public JsonString(CharSequence value) {
_value = value == null ? null : value.toString();
}
@Override
public void writeJson(JsonWriter writer) throws IOException {
writer.value(getValue());
}
public String getValue() {
return _value;
}
public Map<String, Object> serialize() {
HashMap<String, Object> theSingleValue = new HashMap<String, Object>();
theSingleValue.put("stringValue", _value);
return theSingleValue;
}
public static JsonString deserialize(Map<String, Object> map) {
return new JsonString(map.get("stringValue").toString());
}
@Override
public String toString() {
return _value;
}
}

View File

@ -0,0 +1,156 @@
package com.boydti.fawe.bukkit.chat;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
/**
* Internal class: Represents a component of a JSON-serializable {@link FancyMessage}.
*/
final class MessagePart implements JsonRepresentedObject, ConfigurationSerializable, Cloneable {
ChatColor color = ChatColor.WHITE;
ArrayList<ChatColor> styles = new ArrayList<>();
String clickActionName = null;
String clickActionData = null;
String hoverActionName = null;
JsonRepresentedObject hoverActionData = null;
TextualComponent text = null;
String insertionData = null;
ArrayList<JsonRepresentedObject> translationReplacements = new ArrayList<>();
MessagePart(final TextualComponent text) {
this.text = text;
}
MessagePart() {
this.text = null;
}
boolean hasText() {
return text != null;
}
@Override
@SuppressWarnings("unchecked")
public MessagePart clone() throws CloneNotSupportedException {
MessagePart obj = (MessagePart) super.clone();
obj.styles = (ArrayList<ChatColor>) styles.clone();
if (hoverActionData instanceof JsonString) {
obj.hoverActionData = new JsonString(((JsonString) hoverActionData).getValue());
} else if (hoverActionData instanceof FancyMessage) {
obj.hoverActionData = ((FancyMessage) hoverActionData).clone();
}
obj.translationReplacements = (ArrayList<JsonRepresentedObject>) translationReplacements.clone();
return obj;
}
static final BiMap<ChatColor, String> stylesToNames;
static {
ImmutableBiMap.Builder<ChatColor, String> builder = ImmutableBiMap.builder();
for (final ChatColor style : ChatColor.values()) {
if (!style.isFormat()) {
continue;
}
String styleName;
switch (style) {
case MAGIC:
styleName = "obfuscated";
break;
case UNDERLINE:
styleName = "underlined";
break;
default:
styleName = style.name().toLowerCase();
break;
}
builder.put(style, styleName);
}
stylesToNames = builder.build();
}
public void writeJson(JsonWriter json) {
try {
json.beginObject();
text.writeJson(json);
json.name("color").value(color.name().toLowerCase());
for (final ChatColor style : styles) {
json.name(stylesToNames.get(style)).value(true);
}
if (clickActionName != null && clickActionData != null) {
json.name("clickEvent")
.beginObject()
.name("action").value(clickActionName)
.name("value").value(clickActionData)
.endObject();
}
if (hoverActionName != null && hoverActionData != null) {
json.name("hoverEvent")
.beginObject()
.name("action").value(hoverActionName)
.name("value");
hoverActionData.writeJson(json);
json.endObject();
}
if (insertionData != null) {
json.name("insertion").value(insertionData);
}
if (translationReplacements.size() > 0 && text != null && TextualComponent.isTranslatableText(text)) {
json.name("with").beginArray();
for (JsonRepresentedObject obj : translationReplacements) {
obj.writeJson(json);
}
json.endArray();
}
json.endObject();
} catch (IOException e) {
Bukkit.getLogger().log(Level.WARNING, "A problem occured during writing of JSON string", e);
}
}
public Map<String, Object> serialize() {
HashMap<String, Object> map = new HashMap<>();
map.put("text", text);
map.put("styles", styles);
map.put("color", color.getChar());
map.put("hoverActionName", hoverActionName);
map.put("hoverActionData", hoverActionData);
map.put("clickActionName", clickActionName);
map.put("clickActionData", clickActionData);
map.put("insertion", insertionData);
map.put("translationReplacements", translationReplacements);
return map;
}
@SuppressWarnings("unchecked")
public static MessagePart deserialize(Map<String, Object> serialized) {
MessagePart part = new MessagePart((TextualComponent) serialized.get("text"));
part.styles = (ArrayList<ChatColor>) serialized.get("styles");
part.color = ChatColor.getByChar(serialized.get("color").toString());
part.hoverActionName = (String) serialized.get("hoverActionName");
part.hoverActionData = (JsonRepresentedObject) serialized.get("hoverActionData");
part.clickActionName = (String) serialized.get("clickActionName");
part.clickActionData = (String) serialized.get("clickActionData");
part.insertionData = (String) serialized.get("insertion");
part.translationReplacements = (ArrayList<JsonRepresentedObject>) serialized.get("translationReplacements");
return part;
}
static {
ConfigurationSerialization.registerClass(MessagePart.class);
}
}

View File

@ -0,0 +1,205 @@
package com.boydti.fawe.bukkit.chat;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
/**
* A class containing static utility methods and caches which are intended as reflective conveniences.
* Unless otherwise noted, upon failure methods will return {@code null}.
*/
public final class Reflection {
/**
* Stores loaded classes from the {@code net.minecraft.server} package.
*/
private static final Map<String, Class<?>> _loadedNMSClasses = new HashMap<>();
/**
* Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages).
*/
private static final Map<String, Class<?>> _loadedOBCClasses = new HashMap<>();
private static final Map<Class<?>, Map<String, Field>> _loadedFields = new HashMap<>();
/**
* Contains loaded methods in a cache.
* The map maps [types to maps of [method names to maps of [parameter types to method instances]]].
*/
private static final Map<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>> _loadedMethods = new HashMap<>();
private static String _versionString;
private Reflection() { }
/**
* Gets the version string from the package name of the CraftBukkit server implementation.
* This is needed to bypass the JAR package name changing on each update.
*
* @return The version string of the OBC and NMS packages, <em>including the trailing dot</em>.
*/
public synchronized static String getVersion() {
if (_versionString == null) {
if (Bukkit.getServer() == null) {
// The server hasn't started, static initializer call?
return null;
}
String name = Bukkit.getServer().getClass().getPackage().getName();
_versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
}
return _versionString;
}
/**
* Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
*
* @param className The name of the class, excluding the package, within NMS.
* @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getNMSClass(String className) {
if (_loadedNMSClasses.containsKey(className)) {
return _loadedNMSClasses.get(className);
}
String fullName = "net.minecraft.server." + getVersion() + className;
Class<?> clazz;
try {
clazz = Class.forName(fullName);
} catch (ClassNotFoundException e) {
_loadedNMSClasses.put(className, null);
throw new RuntimeException(e);
}
_loadedNMSClasses.put(className, clazz);
return clazz;
}
/**
* Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
*
* @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}.
* @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getOBCClass(String className) {
if (_loadedOBCClasses.containsKey(className)) {
return _loadedOBCClasses.get(className);
}
String fullName = "org.bukkit.craftbukkit." + getVersion() + className;
Class<?> clazz;
try {
clazz = Class.forName(fullName);
} catch (ClassNotFoundException e) {
_loadedOBCClasses.put(className, null);
throw new RuntimeException(e);
}
_loadedOBCClasses.put(className, clazz);
return clazz;
}
/**
* Attempts to get the NMS handle of a CraftBukkit object.
* <p>
* The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object.
* </p>
*
* @param obj The object for which to retrieve an NMS handle.
* @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}.
*/
public synchronized static Object getHandle(Object obj) throws InvocationTargetException, IllegalAccessException, IllegalArgumentException {
return getMethod(obj.getClass(), "getHandle").invoke(obj);
}
/**
* Retrieves a {@link Field} instance declared by the specified class with the specified name.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that
* no field will be reflectively looked up twice.
* </p>
* <p>
* If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* </p>
*
* @param clazz The class which contains the field to retrieve.
* @param name The declared name of the field in the class.
* @return A field object with the specified name declared by the specified class.
* @see Class#getDeclaredField(String)
*/
public synchronized static Field getField(Class<?> clazz, String name) {
Map<String, Field> loaded;
if (!_loadedFields.containsKey(clazz)) {
loaded = new HashMap<>();
_loadedFields.put(clazz, loaded);
} else {
loaded = _loadedFields.get(clazz);
}
if (loaded.containsKey(name)) {
// If the field is loaded (or cached as not existing), return the relevant value, which might be null
return loaded.get(name);
}
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
loaded.put(name, field);
return field;
} catch (NoSuchFieldException | SecurityException e) {
// Error loading
e.printStackTrace();
// Cache field as not existing
loaded.put(name, null);
return null;
}
}
/**
* Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that
* no method will be reflectively looked up twice.
* <p>
* If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* <p>
* This method does <em>not</em> search superclasses of the specified type for methods with the specified signature.
* Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}.
*
* @param clazz The class which contains the method to retrieve.
* @param name The declared name of the method in the class.
* @param args The formal argument types of the method.
* @return A method object with the specified name declared by the specified class.
*/
public synchronized static Method getMethod(Class<?> clazz, String name, Class<?>... args) {
if (!_loadedMethods.containsKey(clazz)) {
_loadedMethods.put(clazz, new HashMap<String, Map<ArrayWrapper<Class<?>>, Method>>());
}
Map<String, Map<ArrayWrapper<Class<?>>, Method>> loadedMethodNames = _loadedMethods.get(clazz);
if (!loadedMethodNames.containsKey(name)) {
loadedMethodNames.put(name, new HashMap<ArrayWrapper<Class<?>>, Method>());
}
Map<ArrayWrapper<Class<?>>, Method> loadedSignatures = loadedMethodNames.get(name);
ArrayWrapper<Class<?>> wrappedArg = new ArrayWrapper<>(args);
if (loadedSignatures.containsKey(wrappedArg)) {
return loadedSignatures.get(wrappedArg);
}
for (Method m : clazz.getMethods()) {
if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) {
m.setAccessible(true);
loadedSignatures.put(wrappedArg, m);
return m;
}
}
loadedSignatures.put(wrappedArg, null);
return null;
}
}

View File

@ -0,0 +1,316 @@
package com.boydti.fawe.bukkit.chat;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
/**
* Represents a textual component of a message part.
* This can be used to not only represent string literals in a JSON message,
* but also to represent localized strings and other text values.
* <p>Different instances of this class can be created with static constructor methods.</p>
*/
public abstract class TextualComponent implements Cloneable {
static {
ConfigurationSerialization.registerClass(ArbitraryTextTypeComponent.class);
ConfigurationSerialization.registerClass(ComplexTextTypeComponent.class);
}
static TextualComponent deserialize(Map<String, Object> map) {
if (map.containsKey("key") && map.size() == 2 && map.containsKey("value")) {
// Arbitrary text component
return ArbitraryTextTypeComponent.deserialize(map);
} else if (map.size() >= 2 && map.containsKey("key") && !map.containsKey("value") /* It contains keys that START WITH value */) {
// Complex JSON object
return ComplexTextTypeComponent.deserialize(map);
}
return null;
}
static boolean isTextKey(String key) {
return key.equals("translate") || key.equals("text") || key.equals("score") || key.equals("selector");
}
static boolean isTranslatableText(TextualComponent component) {
return component instanceof ComplexTextTypeComponent && component.getKey().equals("translate");
}
/**
* Create a textual component representing a string literal.
*
* <p>This is the default type of textual component when a single string
* literal is given to a method.
*
* @param textValue The text which will be represented.
* @return The text component representing the specified literal text.
*/
public static TextualComponent rawText(String textValue) {
return new ArbitraryTextTypeComponent("text", textValue);
}
/**
* Create a textual component representing a localized string.
* The client will see this text component as their localized version of the specified string <em>key</em>, which can be overridden by a
* resource pack.
* <p>
* If the specified translation key is not present on the client resource pack, the translation key will be displayed as a string literal to
* the client.
* </p>
*
* @param translateKey The string key which maps to localized text.
* @return The text component representing the specified localized text.
*/
public static TextualComponent localizedText(String translateKey) {
return new ArbitraryTextTypeComponent("translate", translateKey);
}
private static void throwUnsupportedSnapshot() {
throw new UnsupportedOperationException("This feature is only supported in snapshot releases.");
}
/**
* Create a textual component representing a scoreboard value.
* The client will see their own score for the specified objective as the text represented by this component.
* <p>
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
* </p>
*
* @param scoreboardObjective The name of the objective for which to display the score.
* @return The text component representing the specified scoreboard score (for the viewing player), or {@code null} if an error occurs during
* JSON serialization.
*/
public static TextualComponent objectiveScore(String scoreboardObjective) {
return objectiveScore("*", scoreboardObjective);
}
/**
* Create a textual component representing a scoreboard value.
* The client will see the score of the specified player for the specified objective as the text represented by this component.
*
* <p><b>This method is currently guaranteed to throw an {@code UnsupportedOperationException}
* as it is only supported on snapshot clients.</b>
*
* @param playerName The name of the player whos score will be shown. If
* this string represents the single-character sequence
* "*", the viewing player's score will be displayed.
* Standard minecraft selectors (@a, @p, etc)
* are <em>not</em> supported.
* @param scoreboardObjective The name of the objective for
* which to display the score.
* @return The text component representing the specified scoreboard score
* for the specified player, or {@code null} if an error occurs during JSON serialization.
*/
public static TextualComponent objectiveScore(String playerName, String scoreboardObjective) {
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE
// OVERLOADS documentation accordingly
return new ComplexTextTypeComponent("score", ImmutableMap.<String, String>builder()
.put("name", playerName)
.put("objective", scoreboardObjective)
.build());
}
/**
* Create a textual component representing a player name, retrievable by using a standard minecraft selector.
* The client will see the players or entities captured by the specified selector as the text represented by this component.
* <p>
* <b>This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients.</b>
* </p>
*
* @param selector The minecraft player or entity selector which will capture the entities whose string representations will be displayed in
* the place of this text component.
* @return The text component representing the name of the entities captured by the selector.
*/
public static TextualComponent selector(String selector) {
throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE
// OVERLOADS documentation accordingly
return new ArbitraryTextTypeComponent("selector", selector);
}
@Override
public String toString() {
return getReadableString();
}
/**
* @return The JSON key used to represent text components of this type.
*/
public abstract String getKey();
/**
* @return A readable String
*/
public abstract String getReadableString();
/**
* Clones a textual component instance.
* The returned object should not reference this textual component instance, but should maintain the same key and value.
*/
@Override
public abstract TextualComponent clone() throws CloneNotSupportedException;
/**
* Writes the text data represented by this textual component to the specified JSON writer object.
* A new object within the writer is not started.
*
* @param writer The object to which to write the JSON data.
* @throws IOException If an error occurs while writing to the stream.
*/
public abstract void writeJson(JsonWriter writer) throws IOException;
/**
* Internal class used to represent all types of text components.
* Exception validating done is on keys and values.
*/
private static final class ArbitraryTextTypeComponent extends TextualComponent implements ConfigurationSerializable {
private String key;
private String value;
public ArbitraryTextTypeComponent(String key, String value) {
setKey(key);
setValue(value);
}
public static ArbitraryTextTypeComponent deserialize(Map<String, Object> map) {
return new ArbitraryTextTypeComponent(map.get("key").toString(), map.get("value").toString());
}
@Override
public String getKey() {
return key;
}
public void setKey(String key) {
Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified.");
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
Preconditions.checkArgument(value != null, "The value must be specified.");
this.value = value;
}
@Override
public TextualComponent clone() throws CloneNotSupportedException {
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
return new ArbitraryTextTypeComponent(getKey(), getValue());
}
@Override
public void writeJson(JsonWriter writer) throws IOException {
writer.name(getKey()).value(getValue());
}
@Override
@SuppressWarnings("serial")
public Map<String, Object> serialize() {
return new HashMap<String, Object>() {
{
put("key", getKey());
put("value", getValue());
}
};
}
@Override
public String getReadableString() {
return getValue();
}
}
/**
* Internal class used to represent a text component with a nested JSON
* value.
*
* <p>Exception validating done is on keys and values.
*/
private static final class ComplexTextTypeComponent extends TextualComponent implements ConfigurationSerializable {
private String key;
private Map<String, String> value;
public ComplexTextTypeComponent(String key, Map<String, String> values) {
setKey(key);
setValue(values);
}
public static ComplexTextTypeComponent deserialize(Map<String, Object> map) {
String key = null;
Map<String, String> value = new HashMap<>();
for (Map.Entry<String, Object> valEntry : map.entrySet()) {
if (valEntry.getKey().equals("key")) {
key = (String) valEntry.getValue();
} else if (valEntry.getKey().startsWith("value.")) {
value.put(valEntry.getKey().substring(6) /* Strips out the value prefix */, valEntry.getValue().toString());
}
}
return new ComplexTextTypeComponent(key, value);
}
@Override
public String getKey() {
return key;
}
public void setKey(String key) {
Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified.");
this.key = key;
}
public Map<String, String> getValue() {
return value;
}
public void setValue(Map<String, String> value) {
Preconditions.checkArgument(value != null, "The value must be specified.");
this.value = value;
}
@Override
public TextualComponent clone() {
// Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone
return new ComplexTextTypeComponent(getKey(), getValue());
}
@Override
public void writeJson(JsonWriter writer) throws IOException {
writer.name(getKey());
writer.beginObject();
for (Map.Entry<String, String> jsonPair : value.entrySet()) {
writer.name(jsonPair.getKey()).value(jsonPair.getValue());
}
writer.endObject();
}
@Override
@SuppressWarnings("serial")
public Map<String, Object> serialize() {
return new HashMap<String, Object>() {
{
put("key", getKey());
for (Entry<String, String> valEntry : getValue().entrySet()) {
put("value." + valEntry.getKey(), valEntry.getValue());
}
}
};
}
@Override
public String getReadableString() {
return getKey();
}
}
}

View File

@ -0,0 +1,43 @@
package com.boydti.fawe.bukkit.filter;
import com.boydti.fawe.regions.general.CuboidRegionFilter;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.util.TaskManager;
import com.sk89q.worldedit.BlockVector2D;
import java.util.ArrayDeque;
import java.util.Collection;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import org.bukkit.World;
import static com.google.common.base.Preconditions.checkNotNull;
public class GriefPreventionFilter extends CuboidRegionFilter {
private final Collection<Claim> claims;
private final World world;
public GriefPreventionFilter(World world) {
checkNotNull(world);
this.claims = TaskManager.IMP.sync(new RunnableVal<Collection<Claim>>() {
@Override
public void run(Collection<Claim> claims) {
this.value = new ArrayDeque(GriefPrevention.instance.dataStore.getClaims());
}
});
this.world = world;
}
@Override
public void calculateRegions() {
for (Claim claim : claims) {
org.bukkit.Location bot = claim.getGreaterBoundaryCorner();
if (world.equals(bot.getWorld())) {
org.bukkit.Location top = claim.getGreaterBoundaryCorner();
BlockVector2D pos1 = new BlockVector2D(bot.getBlockX(), bot.getBlockZ());
BlockVector2D pos2 = new BlockVector2D(top.getBlockX(), top.getBlockZ());
add(pos1, pos2);
}
}
}
}

View File

@ -0,0 +1,66 @@
package com.boydti.fawe.bukkit.filter;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.regions.general.CuboidRegionFilter;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.World;
import static com.google.common.base.Preconditions.checkNotNull;
public class WorldGuardFilter extends CuboidRegionFilter {
private final World world;
private boolean large;
private RegionManager manager;
public WorldGuardFilter(World world) {
checkNotNull(world);
this.world = world;
}
@Override
public void calculateRegions() {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
WorldGuardFilter.this.manager = WorldGuardPlugin.inst().getRegionManager(world);
for (ProtectedRegion region : manager.getRegions().values()) {
BlockVector min = region.getMinimumPoint();
BlockVector max = region.getMaximumPoint();
if (max.getBlockX() - min.getBlockX() > 1024 || max.getBlockZ() - min.getBlockZ() > 1024) {
Fawe.debug("Large or complex region shapes cannot be optimized. Filtering will be slower");
large = true;
break;
}
add(min.toVector2D(), max.toVector2D());
}
}
});
}
@Override
public boolean containsChunk(int chunkX, int chunkZ) {
if (!large) return super.containsChunk(chunkX, chunkZ);
BlockVector pos1 = new BlockVector(chunkX << 4, 0, chunkZ << 4);
BlockVector pos2 = new BlockVector(pos1.getBlockX() + 15, 255, pos1.getBlockZ() + 15);
ProtectedCuboidRegion chunkRegion = new ProtectedCuboidRegion("unimportant", pos1, pos2);
ApplicableRegionSet set = manager.getApplicableRegions(chunkRegion);
return set.size() > 0 && !set.getRegions().iterator().next().getId().equals("__global__");
}
@Override
public boolean containsRegion(int mcaX, int mcaZ) {
if (!large) return super.containsRegion(mcaX, mcaZ);
BlockVector pos1 = new BlockVector(mcaX << 9, 0, mcaZ << 9);
BlockVector pos2 = new BlockVector(pos1.getBlockX() + 511, 255, pos1.getBlockZ() + 511);
ProtectedCuboidRegion regionRegion = new ProtectedCuboidRegion("unimportant", pos1, pos2);
ApplicableRegionSet set = manager.getApplicableRegions(regionRegion);
return set.size() > 0 && !set.getRegions().iterator().next().getId().equals("__global__");
}
}

View File

@ -0,0 +1,100 @@
package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.MovableTool;
import com.boydti.fawe.object.brush.ResettableTool;
import com.boydti.fawe.object.brush.scroll.ScrollTool;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.command.tool.Tool;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.Plugin;
public class BrushListener implements Listener {
public BrushListener(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerItemHoldEvent(final PlayerItemHeldEvent event) {
final Player bukkitPlayer = event.getPlayer();
if (bukkitPlayer.isSneaking()) {
return;
}
FawePlayer<Object> fp = FawePlayer.wrap(bukkitPlayer);
com.sk89q.worldedit.entity.Player player = fp.getPlayer();
LocalSession session = fp.getSession();
Tool tool = session.getTool(player);
if (tool instanceof ScrollTool) {
final int slot = event.getNewSlot();
final int oldSlot = event.getPreviousSlot();
final int ri;
if ((((slot - oldSlot) <= 4) && ((slot - oldSlot) > 0)) || (((slot - oldSlot) < -4))) {
ri = 1;
} else {
ri = -1;
}
ScrollTool scrollable = (ScrollTool) tool;
if (scrollable.increment(player, ri)) {
if (Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES) {
bukkitPlayer.getInventory().setHeldItemSlot(oldSlot);
} else {
final PlayerInventory inv = bukkitPlayer.getInventory();
final ItemStack item = inv.getItem(slot);
final ItemStack newItem = inv.getItem(oldSlot);
inv.setItem(slot, newItem);
inv.setItem(oldSlot, item);
bukkitPlayer.updateInventory();
}
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Location from = event.getFrom();
Location to = event.getTo();
if ((from.getYaw() != to.getYaw() && from.getPitch() != to.getPitch()) || from.getBlockX() != to.getBlockX() || from.getBlockZ() != to.getBlockZ() || from.getBlockY() != to.getBlockY()) {
Player bukkitPlayer = event.getPlayer();
FawePlayer<Object> fp = FawePlayer.wrap(bukkitPlayer);
com.sk89q.worldedit.entity.Player player = fp.getPlayer();
LocalSession session = fp.getSession();
Tool tool = session.getTool(player);
if (tool != null) {
if (tool instanceof MovableTool) {
((MovableTool) tool).move(player);
}
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerInteract(final PlayerInteractEvent event) {
Player bukkitPlayer = event.getPlayer();
if (bukkitPlayer.isSneaking()) {
if (event.getAction() == Action.PHYSICAL) {
return;
}
FawePlayer<Object> fp = FawePlayer.wrap(bukkitPlayer);
com.sk89q.worldedit.entity.Player player = fp.getPlayer();
LocalSession session = fp.getSession();
Tool tool = session.getTool(player);
if (tool instanceof ResettableTool) {
if (((ResettableTool) tool).reset()) {
event.setCancelled(true);
}
}
}
}
}

View File

@ -0,0 +1,280 @@
package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.bukkit.util.image.BukkitImageViewer;
import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.BrushSettings;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.ExtentTraverser;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.image.ImageViewer;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
import com.sk89q.worldedit.command.tool.brush.Brush;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Rotation;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.plugin.Plugin;
public class BukkitImageListener implements Listener {
private Location mutable = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
public BukkitImageListener(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteractEntity(AsyncPlayerChatEvent event) {
Set<Player> recipients = event.getRecipients();
Iterator<Player> iter = recipients.iterator();
while (iter.hasNext()) {
Player player = iter.next();
if (player.equals(event.getPlayer())) continue;
FawePlayer<Object> fp = FawePlayer.wrap(player);
if (!fp.hasMeta()) continue;
CFICommands.CFISettings settings = fp.getMeta("CFISettings");
if (settings == null || !settings.hasGenerator()) continue;
String name = player.getName().toLowerCase();
if (!event.getMessage().toLowerCase().contains(name)) {
ArrayDeque<String> buffered = fp.getMeta("CFIBufferedMessages");
if (buffered == null) fp.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque<String>());
String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage());
buffered.add(full);
iter.remove();
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onHangingBreakByEntity(HangingBreakByEntityEvent event) {
if(!(event.getRemover() instanceof Player)) return;
handleInteract(event, (Player) event.getRemover(), event.getEntity(), false);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
if(!(event.getDamager() instanceof Player)) return;
handleInteract(event, (Player) event.getDamager(), event.getEntity(), false);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.useItemInHand() == Event.Result.DENY) return;
Player player = event.getPlayer();
FawePlayer<Object> fp = FawePlayer.wrap(player);
if (fp.getMeta("CFISettings") == null) return;
try {
if (event.getHand() == EquipmentSlot.OFF_HAND) return;
} catch (NoSuchFieldError | NoSuchMethodError ignored) {}
List<Block> target = player.getLastTwoTargetBlocks((Set<Material>) null, 100);
if (target.isEmpty()) return;
Block targetBlock = target.get(0);
World world = player.getWorld();
mutable.setWorld(world);
mutable.setX(targetBlock.getX() + 0.5);
mutable.setY(targetBlock.getY() + 0.5);
mutable.setZ(targetBlock.getZ() + 0.5);
Collection<Entity> entities = world.getNearbyEntities(mutable, 0.46875, 0, 0.46875);
if (!entities.isEmpty()) {
Action action = event.getAction();
boolean primary = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK;
double minDist = Integer.MAX_VALUE;
ItemFrame minItemFrame = null;
for (Entity entity : entities) {
if (entity instanceof ItemFrame) {
ItemFrame itemFrame = (ItemFrame) entity;
Location loc = itemFrame.getLocation();
double dx = loc.getX() - mutable.getX();
double dy = loc.getY() - mutable.getY();
double dz = loc.getZ() - mutable.getZ();
double dist = dx * dx + dy * dy + dz * dz;
if (dist < minDist) {
minItemFrame = itemFrame;
minDist = dist;
}
}
}
if (minItemFrame != null) {
handleInteract(event, minItemFrame, primary);
if (event.isCancelled()) return;
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
handleInteract(event, event.getRightClicked(), true);
}
private BukkitImageViewer get(HeightMapMCAGenerator generator) {
if (generator == null) return null;
ImageViewer viewer = generator.getImageViewer();
if (viewer == null || !(viewer instanceof BukkitImageViewer)) return null;
BukkitImageViewer biv = (BukkitImageViewer) viewer;
return biv;
}
private void handleInteract(PlayerEvent event, Entity entity, boolean primary) {
handleInteract(event, event.getPlayer(), entity, primary);
}
private void handleInteract(Event event, Player player, Entity entity, boolean primary) {
if (!(entity instanceof ItemFrame)) return;
ItemFrame itemFrame = (ItemFrame) entity;
FawePlayer<Object> fp = FawePlayer.wrap(player);
CFICommands.CFISettings settings = fp.getMeta("CFISettings");
HeightMapMCAGenerator generator = settings == null ? null : settings.getGenerator();
BukkitImageViewer viewer = get(generator);
if (viewer == null) return;
if (itemFrame.getRotation() != Rotation.NONE) {
itemFrame.setRotation(Rotation.NONE);
}
LocalSession session = fp.getSession();
BrushTool tool;
try {
tool = session.getBrushTool(fp.getPlayer(), false);
} catch (InvalidToolBindException e) { return; }
ItemFrame[][] frames = viewer.getItemFrames();
if (frames == null || tool == null) {
viewer.selectFrame(itemFrame);
player.updateInventory();
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {
viewer.view(generator);
}
}, 1);
return;
}
if (tool == null) return;
BrushSettings context = primary ? tool.getPrimary() : tool.getSecondary();
Brush brush = context.getBrush();
if (brush == null) return;
tool.setContext(context);
if (event instanceof Cancellable) {
((Cancellable) event).setCancelled(true);
}
Location target = itemFrame.getLocation();
Location source = player.getLocation();
double yawRad = Math.toRadians(source.getYaw() + 90d);
double pitchRad = Math.toRadians(-source.getPitch());
double a = Math.cos(pitchRad);
double xRat = Math.cos(yawRad) * a;
double zRat = Math.sin(yawRad) * a;
BlockFace facing = itemFrame.getFacing();
double thickness = 1/32d + 1/128d;
double modX = facing.getModX();
double modZ = facing.getModZ();
double dx = source.getX() - target.getX() - modX * thickness;
double dy = source.getY() + player.getEyeHeight() - target.getY();
double dz = source.getZ() - target.getZ() - modZ * thickness;
double offset;
double localX;
if (modX != 0) {
offset = dx / xRat;
localX = (-modX) * (dz - offset * zRat);
} else {
offset = dz / zRat;
localX = (modZ) * (dx - offset * xRat);
}
double localY = dy - offset * Math.sin(pitchRad);
int localPixelX = (int)((localX + 0.5) * 128);
int localPixelY = (int)((localY + 0.5) * 128);
UUID uuid = itemFrame.getUniqueId();
for (int blockX = 0; blockX < frames.length; blockX++) {
for (int blockY = 0; blockY < frames[0].length; blockY++) {
if (uuid.equals(frames[blockX][blockY].getUniqueId())) {
int pixelX = localPixelX + blockX * 128;
int pixelY = (128 * frames[0].length) - (localPixelY + blockY * 128 + 1);
int width = generator.getWidth();
int length = generator.getLength();
int worldX = (int) (pixelX * width / (frames.length * 128d));
int worldZ = (int) (pixelY * length / (frames[0].length * 128d));
if (worldX < 0 || worldX > width || worldZ < 0 || worldZ > length) return;
Vector wPos = new Vector(worldX, 0, worldZ);
fp.runAction(new Runnable() {
@Override
public void run() {
viewer.refresh();
int topY = generator.getNearestSurfaceTerrainBlock(wPos.getBlockX(), wPos.getBlockZ(), 255, 0, 255);
wPos.mutY(topY);
EditSession es = new EditSessionBuilder(fp.getWorld()).player(fp).combineStages(false).autoQueue(false).blockBag(null).limitUnlimited().build();
ExtentTraverser last = new ExtentTraverser(es.getExtent()).last();
if (last.get() instanceof FastWorldEditExtent) last = last.previous();
last.setNext(generator);
try {
brush.build(es, wPos, context.getMaterial(), context.getSize());
} catch (WorldEditException e) {
e.printStackTrace();
}
es.flushQueue();
viewer.view(generator);
}
}, true, true);
return;
}
}
}
}
}

View File

@ -0,0 +1,336 @@
package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal3;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.util.SetQueue;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
import com.sk89q.worldedit.event.platform.Interaction;
import com.sk89q.worldedit.extension.platform.PlatformManager;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.Plugin;
/**
* The CFIPacketListener handles packets for editing the VirtualWorld
* The generator is a virtual world which only the creator can see
* - The virtual world is displayed inside the current world
* - Block/Chunk/Movement packets need to be handled properly
*/
public class CFIPacketListener implements Listener {
private final Plugin plugin;
private final ProtocolManager protocolmanager;
public CFIPacketListener(Plugin plugin) {
this.plugin = plugin;
this.protocolmanager = ProtocolLibrary.getProtocolManager();
// Direct digging to the virtual world
registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
try {
Player plr = event.getPlayer();
Vector realPos = pt.add(gen.getOrigin());
if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) {
gen.setBlock(pt, EditSession.nullBlock);
}
} catch (WorldEditException e) {
e.printStackTrace();
}
}
});
// Direct placing to the virtual world
RunnableVal3<PacketEvent, VirtualWorld, Vector> placeTask = new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
try {
Player plr = event.getPlayer();
List<EnumWrappers.Hand> hands = event.getPacket().getHands().getValues();
EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0);
PlayerInventory inv = plr.getInventory();
ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand();
if (hand != null && hand.getType().isBlock()) {
switch (hand.getType()) {
case AIR:
case CAVE_AIR:
case VOID_AIR:
break;
default:
BlockStateHolder block = BukkitAdapter.asBlockState(hand);
if (block != null) {
gen.setBlock(pt, block);
return;
}
}
}
pt = getRelPos(event, gen);
sendBlockChange(plr, gen, pt, Interaction.OPEN);
} catch (WorldEditException e) {
e.printStackTrace();
}
}
};
registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask);
registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask);
// Cancel block change packets where the real world overlaps with the virtual one
registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3<PacketEvent, VirtualWorld, Vector>() {
@Override
public void run(PacketEvent event, VirtualWorld gen, Vector pt) {
// Do nothing
}
});
// Modify chunk packets where the real world overlaps with the virtual one
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) {
@Override
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
PacketContainer packet = event.getPacket();
StructureModifier<Integer> ints = packet.getIntegers();
int cx = ints.read(0);
int cz = ints.read(1);
int ocx = origin.getBlockX() >> 4;
int ocz = origin.getBlockZ() >> 4;
if (gen.contains(new Vector((cx - ocx) << 4, 0, (cz - ocz) << 4))) {
event.setCancelled(true);
Player plr = event.getPlayer();
FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false);
FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz);
toSend.setLoc(gen, cx, cz);
queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr));
}
}
}
});
// The following few listeners are to ignore block collisions where the virtual and real world overlap
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_VELOCITY) {
@Override
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
Player player = event.getPlayer();
Location pos = player.getLocation();
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
Vector pt = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
StructureModifier<Integer> ints = event.getPacket().getIntegers();
int id = ints.read(0);
int mx = ints.read(1);
int my = ints.read(2);
int mz = ints.read(3);
if (gen.contains(pt.subtract(origin)) && mx == 0 && my == 0 && mz == 0) {
event.setCancelled(true);
}
}
}
});
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.POSITION) {
@Override
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
Player player = event.getPlayer();
Location pos = player.getLocation();
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector origin = gen.getOrigin();
Vector from = new Vector(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
PacketContainer packet = event.getPacket();
StructureModifier<Double> doubles = packet.getDoubles();
Vector to = new Vector(doubles.read(0), doubles.read(1), doubles.read(2));
if (gen.contains(to.subtract(origin)) && from.distanceSq(to) < 8) {
int id = packet.getIntegers().read(0);
PacketContainer reply = new PacketContainer(PacketType.Play.Client.TELEPORT_ACCEPT);
reply.getIntegers().write(0, id);
try {
protocolmanager.recieveClientPacket(player, reply);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
event.setCancelled(true);
}
}
}
});
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MULTI_BLOCK_CHANGE) {
@Override
public void onPacketSending(PacketEvent event) {
if (!event.isServerPacket()) return;
VirtualWorld gen = getGenerator(event);
if (gen != null) {
PacketContainer packet = event.getPacket();
ChunkCoordIntPair chunk = packet.getChunkCoordIntPairs().read(0);
Vector origin = gen.getOrigin();
int cx = chunk.getChunkX() - (origin.getBlockX() >> 4);
int cz = chunk.getChunkZ() - (origin.getBlockX() >> 4);
if (gen.contains(new Vector(cx << 4, 0, cz << 4))) {
event.setCancelled(true);
}
}
}
});
}
@EventHandler
public void onTeleport(PlayerTeleportEvent event) {
final Player player = event.getPlayer();
VirtualWorld gen = getGenerator(player);
if (gen != null) {
Location from = event.getFrom();
Location to = event.getTo();
if (to.getWorld().equals(from.getWorld()) && to.distanceSquared(from) < 8) {
event.setTo(player.getLocation());
event.setCancelled(true);
player.setVelocity(player.getVelocity());
}
}
}
private boolean sendBlockChange(Player plr, VirtualWorld gen, Vector pt, Interaction action) {
PlatformManager platform = WorldEdit.getInstance().getPlatformManager();
com.sk89q.worldedit.entity.Player actor = FawePlayer.wrap(plr).getPlayer();
com.sk89q.worldedit.util.Location location = new com.sk89q.worldedit.util.Location(actor.getWorld(), pt);
BlockInteractEvent toCall = new BlockInteractEvent(actor, location, action);
platform.handleBlockInteract(toCall);
if (toCall.isCancelled() || action == Interaction.OPEN) {
Vector realPos = pt.add(gen.getOrigin());
BlockStateHolder block = gen.getBlock(pt);
sendBlockChange(plr, realPos, block);
return true;
}
return false;
}
private void sendBlockChange(Player plr, Vector pt, BlockStateHolder block) {
plr.sendBlockChange(new Location(plr.getWorld(), pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()), BukkitAdapter.adapt(block));
}
private VirtualWorld getGenerator(PacketEvent event) {
return getGenerator(event.getPlayer());
}
private VirtualWorld getGenerator(Player player) {
FawePlayer<Object> fp = FawePlayer.wrap(player);
VirtualWorld vw = fp.getSession().getVirtualWorld();
if (vw != null) return vw;
CFICommands.CFISettings settings = fp.getMeta("CFISettings");
if (settings != null && settings.hasGenerator() && settings.getGenerator().hasPacketViewer()) {
return settings.getGenerator();
}
return null;
}
private Vector getRelPos(PacketEvent event, VirtualWorld generator) {
PacketContainer packet = event.getPacket();
StructureModifier<BlockPosition> position = packet.getBlockPositionModifier();
BlockPosition loc = position.readSafely(0);
if (loc == null) return null;
Vector origin = generator.getOrigin();
Vector pt = new Vector(loc.getX() - origin.getBlockX(), loc.getY() - origin.getBlockY(), loc.getZ() - origin.getBlockZ());
return pt;
}
private void handleBlockEvent(PacketEvent event, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, Vector> task) {
VirtualWorld gen = getGenerator(event);
if (gen != null) {
Vector pt = getRelPos(event, gen);
if (pt != null) {
if (relative) pt = getRelative(event, pt);
if (gen.contains(pt)) {
event.setCancelled(true);
task.run(event, gen, pt);
}
}
}
}
private void registerBlockEvent(PacketType type, boolean relative, RunnableVal3<PacketEvent, VirtualWorld, Vector> task) {
protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, type) {
@Override
public void onPacketReceiving(final PacketEvent event) {
if (type.isClient() || event.isServerPacket()) handleBlockEvent(event, relative, task);
}
@Override
public void onPacketSending(PacketEvent event) {
onPacketReceiving(event);
}
});
}
private Vector getRelative(PacketEvent container, Vector pt) {
PacketContainer packet = container.getPacket();
StructureModifier<EnumWrappers.Direction> dirs = packet.getDirections();
EnumWrappers.Direction dir = dirs.readSafely(0);
if (dir == null) return pt;
switch (dir.ordinal()) {
case 0: return pt.add(0, -1, 0);
case 1: return pt.add(0, 1, 0);
case 2: return pt.add(0, 0, -1);
case 3: return pt.add(0, 0, 1);
case 4: return pt.add(-1, 0, 0);
case 5: return pt.add(1, 0, 0);
default: return pt;
}
}
}

View File

@ -0,0 +1,136 @@
package com.boydti.fawe.bukkit.listener;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.TaskManager;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
public class RenderListener implements Listener {
private final Map<UUID, int[]> views = new ConcurrentHashMap<>();
private Iterator<Map.Entry<UUID, int[]>> entrySet;
private int OFFSET = 6;
public RenderListener(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
TaskManager.IMP.repeat(new Runnable() {
private long last = 0;
@Override
public void run() {
if (views.isEmpty()) return;
long now = System.currentTimeMillis();
int tps32 = (int) (Math.round(Fawe.get().getTimer().getTPS()) * 32);
long diff = now - last;
last = now;
if (diff > 75) {
OFFSET = diff > 100 ? 0 : 4;
return;
}
int timeOut;
if (diff < 55 && tps32 > 608) {
OFFSET = 8;
timeOut = 2;
} else {
int tpsSqr = tps32 * tps32;
OFFSET = 1 + (tps32 / 102400);
timeOut = 162 - (tps32 / 2560);
}
if (entrySet == null || !entrySet.hasNext()) {
entrySet = views.entrySet().iterator();
}
int nowTick = (int) (Fawe.get().getTimer().getTick());
while (entrySet.hasNext()) {
Map.Entry<UUID, int[]> entry = entrySet.next();
Player player = Bukkit.getPlayer(entry.getKey());
if (player != null) {
int[] value = entry.getValue();
if (nowTick - value[1] >= timeOut) {
value[1] = nowTick + 1;
setViewDistance(player, Math.max(4, value[0] + 1));
long spent = System.currentTimeMillis() - now;
if (spent > 5) {
if (spent > 10)
value[1] = nowTick + 20;
return;
}
}
}
}
}
}, 1);
}
public void setViewDistance(Player player, int value) {
UUID uuid = player.getUniqueId();
if (value == Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING) {
views.remove(uuid);
} else {
int[] val = views.get(uuid);
if (val == null) {
val = new int[] {value, (int) Fawe.get().getTimer().getTick()};
UUID uid = player.getUniqueId();
views.put(uid, val);
} else {
if (value <= val[0]) {
val[1] = (int) Fawe.get().getTimer().getTick();
}
if (val[0] == value) {
return;
} else {
val[0] = value;
}
}
}
player.setViewDistance(value);
}
public int getViewDistance(Player player) {
int[] value = views.get(player.getUniqueId());
return value == null ? Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING : value[0];
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) {
setViewDistance(event.getPlayer(), 1);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Location from = event.getFrom();
Location to = event.getTo();
if (from.getBlockX() >> OFFSET != to.getBlockX() >> OFFSET || from.getBlockZ() >> OFFSET != to.getBlockZ() >> OFFSET) {
Player player = event.getPlayer();
int currentView = getViewDistance(player);
setViewDistance(player, Math.max(currentView - 1, 1));
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
Player player = event.getPlayer();
setViewDistance(player, 1);
FawePlayer fp = FawePlayer.wrap(player);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(org.bukkit.event.player.PlayerQuitEvent event) {
Player player = event.getPlayer();
UUID uid = player.getUniqueId();
views.remove(uid);
}
}

View File

@ -0,0 +1,55 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.regions.FaweMask;
import com.wasteofplastic.askyblock.ASkyBlockAPI;
import com.wasteofplastic.askyblock.Island;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class ASkyBlockHook extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin aSkyBlock;
public ASkyBlockHook(final Plugin aSkyBlock, final FaweBukkit p3) {
super(aSkyBlock.getName());
this.aSkyBlock = aSkyBlock;
this.plugin = p3;
}
public boolean isAllowed(Player player, Island island, MaskType type) {
return island != null && (player.getUniqueId().equals(island.getOwner()) || (type == MaskType.MEMBER && island.getMembers().contains(player.getUniqueId()) && hasMemberPermission(player)));
}
@Override
public FaweMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Location location = player.getLocation();
Island island = ASkyBlockAPI.getInstance().getIslandAt(location);
if (island != null && isAllowed(player, island, type)) {
int minX = island.getMinProtectedX();
int minZ = island.getMinProtectedZ();
World world = location.getWorld();
Location center = island.getCenter();
Location pos1 = new Location(world, island.getMinProtectedX(), 0, island.getMinProtectedZ());
Location pos2 = center.add(center.subtract(pos1));
pos2.setY(255);
return new BukkitMask(pos1, pos2, "ISLAND: " + minX + "," + minZ) {
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, island, type);
}
};
}
return null;
}
}

View File

@ -0,0 +1,16 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.regions.FaweMask;
import com.sk89q.worldedit.BlockVector;
import org.bukkit.Location;
public class BukkitMask extends FaweMask {
public BukkitMask(Location pos1, Location pos2) {
this(pos1, pos2, null);
}
public BukkitMask(Location pos1, Location pos2, String name) {
super(new BlockVector(pos1.getBlockX(), pos1.getBlockY(), pos1.getBlockZ()), new BlockVector(pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ()), name);
}
}

View File

@ -0,0 +1,15 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.regions.FaweMaskManager;
import org.bukkit.entity.Player;
public abstract class BukkitMaskManager extends FaweMaskManager<Player> {
public BukkitMaskManager(final String plugin) {
super(plugin);
}
public boolean hasMemberPermission(Player player) {
return player.hasPermission("fawe." + getKey() + ".member");
}
}

View File

@ -0,0 +1,63 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.massivecore.ps.PS;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class FactionsFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin factions;
public FactionsFeature(final Plugin factionsPlugin, final FaweBukkit p3) {
super(factionsPlugin.getName());
this.factions = factionsPlugin;
this.plugin = p3;
BoardColl.get();
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Location loc = player.getLocation();
final PS ps = PS.valueOf(loc);
final Faction fac = BoardColl.get().getFactionAt(ps);
if (fac != null) {
if (type == MaskType.OWNER) {
MPlayer leader = fac.getLeader();
if (leader != null && fp.getUUID().equals(leader.getUuid())) {
final Chunk chunk = loc.getChunk();
final Location pos1 = new Location(loc.getWorld(), chunk.getX() * 16, 0, chunk.getZ() * 16);
final Location pos2 = new Location(loc.getWorld(), (chunk.getX() * 16) + 15, 156, (chunk.getZ() * 16) + 15);
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "CHUNK:" + loc.getChunk().getX() + "," + loc.getChunk().getZ();
}
};
}
}
else if (fac.getOnlinePlayers().contains(player)) {
if (fac.getComparisonName().equals("wilderness") == false) {
final Chunk chunk = loc.getChunk();
final Location pos1 = new Location(loc.getWorld(), chunk.getX() * 16, 0, chunk.getZ() * 16);
final Location pos2 = new Location(loc.getWorld(), (chunk.getX() * 16) + 15, 156, (chunk.getZ() * 16) + 15);
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "CHUNK:" + loc.getChunk().getX() + "," + loc.getChunk().getZ();
}
};
}
}
}
return null;
}
}

View File

@ -0,0 +1,119 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.Perm;
import com.massivecraft.factions.FLocation;
import java.lang.reflect.Method;
import java.util.List;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class FactionsOneFeature extends BukkitMaskManager implements Listener {
private final Class clazzBoard;
private final Method methodGetFactionAt;
public FactionsOneFeature(final Plugin factionsPlugin, final FaweBukkit p3) throws Throwable {
super(factionsPlugin.getName());
this.clazzBoard = Class.forName("com.massivecraft.factions.Board");
this.methodGetFactionAt = clazzBoard.getDeclaredMethod("getFactionAt", FLocation.class);
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Chunk chunk = player.getLocation().getChunk();
final boolean perm = Perm.hasPermission(FawePlayer.wrap(player), "fawe.factions.wilderness");
final World world = player.getWorld();
RegionWrapper locs = new RegionWrapper(chunk.getX(), chunk.getX(), chunk.getZ(), chunk.getZ());
int count = 32;
if (this.isAdded(locs, world, player, perm, type)) {
boolean hasPerm = true;
RegionWrapper chunkSelection;
while (hasPerm && (count > 0)) {
count--;
hasPerm = false;
chunkSelection = new RegionWrapper(locs.maxX + 1, locs.maxX + 1, locs.minZ, locs.maxZ);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX + 1, locs.minZ, locs.maxZ);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX - 1, locs.minX - 1, locs.minZ, locs.maxZ);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX - 1, locs.maxX, locs.minZ, locs.maxZ);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX, locs.maxX, locs.maxZ + 1, locs.maxZ + 1);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX, locs.minZ, locs.maxZ + 1);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX, locs.maxX, locs.minZ - 1, locs.minZ - 1);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX, locs.minZ - 1, locs.maxZ);
hasPerm = true;
}
}
final Location pos1 = new Location(world, locs.minX << 4, 1, locs.minZ << 4);
final Location pos2 = new Location(world, 15 + (locs.maxX << 4), 256, 15 + (locs.maxZ << 4));
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "CHUNK:" + pos1.getChunk().getX() + "," + pos1.getChunk().getZ();
}
};
}
return null;
}
public boolean isAdded(final RegionWrapper locs, final World world, final Player player, final boolean perm, MaskType type) {
try {
for (int x = locs.minX; x <= locs.maxX; x++) {
for (int z = locs.minZ; z <= locs.maxZ; z++) {
final Object fac = methodGetFactionAt.invoke(null, new FLocation(world.getName(), x, z));
if (fac == null) {
return false;
}
if (type == MaskType.OWNER) {
Object leader = fac.getClass().getDeclaredMethod("getFPlayerLeader").invoke(fac);
return player.getName().equals(leader.getClass().getDeclaredMethod("getName").invoke(leader));
}
Method methodGetOnlinePlayers = fac.getClass().getDeclaredMethod("getOnlinePlayers");
List<Player> players = (List<Player>) methodGetOnlinePlayers.invoke(fac);
if (!players.contains(player)) {
return false;
}
Method isNone = fac.getClass().getDeclaredMethod("isNone");
if ((boolean) isNone.invoke(fac)) {
return false;
}
}
}
return true;
} catch (Throwable e) {
MainUtil.handleError(e);
return false;
}
}
}

View File

@ -0,0 +1,104 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.Perm;
import com.massivecraft.factions.Board;
import com.massivecraft.factions.FLocation;
import com.massivecraft.factions.Faction;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class FactionsUUIDFeature extends BukkitMaskManager implements Listener {
private final Board instance;
public FactionsUUIDFeature(final Plugin factionsPlugin, final FaweBukkit p3) {
super(factionsPlugin.getName());
this.instance = Board.getInstance();
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Chunk chunk = player.getLocation().getChunk();
final boolean perm = Perm.hasPermission(FawePlayer.wrap(player), "fawe.factions.wilderness");
final World world = player.getWorld();
RegionWrapper locs = new RegionWrapper(chunk.getX(), chunk.getX(), chunk.getZ(), chunk.getZ());
int count = 32;
if (this.isAdded(locs, world, player, perm, type)) {
boolean hasPerm = true;
RegionWrapper chunkSelection;
while (hasPerm && (count > 0)) {
count--;
hasPerm = false;
chunkSelection = new RegionWrapper(locs.maxX + 1, locs.maxX + 1, locs.minZ, locs.maxZ);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX + 1, locs.minZ, locs.maxZ);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX - 1, locs.minX - 1, locs.minZ, locs.maxZ);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX - 1, locs.maxX, locs.minZ, locs.maxZ);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX, locs.maxX, locs.maxZ + 1, locs.maxZ + 1);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX, locs.minZ, locs.maxZ + 1);
hasPerm = true;
}
chunkSelection = new RegionWrapper(locs.minX, locs.maxX, locs.minZ - 1, locs.minZ - 1);
if (this.isAdded(chunkSelection, world, player, perm, type)) {
locs = new RegionWrapper(locs.minX, locs.maxX, locs.minZ - 1, locs.maxZ);
hasPerm = true;
}
}
final Location pos1 = new Location(world, locs.minX << 4, 1, locs.minZ << 4);
final Location pos2 = new Location(world, 15 + (locs.maxX << 4), 256, 15 + (locs.maxZ << 4));
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "CHUNK:" + pos1.getChunk().getX() + "," + pos1.getChunk().getZ();
}
};
}
return null;
}
public boolean isAdded(final RegionWrapper locs, final World world, final Player player, final boolean perm, MaskType type) {
for (int x = locs.minX; x <= locs.maxX; x++) {
for (int z = locs.minZ; z <= locs.maxZ; z++) {
final Faction fac = this.instance.getFactionAt(new FLocation(world.getName(), x, z));
if (fac == null) {
return false;
}
// TODO types
if (!fac.getOnlinePlayers().contains(player)) {
return false;
}
if (fac.isWilderness() && !perm) {
return false;
}
}
}
return true;
}
}

View File

@ -0,0 +1,61 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.filter.GriefPreventionFilter;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.regions.general.RegionFilter;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class GriefPreventionFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin griefprevention;
public GriefPreventionFeature(final Plugin griefpreventionPlugin, final FaweBukkit p3) {
super(griefpreventionPlugin.getName());
this.griefprevention = griefpreventionPlugin;
this.plugin = p3;
}
public boolean isAllowed(Player player, Claim claim, MaskType type) {
return claim != null && (claim.getOwnerName().equalsIgnoreCase(player.getName()) || claim.getOwnerName().equals(player.getUniqueId()) || (type == MaskType.MEMBER && (claim.allowBuild(player, Material.AIR) == null)));
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Location location = player.getLocation();
final Claim claim = GriefPrevention.instance.dataStore.getClaimAt(location, true, null);
if (claim != null) {
final String uuid = player.getUniqueId().toString();
if (isAllowed(player, claim, type)) {
claim.getGreaterBoundaryCorner().getBlockX();
final Location pos1 = new Location(location.getWorld(), claim.getLesserBoundaryCorner().getBlockX(), 0, claim.getLesserBoundaryCorner().getBlockZ());
final Location pos2 = new Location(location.getWorld(), claim.getGreaterBoundaryCorner().getBlockX(), 256, claim.getGreaterBoundaryCorner().getBlockZ());
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "CLAIM:" + claim.toString();
}
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, claim, type);
}
};
}
}
return null;
}
@Override
public RegionFilter getFilter(String world) {
return new GriefPreventionFilter(Bukkit.getWorld(world));
}
}

View File

@ -0,0 +1,56 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.worldcretornica.plotme_core.Plot;
import com.worldcretornica.plotme_core.PlotMe_Core;
import com.worldcretornica.plotme_core.bukkit.PlotMe_CorePlugin;
import com.worldcretornica.plotme_core.bukkit.api.BukkitPlayer;
import com.worldcretornica.plotme_core.bukkit.api.BukkitWorld;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class PlotMeFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
PlotMe_Core plotme;
public PlotMeFeature(final Plugin plotmePlugin, final FaweBukkit p3) {
super(plotmePlugin.getName());
this.plotme = ((PlotMe_CorePlugin) plotmePlugin).getAPI();
this.plugin = p3;
}
public boolean isAllowed(Player player, Plot plot, MaskType type) {
return plot != null && type == MaskType.MEMBER ? plot.isAllowed(player.getUniqueId()) : player.getUniqueId().equals(plot.getOwnerId());
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Location location = player.getLocation();
final Plot plot = this.plotme.getPlotMeCoreManager().getPlotById(new BukkitPlayer(player));
if (plot == null) {
return null;
}
if (isAllowed(player, plot, type)) {
final Location pos1 = new Location(location.getWorld(), this.plotme.getGenManager(player.getWorld().getName()).bottomX(plot.getId(), new BukkitWorld(player.getWorld())), 0, this.plotme
.getGenManager(player.getWorld().getName()).bottomZ(plot.getId(), new BukkitWorld(player.getWorld())));
final Location pos2 = new Location(location.getWorld(), this.plotme.getGenManager(player.getWorld().getName()).topX(plot.getId(), new BukkitWorld(player.getWorld())), 256, this.plotme
.getGenManager(player.getWorld().getName()).topZ(plot.getId(), new BukkitWorld(player.getWorld())));
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return plot.getId();
}
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, plot, type);
}
};
}
return null;
}
}

View File

@ -0,0 +1,55 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.regions.FaweMask;
import com.sk89q.worldedit.BlockVector;
import java.util.List;
import net.sacredlabyrinth.Phaed.PreciousStones.PreciousStones;
import net.sacredlabyrinth.Phaed.PreciousStones.field.Field;
import net.sacredlabyrinth.Phaed.PreciousStones.field.FieldFlag;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class PreciousStonesFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin preciousstones;
public PreciousStonesFeature(final Plugin preciousstonesPlugin, final FaweBukkit p3) {
super(preciousstonesPlugin.getName());
this.preciousstones = preciousstonesPlugin;
this.plugin = p3;
}
public boolean isAllowed(Player player, Field field, MaskType type, boolean allowMember) {
return field != null && (field.isOwner(player.getName()) || (type == MaskType.MEMBER && allowMember && field.getAllAllowed().contains(player.getName())));
}
@Override
public FaweMask getMask(final FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final Location location = player.getLocation();
final List<Field> fields = PreciousStones.API().getFieldsProtectingArea(FieldFlag.ALL, location);
if (fields.isEmpty()) {
return null;
}
String name = player.getName();
boolean member = fp.hasPermission("fawe.preciousstones.member");
for (final Field myField : fields) {
if (isAllowed(player, myField, type, member)) {
BlockVector pos1 = new BlockVector(myField.getMinx(), myField.getMiny(), myField.getMinz());
BlockVector pos2 = new BlockVector(myField.getMaxx(), myField.getMaxy(), myField.getMaxz());
return new FaweMask(pos1, pos2, "FIELD: " + myField) {
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, myField, type, fp.hasPermission("fawe.preciousstones.member"));
}
};
}
}
return null;
}
}

View File

@ -0,0 +1,58 @@
package com.boydti.fawe.bukkit.regions;
import com.bekvon.bukkit.residence.Residence;
import com.bekvon.bukkit.residence.protection.ClaimedResidence;
import com.bekvon.bukkit.residence.protection.CuboidArea;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class ResidenceFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin residence;
public ResidenceFeature(final Plugin residencePlugin, final FaweBukkit p3) {
super(residencePlugin.getName());
this.residence = residencePlugin;
this.plugin = p3;
}
public boolean isAllowed(Player player, ClaimedResidence residence, MaskType type) {
return residence != null && (residence.getOwner().equals(player.getName()) || residence.getOwner().equals(player.getUniqueId().toString()) || type == MaskType.MEMBER && residence.getPermissions().playerHas(player, "build", false));
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp, final MaskType type) {
final Player player = fp.parent;
final Location location = player.getLocation();
ClaimedResidence residence = Residence.getInstance().getResidenceManager().getByLoc(location);
if (residence != null) {
boolean isAllowed;
while (!(isAllowed = isAllowed(player, residence, type)) && residence != null) {
residence = residence.getSubzoneByLoc(location);
}
if (isAllowed) {
final CuboidArea area = residence.getAreaArray()[0];
final Location pos1 = area.getLowLoc();
final Location pos2 = area.getHighLoc();
final ClaimedResidence finalResidence = residence;
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "RESIDENCE: " + finalResidence.getName();
}
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, finalResidence, type);
}
};
}
}
return null;
}
}

View File

@ -0,0 +1,96 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.object.FawePlayer;
import com.palmergames.bukkit.towny.Towny;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.PlayerCache;
import com.palmergames.bukkit.towny.object.TownBlock;
import com.palmergames.bukkit.towny.object.TownyUniverse;
import com.palmergames.bukkit.towny.object.WorldCoord;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class TownyFeature extends BukkitMaskManager implements Listener {
FaweBukkit plugin;
Plugin towny;
public TownyFeature(final Plugin townyPlugin, final FaweBukkit p3) {
super(townyPlugin.getName());
this.towny = townyPlugin;
this.plugin = p3;
}
public boolean isAllowed(Player player, TownBlock block) {
if (block == null) {
return false;
}
try {
if (block.getResident().getName().equals(player.getName())) {
return true;
}
} catch (final Exception ignore) {}
if (player.hasPermission("fawe.towny.*")) {
return true;
} else try {
if (block.getTown().isMayor(TownyUniverse.getDataSource().getResident(player.getName()))) {
return true;
}
} catch (NotRegisteredException ignore) {}
return false;
}
@Override
public BukkitMask getMask(final FawePlayer<Player> fp) {
final Player player = fp.parent;
final Location location = player.getLocation();
try {
final PlayerCache cache = ((Towny) this.towny).getCache(player);
final WorldCoord mycoord = cache.getLastTownBlock();
if (mycoord == null) {
return null;
} else {
final TownBlock myplot = mycoord.getTownBlock();
if (myplot == null) {
return null;
} else {
boolean isMember = false;
try {
if (myplot.getResident().getName().equals(player.getName())) {
isMember = true;
}
} catch (final Exception e) {
}
if (!isMember) {
if (player.hasPermission("fawe.towny.*")) {
isMember = true;
} else if (myplot.getTown().isMayor(TownyUniverse.getDataSource().getResident(player.getName()))) {
isMember = true;
}
}
if (isMember) {
final Chunk chunk = location.getChunk();
final Location pos1 = new Location(location.getWorld(), chunk.getX() * 16, 0, chunk.getZ() * 16);
final Location pos2 = new Location(location.getWorld(), (chunk.getX() * 16) + 15, 156, (chunk.getZ() * 16) + 15);
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return "PLOT:" + location.getChunk().getX() + "," + location.getChunk().getZ();
}
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed((Player) player.parent, myplot);
}
};
}
}
}
} catch (final Exception e) {}
return null;
}
}

View File

@ -0,0 +1,188 @@
package com.boydti.fawe.bukkit.regions;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.filter.WorldGuardFilter;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.regions.FaweMask;
import com.boydti.fawe.regions.general.RegionFilter;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.AbstractRegion;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Polygonal2DRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
public class Worldguard extends BukkitMaskManager implements Listener {
WorldGuardPlugin worldguard;
FaweBukkit plugin;
private WorldGuardPlugin getWorldGuard() {
final Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldGuard");
// WorldGuard may not be loaded
if ((plugin == null) || !(plugin instanceof WorldGuardPlugin)) {
return null; // Maybe you want throw an exception instead
}
return (WorldGuardPlugin) plugin;
}
public Worldguard(final Plugin p2, final FaweBukkit p3) {
super(p2.getName());
this.worldguard = this.getWorldGuard();
this.plugin = p3;
}
public ProtectedRegion getRegion(final com.sk89q.worldguard.LocalPlayer player, final Location loc) {
RegionManager manager = this.worldguard.getRegionManager(loc.getWorld());
if (manager == null) {
if (this.worldguard.getGlobalStateManager().get(loc.getWorld()).useRegions) {
System.out.println("Region capability is not enabled for WorldGuard.");
} else {
System.out.println("WorldGuard is not enabled for that world.");
}
return null;
}
final ProtectedRegion global = manager.getRegion("__global__");
if (global != null && isAllowed(player, global)) {
return global;
}
final ApplicableRegionSet regions = manager.getApplicableRegions(loc);
for (final ProtectedRegion region : regions) {
if (isAllowed(player, region)) {
return region;
}
}
return null;
}
public boolean isAllowed(LocalPlayer localplayer, ProtectedRegion region) {
if (region.isOwner(localplayer) || region.isOwner(localplayer.getName())) {
return true;
} else if (region.getId().toLowerCase().equals(localplayer.getName().toLowerCase())) {
return true;
} else if (region.getId().toLowerCase().contains(localplayer.getName().toLowerCase() + "//")) {
return true;
} else if (region.isOwner("*")) {
return true;
}
if (localplayer.hasPermission("fawe.worldguard.member")) {
if (region.isMember(localplayer) || region.isMember(localplayer.getName())) {
return true;
} else if (region.isMember("*")) {
return true;
}
}
return false;
}
@Override
public FaweMask getMask(FawePlayer<Player> fp, MaskType type) {
final Player player = fp.parent;
final com.sk89q.worldguard.LocalPlayer localplayer = this.worldguard.wrapPlayer(player);
final Location location = player.getLocation();
final ProtectedRegion myregion = this.getRegion(localplayer, location);
if (myregion != null) {
final Location pos1;
final Location pos2;
if (myregion.getId().equals("__global__")) {
pos1 = new Location(location.getWorld(), Integer.MIN_VALUE, 0, Integer.MIN_VALUE);
pos2 = new Location(location.getWorld(), Integer.MAX_VALUE, 255, Integer.MAX_VALUE);
} else {
if (myregion instanceof ProtectedCuboidRegion) {
pos1 = new Location(location.getWorld(), myregion.getMinimumPoint().getBlockX(), myregion.getMinimumPoint().getBlockY(), myregion.getMinimumPoint().getBlockZ());
pos2 = new Location(location.getWorld(), myregion.getMaximumPoint().getBlockX(), myregion.getMaximumPoint().getBlockY(), myregion.getMaximumPoint().getBlockZ());
} else {
return new FaweMask(adapt(myregion), myregion.getId()) {
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed(worldguard.wrapPlayer((Player) player.parent), myregion);
}
};
}
}
return new BukkitMask(pos1, pos2) {
@Override
public String getName() {
return myregion.getId();
}
@Override
public boolean isValid(FawePlayer player, MaskType type) {
return isAllowed(worldguard.wrapPlayer((Player) player.parent), myregion);
}
};
} else {
return null;
}
}
@Override
public RegionFilter getFilter(String world) {
return new WorldGuardFilter(Bukkit.getWorld(world));
}
private static class AdaptedRegion extends AbstractRegion {
private final ProtectedRegion region;
public AdaptedRegion(ProtectedRegion region) {
super(null);
this.region = region;
}
@Override
public Vector getMinimumPoint() {
return region.getMinimumPoint();
}
@Override
public Vector getMaximumPoint() {
return region.getMaximumPoint();
}
@Override
public void expand(Vector... changes) {
throw new UnsupportedOperationException("Region is immutable");
}
@Override
public void contract(Vector... changes) {
throw new UnsupportedOperationException("Region is immutable");
}
@Override
public boolean contains(Vector position) {
return region.contains(position);
}
}
private static Region adapt(ProtectedRegion region) {
if (region instanceof ProtectedCuboidRegion) {
return new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint());
}
if (region instanceof GlobalProtectedRegion) {
return RegionWrapper.GLOBAL();
}
if (region instanceof ProtectedPolygonalRegion) {
ProtectedPolygonalRegion casted = (ProtectedPolygonalRegion) region;
BlockVector max = region.getMaximumPoint();
BlockVector min = region.getMinimumPoint();
return new Polygonal2DRegion(null, casted.getPoints(), min.getBlockY(), max.getBlockY());
}
return new AdaptedRegion(region);
}
}

View File

@ -0,0 +1,109 @@
package com.boydti.fawe.bukkit.util;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.Server;
public class BukkitReflectionUtils {
/**
* prefix of bukkit classes
*/
private static volatile String preClassB = null;
/**
* prefix of minecraft classes
*/
private static volatile String preClassM = null;
/**
* boolean value, TRUE if server uses forge or MCPC+
*/
private static boolean forge = false;
/**
* check server version and class names
*/
public static void init() {
if (Bukkit.getServer() != null) {
if (Bukkit.getVersion().contains("MCPC") || Bukkit.getVersion().contains("Forge")) {
forge = true;
}
final Server server = Bukkit.getServer();
final Class<?> bukkitServerClass = server.getClass();
String[] pas = bukkitServerClass.getName().split("\\.");
if (pas.length == 5) {
final String verB = pas[3];
preClassB = "org.bukkit.craftbukkit." + verB;
}
try {
final Method getHandle = bukkitServerClass.getDeclaredMethod("getHandle");
final Object handle = getHandle.invoke(server);
final Class handleServerClass = handle.getClass();
pas = handleServerClass.getName().split("\\.");
if (pas.length == 5) {
final String verM = pas[3];
preClassM = "net.minecraft.server." + verM;
}
} catch (final Exception ignored) {
MainUtil.handleError(ignored);
}
}
}
/**
* @return true if server has forge classes
*/
public static boolean isForge() {
return forge;
}
/**
* Get class for name. Replace {nms} to net.minecraft.server.V*. Replace {cb} to org.bukkit.craftbukkit.V*. Replace
* {nm} to net.minecraft
*
* @param classes possible class paths
* @return RefClass object
* @throws RuntimeException if no class found
*/
public static ReflectionUtils.RefClass getRefClass(final String... classes) throws RuntimeException {
if (preClassM == null) {
init();
}
for (String className : classes) {
try {
className = className.replace("{cb}", preClassB).replace("{nms}", preClassM).replace("{nm}", "net.minecraft");
return ReflectionUtils.getRefClass(Class.forName(className));
} catch (final ClassNotFoundException ignored) {
}
}
throw new RuntimeException("no class found: " + classes[0].replace("{cb}", preClassB).replace("{nms}", preClassM).replace("{nm}", "net.minecraft"));
}
public static Class<?> getNmsClass(final String name) {
final String className = "net.minecraft.server." + getVersion() + "." + name;
return ReflectionUtils.getClass(className);
}
public static Class<?> getCbClass(final String name) {
final String className = "org.bukkit.craftbukkit." + getVersion() + "." + name;
return ReflectionUtils.getClass(className);
}
public static Class<?> getUtilClass(final String name) {
try {
return Class.forName(name); //Try before 1.8 first
} catch (final ClassNotFoundException ex) {
try {
return Class.forName("net.minecraft.util." + name); //Not 1.8
} catch (final ClassNotFoundException ex2) {
return null;
}
}
}
public static String getVersion() {
final String packageName = Bukkit.getServer().getClass().getPackage().getName();
return packageName.substring(packageName.lastIndexOf('.') + 1);
}
}

View File

@ -0,0 +1,63 @@
package com.boydti.fawe.bukkit.util;
import com.boydti.fawe.util.TaskManager;
import org.apache.commons.lang.mutable.MutableInt;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
public class BukkitTaskMan extends TaskManager {
private final Plugin plugin;
public BukkitTaskMan(final Plugin plugin) {
this.plugin = plugin;
}
@Override
public int repeat(final Runnable r, final int interval) {
return this.plugin.getServer().getScheduler().scheduleSyncRepeatingTask(this.plugin, r, interval, interval);
}
@Override
public int repeatAsync(final Runnable r, final int interval) {
return this.plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(this.plugin, r, interval, interval);
}
public MutableInt index = new MutableInt(0);
@Override
public void async(final Runnable r) {
if (r == null) {
return;
}
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, r).getTaskId();
}
@Override
public void task(final Runnable r) {
if (r == null) {
return;
}
this.plugin.getServer().getScheduler().runTask(this.plugin, r).getTaskId();
}
@Override
public void later(final Runnable r, final int delay) {
if (r == null) {
return;
}
this.plugin.getServer().getScheduler().runTaskLater(this.plugin, r, delay).getTaskId();
}
@Override
public void laterAsync(final Runnable r, final int delay) {
this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, r, delay);
}
@Override
public void cancel(final int task) {
if (task != -1) {
Bukkit.getScheduler().cancelTask(task);
}
}
}

View File

@ -0,0 +1,96 @@
package com.boydti.fawe.bukkit.util;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.bukkit.inventory.ItemStack;
public class ItemUtil {
private final Class<?> classCraftItemStack;
private final Method methodAsNMSCopy;
private final Class<?> classNMSItem;
private final Method methodGetTag;
private final Method methodHasTag;
private final Method methodSetTag;
private final Method methodAsBukkitCopy;
private final Field fieldHandle;
private SoftReference<Int2ObjectOpenHashMap<WeakReference<Tag>>> hashToNMSTag = new SoftReference(new Int2ObjectOpenHashMap<>());
public ItemUtil() throws Exception {
this.classCraftItemStack = BukkitReflectionUtils.getCbClass("inventory.CraftItemStack");
this.classNMSItem = BukkitReflectionUtils.getNmsClass("ItemStack");
this.methodAsNMSCopy = ReflectionUtils.setAccessible(classCraftItemStack.getDeclaredMethod("asNMSCopy", ItemStack.class));
this.methodHasTag = ReflectionUtils.setAccessible(classNMSItem.getDeclaredMethod("hasTag"));
this.methodGetTag = ReflectionUtils.setAccessible(classNMSItem.getDeclaredMethod("getTag"));
this.fieldHandle = ReflectionUtils.setAccessible(classCraftItemStack.getDeclaredField("handle"));
Class<?> classNBTTagCompound = BukkitReflectionUtils.getNmsClass("NBTTagCompound");
this.methodSetTag = ReflectionUtils.setAccessible(classNMSItem.getDeclaredMethod("setTag", classNBTTagCompound));
this.methodAsBukkitCopy = ReflectionUtils.setAccessible(classCraftItemStack.getDeclaredMethod("asBukkitCopy", classNMSItem));
}
public Object getNMSItem(ItemStack item) {
try {
Object nmsItem = fieldHandle.get(item);
if (nmsItem == null) nmsItem = methodAsNMSCopy.invoke(null, item);
return nmsItem;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public CompoundTag getNBT(ItemStack item) {
try {
if (!item.hasItemMeta()) return null;
Object nmsItem = fieldHandle.get(item);
if (nmsItem == null) nmsItem = methodAsNMSCopy.invoke(null, item);
if (methodHasTag.invoke(nmsItem).equals(true)) {
Object nmsTag = methodGetTag.invoke(nmsItem);
if (nmsTag == null) return null;
Int2ObjectOpenHashMap<WeakReference<Tag>> map = hashToNMSTag.get();
if (map == null) {
map = new Int2ObjectOpenHashMap<>();
hashToNMSTag = new SoftReference(new Int2ObjectOpenHashMap<>(map));
}
WeakReference<Tag> nativeTagRef = map.get(nmsTag.hashCode());
if (nativeTagRef != null) {
Tag nativeTag = nativeTagRef.get();
if (nativeTag != null) return (CompoundTag) nativeTag;
}
Tag nativeTag = BukkitQueue_0.toNative(nmsTag);
map.put(nmsTag.hashCode(), new WeakReference<Tag>(nativeTag));
return null;
}
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public ItemStack setNBT(ItemStack item, CompoundTag tag) {
try {
Object nmsItem = fieldHandle.get(item);
boolean copy = false;
if (nmsItem == null) {
copy = true;
nmsItem = methodAsNMSCopy.invoke(null, item);
}
Object nmsTag = BukkitQueue_0.fromNative(tag);
methodSetTag.invoke(nmsItem, nmsTag);
if (copy) return (ItemStack) methodAsBukkitCopy.invoke(null, nmsItem);
return item;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,18 @@
package com.boydti.fawe.bukkit.util;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
public class VaultUtil {
public final Permission permission;
public VaultUtil() {
final RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServer().getServicesManager().getRegistration(net.milkbowl.vault.permission.Permission.class);
if (permissionProvider != null) {
this.permission = permissionProvider.getProvider();
} else {
this.permission = null;
}
}
}

View File

@ -0,0 +1,31 @@
package com.boydti.fawe.bukkit.util.cui;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.cui.CUI;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.plugin.Plugin;
public class CUIListener implements Listener {
public CUIListener(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Location from = event.getFrom();
Location to = event.getTo();
if ((int) from.getX() >> 2 != (int) to.getX() >> 2 || (int) from.getZ() >> 2 != (int) to.getZ() >> 2 || (int) from.getY() >> 2 != (int) to.getY() >> 2) {
FawePlayer<Object> player = FawePlayer.wrap(event.getPlayer());
CUI cui = player.getMeta("CUI");
if (cui instanceof StructureCUI) {
StructureCUI sCui = (StructureCUI) cui;
sCui.update();
}
}
}
}

View File

@ -0,0 +1,197 @@
package com.boydti.fawe.bukkit.util.cui;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.cui.CUI;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.internal.cui.SelectionPointEvent;
import com.sk89q.worldedit.internal.cui.SelectionShapeEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class StructureCUI extends CUI {
private boolean cuboid = true;
private Vector pos1;
private Vector pos2;
private Vector remove;
private NbtCompound removeTag;
private int combined;
public StructureCUI(FawePlayer player) {
super(player);
}
@Override
public void dispatchCUIEvent(CUIEvent event) {
if (event instanceof SelectionShapeEvent) {
clear();
this.cuboid = event.getParameters()[0].equalsIgnoreCase("cuboid");
} else if (cuboid && event instanceof SelectionPointEvent) {
SelectionPointEvent spe = (SelectionPointEvent) event;
String[] param = spe.getParameters();
int id = Integer.parseInt(param[0]);
int x = Integer.parseInt(param[1]);
int y = Integer.parseInt(param[2]);
int z = Integer.parseInt(param[3]);
Vector pos = new Vector(x, y, z);
if (id == 0) {
pos1 = pos;
} else {
pos2 = pos;
}
update();
}
}
private int viewDistance() {
Player player = this.<Player>getPlayer().parent;
if (Bukkit.getVersion().contains("paper")) {
return player.getViewDistance();
} else {
return Bukkit.getViewDistance();
}
}
public void clear() {
pos1 = null;
pos2 = null;
update();
}
private NbtCompound constructStructureNbt(int x, int y, int z, int posX, int posY, int posZ, int sizeX, int sizeY, int sizeZ) {
HashMap<String, Object> tag = new HashMap<>();
tag.put("name", UUID.randomUUID().toString());
tag.put("author", "Empire92"); // :D
tag.put("metadata", "");
tag.put("x", x);
tag.put("y", y);
tag.put("z", z);
tag.put("posX", posX);
tag.put("posY", posY);
tag.put("posZ", posZ);
tag.put("sizeX", sizeX);
tag.put("sizeY", sizeY);
tag.put("sizeZ", sizeZ);
tag.put("rotation", "NONE");
tag.put("mirror", "NONE");
tag.put("mode", "SAVE");
tag.put("ignoreEntities", true);
tag.put("powered", false);
tag.put("showair", false);
tag.put("showboundingbox", true);
tag.put("integrity", 1.0f);
tag.put("seed", 0);
tag.put("id", "minecraft:structure_block");
Object nmsTag = BukkitQueue_0.fromNative(FaweCache.asTag(tag));
return NbtFactory.fromNMSCompound(nmsTag);
}
private void sendOp() {
Player player = this.<Player>getPlayer().parent;
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
PacketConstructor statusCtr = manager.createPacketConstructor(PacketType.Play.Server.ENTITY_STATUS, player, (byte) 28);
PacketContainer status = statusCtr.createPacket(player, (byte) 28);
try {
manager.sendServerPacket(player, status);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private void sendNbt(Vector pos, NbtCompound compound) {
Player player = this.<Player>getPlayer().parent;
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
PacketContainer blockNbt = new PacketContainer(PacketType.Play.Server.TILE_ENTITY_DATA);
blockNbt.getBlockPositionModifier().write(0, new BlockPosition(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()));
blockNbt.getIntegers().write(0, 7);
blockNbt.getNbtModifier().write(0, compound);
try {
manager.sendServerPacket(player, blockNbt);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public synchronized void update() {
Player player = this.<Player>getPlayer().parent;
Location playerLoc = player.getLocation();
boolean setOp = remove == null && !player.isOp();
if (remove != null) {
int cx = playerLoc.getBlockX() >> 4;
int cz = playerLoc.getBlockZ() >> 4;
int viewDistance = viewDistance();
if (Math.abs(cx - (remove.getBlockX() >> 4)) <= viewDistance && Math.abs(cz - (remove.getBlockZ() >> 4)) <= viewDistance) {
Map<String, NbtBase<?>> map = removeTag.getValue();
map.put("sizeX", NbtFactory.of("sizeX", 0));
sendNbt(remove, removeTag);
Location removeLoc = new Location(player.getWorld(), remove.getX(), remove.getY(), remove.getZ());
player.sendBlockChange(removeLoc, BukkitAdapter.getBlockData(combined));
}
remove = null;
}
if (pos1 == null || pos2 == null) return;
Vector min = Vector.getMinimum(pos1, pos2);
Vector max = Vector.getMaximum(pos1, pos2);
// Position
double rotX = playerLoc.getYaw();
double rotY = playerLoc.getPitch();
double xz = Math.cos(Math.toRadians(rotY));
int x = (int) (playerLoc.getX() - (-xz * Math.sin(Math.toRadians(rotX))) * 12);
int z = (int) (playerLoc.getZ() - (xz * Math.cos(Math.toRadians(rotX))) * 12);
int y = Math.max(0, Math.min(Math.min(255, max.getBlockY() + 32), playerLoc.getBlockY() + 3));
int minX = Math.max(Math.min(32, min.getBlockX() - x), -32);
int maxX = Math.max(Math.min(32, max.getBlockX() - x + 1), -32);
int minY = Math.max(Math.min(32, min.getBlockY() - y), -32);
int maxY = Math.max(Math.min(32, max.getBlockY() - y + 1), -32);
int minZ = Math.max(Math.min(32, min.getBlockZ() - z), -32);
int maxZ = Math.max(Math.min(32, max.getBlockZ() - z + 1), -32);
int sizeX = Math.min(32, maxX - minX);
int sizeY = Math.min(32, maxY - minY);
int sizeZ = Math.min(32, maxZ - minZ);
if (sizeX == 0 || sizeY == 0 || sizeZ == 0) return;
// maxX - 32;
int posX = Math.max(minX, Math.min(16, maxX) - 32);
int posY = Math.max(minY, Math.min(16, maxY) - 32);
int posZ = Math.max(minZ, Math.min(16, maxZ) - 32);
// NBT
NbtCompound compound = constructStructureNbt(x, y, z, posX, posY, posZ, sizeX, sizeY, sizeZ);
Block block = player.getWorld().getBlockAt(x, y, z);
remove = new Vector(x, y, z);
combined = BukkitAdapter.adapt(block.getBlockData()).getInternalId();
removeTag = compound;
Location blockLoc = new Location(player.getWorld(), x, y, z);
player.sendBlockChange(blockLoc, Material.STRUCTURE_BLOCK, (byte) 0);
if (setOp) sendOp();
sendNbt(remove, compound);
}
}

View File

@ -0,0 +1,191 @@
package com.boydti.fawe.bukkit.util.image;
import com.boydti.fawe.util.image.Drawable;
import com.boydti.fawe.util.image.ImageUtil;
import com.boydti.fawe.util.image.ImageViewer;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Collection;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Rotation;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
//import org.inventivetalent.mapmanager.MapManagerPlugin;
//import org.inventivetalent.mapmanager.controller.MapController;
//import org.inventivetalent.mapmanager.controller.MultiMapController;
//import org.inventivetalent.mapmanager.manager.MapManager;
//import org.inventivetalent.mapmanager.wrapper.MapWrapper;
public class BukkitImageViewer implements ImageViewer {
// private final MapManager mapManager;
// private final Player player;
// private BufferedImage last;
private ItemFrame[][] frames;
// private boolean reverse;
public BukkitImageViewer(Player player) {
// mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager();
// this.player = player;
}
//
public void selectFrame(ItemFrame start) {
// Location pos1 = start.getLocation().clone();
// Location pos2 = start.getLocation().clone();
//
// BlockFace facing = start.getFacing();
// int planeX = facing.getModX() == 0 ? 1 : 0;
// int planeY = facing.getModY() == 0 ? 1 : 0;
// int planeZ = facing.getModZ() == 0 ? 1 : 0;
//
// ItemFrame[][] res = find(pos1, pos2, facing);
// Location tmp;
// while (true) {
// if (res != null) {
// frames = res;
// }
// tmp = pos1.clone().subtract(planeX, planeY, planeZ);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(planeX, planeY, planeZ);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// tmp = pos1.clone().subtract(planeX, 0, planeZ);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(planeX, 0, planeZ);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// tmp = pos1.clone().subtract(0, 1, 0);
// if ((res = find(tmp, pos2, facing)) != null) {
// pos1 = tmp;
// continue;
// }
// tmp = pos2.clone().add(0, 1, 0);
// if ((res = find(pos1, tmp, facing)) != null) {
// pos2 = tmp;
// continue;
// }
// break;
// }
}
//
public ItemFrame[][] getItemFrames() {
return frames;
}
//
// private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) {
// try {
// Location distance = pos2.clone().subtract(pos1).add(1, 1, 1);
// int width = Math.max(distance.getBlockX(), distance.getBlockZ());
// ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()];
//
// World world = pos1.getWorld();
//
// this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST);
// int v = 0;
// for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) {
// int h = 0;
// for (double z = pos1.getZ(); z <= pos2.getZ(); z++) {
// for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) {
// Location pos = new Location(world, x, y, z);
// Collection<Entity> entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1);
// boolean contains = false;
// for (Entity ent : entities) {
// if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) {
// ItemFrame itemFrame = (ItemFrame) ent;
// itemFrame.setRotation(Rotation.NONE);
// contains = true;
// frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent;
// break;
// }
// }
// if (!contains) return null;
// }
// }
// }
// return frames;
// } catch (Throwable e) {
// e.printStackTrace();
// }
// return null;
// }
@Override
public void view(Drawable drawable) {
// view(null, drawable);
}
//
// private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) {
// if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null");
// boolean initializing = last == null;
//
// if (this.frames != null) {
// if (image == null && drawable != null) image = drawable.draw();
// last = image;
// int width = frames.length;
// int height = frames[0].length;
// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
// MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height);
// MultiMapController controller = (MultiMapController) mapWrapper.getController();
// controller.addViewer(player);
// controller.sendContent(player);
// controller.showInFrames(player, frames, true);
// } else {
// int slot = getMapSlot(player);
// if (slot == -1) {
// if (initializing) {
// player.getInventory().setItemInMainHand(new ItemStack(Material.MAP));
// } else {
// return;
// }
// } else if (player.getInventory().getHeldItemSlot() != slot) {
// player.getInventory().setHeldItemSlot(slot);
// }
// if (image == null && drawable != null) image = drawable.draw();
// last = image;
// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
// MapWrapper mapWrapper = mapManager.wrapImage(scaled);
// MapController controller = mapWrapper.getController();
// controller.addViewer(player);
// controller.sendContent(player);
// controller.showInHand(player, true);
// }
// }
//
// private int getMapSlot(Player player) {
// PlayerInventory inventory = player.getInventory();
// for (int i = 0; i < 9; i++) {
// ItemStack item = inventory.getItem(i);
// if (item != null && item.getType() == Material.MAP) {
// return i;
// }
// }
// return -1;
// }
//
public void refresh() {
// if (last != null) view(last, null);
}
@Override
public void close() throws IOException {
// last = null;
}
}

View File

@ -0,0 +1,378 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.example.IntFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.entity.EntityTypes;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
public class BukkitChunk_All extends IntFaweChunk<Chunk, BukkitQueue_All> {
/**
* A FaweSections object represents a chunk and the blocks that you wish to change in it.
*
* @param parent
* @param x
* @param z
*/
public BukkitChunk_All(FaweQueue parent, int x, int z) {
super(parent, x, z);
}
public BukkitChunk_All(FaweQueue parent, int x, int z, int[][] ids, short[] count, short[] air, byte[] heightMap) {
super(parent, x, z, ids, count, air, heightMap);
}
@Override
public IntFaweChunk copy(boolean shallow) {
BukkitChunk_All copy;
if (shallow) {
copy = new BukkitChunk_All(getParent(), getX(), getZ(), ids, count, air, heightMap);
copy.biomes = biomes;
} else {
copy = new BukkitChunk_All(getParent(), getX(), getZ(), (int[][]) MainUtil.copyNd(ids), count.clone(), air.clone(), heightMap.clone());
copy.biomes = biomes != null ? biomes.clone() : null;
}
copy.chunk = chunk;
return copy;
}
@Override
public Chunk getNewChunk() {
return Bukkit.getWorld(getParent().getWorldName()).getChunkAt(getX(), getZ());
}
private int layer = -1;
private int index;
private boolean place = true;
@Override
public void start() {
getChunk().load(true);
}
private static boolean canTick(BlockType type) {
return type.getMaterial().isTicksRandomly();
}
/**
*
* @return
*/
@Override
public FaweChunk call() {
long start = System.currentTimeMillis();
int recommended = 25 + BukkitQueue_All.ALLOCATE;
boolean more = true;
final BukkitQueue_All parent = (BukkitQueue_All) getParent();
BukkitImplAdapter adapter = BukkitQueue_0.getAdapter();
final Chunk chunk = getChunk();
Object[] disableResult = parent.disableLighting(chunk);
final World world = chunk.getWorld();
int[][] sections = getCombinedIdArrays();
final int bx = getX() << 4;
final int bz = getZ() << 4;
if (layer == -1) {
if (adapter != null)
{
// Run change task
RunnableVal2<FaweChunk, FaweChunk> task = parent.getChangeTask();
BukkitChunk_All_ReadonlySnapshot previous;
if (task != null){
ChunkSnapshot snapshot = parent.ensureChunkLoaded(getX(), getZ());
previous = new BukkitChunk_All_ReadonlySnapshot(parent, this, snapshot, biomes != null);
for (BlockState tile : chunk.getTileEntities()) {
int x = tile.getX();
int y = tile.getY();
int z = tile.getZ();
if (getBlockCombinedId(x & 15, y, z & 15) != 0) {
CompoundTag nbt = adapter.getBlock(new Location(world, x, y, z)).getNbtData();
if (nbt != null) {
previous.setTile(x & 15, y, z & 15, nbt);
}
}
}
} else {
previous = null;
}
// Set entities
if (adapter != null) {
Set<CompoundTag> entitiesToSpawn = this.getEntities();
if (!entitiesToSpawn.isEmpty()) {
for (CompoundTag tag : entitiesToSpawn) {
String id = tag.getString("Id");
ListTag posTag = tag.getListTag("Pos");
ListTag rotTag = tag.getListTag("Rotation");
if (id == null || posTag == null || rotTag == null) {
Fawe.debug("Unknown entity tag: " + tag);
continue;
}
double x = posTag.getDouble(0);
double y = posTag.getDouble(1);
double z = posTag.getDouble(2);
float yaw = rotTag.getFloat(0);
float pitch = rotTag.getFloat(1);
Location loc = new Location(world, x, y, z, yaw, pitch);
Entity created = adapter.createEntity(loc, new BaseEntity(EntityTypes.get(id), tag));
if (previous != null) {
UUID uuid = created.getUniqueId();
Map<String, Tag> map = ReflectionUtils.getMap(tag.getValue());
map.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits()));
map.put("UUIDMost", new LongTag(uuid.getMostSignificantBits()));
}
}
}
HashSet<UUID> entsToRemove = this.getEntityRemoves();
if (!entsToRemove.isEmpty()) {
for (Entity entity : chunk.getEntities()) {
if (entsToRemove.contains(entity.getUniqueId())) {
entity.remove();
}
}
}
}
if (previous != null) {
task.run(previous, this);
}
// Biomes
if (layer == 0) {
final byte[] biomes = getBiomeArray();
if (biomes != null) {
int index = 0;
for (int z = 0; z < 16; z++) {
int zz = bx + z;
for (int x = 0; x < 16; x++) {
int xx = bz + x;
Biome bukkitBiome = adapter.getBiome(biomes[index++] & 0xFF);
world.setBiome(xx, zz, bukkitBiome);
}
}
}
}
}
} else if (index != 0) {
if (place) {
layer--;
} else {
layer++;
}
}
mainloop:
do {
if (place) {
if (++layer >= sections.length) {
place = false;
layer = sections.length - 1;
}
} else if (--layer < 0) {
more = false;
break;
}
try {
// Efficiently merge sections
int changes = getCount(layer);
if (changes == 0) {
continue;
}
final int[] newArray = sections[layer];
if (newArray == null) {
continue;
}
final byte[] cacheX = FaweCache.CACHE_X[layer];
final short[] cacheY = FaweCache.CACHE_Y[layer];
final byte[] cacheZ = FaweCache.CACHE_Z[layer];
boolean checkTime = !((getAir(layer) == 4096 || (getCount(layer) == 4096 && getAir(layer) == 0) || (getCount(layer) == getAir(layer))));
Location mutableLoc = new Location(world, 0, 0, 0);
if (!checkTime) {
System.out.println("Set " + layer);
int index = 0;
for (int y = 0; y < 16; y++) {
int yy = (layer << 4) + y;
for (int z = 0; z < 16; z++) {
int zz = bz + z;
for (int x = 0; x < 16; x++, index++) {
int combined = newArray[index];
if (combined == 0) continue;
int xx = bx + x;
BlockTypes type = BlockTypes.getFromStateId(combined);
switch (type) {
case __RESERVED__:
continue;
case AIR:
case CAVE_AIR:
case VOID_AIR:
if (!place) {
mutableLoc.setX(xx);
mutableLoc.setY(yy);
mutableLoc.setZ(zz);
setBlock(adapter, mutableLoc, combined);
}
continue;
default:
if (place) {
if (type.getMaterial().hasContainer() && adapter != null) {
CompoundTag nbt = getTile(x, yy, z);
if (nbt != null) {
mutableLoc.setX(xx);
mutableLoc.setY(yy);
mutableLoc.setZ(zz);
synchronized (BukkitChunk_All.this) {
BaseBlock state = BaseBlock.getFromInternalId(combined, nbt);
adapter.setBlock(mutableLoc, state, false);
}
continue;
}
}
if (type.getMaterial().isTicksRandomly()) {
synchronized (BukkitChunk_All.this) {
setBlock(adapter, mutableLoc, combined);
}
} else {
mutableLoc.setX(xx);
mutableLoc.setY(yy);
mutableLoc.setZ(zz);
setBlock(adapter, mutableLoc, combined);
}
}
continue;
}
}
}
}
} else {
for (;index < 4096; index++) {
int j = place ? index : 4095 - index;
int combined = newArray[j];
BlockTypes type = BlockTypes.getFromStateId(combined);
switch (type) {
case __RESERVED__:
continue;
case AIR:
case CAVE_AIR:
case VOID_AIR:
if (!place) {
int x = cacheX[j];
int z = cacheZ[j];
int y = cacheY[j];
mutableLoc.setX(bx + x);
mutableLoc.setY(y);
mutableLoc.setZ(bz + z);
setBlock(adapter, mutableLoc, combined);
}
break;
default:
boolean light = type.getMaterial().getLightValue() > 0;
if (light) {
if (place) {
continue;
}
light = light && getParent().getSettings().LIGHTING.MODE != 0;
if (light) {
parent.enableLighting(disableResult);
}
} else if (!place) {
continue;
}
int x = cacheX[j];
int z = cacheZ[j];
int y = cacheY[j];
if (type.getMaterial().hasContainer() && adapter != null) {
CompoundTag tile = getTile(x, y, z);
if (tile != null) {
synchronized (BukkitChunk_All.this) {
BaseBlock state = BaseBlock.getFromInternalId(combined, tile);
adapter.setBlock(new Location(world, bx + x, y, bz + z), state, false);
}
break;
}
}
if (type.getMaterial().isTicksRandomly()) {
synchronized (BukkitChunk_All.this) {
mutableLoc.setX(bx + x);
mutableLoc.setY(y);
mutableLoc.setZ(bz + z);
setBlock(adapter, mutableLoc, combined);
}
} else {
mutableLoc.setX(bx + x);
mutableLoc.setY(y);
mutableLoc.setZ(bz + z);
setBlock(adapter, mutableLoc, combined);
}
if (light) {
parent.disableLighting(disableResult);
}
break;
}
if (checkTime && System.currentTimeMillis() - start > recommended) {
index++;
break mainloop;
}
}
index = 0;
}
} catch (final Throwable e) {
MainUtil.handleError(e);
}
} while (System.currentTimeMillis() - start < recommended);
if (more || place) {
this.addToQueue();
}
parent.resetLighting(disableResult);
return this;
}
public void setBlock(BukkitImplAdapter adapter, Block block, BlockStateHolder state) {
if (adapter != null) {
adapter.setBlock(block.getLocation(), state, false);
} else {
block.setBlockData(BukkitAdapter.adapt(state), false);
}
}
public void setBlock(BukkitImplAdapter adapter, Location location, int combinedId) {
if (adapter != null) {
adapter.setBlock(location, com.sk89q.worldedit.world.block.BlockState.get(combinedId), false);
} else {
Block block = location.getWorld().getBlockAt(location);
block.setBlockData(BukkitAdapter.getBlockData(combinedId), false);
}
}
}

View File

@ -0,0 +1,156 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import java.util.*;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.bukkit.ChunkSnapshot;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import javax.annotation.Nullable;
public class BukkitChunk_All_ReadonlySnapshot extends FaweChunk {
private final ChunkSnapshot snapshot;
private final boolean hasBiomes;
private final BukkitChunk_All next;
private Set<CompoundTag> entities = new HashSet<>();
private Map<Short, CompoundTag> tiles = new HashMap<>();
public BukkitChunk_All_ReadonlySnapshot(BukkitQueue_All parent, BukkitChunk_All next, ChunkSnapshot snapshot, boolean biomes) {
super(parent, snapshot.getX(), snapshot.getZ());
this.next = next;
this.snapshot = snapshot;
this.hasBiomes = biomes;
}
public void setTiles(Map<Short, CompoundTag> tiles) {
this.tiles = tiles;
}
public void setEntities(Set<CompoundTag> entities) {
this.entities = entities;
}
@Override
public BukkitQueue_All getParent() {
return (BukkitQueue_All) super.getParent();
}
@Override
public int getBitMask() {
return Character.MAX_VALUE;
}
@Override
public int getBlockCombinedId(int x, int y, int z) {
BlockData blockData = snapshot.getBlockData(x, y, z);
return BukkitAdapter.adapt(blockData).getInternalId();
}
@Override
public byte[] getBiomeArray() {
if (!hasBiomes || next.biomes == null) return null;
BukkitImplAdapter adapter = getParent().getAdapter();
byte[] biomes = Arrays.copyOf(next.biomes, next.biomes.length);
int index = 0;
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++, index++) {
if (biomes[index] != 0) {
Biome biome = snapshot.getBiome(x, z);
biomes[index] = (byte) adapter.getBiomeId(biome);
}
}
}
return biomes;
}
@Nullable
@Override
public int[] getIdArray(int layer) {
int[] nextLayer = next.ids[layer];
if (nextLayer == null) return null;
int[] ids = Arrays.copyOf(nextLayer, nextLayer.length);
int index = 0;
int by = layer << 4;
for (int y = 0; y < 16; y++) {
int yy = by + y;
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++, index++) {
if (ids[index] != 0) {
ids[index] = getBlockCombinedId(x, yy, z);
}
}
}
}
return ids;
}
@Override
public Object getChunk() {
return snapshot;
}
@Override
public void setTile(int x, int y, int z, CompoundTag tile) {
tiles.put(MathMan.tripleBlockCoord(x, y, z), tile);
}
@Override
public void setEntity(CompoundTag entity) {
entities.add(entity);
}
@Override
public void removeEntity(UUID uuid) {
throw new UnsupportedOperationException("Read only");
}
@Override
public void setBlock(int x, int y, int z, int combinedId) {
throw new UnsupportedOperationException("Read only");
}
@Override
public Set<CompoundTag> getEntities() {
return entities;
}
@Override
public Set<UUID> getEntityRemoves() {
throw new UnsupportedOperationException("Read only");
}
@Override
public Map<Short, CompoundTag> getTiles() {
return tiles;
}
@Override
public CompoundTag getTile(int x, int y, int z) {
if (tiles == null) return null;
short pair = MathMan.tripleBlockCoord(x, y, z);
return tiles.get(pair);
}
@Override
public void setBiome(int x, int z, byte biome) {
throw new UnsupportedOperationException("Read only");
}
@Override
public FaweChunk copy(boolean shallow) {
return null;
}
@Override
public FaweChunk call() {
return null;
}
}

View File

@ -0,0 +1,401 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.bukkit.BukkitPlayer;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
import com.boydti.fawe.example.IntFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.queue.LazyFaweChunk;
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.Plugin;
public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMappedFaweQueue<World, CHUNK, CHUNKSECTIONS, SECTION> implements Listener {
protected static boolean PAPER = true;
private static BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
private static Method methodGetHandle;
static {
Class<?> classCraftChunk = BukkitReflectionUtils.getCbClass("CraftChunk");
try {
methodGetHandle = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle"));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public BukkitQueue_0(final com.sk89q.worldedit.world.World world) {
super(world);
if (!registered) {
registered = true;
Bukkit.getServer().getPluginManager().registerEvents(this, ((FaweBukkit) Fawe.imp()).getPlugin());
}
}
public BukkitQueue_0(String world) {
super(world);
if (!registered) {
registered = true;
Bukkit.getServer().getPluginManager().registerEvents(this, ((FaweBukkit) Fawe.imp()).getPlugin());
}
}
@Override
public boolean supports(Capability capability) {
switch (capability) {
case CHUNK_PACKETS:
Plugin plib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
return plib != null && plib.isEnabled();
}
return super.supports(capability);
}
@Override
public void sendChunk(FaweChunk fc) {
if (!Fawe.isMainThread()) {
startSet(true);
try {
super.sendChunk(fc);
} finally {
endSet(true);
}
} else super.sendChunk(fc);
}
@Override
public void sendChunkUpdate(FaweChunk chunk, FawePlayer... players) {
if (supports(Capability.CHUNK_PACKETS)) {
sendChunkUpdatePLIB(chunk, players);
} else {
sendBlockUpdate(chunk, players);
}
}
public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) {
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
WirePacket packet = null;
int viewDistance = Bukkit.getViewDistance();
try {
for (int i = 0; i < players.length; i++) {
int cx = chunk.getX();
int cz = chunk.getZ();
Player player = ((BukkitPlayer) players[i]).parent;
Location loc = player.getLocation();
if (Math.abs((loc.getBlockX() >> 4) - cx) <= viewDistance && Math.abs((loc.getBlockZ() >> 4) - cz) <= viewDistance) {
if (packet == null) {
byte[] data;
byte[] buffer = new byte[8192];
if (chunk instanceof LazyFaweChunk) {
chunk = (FaweChunk) chunk.getChunk();
}
// TODO FIXME
// if (chunk instanceof MCAChunk) {
// data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer);
// } else {
// data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer);
// }
// packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data);
}
manager.sendWirePacket(player, packet);
}
}
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean queueChunkLoad(int cx, int cz, RunnableVal<CHUNK> operation) {
if (PAPER) {
try {
new PaperChunkCallback(getImpWorld(), cx, cz) {
@Override
public void onLoad(Chunk bukkitChunk) {
try {
CHUNK chunk = (CHUNK) methodGetHandle.invoke(bukkitChunk);
try {
operation.run(chunk);
} catch (Throwable e) {
e.printStackTrace();
}
} catch (Throwable e) {
PAPER = false;
}
}
};
return true;
} catch (Throwable ignore) {
PAPER = false;
}
}
return super.queueChunkLoad(cx, cz);
}
public static BukkitImplAdapter getAdapter() {
return adapter;
}
public static Tag toNative(Object tag) {
BukkitImplAdapter adapter = getAdapter();
return adapter.toNative(tag);
}
public static Object fromNative(Tag tag) {
BukkitImplAdapter adapter = getAdapter();
return adapter.fromNative(tag);
}
@Override
public File getSaveFolder() {
return new File(Bukkit.getWorldContainer(), getWorldName() + File.separator + "region");
}
@Override
public void setFullbright(CHUNKSECTIONS sections) {}
@Override
public void relight(int x, int y, int z) {}
@Override
public void relightBlock(int x, int y, int z) {}
@Override
public void relightSky(int x, int y, int z) {}
@Override
public boolean removeSectionLighting(SECTION sections, int layer, boolean hasSky) {
return false;
}
public static void checkVersion(String supported) {
String version = Bukkit.getServer().getClass().getPackage().getName();
if (!version.contains(supported)) {
throw new IllegalStateException("Unsupported version: " + version + " (supports: " + supported + ")");
}
}
protected static boolean registered = false;
protected static boolean disableChunkLoad = false;
@EventHandler
public static void onWorldLoad(WorldInitEvent event) {
if (disableChunkLoad) {
World world = event.getWorld();
world.setKeepSpawnInMemory(false);
}
}
public static ConcurrentHashMap<Long, Long> keepLoaded = new ConcurrentHashMap<>(8, 0.9f, 1);
@EventHandler
public static void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
keepLoaded.putIfAbsent(pair, Fawe.get().getTimer().getTickStart());
}
@EventHandler
public static void onChunkUnload(ChunkUnloadEvent event) {
Chunk chunk = event.getChunk();
long pair = MathMan.pairInt(chunk.getX(), chunk.getZ());
Long lastLoad = keepLoaded.get(pair);
if (lastLoad != null) {
if (Fawe.get().getTimer().getTickStart() - lastLoad < 10000) {
event.setCancelled(true);
} else {
keepLoaded.remove(pair);
}
}
}
@Override
public boolean queueChunkLoad(int cx, int cz) {
if (super.queueChunkLoad(cx, cz)) {
keepLoaded.put(MathMan.pairInt(cx, cz), System.currentTimeMillis());
return true;
}
return false;
}
public World createWorld(final WorldCreator creator) {
World world = TaskManager.IMP.sync(new RunnableVal<World>() {
@Override
public void run(World value) {
disableChunkLoad = true;
this.value = creator.createWorld();
disableChunkLoad = false;
}
});
return world;
}
@Override
public World getImpWorld() {
return getWorldName() != null ? Bukkit.getWorld(getWorldName()) : null;
}
@Override
public void sendChunk(int x, int z, int bitMask) {}
@Override
public void refreshChunk(FaweChunk fs) {}
@Override
public boolean regenerateChunk(World world, int x, int z, BaseBiome biome, Long seed) {
if (!keepLoaded.isEmpty()) keepLoaded.remove(MathMan.pairInt(x, z));
boolean result = world.regenerateChunk(x, z);
return result;
}
@Override
public IntFaweChunk getPrevious(IntFaweChunk fs, CHUNKSECTIONS sections, Map<?, ?> tiles, Collection<?>[] entities, Set<UUID> createdEntities, boolean all) throws Exception {
return fs;
}
@Override
public boolean hasSky() {
World world = getWorld();
return world == null || world.getEnvironment() == World.Environment.NORMAL;
}
private volatile boolean timingsEnabled;
private static boolean alertTimingsChange = true;
private static Field fieldTimingsEnabled;
private static Field fieldAsyncCatcherEnabled;
private static Method methodCheck;
static {
try {
fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled");
fieldAsyncCatcherEnabled.setAccessible(true);
} catch (Throwable ignore) {}
try {
fieldTimingsEnabled = Class.forName("co.aikar.timings.Timings").getDeclaredField("timingsEnabled");
fieldTimingsEnabled.setAccessible(true);
methodCheck = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled");
methodCheck.setAccessible(true);
} catch (Throwable ignore){}
}
@Override
public void startSet(boolean parallel) {
ChunkListener.physicsFreeze = true;
if (parallel) {
try {
if (fieldAsyncCatcherEnabled != null) {
fieldAsyncCatcherEnabled.set(null, false);
}
if (fieldTimingsEnabled != null) {
timingsEnabled = (boolean) fieldTimingsEnabled.get(null);
if (timingsEnabled) {
if (alertTimingsChange) {
alertTimingsChange = false;
Fawe.debug("Having `parallel-threads` > 1 interferes with the timings.");
}
fieldTimingsEnabled.set(null, false);
methodCheck.invoke(null);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void endSet(boolean parallel) {
ChunkListener.physicsFreeze = false;
if (parallel) {
try {
if (fieldAsyncCatcherEnabled != null) {
fieldAsyncCatcherEnabled.set(null, true);
}
if (fieldTimingsEnabled != null && timingsEnabled) {
fieldTimingsEnabled.set(null, true);
methodCheck.invoke(null);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void sendBlockUpdate(final FaweChunk chunk, FawePlayer... players) {
if (players.length == 0) {
return;
}
int cx = chunk.getX();
int cz = chunk.getZ();
int view = Bukkit.getServer().getViewDistance();
boolean sendAny = false;
boolean[] send = new boolean[players.length];
for (int i = 0; i < players.length; i++) {
FawePlayer player = players[i];
Player bp = ((BukkitPlayer) player).parent;
Location loc = bp.getLocation();
if (Math.abs((loc.getBlockX() >> 4) - cx) <= view && Math.abs((loc.getBlockZ() >> 4) - cz) <= view) {
sendAny = true;
send[i] = true;
}
}
if (!sendAny) {
return;
}
final World world = getWorld();
final int bx = cx << 4;
final int bz = cz << 4;
chunk.forEachQueuedBlock(new FaweChunkVisitor() {
@Override
public void run(int localX, int y, int localZ, int combined) {
Location loc = new Location(world, bx + localX, y, bz + localZ);
for (int i = 0; i < players.length; i++) {
if (send[i]) {
((BukkitPlayer) players[i]).parent.sendBlockChange(loc, BukkitAdapter.adapt(BlockState.get(combined)));
}
}
}
});
}
}

View File

@ -0,0 +1,406 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.NullRelighter;
import com.boydti.fawe.example.Relighter;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.google.common.collect.MapMaker;
import com.sk89q.jnbt.CompoundTag;
import java.io.File;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
public class BukkitQueue_All extends BukkitQueue_0<ChunkSnapshot, ChunkSnapshot, ChunkSnapshot> {
public static int ALLOCATE;
private ConcurrentMap<Long, ChunkSnapshot> chunkCache = new MapMaker()
.weakValues()
.makeMap();
public BukkitQueue_All(com.sk89q.worldedit.world.World world) {
super(world);
if (Settings.IMP.QUEUE.EXTRA_TIME_MS != Integer.MIN_VALUE) {
ALLOCATE = Settings.IMP.QUEUE.EXTRA_TIME_MS;
Settings.IMP.QUEUE.EXTRA_TIME_MS = Integer.MIN_VALUE;
Settings.IMP.QUEUE.PARALLEL_THREADS = 1;
}
}
public BukkitQueue_All(String world) {
super(world);
if (Settings.IMP.QUEUE.EXTRA_TIME_MS != Integer.MIN_VALUE) {
ALLOCATE = Settings.IMP.QUEUE.EXTRA_TIME_MS;
Settings.IMP.QUEUE.EXTRA_TIME_MS = Integer.MIN_VALUE;
Settings.IMP.QUEUE.PARALLEL_THREADS = 1;
}
}
@Override
public boolean queueChunkLoad(int cx, int cz, RunnableVal<ChunkSnapshot> operation) {
if (PAPER) {
try {
new PaperChunkCallback(getImpWorld(), cx, cz) {
@Override
public void onLoad(Chunk chunk) {
try {
ChunkSnapshot snapshot = chunk.getChunkSnapshot();
operation.run(snapshot);
} catch (Throwable e) {
PAPER = false;
}
}
};
return true;
} catch (Throwable ignore) {
PAPER = false;
}
}
return super.queueChunkLoad(cx, cz);
}
@Override
public Relighter getRelighter() {
return NullRelighter.INSTANCE;
}
private static Class<?> classRegionFileCache;
private static Class<?> classRegionFile;
private static Class<?> classCraftChunk;
private static Class<?> classCraftWorld;
private static Class<?> classNMSChunk;
private static Class<?> classNMSWorld;
private static Class<?> classChunkProviderServer;
private static Class<?> classIChunkLoader;
private static Class<?> classChunkRegionLoader;
private static Class<?> classIChunkProvider;
private static Method methodGetHandleChunk;
private static Method methodGetHandleWorld;
private static Method methodFlush;
private static Method methodNeedsSaving;
private static Field fieldChunkProvider;
private static Field fieldChunkLoader;
private static Field fieldRegionMap;
private static Field fieldRegionRAF;
static {
try {
BukkitReflectionUtils.init();
classRegionFileCache = BukkitReflectionUtils.getNmsClass("RegionFileCache");
classRegionFile = BukkitReflectionUtils.getNmsClass("RegionFile");
classCraftChunk = BukkitReflectionUtils.getCbClass("CraftChunk");
classNMSChunk = BukkitReflectionUtils.getNmsClass("Chunk");
classCraftWorld = BukkitReflectionUtils.getCbClass("CraftWorld");
classNMSWorld = BukkitReflectionUtils.getNmsClass("World");
classChunkProviderServer = BukkitReflectionUtils.getNmsClass("ChunkProviderServer");
classIChunkProvider = BukkitReflectionUtils.getNmsClass("IChunkProvider");
classIChunkLoader = BukkitReflectionUtils.getNmsClass("IChunkLoader");
classChunkRegionLoader = BukkitReflectionUtils.getNmsClass("ChunkRegionLoader");
methodGetHandleChunk = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle"));
methodGetHandleWorld = ReflectionUtils.setAccessible(classCraftWorld.getDeclaredMethod("getHandle"));
methodFlush = ReflectionUtils.findMethod(classChunkRegionLoader, boolean.class);
methodNeedsSaving = ReflectionUtils.findMethod(classNMSChunk, boolean.class, boolean.class);
fieldChunkProvider = ReflectionUtils.findField(classNMSWorld, classIChunkProvider);
fieldChunkLoader = ReflectionUtils.findField(classChunkProviderServer, classIChunkLoader);
fieldRegionMap = ReflectionUtils.findField(classRegionFileCache, Map.class);
fieldRegionRAF = ReflectionUtils.findField(classRegionFile, RandomAccessFile.class);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
@Override
public boolean setMCA(int mcaX, int mcaZ, RegionWrapper allowed, Runnable whileLocked, boolean saveChunks, boolean load) {
if (classRegionFileCache == null) {
return super.setMCA(mcaX, mcaZ, allowed, whileLocked, saveChunks, load);
}
TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
long start = System.currentTimeMillis();
long last = start;
synchronized (classRegionFileCache) {
try {
World world = getWorld();
boolean autoSave = world.isAutoSave();
if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false);
ArrayDeque<Chunk> unloaded = null;
if (load) {
int bcx = mcaX << 5;
int bcz = mcaZ << 5;
int tcx = bcx + 31;
int tcz = bcz + 31;
for (Chunk chunk : world.getLoadedChunks()) {
int cx = chunk.getX();
int cz = chunk.getZ();
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
Object nmsChunk = methodGetHandleChunk.invoke(chunk);
boolean mustSave = saveChunks && (boolean) methodNeedsSaving.invoke(nmsChunk, false);
chunk.unload(mustSave, false);
if (unloaded == null) unloaded = new ArrayDeque<Chunk>();
unloaded.add(chunk);
}
}
} else {
world.save();
}
Object nmsWorld = methodGetHandleWorld.invoke(world);
Object chunkProviderServer = fieldChunkProvider.get(nmsWorld);
Object chunkRegionLoader = fieldChunkLoader.get(chunkProviderServer);
while ((boolean) methodFlush.invoke(chunkRegionLoader));
if (unloaded != null) {
Map regionMap = (Map) fieldRegionMap.get(null);
File file = new File(world.getWorldFolder(), "region" + File.separator + "r." + mcaX + "." + mcaZ + ".mca");
Object regionFile = regionMap.remove(file);
if (regionFile != null) {
RandomAccessFile raf = (RandomAccessFile) fieldRegionRAF.get(regionFile);
raf.close();
}
}
whileLocked.run();
if (load && unloaded != null) {
final ArrayDeque<Chunk> finalUnloaded = unloaded;
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
for (Chunk chunk : finalUnloaded) {
int cx = chunk.getX();
int cz = chunk.getZ();
if (world.isChunkLoaded(cx, cz)) continue;
SetQueue.IMP.addTask(() -> {
world.loadChunk(chunk.getX(), chunk.getZ(), false);
world.refreshChunk(chunk.getX(), chunk.getZ());
});
}
}
});
// load chunks
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
});
return true;
}
@Override
public void setHeightMap(FaweChunk chunk, byte[] heightMap) {
// Not supported
}
@Override
public void setSkyLight(ChunkSnapshot chunk, int x, int y, int z, int value) {
// Not supported
}
@Override
public void setBlockLight(ChunkSnapshot chunk, int x, int y, int z, int value) {
// Not supported
}
@Override
public int getCombinedId4Data(ChunkSnapshot chunk, int x, int y, int z) {
if (chunk.isSectionEmpty(y >> 4)) {
return 0;
}
BlockData blockData = chunk.getBlockData(x & 15, y, z & 15);
return BukkitAdapter.adapt(blockData).getInternalId();
}
@Override
public int getBiome(ChunkSnapshot chunkSnapshot, int x, int z) {
Biome biome = chunkSnapshot.getBiome(x & 15, z & 15);
return getAdapter().getBiomeId(biome);
}
@Override
public ChunkSnapshot getSections(ChunkSnapshot chunkSnapshot) {
return chunkSnapshot;
}
@Override
public ChunkSnapshot getCachedChunk(World world, int cx, int cz) {
long pair = MathMan.pairInt(cx, cz);
ChunkSnapshot cached = chunkCache.get(pair);
if (cached != null) return cached;
if (world.isChunkLoaded(cx, cz)) {
Long originalKeep = keepLoaded.get(pair);
keepLoaded.put(pair, Long.MAX_VALUE);
if (world.isChunkLoaded(cx, cz)) {
Chunk chunk = world.getChunkAt(cx, cz);
ChunkSnapshot snapshot = getAndCacheChunk(chunk);
if (originalKeep != null) {
keepLoaded.put(pair, originalKeep);
} else {
keepLoaded.remove(pair);
}
return snapshot;
} else {
keepLoaded.remove(pair);
return null;
}
} else {
return null;
}
}
@Override
public int getEmmittedLight(final ChunkSnapshot chunk, int x, int y, int z) {
return chunk.getBlockEmittedLight(x & 15, y, z & 15);
}
@Override
public int getSkyLight(final ChunkSnapshot chunk, int x, int y, int z) {
return chunk.getBlockSkyLight(x & 15, y, z & 15);
}
@Override
public int getLight(final ChunkSnapshot chunk, int x, int y, int z) {
x = x & 15;
z = z & 15;
return Math.max(chunk.getBlockEmittedLight(x, y, z), chunk.getBlockSkyLight(x, y, z));
}
@Override
public ChunkSnapshot loadChunk(World world, int x, int z, boolean generate) {
Chunk chunk = world.getChunkAt(x, z);
chunk.load(generate);
return chunk.isLoaded() ? getAndCacheChunk(chunk) : null;
}
private ChunkSnapshot getAndCacheChunk(Chunk chunk) {
ChunkSnapshot snapshot = chunk.getChunkSnapshot(false, true, false);
chunkCache.put(MathMan.pairInt(chunk.getX(), chunk.getZ()), snapshot);
return snapshot;
}
@Override
public ChunkSnapshot getCachedSections(World impWorld, int cx, int cz) {
return getCachedChunk(impWorld, cx, cz);
}
@Override
public CompoundTag getTileEntity(ChunkSnapshot chunk, int x, int y, int z) {
if (getAdapter() == null) {
return null;
}
Location loc = new Location(getWorld(), x, y, z);
BlockStateHolder block = getAdapter().getBlock(loc);
return block.getNbtData();
}
@Override
public FaweChunk getFaweChunk(int x, int z) {
return new BukkitChunk_All(this, x, z);
}
@Override
public boolean supports(Capability capability) {
switch (capability) {
case CHANGE_TASKS: return getAdapter() != null;
}
return super.supports(capability);
}
private int skip;
@Override
public void startSet(boolean parallel) {
super.startSet(true);
}
private Field fieldNeighbors;
private Method chunkGetHandle;
/**
* Exploiting a bug in the vanilla lighting algorithm for faster block placement
* - Could have been achieved without reflection by force unloading specific chunks
* - Much faster just setting the variable manually though
* @param chunk
* @return
*/
protected Object[] disableLighting(Chunk chunk) {
try {
if (chunkGetHandle == null) {
chunkGetHandle = chunk.getClass().getDeclaredMethod("getHandle");
chunkGetHandle.setAccessible(true);
}
Object nmsChunk = chunkGetHandle.invoke(chunk);
if (fieldNeighbors == null) {
fieldNeighbors = nmsChunk.getClass().getDeclaredField("neighbors");
fieldNeighbors.setAccessible(true);
}
Object value = fieldNeighbors.get(nmsChunk);
fieldNeighbors.set(nmsChunk, 0);
return new Object[] {nmsChunk, value};
} catch (Throwable ignore) {}
return null;
}
protected void disableLighting(Object[] disableResult) {
if (disableResult != null) {
try {
fieldNeighbors.set(disableResult[0], 0);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
protected void resetLighting(Object[] disableResult) {
if (disableResult != null) {
try {
fieldNeighbors.set(disableResult[0], disableResult[1]);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
}
protected void enableLighting(Object[] disableResult) {
if (disableResult != null) {
try {
fieldNeighbors.set(disableResult[0], 0x739C0);
} catch (Throwable ignore) {}
}
}
@Override
public void endSet(boolean parallel) {
super.endSet(true);
}
}

View File

@ -0,0 +1,378 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.FaweTimer;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockExpEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.block.NotePlayEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.inventory.FurnaceBurnEvent;
import org.bukkit.event.inventory.FurnaceSmeltEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.util.Vector;
public abstract class ChunkListener implements Listener {
protected int rateLimit = 0;
private int[] badLimit = new int[]{Settings.IMP.TICK_LIMITER.PHYSICS_MS, Settings.IMP.TICK_LIMITER.FALLING, Settings.IMP.TICK_LIMITER.ITEMS};
public ChunkListener() {
if (Settings.IMP.TICK_LIMITER.ENABLED) {
Bukkit.getPluginManager().registerEvents(ChunkListener.this, Fawe.<FaweBukkit>imp().getPlugin());
TaskManager.IMP.repeat(new Runnable() {
@Override
public void run() {
rateLimit--;
physicsFreeze = false;
itemFreeze = false;
lastZ = Integer.MIN_VALUE;
physSkip = 0;
physCancelPair = Long.MIN_VALUE;
physCancel = false;
counter.clear();
for (Long2ObjectMap.Entry<Boolean> entry : badChunks.long2ObjectEntrySet()) {
long key = entry.getLongKey();
int x = MathMan.unpairIntX(key);
int z = MathMan.unpairIntY(key);
counter.put(key, badLimit);
}
badChunks.clear();
}
}, Settings.IMP.TICK_LIMITER.INTERVAL);
}
}
protected abstract int getDepth(Exception ex);
protected abstract StackTraceElement getElement(Exception ex, int index);
public static boolean physicsFreeze = false;
public static boolean itemFreeze = false;
protected final Long2ObjectOpenHashMap<Boolean> badChunks = new Long2ObjectOpenHashMap<>();
private Long2ObjectOpenHashMap<int[]> counter = new Long2ObjectOpenHashMap<>();
private int lastX = Integer.MIN_VALUE, lastZ = Integer.MIN_VALUE;
private int[] lastCount;
public int[] getCount(int cx, int cz) {
if (lastX == cx && lastZ == cz) {
return lastCount;
}
lastX = cx;
lastZ = cz;
long pair = MathMan.pairInt(cx, cz);
int[] tmp = lastCount = counter.get(pair);
if (tmp == null) {
lastCount = tmp = new int[3];
counter.put(pair, tmp);
}
return tmp;
}
public void cleanup(Chunk chunk) {
for (Entity entity : chunk.getEntities()) {
if (entity.getType() == EntityType.DROPPED_ITEM) {
entity.remove();
}
}
}
protected int physSkip;
protected boolean physCancel;
protected long physCancelPair;
protected long physStart;
protected long physTick;
private void reset() {
physSkip = 0;
physStart = System.currentTimeMillis();
physCancel = false;
}
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockBurnEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockCanBuildEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockDamageEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockDispenseEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockExpEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockExplodeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockFadeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockFromToEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockGrowEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockIgniteEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockPlaceEvent event) { reset(); }
// @EventHandler(priority = EventPriority.LOWEST)
// public void event(BrewEvent event) { reset(); }
//
// @EventHandler(priority = EventPriority.LOWEST)
// public void event(BrewingStandFuelEvent event) { reset(); }
//
// @EventHandler(priority = EventPriority.LOWEST)
// public void event(CauldronLevelChangeEvent event ) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(FurnaceBurnEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(FurnaceSmeltEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(LeavesDecayEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(NotePlayEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(SignChangeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockRedstoneEvent event) { reset(); }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPhysics(BlockPhysicsEvent event) {
if (physCancel) {
Block block = event.getBlock();
long pair = MathMan.pairInt(block.getX() >> 4, block.getZ() >> 4);
if (physCancelPair == pair) {
event.setCancelled(true);
return;
}
if (badChunks.containsKey(pair)) {
physCancelPair = pair;
event.setCancelled(true);
return;
}
} else {
if ((++physSkip & 1023) != 0) return;
FaweTimer timer = Fawe.get().getTimer();
if (timer.getTick() != physTick) {
physTick = timer.getTick();
physStart = System.currentTimeMillis();
return;
} else if (System.currentTimeMillis() - physStart < Settings.IMP.TICK_LIMITER.PHYSICS_MS) {
return;
}
}
if (physicsFreeze) {
event.setCancelled(true);
return;
}
switch (event.getChangedType()) {
case AIR:
case VOID_AIR:
case CAVE_AIR:
return;
}
Exception e = new Exception();
int depth = getDepth(e);
if (depth >= 256) {
if (containsSetAir(e, event)) {
Block block = event.getBlock();
int cx = block.getX() >> 4;
int cz = block.getZ() >> 4;
physCancelPair = MathMan.pairInt(cx, cz);
if (rateLimit <= 0) {
rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled physics lag source at " + block.getLocation());
}
cancelNearby(cx, cz);
event.setCancelled(true);
physCancel = true;
return;
}
}
physSkip = 1;
physCancel = false;
}
protected boolean containsSetAir(Exception e, BlockPhysicsEvent event) {
for (int frame = 25; frame < 35; frame++) {
StackTraceElement elem = getElement(e, frame);
if (elem != null) {
String methodName = elem.getMethodName();
// setAir | setTypeAndData (hacky, but this needs to be efficient)
if (methodName.charAt(0) == 's' && methodName.length() == 6 || methodName.length() == 14) {
return true;
}
}
}
return false;
}
protected void cancelNearby(int cx, int cz) {
cancel(cx, cz);
cancel(cx + 1, cz);
cancel(cx - 1, cz);
cancel(cx, cz + 1);
cancel(cx, cz - 1);
cancel(cx - 1, cz - 1);
cancel(cx - 1, cz + 1);
cancel(cx + 1, cz - 1);
cancel(cx + 1, cz + 1);
}
private void cancel(int cx, int cz) {
long key = MathMan.pairInt(cx, cz);
badChunks.put(key, (Boolean) true);
counter.put(key, badLimit);
int[] count = getCount(cx, cz);
count[0] = Integer.MAX_VALUE;
count[1] = Integer.MAX_VALUE;
count[2] = Integer.MAX_VALUE;
}
// Falling
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockChange(EntityChangeBlockEvent event) {
if (physicsFreeze) {
event.setCancelled(true);
return;
}
Block block = event.getBlock();
int x = block.getX();
int z = block.getZ();
int cx = x >> 4;
int cz = z >> 4;
int[] count = getCount(cx, cz);
if (count[1] >= Settings.IMP.TICK_LIMITER.FALLING) {
event.setCancelled(true);
return;
}
if (event.getEntityType() == EntityType.FALLING_BLOCK) {
if (++count[1] >= Settings.IMP.TICK_LIMITER.FALLING) {
// Only cancel falling blocks when it's lagging
if (Fawe.get().getTimer().getTPS() < 18) {
cancelNearby(cx, cz);
if (rateLimit <= 0) {
rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled falling block lag source at " + block.getLocation());
}
event.setCancelled(true);
return;
} else {
count[1] = 0;
}
}
}
}
/**
* Prevent FireWorks from loading chunks
* @param event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onChunkLoad(ChunkLoadEvent event) {
if (!Settings.IMP.TICK_LIMITER.FIREWORKS_LOAD_CHUNKS) {
Chunk chunk = event.getChunk();
Entity[] entities = chunk.getEntities();
World world = chunk.getWorld();
Exception e = new Exception();
int start = 14;
int end = 22;
int depth = Math.min(end, getDepth(e));
for (int frame = start; frame < depth; frame++) {
StackTraceElement elem = getElement(e, frame);
if (elem == null) return;
String className = elem.getClassName();
int len = className.length();
if (className != null) {
if (len > 15 && className.charAt(len - 15) == 'E' && className.endsWith("EntityFireworks")) {
for (Entity ent : world.getEntities()) {
if (ent.getType() == EntityType.FIREWORK) {
Vector velocity = ent.getVelocity();
double vertical = Math.abs(velocity.getY());
if (Math.abs(velocity.getX()) > vertical || Math.abs(velocity.getZ()) > vertical) {
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at " + ent.getLocation());
ent.remove();
}
}
}
}
}
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onItemSpawn(ItemSpawnEvent event) {
if (physicsFreeze) {
event.setCancelled(true);
return;
}
Location loc = event.getLocation();
int cx = loc.getBlockX() >> 4;
int cz = loc.getBlockZ() >> 4;
int[] count = getCount(cx, cz);
if (count[2] >= Settings.IMP.TICK_LIMITER.ITEMS) {
event.setCancelled(true);
return;
}
if (++count[2] >= Settings.IMP.TICK_LIMITER.ITEMS) {
cleanup(loc.getChunk());
cancelNearby(cx, cz);
if (rateLimit <= 0) {
rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled item lag source at " + loc);
}
event.setCancelled(true);
return;
}
}
}

View File

@ -0,0 +1,16 @@
package com.boydti.fawe.bukkit.v0;
import sun.misc.SharedSecrets;
public class ChunkListener_8 extends ChunkListener {
@Override
protected int getDepth(Exception ex) {
return SharedSecrets.getJavaLangAccess().getStackTraceDepth(ex);
}
@Override
protected StackTraceElement getElement(Exception ex, int index) {
return SharedSecrets.getJavaLangAccess().getStackTraceElement(ex, index);
}
}

View File

@ -0,0 +1,91 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.FaweTimer;
import com.boydti.fawe.util.MathMan;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockPhysicsEvent;
//
public class ChunkListener_9 extends ChunkListener {
private Exception exception;
private StackTraceElement[] elements;
public ChunkListener_9() {
super();
}
@EventHandler(priority = EventPriority.LOWEST)
@Override
public void onPhysics(BlockPhysicsEvent event) {
if (physicsFreeze) {
event.setCancelled(true);
return;
}
if (physCancel) {
Block block = event.getBlock();
long pair = MathMan.pairInt(block.getX() >> 4, block.getZ() >> 4);
if (physCancelPair == pair) {
event.setCancelled(true);
return;
}
if (badChunks.containsKey(pair)) {
physCancelPair = pair;
event.setCancelled(true);
return;
}
if (System.currentTimeMillis() - physStart > Settings.IMP.TICK_LIMITER.PHYSICS_MS) {
physCancelPair = pair;
event.setCancelled(true);
return;
}
}
FaweTimer timer = Fawe.get().getTimer();
if (timer.getTick() != physTick) {
physTick = timer.getTick();
physStart = System.currentTimeMillis();
physSkip = 0;
physCancel = false;
return;
}
if ((++physSkip & 1023) == 0) {
if (System.currentTimeMillis() - physStart > Settings.IMP.TICK_LIMITER.PHYSICS_MS) {
Block block = event.getBlock();
int cx = block.getX() >> 4;
int cz = block.getZ() >> 4;
physCancelPair = MathMan.pairInt(cx, cz);
if (rateLimit <= 0) {
rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled physics lag source at " + block.getLocation());
}
cancelNearby(cx, cz);
event.setCancelled(true);
physCancel = true;
return;
}
}
}
private StackTraceElement[] getElements(Exception ex) {
if (elements == null || ex != exception) {
exception = ex;
elements = ex.getStackTrace();
}
return elements;
}
@Override
protected int getDepth(Exception ex) {
return getElements(ex).length;
}
@Override
protected StackTraceElement getElement(Exception ex, int i) {
StackTraceElement[] elems = getElements(ex);
return elems.length > i ? elems[i] : null;
}
}

View File

@ -0,0 +1,453 @@
//package com.boydti.fawe.bukkit.v0;
//
//import com.boydti.fawe.FaweCache;
//import com.boydti.fawe.bukkit.util.BukkitReflectionUtils;
//import com.boydti.fawe.util.ReflectionUtils;
//import com.sk89q.jnbt.ByteArrayTag;
//import com.sk89q.jnbt.ByteTag;
//import com.sk89q.jnbt.CompoundTag;
//import com.sk89q.jnbt.DoubleTag;
//import com.sk89q.jnbt.EndTag;
//import com.sk89q.jnbt.FloatTag;
//import com.sk89q.jnbt.IntArrayTag;
//import com.sk89q.jnbt.IntTag;
//import com.sk89q.jnbt.ListTag;
//import com.sk89q.jnbt.LongTag;
//import com.sk89q.jnbt.NBTConstants;
//import com.sk89q.jnbt.ShortTag;
//import com.sk89q.jnbt.StringTag;
//import com.sk89q.jnbt.Tag;
//import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
//import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
//import com.sk89q.worldedit.entity.BaseEntity;
//import com.sk89q.worldedit.internal.Constants;
//import java.lang.reflect.Constructor;
//import java.lang.reflect.Field;
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//import java.lang.reflect.Modifier;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.concurrent.ConcurrentHashMap;
//import javax.annotation.Nullable;
//import org.bukkit.Location;
//import org.bukkit.Material;
//import org.bukkit.World;
//import org.bukkit.block.Biome;
//import org.bukkit.entity.Entity;
//import org.bukkit.event.entity.CreatureSpawnEvent;
//
//public class FaweAdapter_All implements BukkitImplAdapter {
//
// private final Class<?> classCraftBlock;
// private final Method biomeToBiomeBase;
// private final Class<?> classBiomeBase;
// private final Method biomeBaseToTypeId;
// private final Method getBiome;
// private final Method biomeBaseToBiome;
// private final Class<?> classCraftWorld;
// private final Method getHandleWorld;
// private final Class<?> classWorld;
// private final Method getTileEntity1;
// private final Method getTileEntity2;
// private final Class<?> classNBTTagCompound;
// private final Constructor<?> newNBTTagCompound;
// private final Class<?> classTileEntity;
// private final Class<?> classCraftEntity;
// private final Method getHandleEntity;
// private final Class<?> classNBTTagInt;
// private final Class<?> classNBTBase;
// private final Constructor<?> newNBTTagInt;
// private final Method setNBTTagCompound;
// private Class<?> classEntity;
// private Method getBukkitEntity;
// private Method addEntity;
// private Method setLocation;
// private Class<?> classEntityTypes;
// private Method getEntityId;
// private Method createEntityFromId;
// private Method readTagIntoEntity;
// private Method readEntityIntoTag;
// private Constructor<?> newMinecraftKey;
// private Class<?> classMinecraftKey;
// private Method readTagIntoTileEntity;
// private Method readTileEntityIntoTag;
// private Class<?> classBlockPosition;
// private Constructor<?> newBlockPosition;
//
// private Map<Class<? extends Tag>, NMSTagConstructor> WEToNMS = new ConcurrentHashMap<>();
// private Map<Class, WETagConstructor> NMSToWE = new ConcurrentHashMap<>();
// private Map<Class<? extends Tag>, Integer> TagToId = new ConcurrentHashMap<>();
//
// public FaweAdapter_All() throws Throwable {
// BukkitReflectionUtils.init();
// classCraftWorld = BukkitReflectionUtils.getCbClass("CraftWorld");
// classCraftBlock = BukkitReflectionUtils.getCbClass("block.CraftBlock");
// classCraftEntity = BukkitReflectionUtils.getCbClass("entity.CraftEntity");
// classBiomeBase = BukkitReflectionUtils.getNmsClass("BiomeBase");
// classWorld = BukkitReflectionUtils.getNmsClass("World");
// classTileEntity = BukkitReflectionUtils.getNmsClass("TileEntity");
//
// biomeToBiomeBase = ReflectionUtils.setAccessible(classCraftBlock.getDeclaredMethod("biomeToBiomeBase", Biome.class));
// biomeBaseToBiome = ReflectionUtils.setAccessible(classCraftBlock.getDeclaredMethod("biomeBaseToBiome", classBiomeBase));
// getBiome = ReflectionUtils.setAccessible(classBiomeBase.getDeclaredMethod("getBiome", int.class));
// biomeBaseToTypeId = ReflectionUtils.findMethod(classBiomeBase, int.class, classBiomeBase);
// getHandleWorld = ReflectionUtils.setAccessible(classCraftWorld.getDeclaredMethod("getHandle"));
// getHandleEntity = ReflectionUtils.setAccessible(classCraftEntity.getDeclaredMethod("getHandle"));
// try {
// classBlockPosition = BukkitReflectionUtils.getNmsClass("BlockPosition");
// } catch (Throwable ignore) {
// }
// if (classBlockPosition != null) {
// getTileEntity1 = classWorld.getDeclaredMethod("getTileEntity", classBlockPosition);
// getTileEntity2 = null;
// newBlockPosition = ReflectionUtils.setAccessible(classBlockPosition.getConstructor(int.class, int.class, int.class));
// } else {
// getTileEntity1 = null;
// getTileEntity2 = ReflectionUtils.setAccessible(classWorld.getDeclaredMethod("getTileEntity", int.class, int.class, int.class));
// }
//
// classNBTTagCompound = BukkitReflectionUtils.getNmsClass("NBTTagCompound");
// classNBTBase = BukkitReflectionUtils.getNmsClass("NBTBase");
// classNBTTagInt = BukkitReflectionUtils.getNmsClass("NBTTagInt");
// newNBTTagInt = ReflectionUtils.setAccessible(classNBTTagInt.getConstructor(int.class));
// setNBTTagCompound = ReflectionUtils.setAccessible(classNBTTagCompound.getDeclaredMethod("set", String.class, classNBTBase));
// newNBTTagCompound = ReflectionUtils.setAccessible(classNBTTagCompound.getConstructor());
// try {
// readTileEntityIntoTag = ReflectionUtils.setAccessible(classTileEntity.getDeclaredMethod("save", classNBTTagCompound));
// } catch (Throwable ignore) {
// readTileEntityIntoTag = ReflectionUtils.findMethod(classTileEntity, classNBTTagCompound, classNBTTagCompound);
// if (readTileEntityIntoTag == null) {
// readTileEntityIntoTag = ReflectionUtils.findMethod(classTileEntity, 1, Void.TYPE, classNBTTagCompound);
// }
// }
//
//
// try {
// readTagIntoTileEntity = ReflectionUtils.setAccessible(classTileEntity.getDeclaredMethod("load", classNBTTagCompound));
// } catch (Throwable ignore) {
// readTagIntoTileEntity = ReflectionUtils.findMethod(classTileEntity, 0, Void.TYPE, classNBTTagCompound);
// }
//
//
// List<String> nmsClasses = Arrays.asList("NBTTagCompound", "NBTTagByte", "NBTTagByteArray", "NBTTagDouble", "NBTTagFloat", "NBTTagInt", "NBTTagIntArray", "NBTTagList", "NBTTagEnd", "NBTTagString", "NBTTagShort", "NBTTagLong");
// List<Class<? extends Tag>> weClasses = Arrays.asList(CompoundTag.class, ByteTag.class, ByteArrayTag.class, DoubleTag.class, FloatTag.class, IntTag.class, IntArrayTag.class, ListTag.class, EndTag.class, StringTag.class, ShortTag.class, LongTag.class);
// int[] ids = new int[]{10, 1, 7, 6, 5, 3, 11, 9, 0, 8, 2, 4};
//
// int noMods = Modifier.STATIC;
// int hasMods = 0;
// for (int i = 0; i < nmsClasses.size(); i++) {
// Class<?> nmsClass = BukkitReflectionUtils.getNmsClass(nmsClasses.get(i));
// Class<? extends Tag> weClass = weClasses.get(i);
// TagToId.put(weClass, ids[i]);
//
// Constructor nmsConstructor = ReflectionUtils.setAccessible(nmsClass.getDeclaredConstructor());
//
// if (weClass == EndTag.class) {
// NMSToWE.put(nmsClass, value -> new EndTag());
// WEToNMS.put(weClass, value -> nmsConstructor.newInstance());
// } else if (weClass == CompoundTag.class) {
// Field mapField = ReflectionUtils.findField(nmsClass, Map.class, hasMods, noMods);
// Constructor<? extends Tag> weConstructor = ReflectionUtils.setAccessible(CompoundTag.class.getConstructor(Map.class));
//
// NMSToWE.put(nmsClass, value -> {
// Map<String, Object> map = (Map) mapField.get(value);
// Map<String, Tag> weMap = new HashMap<String, Tag>();
// for (Map.Entry<String, Object> entry : map.entrySet()) {
// weMap.put(entry.getKey(), toNative(entry.getValue()));
// }
// return new CompoundTag(weMap);
// });
//
// WEToNMS.put(weClass, value -> {
// Map<String, Tag> map = ReflectionUtils.getMap(((CompoundTag) value).getValue());
// Object nmsTag = nmsConstructor.newInstance();
// Map<String, Object> nmsMap = (Map<String, Object>) mapField.get(nmsTag);
// for (Map.Entry<String, Tag> entry : map.entrySet()) {
// nmsMap.put(entry.getKey(), fromNative(entry.getValue()));
// }
// return nmsTag;
// });
// } else if (weClass == ListTag.class) {
// Field listField = ReflectionUtils.findField(nmsClass, List.class, hasMods, noMods);
// Field typeField = ReflectionUtils.findField(nmsClass, byte.class, hasMods, noMods);
// Constructor<? extends Tag> weConstructor = ReflectionUtils.setAccessible(ListTag.class.getConstructor(Class.class, List.class));
//
// NMSToWE.put(nmsClass, tag -> {
// int type = ((Number) typeField.get(tag)).intValue();
// List list = (List) listField.get(tag);
//
// Class<? extends Tag> weType = NBTConstants.getClassFromType(type);
// ArrayList<Tag> weList = new ArrayList<>();
// for (Object nmsTag : list) {
// weList.add(toNative(nmsTag));
// }
// return new ListTag(weType, weList);
// });
// WEToNMS.put(weClass, tag -> {
// ListTag lt = (ListTag) tag;
// List<Tag> list = ReflectionUtils.getList(lt.getValue());
// Class<? extends Tag> type = lt.getType();
//
// int typeId = TagToId.get(type);
// Object nmsTagList = nmsConstructor.newInstance();
// typeField.set(nmsTagList, (byte) typeId);
// ArrayList<Object> nmsList = (ArrayList<Object>) listField.get(nmsTagList);
// for (Tag weTag : list) {
// nmsList.add(fromNative(weTag));
// }
// return nmsTagList;
// });
// } else {
// Field typeField = ReflectionUtils.findField(nmsClass, null, hasMods, noMods);
// Constructor<? extends Tag> weConstructor = ReflectionUtils.setAccessible(weClass.getConstructor(typeField.getType()));
//
// NMSToWE.put(nmsClass, tag -> {
// Object value = typeField.get(tag);
// return weConstructor.newInstance(value);
// });
//
// WEToNMS.put(weClass, tag -> {
// Object nmsTag = nmsConstructor.newInstance();
// typeField.set(nmsTag, tag.getValue());
// return nmsTag;
// });
// }
// }
// try {
// classEntity = BukkitReflectionUtils.getNmsClass("Entity");
// classEntityTypes = BukkitReflectionUtils.getNmsClass("EntityTypes");
//
// getBukkitEntity = ReflectionUtils.setAccessible(classEntity.getDeclaredMethod("getBukkitEntity"));
// addEntity = ReflectionUtils.setAccessible(classWorld.getDeclaredMethod("addEntity", classEntity, CreatureSpawnEvent.SpawnReason.class));
// setLocation = ReflectionUtils.setAccessible(classEntity.getDeclaredMethod("setLocation", double.class, double.class, double.class, float.class, float.class));
//
// try {
// classMinecraftKey = BukkitReflectionUtils.getNmsClass("MinecraftKey");
// newMinecraftKey = classMinecraftKey.getConstructor(String.class);
// } catch (Throwable ignore) {
// }
// if (classMinecraftKey != null) {
// getEntityId = ReflectionUtils.findMethod(classEntityTypes, classMinecraftKey, classEntity);
// createEntityFromId = ReflectionUtils.findMethod(classEntityTypes, classEntity, classMinecraftKey, classWorld);
// } else {
// getEntityId = ReflectionUtils.findMethod(classEntityTypes, String.class, classEntity);
// createEntityFromId = ReflectionUtils.findMethod(classEntityTypes, classEntity, String.class, classWorld);
// }
//
// noMods = Modifier.ABSTRACT | Modifier.PROTECTED | Modifier.PRIVATE;
// try {
// readEntityIntoTag = classEntity.getDeclaredMethod("save", classNBTTagCompound);
// } catch (Throwable ignore) {
// readEntityIntoTag = ReflectionUtils.findMethod(classEntity, classNBTTagCompound, classNBTTagCompound);
// if (readEntityIntoTag == null) {
// readEntityIntoTag = ReflectionUtils.findMethod(classEntity, 0, 0, noMods, Void.TYPE, classNBTTagCompound);
// }
// }
// ReflectionUtils.setAccessible(readEntityIntoTag);
// readTagIntoEntity = ReflectionUtils.findMethod(classEntity, 1, 0, noMods, Void.TYPE, classNBTTagCompound);
// if (readTagIntoEntity == null) {
// readTagIntoEntity = ReflectionUtils.findMethod(classEntity, 0, 0, noMods, Void.TYPE, classNBTTagCompound);
// }
// } catch (Throwable e) {
// e.printStackTrace();
// classEntity = null;
// }
// }
//
// @Nullable
// @Override
// public BaseEntity getEntity(Entity entity) {
// try {
// if (classEntity == null) return null;
// Object nmsEntity = getHandleEntity.invoke(entity);
//
// String id = getEntityId(nmsEntity);
//
// if (id != null) {
// Object tag = newNBTTagCompound.newInstance();
// readEntityIntoTag.invoke(nmsEntity, tag);
// return new BaseEntity(id, (CompoundTag) toNative(tag));
// }
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// return null;
// }
//
// private String getEntityId(Object entity) throws InvocationTargetException, IllegalAccessException {
// Object res = getEntityId.invoke(null, entity);
// return res == null ? null : res.toString();
// }
//
// private Object createEntityFromId(String id, Object world) throws InvocationTargetException, IllegalAccessException, InstantiationException {
// if (classMinecraftKey != null) {
// Object key = newMinecraftKey.newInstance(id);
// return createEntityFromId.invoke(null, key, world);
// } else {
// return createEntityFromId.invoke(null, id, world);
// }
// }
//
// @Nullable
// @Override
// public Entity createEntity(Location location, BaseEntity state) {
// try {
// if (classEntity == null) return null;
// World world = location.getWorld();
// Object nmsWorld = getHandleWorld.invoke(world);
//
// Object createdEntity = createEntityFromId(state.getTypeId(), nmsWorld);
//
// if (createdEntity != null) {
// CompoundTag nativeTag = state.getNbtData();
// Map<String, Tag> rawMap = ReflectionUtils.getMap(nativeTag.getValue());
// for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
// rawMap.remove(name);
// }
// if (nativeTag != null) {
// Object tag = fromNative(nativeTag);
// readTagIntoEntity.invoke(createdEntity, tag);
// }
//
// setLocation.invoke(createdEntity, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
//
// addEntity.invoke(nmsWorld, createdEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
// return (Entity) getBukkitEntity.invoke(createdEntity);
// }
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// return null;
// }
//
// public Tag toNative(Object nmsTag) {
// try {
// return NMSToWE.get(nmsTag.getClass()).construct(nmsTag);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
//
// public Object fromNative(Tag tag) {
// try {
// return WEToNMS.get(tag.getClass()).construct(tag);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
//
// @Override
// public int getBlockId(Material material) {
// return material.getId();
// }
//
// @Override
// public Material getMaterial(int id) {
// return Material.getMaterial(id);
// }
//
// @Override
// public int getBiomeId(Biome biome) {
// try {
// Object biomeBase = biomeToBiomeBase.invoke(null, biome);
// if (biomeBase != null) return (int) biomeBaseToTypeId.invoke(null, biomeBase);
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// return 0;
// }
//
// @Override
// public Biome getBiome(int id) {
// try {
// Object biomeBase = getBiome.invoke(null, id);
// if (biomeBase != null) return (Biome) biomeBaseToBiome.invoke(null, biomeBase);
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// return Biome.OCEAN;
// }
//
// @Override
// public BaseBlock getBlock(Location location) {
// try {
// World craftWorld = location.getWorld();
// int x = location.getBlockX();
// int y = location.getBlockY();
// int z = location.getBlockZ();
//
// org.bukkit.block.Block bukkitBlock = location.getBlock();
// BaseBlock block = FaweCache.getBlock(bukkitBlock.getTypeId(), bukkitBlock.getData());
//
// // Read the NBT data
// Object nmsWorld = getHandleWorld.invoke(craftWorld);
// Object tileEntity = getTileEntity(nmsWorld, x, y, z);
//
// if (tileEntity != null) {
// block = new BaseBlock(block);
// Object tag = newNBTTagCompound.newInstance();
// readTileEntityIntoTag.invoke(tileEntity, tag);
// block.setNbtData((CompoundTag) toNative(tag));
// }
// return block;
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// }
//
// public Object getTileEntity(Object nmsWorld, int x, int y, int z) {
// try {
// if (getTileEntity1 != null) {
// Object pos = newBlockPosition.newInstance(x, y, z);
// return getTileEntity1.invoke(nmsWorld, pos);
// } else {
// return getTileEntity2.invoke(nmsWorld, x, y, z);
// }
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
//
// @Override
// public boolean setBlock(Location location, BaseBlock block, boolean notifyAndLight) {
// World craftWorld = location.getWorld();
// int x = location.getBlockX();
// int y = location.getBlockY();
// int z = location.getBlockZ();
//
// boolean changed = location.getBlock().setTypeIdAndData(block.getId(), (byte) block.getData(), notifyAndLight);
//
// CompoundTag nativeTag = block.getNbtData();
// if (nativeTag != null) {
// try {
// Object nmsWorld = getHandleWorld.invoke(craftWorld);
// Object tileEntity = getTileEntity(nmsWorld, x, y, z);
// if (tileEntity != null) {
// Object tag = fromNative(nativeTag);
//
// setNBTTagCompound.invoke(tag, "x", newNBTTagInt.newInstance(x));
// setNBTTagCompound.invoke(tag, "y", newNBTTagInt.newInstance(y));
// setNBTTagCompound.invoke(tag, "z", newNBTTagInt.newInstance(z));
// readTagIntoTileEntity.invoke(tileEntity, tag); // Load data
// }
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// }
//
// return changed;
// }
//
// private interface NMSTagConstructor {
// Object construct(Tag value) throws Exception;
// }
//
// private interface WETagConstructor {
// Tag construct(Object value) throws Exception;
// }
//}

View File

@ -0,0 +1,12 @@
package com.boydti.fawe.bukkit.v0;
import org.bukkit.Chunk;
import org.bukkit.World;
public abstract class PaperChunkCallback {
public PaperChunkCallback(World world, int x, int z) {
world.getChunkAtAsync(x, z, chunk -> PaperChunkCallback.this.onLoad(chunk));
}
public abstract void onLoad(Chunk chunk);
}

View File

@ -0,0 +1,308 @@
package com.boydti.fawe.bukkit.wrapper;
import com.bekvon.bukkit.residence.commands.tool;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.wrapper.state.AsyncSign;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BlockID;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
public class AsyncBlock implements Block {
public final int z;
public final int y;
public final int x;
public final FaweQueue queue;
public final AsyncWorld world;
public AsyncBlock(AsyncWorld world, FaweQueue queue, int x, int y, int z) {
this.world = world;
this.queue = queue;
this.x = x;
this.y = Math.max(0, Math.min(255, y));
this.z = z;
}
@Override
@Deprecated
public byte getData() {
return (byte) (queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()) & 0xF);
}
@Override
public Block getRelative(int modX, int modY, int modZ) {
return new AsyncBlock(world, queue, x + modX, y + modY, z + modZ);
}
@Override
public Block getRelative(BlockFace face) {
return this.getRelative(face.getModX(), face.getModY(), face.getModZ());
}
@Override
public Block getRelative(BlockFace face, int distance) {
return this.getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
}
@Override
public Material getType() {
return getBlockData().getMaterial();
}
@Override
public BlockData getBlockData() {
return BukkitAdapter.getBlockData(queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()));
}
@Override
public byte getLightLevel() {
return (byte) queue.getLight(x, y, z);
}
@Override
public byte getLightFromSky() {
return (byte) queue.getSkyLight(x, y, z);
}
@Override
public byte getLightFromBlocks() {
return (byte) queue.getEmmittedLight(x, y, z);
}
@Override
public World getWorld() {
return world;
}
@Override
public int getX() {
return x;
}
@Override
public int getY() {
return y;
}
@Override
public int getZ() {
return z;
}
@Override
public Location getLocation() {
return new Location(world, x, y, z);
}
@Override
public Location getLocation(Location loc) {
if(loc != null) {
loc.setWorld(this.getWorld());
loc.setX((double)this.x);
loc.setY((double)this.y);
loc.setZ((double)this.z);
}
return loc;
}
@Override
public Chunk getChunk() {
return world.getChunkAt(x >> 4, z >> 4);
}
@Override
public void setBlockData(BlockData blockData) {
try {
queue.setBlock(x, y, z, BukkitAdapter.adapt(blockData));
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
@Override
public void setBlockData(BlockData blockData, boolean b) {
setBlockData(blockData);
}
@Override
public void setType(Material type) {
try {
queue.setBlock(x, y, z, BukkitAdapter.adapt(type).getDefaultState());
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
@Override
public void setType(Material type, boolean applyPhysics) {
setType(type);
}
@Override
public BlockFace getFace(Block block) {
BlockFace[] directions = BlockFace.values();
for(int i = 0; i < directions.length; ++i) {
BlockFace face = directions[i];
if(this.getX() + face.getModX() == block.getX() && this.getY() + face.getModY() == block.getY() && this.getZ() + face.getModZ() == block.getZ()) {
return face;
}
}
return null;
}
@Override
public BlockState getState() {
int combined = queue.getCombinedId4Data(x, y, z, 0);
BlockTypes type = BlockTypes.getFromStateId(combined);
switch (type) {
case SIGN:
case WALL_SIGN:
return new AsyncSign(this, combined);
}
return new AsyncBlockState(this, combined);
}
@Override
public BlockState getState(boolean useSnapshot) {
return getState();
}
@Override
public Biome getBiome() {
return world.getAdapter().getBiome(queue.getBiomeId(x, z));
}
@Override
public void setBiome(Biome bio) {
int id = world.getAdapter().getBiomeId(bio);
queue.setBiome(x, z, FaweCache.getBiome(id));
}
@Override
public boolean isBlockPowered() {
return false;
}
@Override
public boolean isBlockIndirectlyPowered() {
return false;
}
@Override
public boolean isBlockFacePowered(BlockFace face) {
return false;
}
@Override
public boolean isBlockFaceIndirectlyPowered(BlockFace face) {
return false;
}
@Override
public int getBlockPower(BlockFace face) {
return 0;
}
@Override
public int getBlockPower() {
return 0;
}
@Override
public boolean isEmpty() {
switch (getType()) {
case AIR:
case CAVE_AIR:
case VOID_AIR:
return true;
default:
return false;
}
}
@Override
public boolean isLiquid() {
int combined = queue.getCombinedId4Data(x, y, z, 0);
BlockTypes type = BlockTypes.getFromStateId(combined);
return type.getMaterial().isLiquid();
}
@Override
public double getTemperature() {
return this.getWorld().getTemperature(this.getX(), this.getZ());
}
@Override
public double getHumidity() {
return this.getWorld().getHumidity(this.getX(), this.getZ());
}
@Override
public PistonMoveReaction getPistonMoveReaction() {
return null;
}
public Block getBukkitBlock() {
return world.getBukkitWorld().getBlockAt(x, y, z);
}
@Override
public boolean breakNaturally() {
return TaskManager.IMP.sync(() -> getBukkitBlock().breakNaturally());
}
@Override
public boolean breakNaturally(ItemStack tool) {
return TaskManager.IMP.sync(() -> getBukkitBlock().breakNaturally(tool));
}
@Override
public Collection<ItemStack> getDrops() {
return TaskManager.IMP.sync(() -> getBukkitBlock().getDrops());
}
@Override
public Collection<ItemStack> getDrops(ItemStack tool) {
return TaskManager.IMP.sync(() -> getBukkitBlock().getDrops(tool));
}
@Override
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
this.getBukkitBlock().setMetadata(metadataKey, newMetadataValue);
}
@Override
public List<MetadataValue> getMetadata(String metadataKey) {
return this.getBukkitBlock().getMetadata(metadataKey);
}
@Override
public boolean hasMetadata(String metadataKey) {
return this.getBukkitBlock().hasMetadata(metadataKey);
}
@Override
public void removeMetadata(String metadataKey, Plugin owningPlugin) {
this.getBukkitBlock().removeMetadata(metadataKey, owningPlugin);
}
}

View File

@ -0,0 +1,183 @@
package com.boydti.fawe.bukkit.wrapper;
import com.boydti.fawe.FaweCache;
import com.sk89q.jnbt.CompoundTag;
import java.util.List;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
public class AsyncBlockState implements BlockState {
private int combinedId;
private BlockData blockData;
private CompoundTag nbt;
private final AsyncBlock block;
public AsyncBlockState(AsyncBlock block) {
this(block, block.queue.getCombinedId4Data(block.x, block.y, block.z, 0));
}
public AsyncBlockState(AsyncBlock block, int combined) {
this.combinedId = combined;
this.block = block;
this.blockData = BukkitAdapter.getBlockData(combined);
if (BlockTypes.getFromStateId(combined).getMaterial().hasContainer()) {
this.nbt = block.queue.getTileEntity(block.x, block.y, block.z);
}
}
@Override
public Block getBlock() {
return block;
}
@Override
public BlockData getBlockData() {
return blockData;
}
@Override
public MaterialData getData() {
return new MaterialData(blockData.getMaterial());
}
@Override
public Material getType() {
return blockData.getMaterial();
}
@Override
public byte getLightLevel() {
return (byte) BlockTypes.getFromStateId(combinedId).getMaterial().getLightValue();
}
@Override
public World getWorld() {
return block.world;
}
@Override
public int getX() {
return block.x;
}
@Override
public int getY() {
return block.y;
}
@Override
public int getZ() {
return block.z;
}
@Override
public Location getLocation() {
return block.getLocation();
}
@Override
public Location getLocation(Location loc) {
return block.getLocation(loc);
}
@Override
public Chunk getChunk() {
return block.getChunk();
}
@Override
public void setData(MaterialData data) {
setBlockData(data.getItemType().createBlockData());
}
@Override
public void setBlockData(BlockData blockData) {
this.blockData = blockData;
this.combinedId = BukkitAdapter.adapt(blockData).getInternalId();
}
@Override
public void setType(Material type) {
setBlockData(type.createBlockData());
}
@Override
public boolean update() {
return update(false);
}
@Override
public boolean update(boolean force) {
return update(force, true);
}
@Override
public boolean update(boolean force, boolean applyPhysics) {
try {
boolean result = block.queue.setBlock(block.x, block.y, block.z, BukkitAdapter.adapt(blockData));
if (nbt != null) {
block.queue.setTile(block.x, block.y, block.z, nbt);
}
return result;
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
public CompoundTag getNbtData() {
return nbt;
}
public void setNbtData(CompoundTag nbt) {
this.nbt = nbt;
}
@Override
public byte getRawData() {
return (byte) (combinedId >> BlockTypes.BIT_OFFSET);
}
@Override
public void setRawData(byte data) {
this.combinedId = (combinedId & BlockTypes.BIT_MASK) + data;
this.blockData = BukkitAdapter.getBlockData(this.combinedId);
}
@Override
public boolean isPlaced() {
return true;
}
@Override
public void setMetadata(String key, MetadataValue value) {
block.setMetadata(key, value);
}
@Override
public List<MetadataValue> getMetadata(String key) {
return block.getMetadata(key);
}
@Override
public boolean hasMetadata(String key) {
return block.hasMetadata(key);
}
@Override
public void removeMetadata(String key, Plugin plugin) {
block.removeMetadata(key, plugin);
}
}

View File

@ -0,0 +1,172 @@
package com.boydti.fawe.bukkit.wrapper;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
public class AsyncChunk implements Chunk {
private final AsyncWorld world;
private final int z;
private final int x;
private final FaweQueue queue;
public AsyncChunk(World world, FaweQueue queue, int x, int z) {
this.world = world instanceof AsyncWorld ? (AsyncWorld) world : new AsyncWorld(world, true);
this.queue = queue;
this.x = x;
this.z = z;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Chunk)) {
return false;
}
Chunk other = (Chunk) obj;
return other.getX() == x && other.getZ() == z && world.equals(other.getWorld());
}
@Override
public int hashCode() {
return MathMan.pair((short) x, (short) z);
}
@Override
public int getX() {
return x;
}
@Override
public int getZ() {
return z;
}
@Override
public World getWorld() {
return world;
}
@Override
public Block getBlock(int x, int y, int z) {
return new AsyncBlock(world, queue, (this.x << 4) + x, y, (this.z << 4) + z);
}
@Override
public ChunkSnapshot getChunkSnapshot() {
return getChunkSnapshot(false, true, false);
}
@Override
public ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain) {
if (Thread.currentThread() == Fawe.get().getMainThread()) {
return world.getChunkAt(x, z).getChunkSnapshot(includeMaxblocky, includeBiome, includeBiomeTempRain);
}
return whenLoaded(new RunnableVal<ChunkSnapshot>() {
@Override
public void run(ChunkSnapshot value) {
this.value = world.getChunkAt(x, z).getChunkSnapshot(includeBiome, includeBiome, includeBiomeTempRain);
}
});
}
private <T> T whenLoaded(RunnableVal<T> task) {
if (Thread.currentThread() == Fawe.get().getMainThread()) {
task.run();
return task.value;
}
if (queue instanceof BukkitQueue_0) {
BukkitQueue_0 bq = (BukkitQueue_0) queue;
if (world.isChunkLoaded(x, z)) {
long pair = MathMan.pairInt(x, z);
Long originalKeep = bq.keepLoaded.get(pair);
bq.keepLoaded.put(pair, Long.MAX_VALUE);
if (world.isChunkLoaded(x, z)) {
task.run();
if (originalKeep != null) {
bq.keepLoaded.put(pair, originalKeep);
} else {
bq.keepLoaded.remove(pair);
}
return task.value;
}
}
}
return TaskManager.IMP.sync(task);
}
@Override
public Entity[] getEntities() {
if (!isLoaded()) {
return new Entity[0];
}
return whenLoaded(new RunnableVal<Entity[]>() {
@Override
public void run(Entity[] value) {
world.getChunkAt(x, z).getEntities();
}
});
}
@Override
public BlockState[] getTileEntities() {
if (!isLoaded()) {
return new BlockState[0];
}
return TaskManager.IMP.sync(new RunnableVal<BlockState[]>() {
@Override
public void run(BlockState[] value) {
this.value = world.getChunkAt(x, z).getTileEntities();
}
});
}
@Override
public boolean isLoaded() {
return world.isChunkLoaded(x, z);
}
@Override
public boolean load(final boolean generate) {
return TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
this.value = world.loadChunk(x, z, generate);
}
});
}
@Override
public boolean load() {
return load(false);
}
@Override
public boolean unload(boolean save, boolean safe) {
return world.unloadChunk(x, z, save, safe);
}
@Override
public boolean unload(boolean save) {
return unload(true, false);
}
@Override
public boolean unload() {
return unload(true);
}
@Override
public boolean isSlimeChunk() {
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
package com.boydti.fawe.bukkit.wrapper.state;
import com.boydti.fawe.bukkit.chat.FancyMessage;
import com.boydti.fawe.bukkit.wrapper.AsyncBlock;
import com.boydti.fawe.bukkit.wrapper.AsyncBlockState;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import java.util.Map;
import org.bukkit.block.Sign;
public class AsyncSign extends AsyncBlockState implements Sign {
public AsyncSign(AsyncBlock block, int combined) {
super(block, combined);
}
@Override
public String[] getLines() {
CompoundTag nbt = getNbtData();
String[] data = new String[4];
if (nbt != null) {
for (int i = 1; i <= 4; i++) {
data[i - 1] = fromJson(nbt.getString("Text" + i));
}
}
return data;
}
private String fromJson(String jsonInput) {
if (jsonInput == null || jsonInput.isEmpty()) return "";
return FancyMessage.deserialize(jsonInput).toOldMessageFormat();
}
private String toJson(String oldInput) {
if (oldInput == null || oldInput.isEmpty()) return "";
return new FancyMessage("").color(oldInput).toJSONString();
}
@Override
public String getLine(int index) throws IndexOutOfBoundsException {
CompoundTag nbt = getNbtData();
return nbt == null ? null : fromJson(nbt.getString("Text" + (index + 1)));
}
@Override
public void setLine(int index, String line) throws IndexOutOfBoundsException {
CompoundTag nbt = getNbtData();
if (nbt != null) {
Map<String, Tag> map = ReflectionUtils.getMap(nbt.getValue());
map.put("Text" + (index + 1), new StringTag(toJson(line)));
}
}
}

View File

@ -35,7 +35,11 @@ import java.util.Set;
public class CommandRegistration {
static {
Bukkit.getServer().getHelpMap().registerHelpTopicFactory(DynamicPluginCommand.class, new DynamicPluginCommandHelpTopic.Factory());
try {
Bukkit.getServer().getHelpMap().registerHelpTopicFactory(DynamicPluginCommand.class, new DynamicPluginCommandHelpTopic.Factory());
} catch (Throwable e) {
e.printStackTrace();
}
}
protected final Plugin plugin;

View File

@ -34,9 +34,8 @@ public class FallbackRegistrationListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
if (commandRegistration.dispatch(event.getPlayer(), event.getMessage())) {
if (commandRegistration.dispatch(event.getPlayer(), event.getMessage().substring(1))) {
event.setCancelled(true);
}
}
}
}

View File

@ -19,16 +19,16 @@
package com.sk89q.wepif;
import com.sk89q.util.yaml.YAMLNode;
import com.sk89q.util.yaml.YAMLProcessor;
import org.bukkit.OfflinePlayer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sk89q.util.yaml.YAMLNode;
import com.sk89q.util.yaml.YAMLProcessor;
import org.bukkit.OfflinePlayer;
public class ConfigurationPermissionsResolver implements PermissionsResolver {
private YAMLProcessor config;
private Map<String, Set<String>> userPermissionsCache;

View File

@ -19,7 +19,6 @@
package com.sk89q.wepif;
import com.nijikokun.bukkit.Permissions.Permissions;
import com.sk89q.util.yaml.YAMLProcessor;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@ -28,6 +27,7 @@ import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.nijikokun.bukkit.Permissions.Permissions;
import java.util.logging.Level;
import java.util.logging.Logger;

View File

@ -21,6 +21,7 @@ package com.sk89q.wepif;
import com.sk89q.util.yaml.YAMLFormat;
import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.event.EventHandler;
@ -102,7 +103,6 @@ public class PermissionsResolverManager implements PermissionsResolver {
protected PermissionsResolverManager(Plugin plugin) {
this.server = plugin.getServer();
(new ServerListener()).register(plugin); // Register the events
loadConfig(new File("wepif.yml"));
findResolver();
}

View File

@ -21,13 +21,12 @@ package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Function;
import com.bekvon.bukkit.residence.commands.material;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
@ -40,24 +39,19 @@ import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* Adapts between Bukkit and WorldEdit equivalent objects.
*/
public class BukkitAdapter {
private BukkitAdapter() {
}
private static final ParserContext TO_BLOCK_CONTEXT = new ParserContext();
static {
@ -107,26 +101,6 @@ public class BukkitAdapter {
return new BukkitWorld(world);
}
/**
* Create a WorldEdit Player from a Bukkit Player.
*
* @param player The Bukkit player
* @return The WorldEdit player
*/
public static BukkitPlayer adapt(Player player) {
return WorldEditPlugin.getInstance().wrapPlayer(player);
}
/**
* Create a Bukkit Player from a WorldEdit Player.
*
* @param player The WorldEdit player
* @return The Bukkit player
*/
public static Player adapt(com.sk89q.worldedit.entity.Player player) {
return ((BukkitPlayer) player).getPlayer();
}
/**
* Create a Bukkit world from a WorldEdit world.
*
@ -247,6 +221,8 @@ public class BukkitAdapter {
return Material.getMaterial(itemType.getId().replace("minecraft:", "").toUpperCase());
}
private static boolean test;
/**
* Create a Bukkit Material form a WorldEdit BlockType
*
@ -258,7 +234,8 @@ public class BukkitAdapter {
if (!blockType.getId().startsWith("minecraft:")) {
throw new IllegalArgumentException("Bukkit only supports Minecraft blocks");
}
return Material.getMaterial(blockType.getId().replace("minecraft:", "").toUpperCase());
String id = blockType.getId().substring(10).toUpperCase();
return Material.getMaterial(id);
}
/**
@ -281,11 +258,18 @@ public class BukkitAdapter {
public static BlockType asBlockType(Material material) {
checkNotNull(material);
if (!material.isBlock()) {
throw new IllegalArgumentException(material.getKey().toString() + " is not a block!");
throw new IllegalArgumentException(material.getKey().toString() + " is not a block!") {
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
};
}
return BlockTypes.get(material.getKey().toString());
}
/**
* Converts a Material to a ItemType
*
@ -293,35 +277,25 @@ public class BukkitAdapter {
* @return The itemtype
*/
public static ItemType asItemType(Material material) {
checkNotNull(material);
if (!material.isItem()) {
throw new IllegalArgumentException(material.getKey().toString() + " is not an item!");
}
return ItemTypes.get(material.getKey().toString());
return CachedBukkitAdapter.asItemType(material);
}
private static Map<String, BlockState> blockStateCache = new HashMap<>();
/**
* Create a WorldEdit BlockState from a Bukkit BlockData
* Create a WorldEdit BlockStateHolder from a Bukkit BlockData
*
* @param blockData The Bukkit BlockData
* @return The WorldEdit BlockState
*/
public static BlockState adapt(BlockData blockData) {
checkNotNull(blockData);
return blockStateCache.computeIfAbsent(blockData.getAsString(), new Function<String, BlockState>() {
@Nullable
@Override
public BlockState apply(@Nullable String input) {
try {
return WorldEdit.getInstance().getBlockFactory().parseFromInput(input, TO_BLOCK_CONTEXT).toImmutableState();
} catch (InputParseException e) {
e.printStackTrace();
return null;
}
}
});
return CachedBukkitAdapter.adapt(blockData);
}
public static BlockData getBlockData(int combinedId) {
return CachedBukkitAdapter.getBlockData(combinedId);
}
public static BlockTypes adapt(Material material) {
return CachedBukkitAdapter.adapt(material);
}
/**
@ -331,17 +305,16 @@ public class BukkitAdapter {
* @return The Bukkit BlockData
*/
public static BlockData adapt(BlockStateHolder block) {
checkNotNull(block);
return Bukkit.createBlockData(block.getAsString());
return CachedBukkitAdapter.adapt(block);
}
/**
* Create a WorldEdit BlockState from a Bukkit ItemStack
* Create a WorldEdit BlockStateHolder from a Bukkit ItemStack
*
* @param itemStack The Bukkit ItemStack
* @return The WorldEdit BlockState
*/
public static BlockState asBlockState(ItemStack itemStack) {
public static BlockStateHolder asBlockState(ItemStack itemStack) {
checkNotNull(itemStack);
if (itemStack.getType().isBlock()) {
return adapt(itemStack.getType().createBlockData());
@ -358,6 +331,8 @@ public class BukkitAdapter {
*/
public static BaseItemStack adapt(ItemStack itemStack) {
checkNotNull(itemStack);
return new BaseItemStack(ItemTypes.get(itemStack.getType().getKey().toString()), itemStack.getAmount());
}
@ -371,4 +346,23 @@ public class BukkitAdapter {
checkNotNull(item);
return new ItemStack(adapt(item.getType()), item.getAmount());
}
/**
* Create a WorldEdit Player from a Bukkit Player.
*
* @param player The Bukkit player
* @return The WorldEdit player
*/
public static BukkitPlayer adapt(Player player) {
return WorldEditPlugin.getInstance().wrapPlayer(player);
}
/**
* Create a Bukkit Player from a WorldEdit Player.
*
* @param player The WorldEdit player
* @return The Bukkit player
*/
public static Player adapt(com.sk89q.worldedit.entity.Player player) {
return ((BukkitPlayer) player).getPlayer();
}
}

View File

@ -25,12 +25,11 @@ import com.sk89q.worldedit.world.biome.BiomeData;
import com.sk89q.worldedit.world.registry.BiomeRegistry;
import org.bukkit.block.Biome;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
/**
* A biome registry for Bukkit.
*/

View File

@ -19,13 +19,21 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import com.bekvon.bukkit.residence.commands.material;
import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.command.tool.BlockDataCyler;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.registry.BundledBlockRegistry;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import com.sk89q.worldedit.world.registry.PassthroughBlockMaterial;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
@ -38,7 +46,11 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
@Nullable
@Override
public BlockMaterial getMaterial(BlockType blockType) {
return materialMap.computeIfAbsent(BukkitAdapter.adapt(blockType),
Material type = BukkitAdapter.adapt(blockType);
if (type == null) {
type = Material.AIR;
}
return materialMap.computeIfAbsent(type,
material -> new BukkitBlockMaterial(BukkitBlockRegistry.super.getMaterial(blockType), material));
}
@ -75,4 +87,16 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
return material.isTransparent();
}
}
@Override
public Collection<String> registerBlocks() {
ArrayList<String> blocks = new ArrayList<>();
for (Material m : Material.values()) {
if (!m.isLegacy() && m.isBlock()) {
BlockData blockData = m.createBlockData();
blocks.add(blockData.getAsString());
}
}
return blocks;
}
}

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.bukkit.util.CommandInspector;
import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.worldedit.extension.platform.Actor;
@ -32,6 +30,8 @@ import org.bukkit.command.CommandSender;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
class BukkitCommandInspector implements CommandInspector {
private static final Logger logger = Logger.getLogger(BukkitCommandInspector.class.getCanonicalName());

View File

@ -19,22 +19,24 @@
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.entity.metadata.Metadatable;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.cui.CUIEvent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.UUID;
import javax.annotation.Nullable;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitCommandSender implements Actor {
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class BukkitCommandSender implements Actor, Metadatable {
/**
* One time generated ID.
@ -43,6 +45,7 @@ public class BukkitCommandSender implements Actor {
private CommandSender sender;
private WorldEditPlugin plugin;
private ConcurrentHashMap<String, Object> meta;
public BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) {
checkNotNull(plugin);
@ -53,6 +56,12 @@ public class BukkitCommandSender implements Actor {
this.sender = sender;
}
@Override
public synchronized Map<String, Object> getMetaMap() {
if (meta == null) meta = new ConcurrentHashMap<>();
return meta;
}
@Override
public UUID getUniqueId() {
return DEFAULT_ID;

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
@ -30,9 +28,10 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.NullWorld;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An adapter to adapt a Bukkit entity into a WorldEdit one.

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.entity.metadata.EntityProperties;
import com.sk89q.worldedit.util.Enums;
import org.bukkit.entity.Ambient;
@ -44,6 +42,8 @@ import org.bukkit.entity.Tameable;
import org.bukkit.entity.Villager;
import org.bukkit.entity.minecart.ExplosiveMinecart;
import static com.google.common.base.Preconditions.checkNotNull;
class BukkitEntityProperties implements EntityProperties {
private static final org.bukkit.entity.EntityType armorStandType =

View File

@ -0,0 +1,18 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.world.registry.BundledItemRegistry;
import org.bukkit.Material;
import java.util.ArrayList;
import java.util.Collection;
public class BukkitItemRegistry extends BundledItemRegistry {
@Override
public Collection<String> registerItems() {
ArrayList<String> items = new ArrayList<>();
for (Material m : Material.values()) {
if (!m.isLegacy() && m.isItem()) items.add(m.getKey().getNamespace() + ":" + m.getKey().getKey());
}
return items;
}
}

View File

@ -0,0 +1,60 @@
package com.sk89q.worldedit.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.util.ItemUtil;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.world.item.ItemType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import javax.annotation.Nullable;
public class BukkitItemStack extends BaseItemStack {
private ItemStack stack;
private boolean loadedNBT;
public BukkitItemStack(ItemStack stack) {
super(BukkitAdapter.asItemType(stack.getType()));
this.stack = stack;
}
public BukkitItemStack(ItemType type, ItemStack stack) {
super(type);
this.stack = stack;
}
@Override
public int getAmount() {
return stack.getAmount();
}
@Nullable
@Override
public Object getNativeItem() {
return super.getNativeItem();
}
@Nullable
@Override
public CompoundTag getNbtData() {
if (!loadedNBT) {
loadedNBT = true;
ItemUtil util = Fawe.<FaweBukkit>imp().getItemUtil();
if (util != null) {
super.setNbtData(util.getNBT(stack));
}
}
return super.getNbtData();
}
@Override
public void setNbtData(@Nullable CompoundTag nbtData) {
ItemUtil util = Fawe.<FaweBukkit>imp().getItemUtil();
if (util != null) {
stack = util.setNBT(stack, nbtData);
}
super.setNbtData(nbtData);
}
}

View File

@ -22,7 +22,8 @@ package com.sk89q.worldedit.bukkit;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extension.platform.AbstractPlayerActor;
@ -70,7 +71,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
ItemStack itemStack = handSide == HandSide.MAIN_HAND
? player.getInventory().getItemInMainHand()
: player.getInventory().getItemInOffHand();
return BukkitAdapter.asBlockState(itemStack).toBaseBlock();
return new BaseBlock(BukkitAdapter.asBlockState(itemStack));
}
@Override
@ -156,7 +157,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
if (params.length > 0) {
send = send + "|" + StringUtil.joinString(params, "|");
}
player.sendPluginMessage(plugin, WorldEditPlugin.CUI_PLUGIN_CHANNEL, send.getBytes(CUIChannelListener.UTF_8_CHARSET));
player.sendPluginMessage(plugin, WorldEditPlugin.getCuiPluginChannel(), send.getBytes(CUIChannelListener.UTF_8_CHARSET));
}
public Player getPlayer() {

View File

@ -19,18 +19,17 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.extent.inventory.BlockBagException;
import com.sk89q.worldedit.extent.inventory.OutOfBlocksException;
import com.sk89q.worldedit.extent.inventory.OutOfSpaceException;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.extent.inventory.*;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.util.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class BukkitPlayerBlockBag extends BlockBag {
public class BukkitPlayerBlockBag extends BlockBag implements SlottableBlockBag {
private Player player;
private ItemStack[] items;
@ -181,4 +180,16 @@ public class BukkitPlayerBlockBag extends BlockBag {
public void addSingleSourcePosition(Location pos) {
}
@Override
public BaseItem getItem(int slot) {
loadInventory();
return BukkitAdapter.adapt(items[slot]);
}
@Override
public void setItem(int slot, BaseItem block) {
loadInventory();
BaseItemStack stack = block instanceof BaseItemStack ? (BaseItemStack) block : new BaseItemStack(block.getType(), block.getNbtData(), 1);
items[slot] = BukkitAdapter.adapt(stack);
}
}

View File

@ -22,6 +22,7 @@ package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.world.registry.BiomeRegistry;
import com.sk89q.worldedit.world.registry.BlockRegistry;
import com.sk89q.worldedit.world.registry.BundledRegistries;
import com.sk89q.worldedit.world.registry.ItemRegistry;
/**
* World data for the Bukkit platform.
@ -30,6 +31,7 @@ class BukkitRegistries extends BundledRegistries {
private static final BukkitRegistries INSTANCE = new BukkitRegistries();
private final BlockRegistry blockRegistry = new BukkitBlockRegistry();
private final ItemRegistry itemRegistry = new BukkitItemRegistry();
private final BiomeRegistry biomeRegistry = new BukkitBiomeRegistry();
/**
@ -48,6 +50,11 @@ class BukkitRegistries extends BundledRegistries {
return biomeRegistry;
}
@Override
public ItemRegistry getItemRegistry() {
return itemRegistry;
}
/**
* Get a static instance.
*

View File

@ -100,7 +100,7 @@ public class BukkitServerInterface implements MultiUserPlatform {
return player;
} else {
org.bukkit.entity.Player bukkitPlayer = server.getPlayerExact(player.getName());
return bukkitPlayer != null ? new BukkitPlayer(plugin, bukkitPlayer) : null;
return bukkitPlayer != null ? plugin.wrapPlayer(bukkitPlayer) : null;
}
}
@ -177,7 +177,7 @@ public class BukkitServerInterface implements MultiUserPlatform {
public Collection<Actor> getConnectedUsers() {
List<Actor> users = new ArrayList<>();
for (org.bukkit.entity.Player player : Bukkit.getServer().getOnlinePlayers()) {
users.add(new BukkitPlayer(plugin, player));
users.add(plugin.wrapPlayer(player));
}
return users;
}

View File

@ -19,16 +19,12 @@
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.EditSession;
import com.boydti.fawe.Fawe;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.LazyBlock;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.history.change.BlockChange;
@ -47,19 +43,17 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class BukkitWorld extends AbstractWorld {
@ -91,7 +85,7 @@ public class BukkitWorld extends AbstractWorld {
List<com.sk89q.worldedit.entity.Entity> entities = new ArrayList<>();
for (Entity ent : ents) {
if (region.contains(BukkitAdapter.asVector(ent.getLocation()))) {
entities.add(BukkitAdapter.adapt(ent));
addEntities(ent, entities);
}
}
return entities;
@ -101,11 +95,43 @@ public class BukkitWorld extends AbstractWorld {
public List<com.sk89q.worldedit.entity.Entity> getEntities() {
List<com.sk89q.worldedit.entity.Entity> list = new ArrayList<>();
for (Entity entity : getWorld().getEntities()) {
list.add(BukkitAdapter.adapt(entity));
addEntities(entity, list);
}
return list;
}
private static com.sk89q.worldedit.entity.Entity adapt(Entity ent) {
if (ent == null) return null;
return BukkitAdapter.adapt(ent);
}
private void addEntities(Entity ent, Collection<com.sk89q.worldedit.entity.Entity> ents) {
ents.add(BukkitAdapter.adapt(ent));
if (ent instanceof Player) {
final Player plr = (Player) ent;
com.sk89q.worldedit.entity.Entity left = adapt(((Player) ent).getShoulderEntityLeft());
com.sk89q.worldedit.entity.Entity right = adapt(((Player) ent).getShoulderEntityRight());
if (left != null) {
ents.add(new DelegateEntity(left) {
@Override
public boolean remove() {
plr.setShoulderEntityLeft(null);
return true;
}
});
}
if (right != null) {
ents.add(new DelegateEntity(right) {
@Override
public boolean remove() {
plr.setShoulderEntityRight(null);
return true;
}
});
}
}
}
@Nullable
@Override
public com.sk89q.worldedit.entity.Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) {
@ -165,7 +191,7 @@ public class BukkitWorld extends AbstractWorld {
@Override
public boolean regenerate(Region region, EditSession editSession) {
BlockStateHolder[] history = new BlockStateHolder[16 * 16 * (getMaxY() + 1)];
com.sk89q.worldedit.world.block.BlockStateHolder[] history = new com.sk89q.worldedit.world.block.BlockState[16 * 16 * (getMaxY() + 1)];
for (Vector2D chunk : region.getChunks()) {
Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);
@ -415,15 +441,15 @@ public class BukkitWorld extends AbstractWorld {
}
@Override
public boolean setBlock(Vector position, BlockStateHolder block, boolean notifyAndLight) throws WorldEditException {
public boolean setBlock(Vector position, BlockStateHolder block, boolean notifyAndLight) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
try {
return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight);
} catch (Exception e) {
if (block instanceof BaseBlock && ((BaseBlock) block).getNbtData() != null) {
if (block.getNbtData() != null) {
logger.warning("Tried to set a corrupt tile entity at " + position.toString());
logger.warning(((BaseBlock) block).getNbtData().toString());
logger.warning(block.getNbtData().toString());
}
e.printStackTrace();
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
@ -432,18 +458,23 @@ public class BukkitWorld extends AbstractWorld {
}
} else {
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
bukkitBlock.setBlockData(BukkitAdapter.adapt(block), notifyAndLight);
bukkitBlock.setBlockData(BukkitAdapter.adapt(block), false);
return true;
}
}
@Override
public BaseBlock getFullBlock(Vector position) {
public com.sk89q.worldedit.world.block.BlockState getLazyBlock(Vector position) {
return getBlock(position);
}
@Override
public com.sk89q.worldedit.world.block.BlockState getFullBlock(Vector position) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
return adapter.getBlock(BukkitAdapter.adapt(getWorld(), position));
} else {
return getBlock(position).toBaseBlock();
return getBlock(position);
}
}

View File

@ -0,0 +1,105 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
public class CachedBukkitAdapter {
private static final BlockData[][] blockDataCache = new BlockData[BlockTypes.size()][];
private static final ItemTypes[] itemTypes;
private static final BlockTypes[] blockTypes;
static {
Material[] materials = Material.values();
itemTypes = new ItemTypes[materials.length];
blockTypes = new BlockTypes[materials.length];
for (int i = 0; i < materials.length; i++) {
Material material = materials[i];
if (material.isBlock() && !material.isLegacy()) {
NamespacedKey key = material.getKey();
blockTypes[i] = BlockTypes.get(key.getNamespace() + ":" + key.getKey());
} else if (material.isItem() && !material.isLegacy()) {
itemTypes[i] = ItemTypes.get(material.getKey().toString());
}
}
}
/**
* Converts a Material to a ItemType
*
* @param material The material
* @return The itemtype
*/
public static ItemType asItemType(Material material) {
return itemTypes[material.ordinal()];
}
/**
* Create a WorldEdit BlockStateHolder from a Bukkit BlockData
*
* @param blockData The Bukkit BlockData
* @return The WorldEdit BlockState
*/
public static BlockState adapt(BlockData blockData) {
checkNotNull(blockData);
Material material = blockData.getMaterial();
BlockTypes type = blockTypes[material.ordinal()];
List<? extends Property> propList = type.getProperties();
if (propList.size() == 0) return type.getDefaultState();
String properties = blockData.getAsString();
return BlockState.get(type, properties, type.getDefaultState().getInternalPropertiesId());
}
public static BlockData getBlockData(int combinedId) {
int typeId = combinedId & BlockTypes.BIT_MASK;
BlockData[] dataCache = blockDataCache[typeId];
if (dataCache == null) {
BlockTypes type = BlockTypes.get(typeId);
blockDataCache[typeId] = dataCache = new BlockData[type.getMaxStateId() + 1];
}
int propId = combinedId >> BlockTypes.BIT_OFFSET;
BlockData blockData = dataCache[propId];
if (blockData == null) {
dataCache[propId] = blockData = Bukkit.createBlockData(BlockState.get(combinedId).getAsString());
}
return blockData;
}
public static BlockTypes adapt(Material material) {
return blockTypes[material.ordinal()];
}
/**
* Create a Bukkit BlockData from a WorldEdit BlockStateHolder
*
* @param block The WorldEdit BlockStateHolder
* @return The Bukkit BlockData
*/
public static BlockData adapt(BlockStateHolder block) {
checkNotNull(block);
int typeId = block.getInternalBlockTypeId();
BlockData[] dataCache = blockDataCache[typeId];
if (dataCache == null) {
BlockTypes type = BlockTypes.get(typeId);
blockDataCache[typeId] = dataCache = new BlockData[type.getMaxStateId() + 1];
}
int propId = block.getInternalPropertiesId();
BlockData blockData = dataCache[propId];
if (blockData == null) {
dataCache[propId] = blockData = Bukkit.createBlockData(block.getAsString());
}
return blockData;
}
}

View File

@ -0,0 +1,47 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
import javax.annotation.Nullable;
public class DelegateEntity implements Entity {
private final Entity parent;
public DelegateEntity(Entity parent) {
this.parent = parent;
}
public Entity getParent() {
return parent;
}
@Override
@Nullable
public BaseEntity getState() {
return parent.getState();
}
@Override
public Location getLocation() {
return parent.getLocation();
}
@Override
public Extent getExtent() {
return parent.getExtent();
}
@Override
public boolean remove() {
return parent.remove();
}
@Override
@Nullable
public <T> T getFacet(Class<? extends T> cls) {
return parent.getFacet(cls);
}
}

View File

@ -21,6 +21,11 @@ package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.bekvon.bukkit.residence.commands.message;
import com.bekvon.bukkit.residence.containers.cmd;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.bukkit.FaweBukkit;
import com.boydti.fawe.bukkit.adapter.Spigot_v1_13_R1;
import com.google.common.base.Joiner;
import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.wepif.PermissionsResolverManager;
@ -35,20 +40,30 @@ import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.NoCapablePlatformException;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -62,13 +77,62 @@ import javax.annotation.Nullable;
public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
private static final Logger log = Logger.getLogger(WorldEditPlugin.class.getCanonicalName());
public static final String CUI_PLUGIN_CHANNEL = "worldedit:cui";
private static final String CUI_PLUGIN_CHANNEL = "worldedit:cui";
private static WorldEditPlugin INSTANCE;
private BukkitImplAdapter bukkitAdapter;
private BukkitServerInterface server;
private BukkitConfiguration config;
private static Map<String, Plugin> lookupNames;
static {
{ // Disable AWE as otherwise both fail to load
PluginManager manager = Bukkit.getPluginManager();
try {
Field pluginsField = manager.getClass().getDeclaredField("plugins");
Field lookupNamesField = manager.getClass().getDeclaredField("lookupNames");
pluginsField.setAccessible(true);
lookupNamesField.setAccessible(true);
List<Plugin> plugins = (List<Plugin>) pluginsField.get(manager);
lookupNames = (Map<String, Plugin>) lookupNamesField.get(manager);
pluginsField.set(manager, plugins = new ArrayList<Plugin>(plugins) {
@Override
public boolean add(Plugin plugin) {
if (plugin.getName().startsWith("AsyncWorldEdit")) {
Fawe.debug("Disabling `" + plugin.getName() + "` as it is incompatible");
} else if (plugin.getName().startsWith("BetterShutdown")) {
Fawe.debug("Disabling `" + plugin.getName() + "` as it is incompatible (Improperly shaded classes from com.sk89q.minecraft.util.commands)");
} else {
return super.add(plugin);
}
return false;
}
});
lookupNamesField.set(manager, lookupNames = new ConcurrentHashMap<String, Plugin>(lookupNames) {
@Override
public Plugin put(String key, Plugin plugin) {
if (plugin.getName().startsWith("AsyncWorldEdit") || plugin.getName().startsWith("BetterShutdown")) {
return null;
}
return super.put(key, plugin);
}
});
} catch (Throwable ignore) {}
}
}
public WorldEditPlugin() {
if (lookupNames != null) lookupNames.putIfAbsent("WorldEdit".toLowerCase(Locale.ENGLISH), this);
}
public WorldEditPlugin(JavaPluginLoader loader, PluginDescriptionFile desc, File dataFolder, File jarFile) {
if (lookupNames != null) lookupNames.putIfAbsent("WorldEdit".toLowerCase(Locale.ENGLISH), this);
}
public static String getCuiPluginChannel() {
return CUI_PLUGIN_CHANNEL;
}
/**
* Called on plugin enable.
*/
@ -76,6 +140,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
@Override
public void onEnable() {
this.INSTANCE = this;
FaweBukkit imp = new FaweBukkit(this);
//noinspection ResultOfMethodCallIgnored
getDataFolder().mkdirs();
@ -90,11 +155,14 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
worldEdit.loadMappings();
loadConfig(); // Load configuration
PermissionsResolverManager.initialize(this); // Setup permission resolver
fail(() -> PermissionsResolverManager.initialize(INSTANCE), "Failed to initialize permissions resolver");
// Register CUI
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL);
fail(() -> {
getServer().getMessenger().registerIncomingPluginChannel(INSTANCE, CUI_PLUGIN_CHANNEL, new CUIChannelListener(INSTANCE));
getServer().getMessenger().registerOutgoingPluginChannel(INSTANCE, CUI_PLUGIN_CHANNEL);
}, "Failed to register CUI");
// Now we can register events
getServer().getPluginManager().registerEvents(new WorldEditListener(this), this);
@ -103,13 +171,35 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
// Forge WorldEdit and there's (probably) not going to be any other
// platforms to be worried about... at the current time of writing
WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent());
{ // Register 1.13 Material ids with LegacyMapper
LegacyMapper legacyMapper = LegacyMapper.getInstance();
for (Material m : Material.values()) {
if (!m.isLegacy() && m.isBlock()) {
legacyMapper.register(m.getId(), 0, BukkitAdapter.adapt(m).getDefaultState());
}
}
}
}
private void fail(Runnable run, String message) {
try {
run.run();
} catch (Throwable e) {
log.log(Level.SEVERE, message);
e.printStackTrace();
}
}
private void loadConfig() {
createDefaultConfiguration("config.yml"); // Create the default configuration file
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), this);
config.load();
createDefaultConfiguration("config-legacy.yml"); // Create the default configuration file
try {
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config-legacy.yml"), true), this);
config.load();
} catch (Throwable e) {
log.log(Level.SEVERE, "Failed to load config.yml");
e.printStackTrace();
}
}
private void loadAdapter() {
@ -117,6 +207,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
// Attempt to load a Bukkit adapter
BukkitImplLoader adapterLoader = new BukkitImplLoader();
adapterLoader.addClass(Spigot_v1_13_R1.class);
try {
adapterLoader.addFromPath(getClass().getClassLoader());
@ -133,14 +224,18 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
bukkitAdapter = adapterLoader.loadAdapter();
log.log(Level.INFO, "Using " + bukkitAdapter.getClass().getCanonicalName() + " as the Bukkit adapter");
} catch (AdapterLoadException e) {
Platform platform = worldEdit.getPlatformManager().queryCapability(Capability.WORLD_EDITING);
if (platform instanceof BukkitServerInterface) {
log.log(Level.WARNING, e.getMessage());
} else {
log.log(Level.INFO, "WorldEdit could not find a Bukkit adapter for this MC version, " +
"but it seems that you have another implementation of WorldEdit installed (" + platform.getPlatformName() + ") " +
"that handles the world editing.");
}
try {
Platform platform = worldEdit.getPlatformManager().queryCapability(Capability.WORLD_EDITING);
if (platform instanceof BukkitServerInterface) {
log.log(Level.WARNING, e.getMessage());
return;
} else {
log.log(Level.INFO, "WorldEdit could not find a Bukkit adapter for this MC version, " +
"but it seems that you have another implementation of WorldEdit installed (" + platform.getPlatformName() + ") " +
"that handles the world editing.");
}
} catch (NoCapablePlatformException ignore) {}
log.log(Level.INFO, "WorldEdit could not find a Bukkit adapter for this MC version");
}
}
@ -149,6 +244,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/
@Override
public void onDisable() {
Fawe.get().onDisable();
WorldEdit worldEdit = WorldEdit.getInstance();
worldEdit.getSessionManager().clear();
worldEdit.getPlatformManager().unregister(server);
@ -342,7 +438,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
* @return an instance of the plugin
* @throws NullPointerException if the plugin hasn't been enabled
*/
static WorldEditPlugin getInstance() {
public static WorldEditPlugin getInstance() {
return checkNotNull(INSTANCE);
}
@ -352,7 +448,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
* @return the adapter
*/
@Nullable
BukkitImplAdapter getBukkitImplAdapter() {
public BukkitImplAdapter getBukkitImplAdapter() {
return bukkitAdapter;
}

View File

@ -19,11 +19,15 @@
package com.sk89q.worldedit.bukkit.adapter;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.world.block.BlockType;
import net.minecraft.server.v1_13_R1.NBTBase;
import org.bukkit.Location;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
@ -35,7 +39,7 @@ import javax.annotation.Nullable;
/**
* An interface for adapters of various Bukkit implementations.
*/
public interface BukkitImplAdapter {
public interface BukkitImplAdapter<T> {
/**
* Get the biome ID for the given biome.
@ -63,7 +67,7 @@ public interface BukkitImplAdapter {
* @param location the location
* @return the block
*/
BaseBlock getBlock(Location location);
BlockState getBlock(Location location);
/**
* Set the block at the given location.
@ -101,4 +105,12 @@ public interface BukkitImplAdapter {
* @return The properties map
*/
Map<String, ? extends Property> getProperties(BlockType blockType);
default Tag toNative(T foreign) {
return null;
}
default T fromNative(Tag foreign) {
return null;
}
}

View File

@ -77,6 +77,10 @@ public class BukkitImplLoader {
}
}
public void addClass(Class cls) {
adapterCandidates.add(0, cls.getName());
}
/**
* Search the given JAR for candidate implementations.
*

View File

@ -0,0 +1,85 @@
#
# WorldEdit's Configuration File
#
# About editing this file:
# - DO NOT USE TABS. You MUST use spaces or Bukkit will complain and post
# errors. If you use an editor, like Notepad++ (recommended for Windows
# users), you must configure it to "replace tabs with spaces."
# This can be changed in Settings > Preferences > Language Menu.
# - Don't get rid of indentations. They are indented so some entries that are
# in categories, like "max-blocks-changed", are placed in the "limits"
# category.
# - If you want to check the format of this file before putting it
# into WorldEdit, paste it into http://yaml-online-parser.appspot.com/
# and see if it gives you "ERROR:".
# - Lines starting with # are comments, so they are ignored.
# - If you want to allow blocks, make sure to change "disallowed-blocks" to []
#
limits:
allow-extra-data-values: false
max-blocks-changed:
default: -1
maximum: -1
max-polygonal-points:
default: -1
maximum: 20
max-radius: -1
max-super-pickaxe-size: 5
max-brush-radius: 100
butcher-radius:
default: -1
maximum: -1
disallowed-blocks: []
use-inventory:
enable: false
allow-override: true
creative-mode-overrides: false
logging:
log-commands: false
file: worldedit.log
# The format of custom log message. This is java general format string (java.util.Formatter). Arguments are:
# 1$ : date - a Date object representing event time of the log record.
# 2$ : source - a string representing the caller, if available; otherwise, the logger's name.
# 3$ : logger - the logger's name.
# 4$ : level - the log level.
# 5$ : message - the formatted log message returned from the Formatter.formatMessage(LogRecord) method. It uses java.text formatting and does not use the java.util.Formatter format argument.
# 6$ : thrown - a string representing the throwable associated with the log record and its backtrace beginning with a newline character, if any; otherwise, an empty string.
# For details see:
# https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html
# https://docs.oracle.com/javase/8/docs/api/java/util/logging/SimpleFormatter.html#format-java.util.logging.LogRecord-
format: "[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s]: %5$s%6$s%n"
super-pickaxe:
drop-items: true
many-drop-items: false
snapshots:
directory:
navigation-wand:
item: minecraft:compass
max-distance: 100
scripting:
timeout: 3000
dir: craftscripts
saving:
dir: schematics
files:
allow-symbolic-links: false
history:
size: 15
expiration: 10
wand-item: minecraft:wooden_axe
shell-save-type:
no-double-slash: false
no-op-permissions: false
debug: false
show-help-on-first-use: true

View File

@ -1,8 +1,164 @@
name: WorldEdit
name: FastAsyncWorldEdit
main: com.sk89q.worldedit.bukkit.WorldEditPlugin
version: "${internalVersion}"
softdepend: [Spout] #hack to fix trove errors
api-version: 1.13
# Permissions aren't here. Read http://wiki.sk89q.com/wiki/WEPIF/DinnerPerms
# for how WorldEdit permissions actually work.
description: Fast Async WorldEdit plugin
authors: [Empire92]
loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector,WorldGuard]
load: STARTUP
database: false
#softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones]
commands:
fcancel:
description: (FAWE) Cancel your edit
aliases: [fawecancel,/fcancel,/cancel,/fawecancel]
permissions:
fawe.plotsquared:
default: true
children:
fawe.plotsquared.trusted: true
fawe.plotme:
default: true
fawe.bypass.regions:
default: false
fawe.bypass:
default: false
children:
fawe.bypass.regions: true
fawe.limit.*: true
fawe.tips:
default: false
fawe.admin:
default: op
fawe.reload:
default: false
fawe.permpack.basic:
default: op
children:
fawe.worldeditregion: true
fawe.cancel: true
worldedit.biome.info: true
worldedit.biome.set: true
worldedit.biome.list: true
worldedit.chunkinfo: true
worldedit.listchunks: true
worldedit.clipboard.cut: true
worldedit.clipboard.paste: true
worldedit.schematic.formats: true
worldedit.schematic.load: true
worldedit.schematic.list: true
worldedit.schematic.save: true
worldedit.clipboard.clear: true
worldedit.clipboard.copy: true
worldedit.clipboard.lazycopy: true
worldedit.clipboard.place: true
worldedit.clipboard.download: true
worldedit.clipboard.flip: true
worldedit.clipboard.rotate: true
worldedit.help: true
worldedit.global-mask: true
worldedit.global-transform: true
worldedit.generation.cylinder: true
worldedit.generation.sphere: true
worldedit.generation.forest: true
worldedit.generation.pumpkins: true
worldedit.generation.pyramid: true
worldedit.generation.shape: true
worldedit.biome.set: true
worldedit.history.undo: true
worldedit.history.redo: true
worldedit.history.rollback: true
worldedit.navigation.unstuck: true
worldedit.navigation.ascend: true
worldedit.navigation.descend: true
worldedit.navigation.ceiling: true
worldedit.navigation.thru.command: true
worldedit.navigation.jumpto.command: true
worldedit.navigation.up: true
worldedit.region.hollow: true
worldedit.region.line: true
worldedit.region.curve: true
worldedit.region.overlay: true
worldedit.region.center: true
worldedit.region.naturalize: true
worldedit.region.walls: true
worldedit.region.faces: true
worldedit.region.smooth: true
worldedit.region.move: true
worldedit.region.forest: true
worldedit.region.replace: true
worldedit.region.stack: true
worldedit.region.set: true
worldedit.selection.pos: true
worldedit.selection.chunk: true
worldedit.selection.hpos: true
worldedit.wand: true
worldedit.wand.toggle: true
worldedit.selection.contract: true
worldedit.selection.outset: true
worldedit.selection.inset: true
worldedit.analysis.distr: true
worldedit.analysis.count: true
worldedit.selection.size: true
worldedit.selection.expand: true
worldedit.selection.shift: true
worldedit.snapshots.list: true
worldedit.superpickaxe: true
worldedit.superpickaxe.area: true
worldedit.superpickaxe.recursive: true
worldedit.brush.blendball: true
worldedit.brush.erode: true
worldedit.brush.pull: true
worldedit.brush.circle: true
worldedit.brush.recursive: true
worldedit.brush.line: true
worldedit.brush.spline: true
worldedit.brush.surfacespline: true
worldedit.brush.shatter: true
worldedit.brush.stencil: true
worldedit.brush.height: true
worldedit.brush.layer: true
worldedit.brush.populateschematic: true
worldedit.brush.scatter: true
worldedit.brush.splatter: true
worldedit.brush.scattercommand: true
worldedit.brush.copy: true
worldedit.brush.command: true
worldedit.brush.apply: true
worldedit.brush.sphere: true
worldedit.brush.cylinder: true
worldedit.brush.clipboard: true
worldedit.brush.smooth: true
worldedit.brush.ex: true
worldedit.brush.gravity: true
worldedit.brush.options.range: true
worldedit.brush.options.material: true
worldedit.brush.options.size: true
worldedit.brush.options.mask: true
worldedit.brush.options.smask: true
worldedit.brush.options.transform: true
worldedit.brush.options.scroll: true
worldedit.brush.options.visualize: true
worldedit.tool.deltree: true
worldedit.tool.farwand: true
worldedit.tool.lrbuild: true
worldedit.tool.info: true
worldedit.tool.tree: true
worldedit.tool.replacer: true
worldedit.tool.data-cycler: true
worldedit.tool.flood-fill: true
worldedit.tool.inspect: true
worldedit.fill.recursive: true
worldedit.drain: true
worldedit.fixlava: true
worldedit.fixwater: true
worldedit.removeabove: true
worldedit.removebelow: true
worldedit.removenear: true
worldedit.replacenear: true
worldedit.snow: true
worldedit.thaw: true
worldedit.green: true
worldedit.extinguish: true
worldedit.calc: true
worldedit.fill: true

View File

@ -19,15 +19,13 @@
package com.sk89q.wepif;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.bukkit.Server;
import org.bukkit.plugin.PluginManager;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class DinnerPermsResolverTest {
private DinnerPermsResolver resolver;

View File

@ -28,11 +28,7 @@ import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
public class TestOfflinePermissible implements OfflinePlayer, Permissible {
private boolean op;

View File

@ -4,15 +4,21 @@ apply plugin: 'idea'
dependencies {
compile 'de.schlichtherle:truezip:6.8.3'
compile 'rhino:js:1.7R2'
compile 'org.yaml:snakeyaml:1.9'
compile 'com.google.guava:guava:21.0'
compile 'com.sk89q:jchronic:0.2.4a'
compile 'com.google.code.findbugs:jsr305:1.3.9'
compile 'com.thoughtworks.paranamer:paranamer:2.6'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.sk89q.lib:jlibnoise:1.0.0'
//compile 'net.sf.trove4j:trove4j:3.0.3'
testCompile 'org.mockito:mockito-core:1.9.0-rc1'
// Fawe depends
compile 'org.yaml:snakeyaml:1.19'
compile 'net.fabiozumbi12:redprotect:1.9.6'
compile group: "com.plotsquared", name: "plotsquared-api", version: "latest"
// compile 'org.primesoft:BlocksHub:2.0'
compile 'com.github.luben:zstd-jni:1.1.1'
compile 'co.aikar:fastutil-lite:1.0'
}
sourceSets {
@ -27,4 +33,14 @@ sourceSets {
}
}
processResources {
from('src/main/resources') {
include 'fawe.properties'
expand(
version: "${project.parent.version}",
name: project.parent.name,
)
}
}
build.dependsOn(shadowJar)

View File

@ -0,0 +1,93 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.blocks;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.extent.Extent;
/**
* A implementation of a lazy block for {@link Extent#getLazyBlock(Vector)}
* that takes the block's ID and metadata, but will defer loading of NBT
* data until time of access.
*
* <p>NBT data is later loaded using a call to {@link Extent#getBlock(Vector)}
* with a stored {@link Extent} and location.</p>
*
* <p>All mutators on this object will throw an
* {@link UnsupportedOperationException}.</p>
*/
public class LazyBlock extends BaseBlock {
private final Extent extent;
private final Vector position;
private boolean loaded = false;
/**
* Create a new lazy block.
*
* @param type the block type
* @param extent the extent to later load the full block data from
* @param position the position to later load the full block data from
*/
public LazyBlock(BlockType type, Extent extent, Vector position) {
super(type);
checkNotNull(extent);
checkNotNull(position);
this.extent = extent;
this.position = position;
}
/**
* Create a new lazy block.
*
* @param state the block state
* @param extent the extent to later load the full block data from
* @param position the position to later load the full block data from
*/
public LazyBlock(BlockState state, Extent extent, Vector position) {
super(state);
checkNotNull(extent);
checkNotNull(position);
this.extent = extent;
this.position = position;
}
@Override
public CompoundTag getNbtData() {
if (!loaded) {
BlockState loadedBlock = extent.getFullBlock(position);
this.nbtData = loadedBlock.getNbtData();
loaded = true;
}
return super.getNbtData();
}
@Override
public void setNbtData(CompoundTag nbtData) {
throw new UnsupportedOperationException("This object is immutable");
}
}

View File

@ -25,8 +25,8 @@ import com.sk89q.jnbt.NBTUtils;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.storage.InvalidFormatException;
import java.util.HashMap;
@ -35,7 +35,7 @@ import java.util.Map;
/**
* A mob spawner block.
*/
public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock {
public class MobSpawnerBlock extends BaseBlock {
private String mobType;
private short delay;
@ -118,7 +118,7 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<>();
Map<String, Tag> values = new HashMap<String, Tag>();
values.put("EntityId", new StringTag(mobType));
values.put("Delay", new ShortTag(delay));
values.put("SpawnCount", new ShortTag(spawnCount));

View File

@ -22,9 +22,9 @@ package com.sk89q.worldedit.blocks;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.util.gson.GsonUtil;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.util.gson.GsonUtil;
import java.util.HashMap;
import java.util.Map;
@ -93,7 +93,7 @@ public class SignBlock extends BaseBlock implements TileEntityBlock {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<>();
Map<String, Tag> values = new HashMap<String, Tag>();
values.put("Text1", new StringTag(text[0]));
values.put("Text2", new StringTag(text[1]));
values.put("Text3", new StringTag(text[2]));

View File

@ -22,8 +22,8 @@ package com.sk89q.worldedit.blocks;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.HashMap;
import java.util.Map;
@ -114,4 +114,4 @@ public class SkullBlock extends BaseBlock implements TileEntityBlock {
owner = ((StringTag) t).getValue();
}
}
}
}

View File

@ -0,0 +1,585 @@
package com.boydti.fawe;
import com.boydti.fawe.command.Cancel;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Commands;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.visualization.VisualQueue;
import com.boydti.fawe.regions.general.plot.PlotSquaredFeature;
import com.boydti.fawe.util.*;
import com.boydti.fawe.util.chat.ChatManager;
import com.boydti.fawe.util.chat.PlainChatManager;
import com.boydti.fawe.util.cui.CUI;
import com.boydti.fawe.util.metrics.BStats;
import com.sk89q.jnbt.*;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.command.*;
import com.sk89q.worldedit.command.composition.SelectionCommand;
import com.sk89q.worldedit.command.tool.*;
import com.sk89q.worldedit.command.tool.brush.GravityBrush;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
import com.sk89q.worldedit.extension.factory.DefaultTransformParser;
import com.sk89q.worldedit.extension.factory.HashTagPatternParser;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.MaskingExtent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
import com.sk89q.worldedit.extent.inventory.BlockBagExtent;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.CombinedRegionFunction;
import com.sk89q.worldedit.function.block.BlockReplace;
import com.sk89q.worldedit.function.block.ExtentBlockCopy;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.*;
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.*;
import com.sk89q.worldedit.function.visitor.*;
import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.runtime.*;
import com.sk89q.worldedit.math.convolution.HeightMap;
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.CylinderRegion;
import com.sk89q.worldedit.regions.EllipsoidRegion;
import com.sk89q.worldedit.regions.selector.*;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.PasteBuilder;
import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.command.SimpleCommandMapping;
import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.fluent.DispatcherNode;
import com.sk89q.worldedit.util.command.parametric.*;
import com.sk89q.worldedit.util.formatting.Fragment;
import com.sk89q.worldedit.util.formatting.component.CommandListBox;
import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.util.formatting.component.MessageBox;
import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
import javax.management.InstanceAlreadyExistsException;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* [ WorldEdit action]
* |
* \|/
* [ EditSession ] - The change is processed (area restrictions, change limit, block type)
* |
* \|/
* [Block change] - A block change from some location
* |
* \|/
* [ Set Queue ] - The SetQueue manages the implementation specific queue
* |
* \|/
* [ Fawe Queue] - A queue of chunks - check if the queue has the chunk for a change
* |
* \|/
* [ Fawe Chunk Implementation ] - Otherwise create a new FaweChunk object which is a wrapper around the Chunk object
* |
* \|/
* [ Execution ] - When done, the queue then sets the blocks for the chunk, performs lighting updates and sends the chunk packet to the clients
* <p>
* Why it's faster:
* - The chunk is modified directly rather than through the API
* \ Removes some overhead, and means some processing can be done async
* - Lighting updates are performed on the chunk level rather than for every block
* \ e.g. A blob of stone: only the visible blocks need to have the lighting calculated
* - Block changes are sent with a chunk packet
* \ A chunk packet is generally quicker to create and smaller for large world edits
* - No physics updates
* \ Physics updates are slow, and are usually performed on each block
* - Block data shortcuts
* \ Some known blocks don't need to have the data set or accessed (e.g. air is never going to have data)
* - Remove redundant extents
* \ Up to 11 layers of extents can be removed
* - History bypassing
* \ FastMode bypasses history and means blocks in the world don't need to be checked and recorded
*/
public class Fawe {
/**
* The FAWE instance;
*/
private static Fawe INSTANCE;
/**
* TPS timer
*/
private final FaweTimer timer;
private FaweVersion version;
private VisualQueue visualQueue;
private Updater updater;
private TextureUtil textures;
private DefaultTransformParser transformParser;
private ChatManager chatManager = new PlainChatManager();
private BStats stats;
/**
* Get the implementation specific class
*
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends IFawe> T imp() {
return INSTANCE != null ? (T) INSTANCE.IMP : null;
}
/**
* Get the implementation independent class
*
* @return
*/
public static Fawe get() {
return INSTANCE;
}
/**
* Setup Fawe
*
* @param implementation
* @throws InstanceAlreadyExistsException
*/
public static void set(final IFawe implementation) throws InstanceAlreadyExistsException, IllegalArgumentException {
if (INSTANCE != null) {
throw new InstanceAlreadyExistsException("FAWE has already been initialized with: " + INSTANCE.IMP);
}
if (implementation == null) {
throw new IllegalArgumentException("Implementation may not be null.");
}
INSTANCE = new Fawe(implementation);
}
public static void debugPlain(String s) {
if (INSTANCE != null) {
INSTANCE.IMP.debug(s);
} else {
System.out.println(BBC.stripColor(BBC.color(s)));
}
}
/**
* Write something to the console
*
* @param s
*/
public static void debug(Object s) {
debugPlain(BBC.PREFIX.original() + " " + s);
}
/**
* The platform specific implementation
*/
private final IFawe IMP;
private Thread thread = Thread.currentThread();
private Fawe(final IFawe implementation) {
this.INSTANCE = this;
this.IMP = implementation;
this.thread = Thread.currentThread();
/*
* Implementation dependent stuff
*/
this.setupConfigs();
TaskManager.IMP = this.IMP.getTaskManager();
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.IMP.PATHS.HISTORY), TimeUnit.DAYS.toMillis(Settings.IMP.HISTORY.DELETE_AFTER_DAYS), false);
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.IMP.PATHS.CLIPBOARD), TimeUnit.DAYS.toMillis(Settings.IMP.CLIPBOARD.DELETE_AFTER_DAYS), false);
}
});
if (Settings.IMP.METRICS) {
try {
this.stats = new BStats();
this.IMP.startMetrics();
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
stats.start();
}
}, 1);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
this.setupCommands();
/*
* Instance independent stuff
*/
this.setupMemoryListener();
this.timer = new FaweTimer();
Fawe.this.IMP.setupVault();
File jar = MainUtil.getJarFile();
// TODO FIXME remove extrablocks.json
// File extraBlocks = MainUtil.copyFile(jar, "extrablocks.json", null);
// if (extraBlocks != null && extraBlocks.exists()) {
// TaskManager.IMP.task(() -> {
// try {
// BundledBlockData.getInstance().loadFromResource();
// BundledBlockData.getInstance().add(extraBlocks.toURI().toURL(), true);
// } catch (Throwable ignore) {
// ignore.printStackTrace();
// Fawe.debug("Invalid format: extrablocks.json");
// }
// });
// }
// Delayed worldedit setup
TaskManager.IMP.later(() -> {
try {
transformParser = new DefaultTransformParser(getWorldEdit());
visualQueue = new VisualQueue(3);
WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers());
WEManager.IMP.managers.add(new PlotSquaredFeature());
Fawe.debug("Plugin 'PlotSquared' found. Using it now.");
} catch (Throwable e) {}
}, 0);
TaskManager.IMP.repeat(timer, 1);
if (!Settings.IMP.UPDATE.equalsIgnoreCase("false")) {
// Delayed updating
updater = new Updater();
TaskManager.IMP.async(() -> update());
TaskManager.IMP.repeatAsync(() -> update(), 36000);
}
}
public void onDisable() {
if (stats != null) {
stats.close();
}
}
private boolean update() {
if (updater != null) {
updater.getUpdate(IMP.getPlatform(), getVersion());
return true;
}
return false;
}
public CUI getCUI(Actor actor) {
FawePlayer<Object> fp = FawePlayer.wrap(actor);
CUI cui = fp.getMeta("CUI");
if (cui == null) {
cui = Fawe.imp().getCUI(fp);
if (cui != null) {
synchronized (fp) {
CUI tmp = fp.getMeta("CUI");
if (tmp == null) {
fp.setMeta("CUI", cui);
} else {
cui = tmp;
}
}
}
}
return cui;
}
public ChatManager getChatManager() {
return chatManager;
}
public void setChatManager(ChatManager chatManager) {
checkNotNull(chatManager);
this.chatManager = chatManager;
}
// @Deprecated
// public boolean isJava8() {
// return isJava8;
// }
public DefaultTransformParser getTransformParser() {
return transformParser;
}
/**
* The FAWE updater class
* - Use to get basic update information (changelog/version etc)
*
* @return
*/
public Updater getUpdater() {
return updater;
}
public TextureUtil getCachedTextureUtil(boolean randomize, int min, int max) {
TextureUtil tu = getTextureUtil();
try {
tu = min == 0 && max == 100 ? tu : new CleanTextureUtil(tu, min, max);
tu = randomize ? new RandomTextureUtil(tu) : new CachedTextureUtil(tu);
} catch (FileNotFoundException neverHappens) {
neverHappens.printStackTrace();
}
return tu;
}
public TextureUtil getTextureUtil() {
TextureUtil tmp = textures;
if (tmp == null) {
synchronized (this) {
tmp = textures;
if (tmp == null) {
try {
textures = tmp = new TextureUtil();
tmp.loadModTextures();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
return tmp;
}
/**
* The FaweTimer is a useful class for monitoring TPS
*
* @return FaweTimer
*/
public FaweTimer getTimer() {
return timer;
}
/**
* The visual queue is used to queue visualizations
*
* @return
*/
public VisualQueue getVisualQueue() {
return visualQueue;
}
/**
* The FAWE version
* - Unofficial jars may be lacking version information
*
* @return FaweVersion
*/
public
@Nullable
FaweVersion getVersion() {
return version;
}
public double getTPS() {
return timer.getTPS();
}
private void setupCommands() {
this.IMP.setupCommand("fcancel", new Cancel());
}
public void setupConfigs() {
MainUtil.copyFile(MainUtil.getJarFile(), "de/message.yml", null);
MainUtil.copyFile(MainUtil.getJarFile(), "ru/message.yml", null);
MainUtil.copyFile(MainUtil.getJarFile(), "ru/commands.yml", null);
MainUtil.copyFile(MainUtil.getJarFile(), "tr/message.yml", null);
MainUtil.copyFile(MainUtil.getJarFile(), "es/message.yml", null);
MainUtil.copyFile(MainUtil.getJarFile(), "es/commands.yml", null);
// Setting up config.yml
File file = new File(this.IMP.getDirectory(), "config.yml");
Settings.IMP.PLATFORM = IMP.getPlatform().replace("\"", "");
try {
InputStream stream = getClass().getResourceAsStream("/fawe.properties");
java.util.Scanner scanner = new java.util.Scanner(stream).useDelimiter("\\A");
String versionString = scanner.next().trim();
scanner.close();
this.version = new FaweVersion(versionString);
Settings.IMP.DATE = new Date(100 + version.year, version.month, version.day).toGMTString();
Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit/" + version.build;
Settings.IMP.COMMIT = "https://github.com/boy0001/FastAsyncWorldedit/commit/" + Integer.toHexString(version.hash);
} catch (Throwable ignore) {}
try {
Settings.IMP.reload(file);
// Setting up message.yml
String lang = Objects.toString(Settings.IMP.LANGUAGE);
BBC.load(new File(this.IMP.getDirectory(), (lang.isEmpty() ? "" : lang + File.separator) + "message.yml"));
Commands.load(new File(INSTANCE.IMP.getDirectory(), "commands.yml"));
} catch (Throwable e) {
debug("====== Failed to load config ======");
debug("Please validate your yaml files:");
debug("====================================");
e.printStackTrace();
debug("====================================");
}
}
public WorldEdit getWorldEdit() {
return WorldEdit.getInstance();
}
public static void setupInjector() {
/*
* Modify the sessions
* - EditSession supports custom queue and a lot of optimizations
* - LocalSession supports VirtualPlayers and undo on disk
*/
if (!Settings.IMP.EXPERIMENTAL.DISABLE_NATIVES) {
try {
com.github.luben.zstd.util.Native.load();
} catch (Throwable e) {
if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL > 6 || Settings.IMP.HISTORY.COMPRESSION_LEVEL > 6) {
Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL);
Settings.IMP.HISTORY.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.HISTORY.COMPRESSION_LEVEL);
debug("====== ZSTD COMPRESSION BINDING NOT FOUND ======");
debug(e);
debug("===============================================");
debug("FAWE will work but won't compress data as much");
debug("===============================================");
}
}
try {
net.jpountz.util.Native.load();
} catch (Throwable e) {
e.printStackTrace();
debug("====== LZ4 COMPRESSION BINDING NOT FOUND ======");
debug(e);
debug("===============================================");
debug("FAWE will work but compression will be slower");
debug(" - Try updating your JVM / OS");
debug(" - Report this issue if you cannot resolve it");
debug("===============================================");
}
}
try {
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
boolean x86OS = arch.endsWith("64") || wow64Arch != null && wow64Arch.endsWith("64") ? false : true;
boolean x86JVM = System.getProperty("sun.arch.data.model").equals("32");
if (x86OS != x86JVM) {
debug("====== UPGRADE TO 64-BIT JAVA ======");
debug("You are running 32-bit Java on a 64-bit machine");
debug(" - This is only a recommendation");
debug("====================================");
}
} catch (Throwable ignore) {}
}
private void setupMemoryListener() {
if (Settings.IMP.MAX_MEMORY_PERCENT < 1 || Settings.IMP.MAX_MEMORY_PERCENT > 99) {
return;
}
try {
final MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
final NotificationEmitter ne = (NotificationEmitter) memBean;
ne.addNotificationListener(new NotificationListener() {
@Override
public void handleNotification(final Notification notification, final Object handback) {
final long heapSize = Runtime.getRuntime().totalMemory();
final long heapMaxSize = Runtime.getRuntime().maxMemory();
if (heapSize < heapMaxSize) {
return;
}
MemUtil.memoryLimitedTask();
}
}, null, null);
final List<MemoryPoolMXBean> memPools = ManagementFactory.getMemoryPoolMXBeans();
for (final MemoryPoolMXBean mp : memPools) {
if (mp.isUsageThresholdSupported()) {
final MemoryUsage mu = mp.getUsage();
final long max = mu.getMax();
if (max < 0) {
continue;
}
final long alert = (max * Settings.IMP.MAX_MEMORY_PERCENT) / 100;
mp.setUsageThreshold(alert);
}
}
} catch (Throwable e) {
debug("====== MEMORY LISTENER ERROR ======");
MainUtil.handleError(e, false);
debug("===================================");
debug("FAWE needs access to the JVM memory system:");
debug(" - Change your Java security settings");
debug(" - Disable this with `max-memory-percent: -1`");
debug("===================================");
}
}
/**
* Get the main thread
*
* @return
*/
public Thread getMainThread() {
return this.thread;
}
public static boolean isMainThread() {
return INSTANCE != null ? INSTANCE.thread == Thread.currentThread() : true;
}
/**
* Sets the main thread to the current thread
*
* @return
*/
public Thread setMainThread() {
return this.thread = Thread.currentThread();
}
private ConcurrentHashMap<String, FawePlayer> players = new ConcurrentHashMap<>(8, 0.9f, 1);
private ConcurrentHashMap<UUID, FawePlayer> playersUUID = new ConcurrentHashMap<>(8, 0.9f, 1);
public <T> void register(FawePlayer<T> player) {
players.put(player.getName(), player);
playersUUID.put(player.getUUID(), player);
}
public <T> void unregister(String name) {
FawePlayer player = players.remove(name);
if (player != null) playersUUID.remove(player.getUUID());
}
public FawePlayer getCachedPlayer(String name) {
return players.get(name);
}
public FawePlayer getCachedPlayer(UUID uuid) {
return playersUUID.get(uuid);
}
public Collection<FawePlayer> getCachedPlayers() {
return players.values();
}
}

View File

@ -0,0 +1,564 @@
package com.boydti.fawe;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.example.NMSRelighter;
import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.WEManager;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
import com.sk89q.worldedit.extension.factory.DefaultTransformParser;
import com.sk89q.worldedit.extension.factory.HashTagPatternParser;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.internal.registry.AbstractFactory;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* The FaweAPI class offers a few useful functions.<br>
* - This class is not intended to replace the WorldEdit API<br>
* - With FAWE installed, you can use the EditSession and other WorldEdit classes from an async thread.<br>
* <br>
* FaweAPI.[some method]
*/
public class FaweAPI {
/**
* Offers a lot of options for building an EditSession
*
* @param world
* @return A new EditSessionBuilder
* @see com.boydti.fawe.util.EditSessionBuilder
*/
public static EditSessionBuilder getEditSessionBuilder(World world) {
return new EditSessionBuilder(world);
}
/**
* The TaskManager has some useful methods for doing things asynchronously
*
* @return TaskManager
*/
public static TaskManager getTaskManager() {
return TaskManager.IMP;
}
/**
* Add a custom mask for use in e.g {@literal //mask #id:<input>}
*
* @param methods The class with a bunch of mask methods
* @return true if the mask was registered
* @see com.sk89q.worldedit.command.MaskCommands
*/
public static boolean registerMasks(Object methods) {
DefaultMaskParser parser = getParser(DefaultMaskParser.class);
if (parser != null) parser.register(methods);
return parser != null;
}
/**
* Add a custom material for use in e.g {@literal //material #id:<input>}
*
* @param methods The class with a bunch of pattern methods
* @return true if the mask was registered
* @see com.sk89q.worldedit.command.PatternCommands
*/
public static boolean registerPatterns(Object methods) {
HashTagPatternParser parser = getParser(HashTagPatternParser.class);
if (parser != null) parser.register(methods);
return parser != null;
}
/**
* Add a custom transform for use in
*
* @param methods The class with a bunch of transform methods
* @return true if the transform was registered
* @see com.sk89q.worldedit.command.TransformCommands
*/
public static boolean registerTransforms(Object methods) {
DefaultTransformParser parser = Fawe.get().getTransformParser();
if (parser != null) parser.register(methods);
return parser != null;
}
public static <T> T getParser(Class<T> parserClass) {
try {
Field field = AbstractFactory.class.getDeclaredField("parsers");
field.setAccessible(true);
ArrayList<InputParser> parsers = new ArrayList<>();
parsers.addAll((List<InputParser>) field.get(WorldEdit.getInstance().getMaskFactory()));
parsers.addAll((List<InputParser>) field.get(WorldEdit.getInstance().getBlockFactory()));
parsers.addAll((List<InputParser>) field.get(WorldEdit.getInstance().getItemFactory()));
parsers.addAll((List<InputParser>) field.get(WorldEdit.getInstance().getPatternFactory()));
for (InputParser parser : parsers) {
if (parserClass.isAssignableFrom(parser.getClass())) {
return (T) parser;
}
}
return null;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* Create a command with the provided aliases and register all methods of the class as sub commands.<br>
* - You should try to register commands during startup
* - If no aliases are specified, all methods become root commands
*
* @param clazz The class containing all the sub command methods
* @param aliases The aliases to give the command (or none)
*/
public static void registerCommands(Object clazz, String... aliases) {
CommandManager.getInstance().registerCommands(clazz, aliases);
}
/**
* Wrap some object into a FawePlayer<br>
* - org.bukkit.entity.Player
* - org.spongepowered.api.entity.living.player
* - com.sk89q.worldedit.entity.Player
* - String (name)
* - UUID (player UUID)
*
* @param obj
* @return
*/
public static FawePlayer wrapPlayer(Object obj) {
return FawePlayer.wrap(obj);
}
public static FaweQueue createQueue(String worldName, boolean autoqueue) {
return SetQueue.IMP.getNewQueue(worldName, true, autoqueue);
}
/**
* You can either use a FaweQueue or an EditSession to change blocks<br>
* - The FaweQueue skips a bit of overhead so it's faster<br>
* - The WorldEdit EditSession can do a lot more<br>
* Remember to enqueue it when you're done!<br>
*
* @param world The name of the world
* @param autoqueue If it should start dispatching before you enqueue it.
* @return
* @see com.boydti.fawe.object.FaweQueue#enqueue()
*/
public static FaweQueue createQueue(World world, boolean autoqueue) {
return SetQueue.IMP.getNewQueue(world, true, autoqueue);
}
public static World getWorld(String worldName) {
Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING);
List<? extends World> worlds = platform.getWorlds();
for (World current : worlds) {
if (Fawe.imp().getWorldName(current).equals(worldName)) {
return WorldWrapper.wrap((AbstractWorld) current);
}
}
for (World current : worlds) {
if (current.getName().equals(worldName)) {
return WorldWrapper.wrap((AbstractWorld) current);
}
}
return null;
}
/**
* Upload the clipboard to the configured web interface
*
* @param clipboard The clipboard (may not be null)
* @param format The format to use (some formats may not be supported)
* @return The download URL or null
*/
public static URL upload(final Clipboard clipboard, final ClipboardFormat format) {
return format.uploadAnonymous(clipboard);
}
/**
* Just forwards to ClipboardFormat.SCHEMATIC.load(file)
*
* @param file
* @return
* @see com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat
* @see com.boydti.fawe.object.schematic.Schematic
*/
public static Schematic load(File file) throws IOException {
return ClipboardFormat.SCHEMATIC.load(file);
}
/**
* Get a list of supported protection plugin masks.
*
* @return Set of FaweMaskManager
*/
public static Set<FaweMaskManager> getMaskManagers() {
return new HashSet<>(WEManager.IMP.managers);
}
/**
* Check if the server has more than the configured low memory threshold
*
* @return True if the server has limited memory
*/
public static boolean isMemoryLimited() {
return MemUtil.isMemoryLimited();
}
/**
* Use ThreadLocalRandom instead
*
* @return
*/
@Deprecated
public static PseudoRandom getFastRandom() {
return new PseudoRandom();
}
/**
* Get a player's allowed WorldEdit region
*
* @param player
* @return
*/
public static Region[] getRegions(FawePlayer player) {
return WEManager.IMP.getMask(player);
}
/**
* Cancel the edit with the following extent<br>
* - The extent must be the one being used by an EditSession, otherwise an error may be thrown <br>
* - Insert an extent into the EditSession using the EditSessionEvent: http://wiki.sk89q.com/wiki/WorldEdit/API/Hooking_EditSession <br>
*
* @param extent
* @param reason
* @see com.sk89q.worldedit.EditSession#getRegionExtent() To get the FaweExtent for an EditSession
*/
public static void cancelEdit(Extent extent, BBC reason) {
try {
WEManager.IMP.cancelEdit(extent, reason);
} catch (WorldEditException ignore) {
}
}
public static void addMaskManager(FaweMaskManager maskMan) {
WEManager.IMP.managers.add(maskMan);
}
/**
* Get the DiskStorageHistory object representing a File
*
* @param file
* @return
*/
public static DiskStorageHistory getChangeSetFromFile(File file) {
if (!file.exists() || file.isDirectory()) {
throw new IllegalArgumentException("Not a file!");
}
if (!file.getName().toLowerCase().endsWith(".bd")) {
throw new IllegalArgumentException("Not a BD file!");
}
if (Settings.IMP.HISTORY.USE_DISK) {
throw new IllegalArgumentException("History on disk not enabled!");
}
String[] path = file.getPath().split(File.separator);
if (path.length < 3) {
throw new IllegalArgumentException("Not in history directory!");
}
String worldName = path[path.length - 3];
String uuidString = path[path.length - 2];
World world = getWorld(worldName);
if (world == null) {
throw new IllegalArgumentException("Corresponding world does not exist: " + worldName);
}
UUID uuid;
try {
uuid = UUID.fromString(uuidString);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid UUID from file path: " + uuidString);
}
DiskStorageHistory history = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
return history;
}
/**
* Used in the RollBack to generate a list of DiskStorageHistory objects<br>
* - Note: An edit outside the radius may be included if it overlaps with an edit inside that depends on it.
*
* @param origin - The origin location
* @param user - The uuid (may be null)
* @param radius - The radius from the origin of the edit
* @param timediff - The max age of the file in milliseconds
* @param shallow - If shallow is true, FAWE will only read the first Settings.IMP.BUFFER_SIZE bytes to obtain history info<br>
* Reading only part of the file will result in unreliable bounds info for large edits
* @return
*/
public static List<DiskStorageHistory> getBDFiles(FaweLocation origin, UUID user, int radius, long timediff, boolean shallow) {
File history = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + origin.world);
if (!history.exists()) {
return new ArrayList<>();
}
long now = System.currentTimeMillis();
ArrayList<File> files = new ArrayList<>();
for (File userFile : history.listFiles()) {
if (!userFile.isDirectory()) {
continue;
}
UUID userUUID;
try {
userUUID = UUID.fromString(userFile.getName());
} catch (IllegalArgumentException e) {
continue;
}
if (user != null && !userUUID.equals(user)) {
continue;
}
ArrayList<Integer> ids = new ArrayList<>();
for (File file : userFile.listFiles()) {
if (file.getName().endsWith(".bd")) {
if (timediff >= Integer.MAX_VALUE || now - file.lastModified() <= timediff) {
files.add(file);
if (files.size() > 2048) {
return null;
}
}
}
}
}
World world = origin.getWorld();
Collections.sort(files, new Comparator<File>() {
@Override
public int compare(File a, File b) {
String aName = a.getName();
String bName = b.getName();
int aI = Integer.parseInt(aName.substring(0, aName.length() - 3));
int bI = Integer.parseInt(bName.substring(0, bName.length() - 3));
long value = aI - bI;
return value == 0 ? 0 : value < 0 ? -1 : 1;
}
});
RegionWrapper bounds = new RegionWrapper(origin.x - radius, origin.x + radius, origin.z - radius, origin.z + radius);
RegionWrapper boundsPlus = new RegionWrapper(bounds.minX - 64, bounds.maxX + 512, bounds.minZ - 64, bounds.maxZ + 512);
HashSet<RegionWrapper> regionSet = new HashSet<RegionWrapper>(Arrays.asList(bounds));
ArrayList<DiskStorageHistory> result = new ArrayList<>();
for (File file : files) {
UUID uuid = UUID.fromString(file.getParentFile().getName());
DiskStorageHistory dsh = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
DiskStorageHistory.DiskStorageSummary summary = dsh.summarize(boundsPlus, shallow);
RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
boolean encompassed = false;
boolean isIn = false;
for (RegionWrapper allowed : regionSet) {
isIn = isIn || allowed.intersects(region);
if (encompassed = allowed.isIn(region.minX, region.maxX) && allowed.isIn(region.minZ, region.maxZ)) {
break;
}
}
if (isIn) {
result.add(0, dsh);
if (!encompassed) {
regionSet.add(region);
}
if (shallow && result.size() > 64) {
return result;
}
}
}
return result;
}
/**
* The DiskStorageHistory class is what FAWE uses to represent the undo on disk.
*
* @param world
* @param uuid
* @param index
* @return
* @see com.boydti.fawe.object.changeset.DiskStorageHistory#toEditSession(com.boydti.fawe.object.FawePlayer)
*/
public static DiskStorageHistory getChangeSetFromDisk(World world, UUID uuid, int index) {
return new DiskStorageHistory(world, uuid, index);
}
/**
* Compare two versions
*
* @param version
* @param major
* @param minor
* @param minor2
* @return true if version is >= major, minor, minor2
*/
public static boolean checkVersion(final int[] version, final int major, final int minor, final int minor2) {
return (version[0] > major) || ((version[0] == major) && (version[1] > minor)) || ((version[0] == major) && (version[1] == minor) && (version[2] >= minor2));
}
@Deprecated
public static int fixLighting(String world, Region selection) {
return fixLighting(world, selection, FaweQueue.RelightMode.ALL);
}
@Deprecated
public static int fixLighting(String world, Region selection, final FaweQueue.RelightMode mode) {
return fixLighting(world, selection, null, mode);
}
@Deprecated
public static int fixLighting(String world, Region selection, @Nullable FaweQueue queue, final FaweQueue.RelightMode mode) {
return fixLighting(getWorld(world), selection, queue, mode);
}
/**
* Fix the lighting in a selection<br>
* - First removes all lighting, then relights
* - Relights in parallel (if enabled) for best performance<br>
* - Also resends chunks<br>
*
* @param world
* @param selection (assumes cuboid)
* @return
*/
public static int fixLighting(World world, Region selection, @Nullable FaweQueue queue, final FaweQueue.RelightMode mode) {
final Vector bot = selection.getMinimumPoint();
final Vector top = selection.getMaximumPoint();
final int minX = bot.getBlockX() >> 4;
final int minZ = bot.getBlockZ() >> 4;
final int maxX = top.getBlockX() >> 4;
final int maxZ = top.getBlockZ() >> 4;
int count = 0;
if (queue == null) {
queue = SetQueue.IMP.getNewQueue(world, true, false);
}
// Remove existing lighting first
if (queue instanceof NMSMappedFaweQueue) {
final NMSMappedFaweQueue nmsQueue = (NMSMappedFaweQueue) queue;
NMSRelighter relighter = new NMSRelighter(nmsQueue);
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
relighter.addChunk(x, z, null, 65535);
count++;
}
}
if (mode != FaweQueue.RelightMode.NONE) {
boolean sky = nmsQueue.hasSky();
if (sky) {
relighter.fixSkyLighting();
}
relighter.fixBlockLighting();
} else {
relighter.removeLighting();
}
relighter.sendChunks();
}
return count;
}
/**
* Set a task to run when the global queue (SetQueue class) is empty
*
* @param whenDone
*/
public static void addTask(final Runnable whenDone) {
SetQueue.IMP.addEmptyTask(whenDone);
}
/**
* Have a task run when the server is low on memory (configured threshold)
*
* @param run
*/
public static void addMemoryLimitedTask(Runnable run) {
MemUtil.addMemoryLimitedTask(run);
}
/**
* Have a task run when the server is no longer low on memory (configured threshold)
*
* @param run
*/
public static void addMemoryPlentifulTask(Runnable run) {
MemUtil.addMemoryPlentifulTask(run);
}
/**
* @return
* @see BBC
*/
public static BBC[] getTranslations() {
return BBC.values();
}
/**
* @see #getEditSessionBuilder(com.sk89q.worldedit.world.World)
* @deprecated
*/
@Deprecated
public static EditSession getNewEditSession(@Nonnull FawePlayer player) {
if (player == null) {
throw new IllegalArgumentException("Player may not be null");
}
return player.getNewEditSession();
}
/**
* @see #getEditSessionBuilder(com.sk89q.worldedit.world.World)
* @deprecated
*/
@Deprecated
public static EditSession getNewEditSession(World world) {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1);
}
}

View File

@ -0,0 +1,218 @@
package com.boydti.fawe;
import com.sk89q.jnbt.*;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.lang.reflect.Field;
import java.util.*;
public class FaweCache {
/**
* [ y | z | x ] => index
*/
public final static short[][][] CACHE_I = new short[256][16][16];
/**
* [ y | z | x ] => index
*/
public final static short[][][] CACHE_J = new short[256][16][16];
/**
* [ i | j ] => x
*/
public final static byte[][] CACHE_X = new byte[16][];
/**
* [ i | j ] => y
*/
public final static short[][] CACHE_Y = new short[16][4096];
/**
* [ i | j ] => z
*/
public final static byte[][] CACHE_Z = new byte[16][];
/**
* Immutable biome cache
*/
public final static BaseBiome[] CACHE_BIOME = new BaseBiome[256];
public static final BaseBiome getBiome(int id) {
return CACHE_BIOME[id];
}
static {
for (int i = 0; i < 256; i++) {
CACHE_BIOME[i] = new BaseBiome(i) {
@Override
public void setId(int id) {
throw new IllegalStateException("Cannot set id");
}
};
}
CACHE_X[0] = new byte[4096];
CACHE_Z[0] = new byte[4096];
for (int y = 0; y < 16; y++) {
CACHE_X[y] = CACHE_X[0];
CACHE_Z[y] = CACHE_Z[0];
}
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
final short j = (short) (((y & 0xF) << 8) | (z << 4) | x);
CACHE_X[0][j] = (byte) x;
CACHE_Z[0][j] = (byte) z;
}
}
}
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 256; y++) {
final short i = (short) (y >> 4);
final short j = (short) (((y & 0xF) << 8) | (z << 4) | x);
CACHE_I[y][z][x] = i;
CACHE_J[y][z][x] = j;
CACHE_Y[i][j] = (short) y;
}
}
}
}
public static Map<String, Object> asMap(Object... pairs) {
HashMap<String, Object> map = new HashMap<String, Object>(pairs.length >> 1);
for (int i = 0; i < pairs.length; i += 2) {
String key = (String) pairs[i];
Object value = pairs[i + 1];
map.put(key, value);
}
return map;
}
public static ShortTag asTag(short value) {
return new ShortTag(value);
}
public static IntTag asTag(int value) {
return new IntTag(value);
}
public static DoubleTag asTag(double value) {
return new DoubleTag(value);
}
public static ByteTag asTag(byte value) {
return new ByteTag(value);
}
public static FloatTag asTag(float value) {
return new FloatTag(value);
}
public static LongTag asTag(long value) {
return new LongTag(value);
}
public static ByteArrayTag asTag(byte[] value) {
return new ByteArrayTag(value);
}
public static IntArrayTag asTag(int[] value) {
return new IntArrayTag(value);
}
public static LongArrayTag asTag(long[] value) {
return new LongArrayTag(value);
}
public static StringTag asTag(String value) {
return new StringTag(value);
}
public static CompoundTag asTag(Map<String, Object> value) {
HashMap<String, Tag> map = new HashMap<>();
for (Map.Entry<String, Object> entry : value.entrySet()) {
Object child = entry.getValue();
Tag tag = asTag(child);
map.put(entry.getKey(), tag);
}
return new CompoundTag(map);
}
public static Tag asTag(Object value) {
if (value instanceof Integer) {
return asTag((int) value);
} else if (value instanceof Short) {
return asTag((short) value);
} else if (value instanceof Double) {
return asTag((double) value);
} else if (value instanceof Byte) {
return asTag((byte) value);
} else if (value instanceof Float) {
return asTag((float) value);
} else if (value instanceof Long) {
return asTag((long) value);
} else if (value instanceof String) {
return asTag((String) value);
} else if (value instanceof Map) {
return asTag((Map) value);
} else if (value instanceof Collection) {
return asTag((Collection) value);
} else if (value instanceof Object[]) {
return asTag((Object[]) value);
} else if (value instanceof byte[]) {
return asTag((byte[]) value);
} else if (value instanceof int[]) {
return asTag((int[]) value);
} else if (value instanceof long[]) {
return asTag((long[]) value);
} else if (value instanceof Tag) {
return (Tag) value;
} else if (value instanceof Boolean) {
return asTag((byte) ((boolean) value ? 1 : 0));
} else if (value == null) {
System.out.println("Invalid nbt: " + value);
return null;
} else {
Class<? extends Object> clazz = value.getClass();
if (clazz.getName().startsWith("com.intellectualcrafters.jnbt")) {
try {
if (clazz.getName().equals("com.intellectualcrafters.jnbt.EndTag")) {
return new EndTag();
}
Field field = clazz.getDeclaredField("value");
field.setAccessible(true);
return asTag(field.get(value));
} catch (Throwable e) {
e.printStackTrace();
}
}
System.out.println("Invalid nbt: " + value);
return null;
}
}
public static ListTag asTag(Object... values) {
Class clazz = null;
List<Tag> list = new ArrayList<>(values.length);
for (Object value : values) {
Tag tag = asTag(value);
if (clazz == null) {
clazz = tag.getClass();
}
list.add(tag);
}
if (clazz == null) clazz = EndTag.class;
return new ListTag(clazz, list);
}
public static ListTag asTag(Collection values) {
Class clazz = null;
List<Tag> list = new ArrayList<>(values.size());
for (Object value : values) {
Tag tag = asTag(value);
if (clazz == null) {
clazz = tag.getClass();
}
list.add(tag);
}
if (clazz == null) clazz = EndTag.class;
return new ListTag(clazz, list);
}
}

View File

@ -0,0 +1,32 @@
package com.boydti.fawe;
public class FaweVersion {
public final int year, month, day, hash, build, major, minor, patch;
public FaweVersion(String version) {
String[] split = version.substring(version.indexOf('=') + 1).split("-");
if (split[0].equals("unknown")) {
this.year = month = day = hash = build = major = minor = patch = 0;
return;
}
String[] date = split[0].split("\\.");
this.year = Integer.parseInt(date[0]);
this.month = Integer.parseInt(date[1]);
this.day = Integer.parseInt(date[2]);
this.hash = Integer.parseInt(split[1], 16);
this.build = Integer.parseInt(split[2]);
String[] semver = split[3].split("\\.");
this.major = Integer.parseInt(semver[0]);
this.minor = Integer.parseInt(semver[1]);
this.patch = Integer.parseInt(semver[2]);
}
@Override
public String toString() {
return "FastAsyncWorldEdit-" + year + "." + month + "." + day + "-" + Integer.toHexString(hash) + "-" + build;
}
public boolean isNewer(FaweVersion other) {
return other.build < this.build && (this.major > other.major || (this.major == other.major && this.minor > other.minor) || (this.major == other.major && this.minor == other.minor && this.patch > other.patch));
}
}

View File

@ -0,0 +1,68 @@
package com.boydti.fawe;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.cui.CUI;
import com.boydti.fawe.util.gui.FormBuilder;
import com.boydti.fawe.util.image.ImageViewer;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.util.Collection;
import java.util.UUID;
public interface IFawe {
public void debug(final String s);
public File getDirectory();
public void setupCommand(final String label, final FaweCommand cmd);
public FawePlayer wrap(final Object obj);
public void setupVault();
public TaskManager getTaskManager();
public FaweQueue getNewQueue(World world, boolean fast);
public FaweQueue getNewQueue(String world, boolean fast);
public String getWorldName(World world);
public Collection<FaweMaskManager> getMaskManagers();
public void startMetrics();
default CUI getCUI(FawePlayer player) { return null; }
default ImageViewer getImageViewer(FawePlayer player) { return null; }
public default void registerPacketListener() {}
default int getPlayerCount() {
return Fawe.get().getCachedPlayers().size();
}
public String getPlatformVersion();
public boolean isOnlineMode();
public String getPlatform();
public UUID getUUID(String name);
public String getName(UUID uuid);
public Object getBlocksHubApi();
public default String getDebugInfo() {
return "";
}
public default FormBuilder getFormBuilder() {
return null;
}
}

View File

@ -0,0 +1,693 @@
package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.*;
import com.boydti.fawe.jnbt.anvil.filters.*;
import com.boydti.fawe.jnbt.anvil.history.IAnvilHistory;
import com.boydti.fawe.jnbt.anvil.history.NullAnvilHistory;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.changeset.AnvilHistory;
import com.boydti.fawe.object.clipboard.remap.ClipboardRemapper;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.*;
import java.util.function.Consumer;
import static com.google.common.base.Preconditions.checkNotNull;
@Command(aliases = {"/anvil"}, desc = "Manipulate billions of blocks: [More Info](https://github.com/boy0001/FastAsyncWorldedit/wiki/Anvil-API)")
public class AnvilCommands {
private final WorldEdit worldEdit;
/**
* Create a new instance.
*
* @param worldEdit reference to WorldEdit
*/
public AnvilCommands(WorldEdit worldEdit) {
checkNotNull(worldEdit);
this.worldEdit = worldEdit;
}
/**
* Run safely on an unloaded world (no selection)
*
* @param player
* @param folder
* @param filter
* @param <G>
* @param <T>
* @return
*/
@Deprecated
public static <G, T extends MCAFilter<G>> T runWithWorld(Player player, String folder, T filter, boolean force) {
return runWithWorld(player, folder, filter, force, false);
}
@Deprecated
public static <G, T extends MCAFilter<G>> T runWithWorld(Player player, String folder, T filter, boolean force, boolean unsafe) {
boolean copy = false;
if (FaweAPI.getWorld(folder) != null) {
if (!force) {
BBC.WORLD_IS_LOADED.send(player);
return null;
}
copy = true;
}
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(defaultQueue);
if (copy && !unsafe) {
return queue.filterCopy(filter, RegionWrapper.GLOBAL());
} else {
return queue.filterWorld(filter);
}
}
/**
* Run safely on an existing world within a selection
*
* @param player
* @param editSession
* @param selection
* @param filter
* @param <G>
* @param <T>
* @return
*/
@Deprecated
public static <G, T extends MCAFilter<G>> T runWithSelection(Player player, EditSession editSession, Region selection, T filter) {
if (!(selection instanceof CuboidRegion)) {
BBC.NO_REGION.send(player);
return null;
}
CuboidRegion cuboid = (CuboidRegion) selection;
RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
MCAQueue queue = new MCAQueue(tmp);
FawePlayer<Object> fp = FawePlayer.wrap(player);
fp.checkAllowedRegion(selection);
recordHistory(fp, editSession.getWorld(), iAnvilHistory -> {
queue.filterCopy(filter, wrappedRegion, iAnvilHistory);
});
return filter;
}
public static void recordHistory(FawePlayer fp, World world, Consumer<IAnvilHistory> run) {
LocalSession session = fp.getSession();
if (session == null || session.hasFastMode()) {
run.accept(new NullAnvilHistory());
} else {
AnvilHistory history = new AnvilHistory(Fawe.imp().getWorldName(world), fp.getUUID());
run.accept(history);
session.remember(fp.getPlayer(), world, history, fp.getLimit());
}
}
// @Command(
// aliases = {"replaceall", "rea", "repall"},
// usage = "<folder> [from-block] <to-block>",
// desc = "Replace all blocks in the selection with another",
// help = "Replace all blocks in the selection with another\n" +
// "The -d flag disabled wildcard data matching\n",
// flags = "df",
// min = 2,
// max = 4
// )
// @CommandPermissions("worldedit.anvil.replaceall")
// public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
// final FaweBlockMatcher matchFrom;
// if (from == null) {
// matchFrom = FaweBlockMatcher.NOT_AIR;
// } else {
// if (from.contains(":")) {
// useData = true; //override d flag, if they specified data they want it
// }
// matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
// }
// final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
// ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo);
// ReplaceSimpleFilter result = runWithWorld(player, folder, filter, true);
// if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
// }
@Command(
aliases = {"remapall"},
usage = "<folder>",
help = "Remap the world between MCPE/PC values",
desc = "Remap the world between MCPE/PC values",
min = 1,
max = 1
)
@CommandPermissions("worldedit.anvil.remapall")
public void remapall(Player player, String folder) throws WorldEditException {
ClipboardRemapper mapper;
ClipboardRemapper.RemapPlatform from;
ClipboardRemapper.RemapPlatform to;
if (Fawe.imp().getPlatform().equalsIgnoreCase("nukkit")) {
from = ClipboardRemapper.RemapPlatform.PC;
to = ClipboardRemapper.RemapPlatform.PE;
} else {
from = ClipboardRemapper.RemapPlatform.PE;
to = ClipboardRemapper.RemapPlatform.PC;
}
RemapFilter filter = new RemapFilter(from, to);
RemapFilter result = runWithWorld(player, folder, filter, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deleteallunvisited", "delunvisited" },
usage = "<folder> <age-ticks> [file-age=60000]",
desc = "Delete all chunks which haven't been occupied",
help = "Delete all chunks which haven't been occupied for `age-ticks` (20t = 1s) and \n" +
"Have not been accessed since `file-duration` (ms) after creation and\n" +
"Have not been used in the past `chunk-inactivity` (ms)" +
"The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`",
min = 2,
max = 3
)
@CommandPermissions("worldedit.anvil.deleteallunvisited")
public void deleteAllUnvisited(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileDurationMillis) throws WorldEditException {
long chunkInactivityMillis = fileDurationMillis; // Use same value for now
DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileDurationMillis, inhabitedTicks, chunkInactivityMillis);
DeleteUninhabitedFilter result = runWithWorld(player, folder, filter, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deleteallunclaimed", "delallunclaimed" },
usage = "<age-ticks> [file-age=60000]",
desc = "(Supports: WG, P2, GP) Delete all chunks which haven't been occupied AND claimed",
help = "(Supports: WG, P2, GP) Delete all chunks which aren't claimed AND haven't been occupied for `age-ticks` (20t = 1s) and \n" +
"Have not been accessed since `file-duration` (ms) after creation and\n" +
"Have not been used in the past `chunk-inactivity` (ms)" +
"The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`",
min = 1,
max = 3
)
@CommandPermissions("worldedit.anvil.deleteallunclaimed")
public void deleteAllUnclaimed(Player player, int inhabitedTicks, @Optional("60000") int fileDurationMillis, @Switch('d') boolean debug) throws WorldEditException {
String folder = Fawe.imp().getWorldName(player.getWorld());
long chunkInactivityMillis = fileDurationMillis; // Use same value for now
DeleteUnclaimedFilter filter = new DeleteUnclaimedFilter(player.getWorld(), fileDurationMillis, inhabitedTicks, chunkInactivityMillis);
if (debug) filter.enableDebug();
DeleteUnclaimedFilter result = runWithWorld(player, folder, filter, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deleteunclaimed"},
usage = "<age-ticks> [file-age=60000]",
desc = "(Supports: WG, P2, GP) Delete all chunks which haven't been occupied AND claimed",
help = "(Supports: WG, P2, GP) Delete all chunks which aren't claimed AND haven't been occupied for `age-ticks` (20t = 1s) and \n" +
"Have not been accessed since `file-duration` (ms) after creation and\n" +
"Have not been used in the past `chunk-inactivity` (ms)" +
"The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`",
min = 1,
max = 3
)
@CommandPermissions("worldedit.anvil.deleteunclaimed")
public void deleteUnclaimed(Player player, EditSession editSession, @Selection Region selection, int inhabitedTicks, @Optional("60000") int fileDurationMillis, @Switch('d') boolean debug) throws WorldEditException {
String folder = Fawe.imp().getWorldName(player.getWorld());
long chunkInactivityMillis = fileDurationMillis; // Use same value for now
DeleteUnclaimedFilter filter = new DeleteUnclaimedFilter(player.getWorld(), fileDurationMillis, inhabitedTicks, chunkInactivityMillis);
if (debug) filter.enableDebug();
DeleteUnclaimedFilter result = runWithSelection(player, editSession, selection, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deletealloldregions", "deloldreg" },
usage = "<folder> <time>",
desc = "Delete regions which haven't been accessed in a certain amount of time",
help = "Delete regions which haven't been accessed in a certain amount of time\n" +
"You can use seconds (s), minutes (m), hours (h), days (d), weeks (w), years (y)\n" +
"(months are not a unit of time)\n" +
"E.g. 8h5m12s\n",
min = 2,
max = 3
)
@CommandPermissions("worldedit.anvil.deletealloldregions")
public void deleteAllOldRegions(Player player, String folder, String time) throws WorldEditException {
long duration = MainUtil.timeToSec(time) * 1000l;
DeleteOldFilter filter = new DeleteOldFilter(duration);
DeleteOldFilter result = runWithWorld(player, folder, filter, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"trimallplots", },
desc = "Trim chunks in a Plot World",
help = "Trim chunks in a Plot World\n" +
"Unclaimed chunks will be deleted\n" +
"Unmodified chunks will be deleted\n" +
"Use -v to also delete unvisited chunks\n"
)
@CommandPermissions("worldedit.anvil.trimallplots")
public void trimAllPlots(Player player, @Switch('v') boolean deleteUnvisited) throws WorldEditException {
String folder = Fawe.imp().getWorldName(player.getWorld());
int visitTime = deleteUnvisited ? 1 : -1;
PlotTrimFilter filter = new PlotTrimFilter(player.getWorld(), 0, visitTime, 600000);
// PlotTrimFilter result = runWithWorld(player, folder, filter, true);
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(defaultQueue);
PlotTrimFilter result = queue.filterWorld(filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"deletebiomechunks", },
desc = "Delete chunks matching a specific biome"
)
@CommandPermissions("worldedit.anvil.trimallair")
public void deleteBiome(Player player, String folder, BaseBiome biome, @Switch('u') boolean unsafe) {
DeleteBiomeFilterSimple filter = new DeleteBiomeFilterSimple(biome);
DeleteBiomeFilterSimple result = runWithWorld(player, folder, filter, true, unsafe);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"trimallair", },
desc = "Trim all air in the world"
)
@CommandPermissions("worldedit.anvil.trimallair")
public void trimAllAir(Player player, String folder, @Switch('u') boolean unsafe) throws WorldEditException {
TrimAirFilter filter = new TrimAirFilter();
TrimAirFilter result = runWithWorld(player, folder, filter, true, unsafe);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"debugfixair", },
desc = "debug - do not use"
)
@CommandPermissions("worldedit.anvil.debugfixair")
public void debugfixair(Player player, String folder) throws WorldEditException {
DebugFixAir filter = new DebugFixAir();
DebugFixAir result = runWithWorld(player, folder, filter, true, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"debugfixroads", },
desc = "debug - do not use"
)
@CommandPermissions("worldedit.anvil.debugfixroads")
public void debugfixroads(Player player, String folder) throws WorldEditException {
DebugFixP2Roads filter = new DebugFixP2Roads();
DebugFixP2Roads result = runWithWorld(player, folder, filter, true, true);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
// @Command(
// aliases = {"replaceallpattern", "reap", "repallpat"},
// usage = "<folder> [from-block] <to-pattern>",
// desc = "Replace all blocks in the selection with another",
// flags = "dm",
// min = 2,
// max = 4
// )
// @CommandPermissions("worldedit.anvil.replaceall")
// public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
// MCAFilterCounter filter;
// if (useMap) {
// if (to instanceof RandomPattern) {
// List<String> split = StringMan.split(from, ',');
// filter = new MappedReplacePatternFilter(from, (RandomPattern) to, useData);
// } else {
// player.print(BBC.getPrefix() + "Must be a pattern list!");
// return;
// }
// } else {
// final FaweBlockMatcher matchFrom;
// if (from == null) {
// matchFrom = FaweBlockMatcher.NOT_AIR;
// } else {
// matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
// }
// filter = new ReplacePatternFilter(matchFrom, to);
// }
// MCAFilterCounter result = runWithWorld(player, folder, filter, true);
// if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
// }
//
// @Command(
// aliases = {"countall"},
// usage = "<folder> [hasSky] <id>",
// desc = "Count all blocks in a world",
// flags = "d",
// min = 2,
// max = 3
// )
// @CommandPermissions("worldedit.anvil.countall")
// public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException {
// Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
// MCAFilterCounter filter;
// if (useData || arg.contains(":")) { // Optimize for both cases
// CountFilter counter = new CountFilter();
// searchBlocks.forEach(counter::addBlock);
// filter = counter;
// } else {
// CountIdFilter counter = new CountIdFilter();
// searchBlocks.forEach(counter::addBlock);
// filter = counter;
// }
// MCAFilterCounter result = runWithWorld(player, folder, filter, true);
// if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
// }
@Command(
aliases = {"clear", "unset"},
desc = "Clear the chunks in a selection (delete without defrag)"
)
@CommandPermissions("worldedit.anvil.clear")
public void unset(Player player, EditSession editSession, @Selection Region selection) throws WorldEditException {
Vector bot = selection.getMinimumPoint();
Vector top = selection.getMaximumPoint();
RegionWrapper region = new RegionWrapper(bot, top);
MCAFilterCounter filter = new MCAFilterCounter() {
@Override
public MCAFile applyFile(MCAFile file) {
int X = file.getX();
int Z = file.getZ();
int bcx = X << 5;
int bcz = Z << 5;
int bx = X << 9;
int bz = Z << 9;
if (region.isIn(bx, bz) && region.isIn(bx + 511, bz + 511)) {
file.setDeleted(true);
get().add(512 * 512 * 256);
} else if (region.isInMCA(X, Z)) {
file.init();
final byte[] empty = new byte[4];
RandomAccessFile raf = file.getRandomAccessFile();
file.forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
if (region.isInChunk(bcx + cx, bcz + cz)) {
int index = ((cx & 31) << 2) + ((cz & 31) << 7);
try {
raf.seek(index);
raf.write(empty);
get().add(16 * 16 * 256);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
file.clear();
}
return null;
}
};
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
// @Command(
// aliases = {"count"},
// usage = "<ids>",
// desc = "Count blocks in a selection",
// flags = "d",
// min = 1,
// max = 2
// )
// @CommandPermissions("worldedit.anvil.count")
// public void count(Player player, EditSession editSession, @Selection Region selection, String arg, @Switch('d') boolean useData) throws WorldEditException {
// Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
// MCAFilterCounter filter;
// if (useData || arg.contains(":")) { // Optimize for both cases
// CountFilter counter = new CountFilter();
// searchBlocks.forEach(counter::addBlock);
// filter = counter;
// } else {
// CountIdFilter counter = new CountIdFilter();
// searchBlocks.forEach(counter::addBlock);
// filter = counter;
// }
// MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
// if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
// }
//
// @Command(
// aliases = {"distr"},
// desc = "Replace all blocks in the selection with another"
// )
// @CommandPermissions("worldedit.anvil.distr")
// public void distr(Player player, EditSession editSession, @Selection Region selection, @Switch('d') boolean useData) throws WorldEditException {
// long total = 0;
// long[] count;
// MCAFilter<long[]> counts;
// if (useData) {
// counts = runWithSelection(player, editSession, selection, new MCAFilter<long[]>() {
// @Override
// public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) {
// counts[block.getCombined()]++;
// }
//
// @Override
// public long[] init() {
// return new long[Character.MAX_VALUE + 1];
// }
// });
// count = new long[Character.MAX_VALUE + 1];
// } else {
// counts = runWithSelection(player, editSession, selection, new MCAFilter<long[]>() {
// @Override
// public void applyBlock(int x, int y, int z, BaseBlock block, long[] counts) {
// counts[block.getId()]++;
// }
//
// @Override
// public long[] init() {
// return new long[4096];
// }
// });
// count = new long[4096];
// }
// for (long[] value : counts) {
// for (int i = 0; i < value.length; i++) {
// count[i] += value[i];
// total += value[i];
// }
// }
// ArrayList<long[]> map = new ArrayList<>();
// for (int i = 0; i < count.length; i++) {
// if (count[i] != 0) map.add(new long[]{i, count[i]});
// }
// Collections.sort(map, new Comparator<long[]>() {
// @Override
// public int compare(long[] a, long[] b) {
// long vA = a[1];
// long vB = b[1];
// return (vA < vB) ? -1 : ((vA == vB) ? 0 : 1);
// }
// });
// if (useData) {
// for (long[] c : map) {
// BaseBlock block = FaweCache.CACHE_BLOCK[(int) c[0]];
// String name = BlockType.fromID(block.getId()).getName();
// String str = String.format("%-7s (%.3f%%) %s #%d:%d",
// String.valueOf(c[1]),
// ((c[1] * 10000) / total) / 100d,
// name == null ? "Unknown" : name,
// block.getType(), block.getData());
// player.print(BBC.getPrefix() + str);
// }
// } else {
// for (long[] c : map) {
// BlockType block = BlockType.fromID((int) c[0]);
// String str = String.format("%-7s (%.3f%%) %s #%d",
// String.valueOf(c[1]),
// ((c[1] * 10000) / total) / 100d,
// block == null ? "Unknown" : block.getName(), c[0]);
// player.print(BBC.getPrefix() + str);
// }
// }
// }
//
// @Command(
// aliases = {"replace", "r"},
// usage = "[from-block] <to-block>",
// desc = "Replace all blocks in the selection with another"
// )
// @CommandPermissions("worldedit.anvil.replace")
// public void replace(Player player, EditSession editSession, @Selection Region selection, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
// final FaweBlockMatcher matchFrom;
// if (from == null) {
// matchFrom = FaweBlockMatcher.NOT_AIR;
// } else {
// matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
// }
// final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
// ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo);
// MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
// if (result != null) {
// player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
// }
// }
//
// @Command(
// aliases = {"replacepattern", "preplace", "rp"},
// usage = "[from-mask] <to-pattern>",
// desc = "Replace all blocks in the selection with a pattern"
// )
// @CommandPermissions("worldedit.anvil.replace")
// // Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap
// public void replacePattern(Player player, EditSession editSession, @Selection Region selection, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
// MCAFilterCounter filter;
// if (useMap) {
// if (to instanceof RandomPattern) {
// List<String> split = StringMan.split(from, ',');
// filter = new MappedReplacePatternFilter(from, (RandomPattern) to, useData);
// } else {
// player.print(BBC.getPrefix() + "Must be a pattern list!");
// return;
// }
// } else {
// final FaweBlockMatcher matchFrom;
// if (from == null) {
// matchFrom = FaweBlockMatcher.NOT_AIR;
// } else {
// matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
// }
// filter = new ReplacePatternFilter(matchFrom, to);
// }
// MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
// if (result != null) {
// player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
// }
// }
@Command(
aliases = {"set"},
usage = "<to-pattern>",
desc = "Set all blocks in the selection with a pattern"
)
@CommandPermissions("worldedit.anvil.set")
// Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap
public void set(Player player, EditSession editSession, @Selection Region selection, final Pattern to) throws WorldEditException {
MCAFilterCounter filter = new SetPatternFilter(to);
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
}
@Command(
aliases = {"removelayers"},
usage = "<id>",
desc = "Removes matching chunk layers",
help = "Remove if all the selected layers in a chunk match the provided id"
)
@CommandPermissions("worldedit.anvil.removelayer")
public void removeLayers(Player player, EditSession editSession, @Selection Region selection, int id) throws WorldEditException {
Vector min = selection.getMinimumPoint();
Vector max = selection.getMaximumPoint();
int minY = min.getBlockY();
int maxY = max.getBlockY();
RemoveLayerFilter filter = new RemoveLayerFilter(minY, maxY, id);
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
}
@Command(
aliases = {"copy"},
desc = "Lazily copy chunks to your anvil clipboard"
)
@CommandPermissions("worldedit.anvil.copychunks")
public void copy(Player player, LocalSession session, EditSession editSession, @Selection Region selection) throws WorldEditException {
if (!(selection instanceof CuboidRegion)) {
BBC.NO_REGION.send(player);
return;
}
CuboidRegion cuboid = (CuboidRegion) selection;
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
MCAQueue queue = new MCAQueue(tmp);
Vector origin = session.getPlacementPosition(player);
MCAClipboard clipboard = new MCAClipboard(queue, cuboid, origin);
FawePlayer fp = FawePlayer.wrap(player);
fp.setMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD, clipboard);
BBC.COMMAND_COPY.send(player, selection.getArea());
}
// @Command(
// aliases = {"paste"},
// desc = "Paste chunks from your anvil clipboard",
// help =
// "Paste the chunks from your anvil clipboard.\n" +
// "The -c flag will align the paste to the chunks.",
// flags = "c"
//
// )
// @CommandPermissions("worldedit.anvil.pastechunks")
// public void paste(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean alignChunk) throws WorldEditException, IOException {
// FawePlayer fp = FawePlayer.wrap(player);
// MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD);
// if (clipboard == null) {
// fp.sendMessage(BBC.getPrefix() + "You must first use `//anvil copy`");
// return;
// }
// CuboidRegion cuboid = clipboard.getRegion();
// RegionWrapper copyRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
// final Vector offset = player.getPosition().subtract(clipboard.getOrigin());
// if (alignChunk) {
// offset.setComponents((offset.getBlockX() >> 4) << 4, offset.getBlockY(), (offset.getBlockZ() >> 4) << 4);
// }
// int oX = offset.getBlockX();
// int oZ = offset.getBlockZ();
// RegionWrapper pasteRegion = new RegionWrapper(copyRegion.minX + oX, copyRegion.maxX + oX, copyRegion.minZ + oZ, copyRegion.maxZ + oZ);
// String pasteWorldName = Fawe.imp().getWorldName(editSession.getWorld());
// FaweQueue tmpTo = SetQueue.IMP.getNewQueue(pasteWorldName, true, false);
// MCAQueue copyQueue = clipboard.getQueue();
// MCAQueue pasteQueue = new MCAQueue(tmpTo);
//
// fp.checkAllowedRegion(pasteRegion);
// recordHistory(fp, editSession.getWorld(), iAnvilHistory -> {
// try {
// pasteQueue.pasteRegion(copyQueue, copyRegion, offset, iAnvilHistory);
// } catch (IOException e) { throw new RuntimeException(e); }
// });
// BBC.COMMAND_PASTE.send(player, player.getPosition().toBlockVector());
// }
}

View File

@ -0,0 +1,100 @@
package com.boydti.fawe.command;
import com.boydti.fawe.config.Commands;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.changeset.CFIChangeSet;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.MethodCommands;
import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import java.io.IOException;
public class CFICommand extends MethodCommands {
private final CFICommands child;
private final SimpleDispatcher dispatcher;
public CFICommand(WorldEdit worldEdit, ParametricBuilder builder) {
super(worldEdit);
this.dispatcher = new SimpleDispatcher();
this.child = new CFICommands(worldEdit, dispatcher);
builder.registerMethodsAsCommands(dispatcher, child);
}
@Command(
aliases = {"cfi", "createfromimage"},
usage = "",
min = 0,
max = -1,
anyFlags = true,
desc = "Start CreateFromImage"
)
@CommandPermissions("worldedit.anvil.cfi")
public void cfi(FawePlayer fp, CommandContext context) throws CommandException, IOException {
CFICommands.CFISettings settings = child.getSettings(fp);
settings.popMessages(fp);
dispatch(fp, settings, context);
HeightMapMCAGenerator gen = settings.getGenerator();
if (gen != null && gen.isModified()) {
gen.update();
CFIChangeSet set = new CFIChangeSet(gen, fp.getUUID());
LocalSession session = fp.getSession();
session.remember(fp.getPlayer(), gen, set, fp.getLimit());
}
}
private void dispatch(FawePlayer fp, CFICommands.CFISettings settings, CommandContext context) throws CommandException {
if (!settings.hasGenerator()) {
switch (context.argsLength()) {
case 0: {
String hmCmd = child.alias() + " ";
if (settings.image == null) {
hmCmd += "image";
} else {
hmCmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + settings.imageArg;
}
child.msg("What do you want to use as the base?").newline()
.text("&7[&aHeightMap&7]").cmdTip(hmCmd).text(" - A heightmap like ").text("&7[&athis&7]").linkTip("http://i.imgur.com/qCd30MR.jpg")
.newline()
.text("&7[&aEmpty&7]").cmdTip(child.alias() + " empty").text("- An empty map of a specific size")
.send(fp);
break;
}
default: {
String remaining = context.getJoinedStrings(0);
if (!dispatcher.contains(context.getString(0))) {
switch (context.argsLength()) {
case 1: {
String cmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + context.getJoinedStrings(0);
dispatcher.call(cmd, context.getLocals(), new String[0]);
return;
}
case 2: {
String cmd = Commands.getAlias(CFICommands.class, "empty") + " " + context.getJoinedStrings(0);
dispatcher.call(cmd, context.getLocals(), new String[0]);
return;
}
}
}
dispatcher.call(remaining, context.getLocals(), new String[0]);
}
}
} else {
switch (context.argsLength()) {
case 0:
settings.setCategory("");
child.mainMenu(fp);
break;
default:
dispatcher.call(context.getJoinedStrings(0), context.getLocals(), new String[0]);
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
package com.boydti.fawe.command;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.worldedit.EditSession;
import java.util.Collection;
import java.util.UUID;
public class Cancel extends FaweCommand {
public Cancel() {
super("fawe.cancel", false);
}
@Override
public boolean execute(final FawePlayer player, final String... args) {
if (player == null) {
return false;
}
int cancelled = player.cancel(false);
BBC.WORLDEDIT_CANCEL_COUNT.send(player, cancelled);
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More