geforkt von Mirrors/FastAsyncWorldEdit
Move Clipboards to /tmp + Update upstream #3
20
.github/workflows/upload-release-assets.yml
vendored
20
.github/workflows/upload-release-assets.yml
vendored
@ -26,3 +26,23 @@ jobs:
|
|||||||
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
|
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
release-tag: ${{ github.event.release.tag_name }}
|
release-tag: ${{ github.event.release.tag_name }}
|
||||||
|
- name: Upload release to CurseForge
|
||||||
|
uses: Kir-Antipov/mc-publish@v3.2
|
||||||
|
with:
|
||||||
|
curseforge-id: 103525
|
||||||
|
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||||
|
|
||||||
|
files-primary: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
|
||||||
|
|
||||||
|
name: FastAsyncWorldEdit ${{ github.event.release.tag_name }}
|
||||||
|
version: ${{ github.event.release.tag_name }}
|
||||||
|
version-type: release
|
||||||
|
|
||||||
|
game-versions: |
|
||||||
|
1.19.1
|
||||||
|
1.19
|
||||||
|
1.18.2
|
||||||
|
1.18.1
|
||||||
|
1.18
|
||||||
|
1.17
|
||||||
|
1.16
|
||||||
|
@ -23,7 +23,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.4.5")
|
var rootVersion by extra("2.4.7")
|
||||||
var snapshot by extra("SNAPSHOT")
|
var snapshot by extra("SNAPSHOT")
|
||||||
var revision: String by extra("")
|
var revision: String by extra("")
|
||||||
var buildNumber by extra("")
|
var buildNumber by extra("")
|
||||||
|
@ -12,7 +12,7 @@ griefprevention = "16.18"
|
|||||||
griefdefender = "2.1.0-SNAPSHOT"
|
griefdefender = "2.1.0-SNAPSHOT"
|
||||||
mcore = "7.0.1"
|
mcore = "7.0.1"
|
||||||
residence = "4.5._13.1"
|
residence = "4.5._13.1"
|
||||||
towny = "0.98.3.5"
|
towny = "0.98.3.8"
|
||||||
redprotect = "1.9.6"
|
redprotect = "1.9.6"
|
||||||
|
|
||||||
# Third party
|
# Third party
|
||||||
@ -38,7 +38,7 @@ text = "3.0.4"
|
|||||||
piston = "0.5.7"
|
piston = "0.5.7"
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
mockito = "4.7.0"
|
mockito = "4.8.0"
|
||||||
|
|
||||||
# Gradle plugins
|
# Gradle plugins
|
||||||
pluginyml = "0.5.2"
|
pluginyml = "0.5.2"
|
||||||
|
@ -3,4 +3,4 @@ build:
|
|||||||
- "./gradlew --stop"
|
- "./gradlew --stop"
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
"/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.4.5-SNAPSHOT.jar"
|
"/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.4.7-SNAPSHOT.jar"
|
||||||
|
@ -473,7 +473,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ import java.util.Collections;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.OptionalLong;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -217,10 +218,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
BiomeProvider biomeProvider = getBiomeProvider();
|
BiomeProvider biomeProvider = getBiomeProvider();
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||||
|
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||||
WorldGenSettings newOpts = levelProperties.worldGenSettings()
|
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||||
.withSeed(originalWorldData.settings.hardcore(), options.getSeed());
|
: originalOpts;
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
originalWorldData.settings.gameType(),
|
originalWorldData.settings.gameType(),
|
||||||
|
@ -588,7 +588,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||||
|
@ -213,10 +213,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
BiomeProvider biomeProvider = getBiomeProvider();
|
BiomeProvider biomeProvider = getBiomeProvider();
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||||
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
|
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||||
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed))
|
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||||
: originalOpts;
|
: originalOpts;
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
|
@ -585,23 +585,6 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
|
||||||
// Quickly add each entity to a list copy.
|
|
||||||
List<Entity> mcEntities = new ArrayList<>();
|
|
||||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
|
||||||
|
|
||||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
|
||||||
mcEntities.forEach((mcEnt) -> {
|
|
||||||
org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity();
|
|
||||||
if (bukkitEntity.isValid()) {
|
|
||||||
list.add(bukkitEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||||
|
@ -676,7 +676,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||||
for (UUID uuid : entityRemoves) {
|
for (UUID uuid : entityRemoves) {
|
||||||
Entity entity = nmsWorld.entityManager.getEntityGetter().get(uuid);
|
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
|
@ -278,20 +278,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||||
// UNLOADED_CHUNK
|
LevelChunk levelChunk;
|
||||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
|
||||||
.getTickingChunkFuture()
|
|
||||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
|
||||||
if (PaperLib.isPaper()) {
|
if (PaperLib.isPaper()) {
|
||||||
// getChunkAtIfLoadedImmediately is paper only
|
// getChunkAtIfLoadedImmediately is paper only
|
||||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
levelChunk = nmsWorld
|
||||||
.getChunkSource()
|
.getChunkSource()
|
||||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
|
} else {
|
||||||
|
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
|
||||||
|
.getTickingChunkFuture() // method is not present with new paper chunk system
|
||||||
|
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
if (optional.isEmpty()) {
|
if (levelChunk == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LevelChunk levelChunk = optional.get();
|
|
||||||
TaskManager.taskManager().task(() -> {
|
TaskManager.taskManager().task(() -> {
|
||||||
ClientboundLevelChunkWithLightPacket packet;
|
ClientboundLevelChunkWithLightPacket packet;
|
||||||
if (PaperLib.isPaper()) {
|
if (PaperLib.isPaper()) {
|
||||||
@ -589,7 +590,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
if (PaperLib.isPaper()) {
|
||||||
|
return Arrays.asList(chunk.entities.getRawData());
|
||||||
|
}
|
||||||
|
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||||
|
@ -214,10 +214,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||||
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
|
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||||
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed))
|
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||||
: originalOpts;
|
: originalOpts;
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
|
@ -110,7 +110,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
|
|||||||
if (player != null && Settings.settings().CLIPBOARD.USE_DISK) {
|
if (player != null && Settings.settings().CLIPBOARD.USE_DISK) {
|
||||||
BukkitPlayer cached = WorldEditPlugin.getInstance().getCachedPlayer(player);
|
BukkitPlayer cached = WorldEditPlugin.getInstance().getCachedPlayer(player);
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
loadClipboardFromDisk();
|
//loadClipboardFromDisk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
@ -141,10 +141,9 @@ public class BukkitWorld extends AbstractWorld {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<com.sk89q.worldedit.entity.Entity> getEntities(Region region) {
|
public List<com.sk89q.worldedit.entity.Entity> getEntities(Region region) {
|
||||||
//FAWE start - allow async entity retrieval
|
World world = getWorld();
|
||||||
List<Entity> ents = WorldEditPlugin.getInstance().getBukkitImplAdapter().getEntities(getWorld());
|
|
||||||
//FAWE end
|
|
||||||
|
|
||||||
|
List<Entity> ents = world.getEntities();
|
||||||
List<com.sk89q.worldedit.entity.Entity> entities = new ArrayList<>();
|
List<com.sk89q.worldedit.entity.Entity> entities = new ArrayList<>();
|
||||||
for (Entity ent : ents) {
|
for (Entity ent : ents) {
|
||||||
if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) {
|
if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) {
|
||||||
@ -157,9 +156,7 @@ public class BukkitWorld extends AbstractWorld {
|
|||||||
@Override
|
@Override
|
||||||
public List<com.sk89q.worldedit.entity.Entity> getEntities() {
|
public List<com.sk89q.worldedit.entity.Entity> getEntities() {
|
||||||
List<com.sk89q.worldedit.entity.Entity> list = new ArrayList<>();
|
List<com.sk89q.worldedit.entity.Entity> list = new ArrayList<>();
|
||||||
//FAWE start - allow async entity retrieval
|
for (Entity entity : getWorld().getEntities()) {
|
||||||
for (Entity entity : WorldEditPlugin.getInstance().getBukkitImplAdapter().getEntities(getWorld())) {
|
|
||||||
//FAWE end
|
|
||||||
list.add(BukkitAdapter.adapt(entity));
|
list.add(BukkitAdapter.adapt(entity));
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
@ -71,16 +71,25 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
private boolean canHaveBiomes = true;
|
private boolean canHaveBiomes = true;
|
||||||
private int nbtBytesRemaining;
|
private int nbtBytesRemaining;
|
||||||
|
|
||||||
|
private static File createTmpFile() {
|
||||||
|
File temp;
|
||||||
|
try {
|
||||||
|
temp = File.createTempFile("fawe", ".bd");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
temp.deleteOnExit();
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DiskOptimizedClipboard for the given region. Creates or overwrites a file using the given UUID as a name.
|
* Creates a new DiskOptimizedClipboard for the given region. Creates or overwrites a file using the given UUID as a name.
|
||||||
*/
|
*/
|
||||||
public DiskOptimizedClipboard(Region region, UUID uuid) {
|
public DiskOptimizedClipboard(Region region, UUID uuid) {
|
||||||
this(
|
this(
|
||||||
region.getDimensions(),
|
region.getDimensions(),
|
||||||
MainUtil.getFile(
|
createTmpFile()
|
||||||
Fawe.instance() != null ? Fawe.platform().getDirectory() : new File("."),
|
|
||||||
Settings.settings().PATHS.CLIPBOARD + File.separator + uuid + ".bd"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
setOffset(region.getMinimumPoint());
|
setOffset(region.getMinimumPoint());
|
||||||
setOrigin(region.getMinimumPoint());
|
setOrigin(region.getMinimumPoint());
|
||||||
|
@ -66,7 +66,7 @@ public class SaturatePattern extends AbstractPattern {
|
|||||||
}
|
}
|
||||||
int newColor = TextureUtil.multiplyColor(currentColor, color);
|
int newColor = TextureUtil.multiplyColor(currentColor, color);
|
||||||
BlockType newBlock = util.getNearestBlock(newColor);
|
BlockType newBlock = util.getNearestBlock(newColor);
|
||||||
if (newBlock.equals(type)) {
|
if (newBlock == null || newBlock.equals(type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return set.setBlock(extent, newBlock.getDefaultState());
|
return set.setBlock(extent, newBlock.getDefaultState());
|
||||||
|
@ -5,6 +5,9 @@ import com.fastasyncworldedit.core.configuration.Settings;
|
|||||||
import com.fastasyncworldedit.core.extent.filter.block.SingleFilterBlock;
|
import com.fastasyncworldedit.core.extent.filter.block.SingleFilterBlock;
|
||||||
import com.fastasyncworldedit.core.util.image.ImageUtil;
|
import com.fastasyncworldedit.core.util.image.ImageUtil;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
@ -24,19 +27,26 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -321,6 +331,8 @@ public class TextureUtil implements TextureHolder {
|
|||||||
new BiomeColor(253, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F),
|
new BiomeColor(253, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F),
|
||||||
new BiomeColor(254, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F),
|
new BiomeColor(254, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F),
|
||||||
new BiomeColor(255, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F)};
|
new BiomeColor(255, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F)};
|
||||||
|
|
||||||
|
private static final String VERSION_MANIFEST = "https://piston-meta.mojang.com/mc/game/version_manifest.json";
|
||||||
private final BlockType[] layerBuffer = new BlockType[2];
|
private final BlockType[] layerBuffer = new BlockType[2];
|
||||||
protected int[] blockColors = new int[BlockTypes.size()];
|
protected int[] blockColors = new int[BlockTypes.size()];
|
||||||
protected long[] blockDistance = new long[BlockTypes.size()];
|
protected long[] blockDistance = new long[BlockTypes.size()];
|
||||||
@ -352,17 +364,43 @@ public class TextureUtil implements TextureHolder {
|
|||||||
try {
|
try {
|
||||||
LOGGER.info("Downloading asset jar from Mojang, please wait...");
|
LOGGER.info("Downloading asset jar from Mojang, please wait...");
|
||||||
new File(Fawe.platform().getDirectory() + "/" + Settings.settings().PATHS.TEXTURES + "/").mkdirs();
|
new File(Fawe.platform().getDirectory() + "/" + Settings.settings().PATHS.TEXTURES + "/").mkdirs();
|
||||||
try (BufferedInputStream in = new BufferedInputStream(
|
|
||||||
new URL("https://piston-data.mojang.com/v1/objects/c0898ec7c6a5a2eaa317770203a1554260699994/client.jar")
|
try {
|
||||||
.openStream());
|
VersionMetadata metadata = getLatestVersion();
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(
|
LOGGER.info("Latest release version is {}", metadata.version());
|
||||||
Fawe.platform().getDirectory() + "/" + Settings.settings().PATHS.TEXTURES + "/1.19.2.jar")) {
|
HashedResource resource = getLatestClientJarUrl(metadata);
|
||||||
byte[] dataBuffer = new byte[1024];
|
|
||||||
int bytesRead;
|
Path out = Path.of(
|
||||||
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
|
Fawe.platform().getDirectory().getPath(),
|
||||||
fileOutputStream.write(dataBuffer, 0, bytesRead);
|
Settings.settings().PATHS.TEXTURES,
|
||||||
|
metadata.version() + ".jar"
|
||||||
|
);
|
||||||
|
// Copy resource to local fs
|
||||||
|
try (final InputStream stream = new URL(resource.resource()).openStream();
|
||||||
|
final OutputStream writer = Files.newOutputStream(out)) {
|
||||||
|
stream.transferTo(writer);
|
||||||
}
|
}
|
||||||
LOGGER.info("Asset jar down has been downloaded successfully.");
|
// Validate sha-1 hash
|
||||||
|
try {
|
||||||
|
final String sha1 = calculateSha1(out);
|
||||||
|
if (!sha1.equals(resource.hash())) {
|
||||||
|
Files.deleteIfExists(out);
|
||||||
|
LOGGER.error(
|
||||||
|
"Hash comparison of final file failed (Expected: '{}', Calculated: '{}')",
|
||||||
|
resource.hash(), sha1
|
||||||
|
);
|
||||||
|
LOGGER.error("To prevent possibly malicious intentions, the downloaded file has been removed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
LOGGER.warn("Couldn't verify integrity of downloaded client file");
|
||||||
|
LOGGER.warn(
|
||||||
|
"Please verify that the downloaded files '{}' hash is equal to '{}'",
|
||||||
|
out, resource.hash()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOGGER.info("Asset jar has been downloaded and validated successfully.");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
"Could not download version jar. Please do so manually by creating a `FastAsyncWorldEdit/textures` " +
|
"Could not download version jar. Please do so manually by creating a `FastAsyncWorldEdit/textures` " +
|
||||||
@ -388,7 +426,7 @@ public class TextureUtil implements TextureHolder {
|
|||||||
HashSet<BlockType> blocks = new HashSet<>();
|
HashSet<BlockType> blocks = new HashSet<>();
|
||||||
for (int typeId = 0; typeId < ids.length; typeId++) {
|
for (int typeId = 0; typeId < ids.length; typeId++) {
|
||||||
if (ids[typeId]) {
|
if (ids[typeId]) {
|
||||||
blocks.add(BlockTypes.get(typeId));
|
blocks.add(BlockTypesCache.values[typeId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fromBlocks(blocks);
|
return fromBlocks(blocks);
|
||||||
@ -406,7 +444,7 @@ public class TextureUtil implements TextureHolder {
|
|||||||
|
|
||||||
TextureUtil tu = Fawe.instance().getTextureUtil();
|
TextureUtil tu = Fawe.instance().getTextureUtil();
|
||||||
for (int typeId : tu.getValidBlockIds()) {
|
for (int typeId : tu.getValidBlockIds()) {
|
||||||
BlockType block = BlockTypes.get(typeId);
|
BlockType block = BlockTypesCache.values[typeId];
|
||||||
extent.init(0, 0, 0, block.getDefaultState().toBaseBlock());
|
extent.init(0, 0, 0, block.getDefaultState().toBaseBlock());
|
||||||
if (mask.test(extent)) {
|
if (mask.test(extent)) {
|
||||||
blocks.add(block);
|
blocks.add(block);
|
||||||
@ -555,6 +593,66 @@ public class TextureUtil implements TextureHolder {
|
|||||||
return totalDistSqr / area;
|
return totalDistSqr / area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the minecraft versions manifest (containing all released versions) and returns the first {@code release}
|
||||||
|
* version (latest)
|
||||||
|
*
|
||||||
|
* @return {@link VersionMetadata} containing the id (= version) and url to the client manifest itself
|
||||||
|
* @throws IOException If any http / i/o operation fails.
|
||||||
|
* @since 2.4.6
|
||||||
|
*/
|
||||||
|
private static VersionMetadata getLatestVersion() throws IOException {
|
||||||
|
try (BufferedInputStream in = new BufferedInputStream(new URL(VERSION_MANIFEST).openStream());
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
|
||||||
|
final JsonElement element = JsonParser.parseReader(reader);
|
||||||
|
for (final JsonElement versions : element.getAsJsonObject().getAsJsonArray("versions")) {
|
||||||
|
JsonObject version = versions.getAsJsonObject();
|
||||||
|
if (!version.get("type").getAsString().equals("release")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String clientJsonUrl = version.get("url").getAsString();
|
||||||
|
final String id = version.get("id").getAsString();
|
||||||
|
return new VersionMetadata(id, clientJsonUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException("Failed to get latest version metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the url to the client.jar based on the previously retrieved {@link VersionMetadata}
|
||||||
|
*
|
||||||
|
* @param metadata The version metadata containing the url to the client.jar
|
||||||
|
* @return The full url to the client.jar including the expected file hash for validation purposes
|
||||||
|
* @throws IOException If any http / i/o operation fails.
|
||||||
|
* @since 2.4.6
|
||||||
|
*/
|
||||||
|
private static HashedResource getLatestClientJarUrl(VersionMetadata metadata) throws IOException {
|
||||||
|
try (BufferedInputStream in = new BufferedInputStream(new URL(metadata.url()).openStream());
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
|
||||||
|
final JsonObject object = JsonParser.parseReader(reader).getAsJsonObject();
|
||||||
|
final JsonObject client = object.getAsJsonObject("downloads").getAsJsonObject("client");
|
||||||
|
return new HashedResource(client.get("url").getAsString(), client.get("sha1").getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the sha-1 hash based on the content of the provided file.
|
||||||
|
*
|
||||||
|
* @param path The path of the resource to generate the sha-1 hash for
|
||||||
|
* @return The hash of the file contents
|
||||||
|
* @throws NoSuchAlgorithmException If the SHA-1 algorithm could not be resolved
|
||||||
|
* @throws IOException If any I/O operation failed
|
||||||
|
* @since 2.4.6
|
||||||
|
*/
|
||||||
|
private static String calculateSha1(Path path) throws NoSuchAlgorithmException, IOException {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
try (final BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(path));
|
||||||
|
final DigestOutputStream digestOutputStream = new DigestOutputStream(OutputStream.nullOutputStream(), digest)) {
|
||||||
|
stream.transferTo(digestOutputStream);
|
||||||
|
return HexFormat.of().formatHex(digest.digest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TextureUtil getTextureUtil() {
|
public TextureUtil getTextureUtil() {
|
||||||
return this;
|
return this;
|
||||||
@ -586,7 +684,7 @@ public class TextureUtil implements TextureHolder {
|
|||||||
if (min == Long.MAX_VALUE) {
|
if (min == Long.MAX_VALUE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return BlockTypes.get(closest);
|
return BlockTypesCache.values[closest];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -615,7 +713,7 @@ public class TextureUtil implements TextureHolder {
|
|||||||
if (min == Long.MAX_VALUE) {
|
if (min == Long.MAX_VALUE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return BlockTypes.get(closest);
|
return BlockTypesCache.values[closest];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -638,8 +736,8 @@ public class TextureUtil implements TextureHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layerBuffer[0] = BlockTypes.get(closest[0]);
|
layerBuffer[0] = BlockTypesCache.values[closest[0]];
|
||||||
layerBuffer[1] = BlockTypes.get(closest[1]);
|
layerBuffer[1] = BlockTypesCache.values[closest[1]];
|
||||||
return layerBuffer;
|
return layerBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,36 +904,25 @@ public class TextureUtil implements TextureHolder {
|
|||||||
if (folder.exists()) {
|
if (folder.exists()) {
|
||||||
// Get all the jar files
|
// Get all the jar files
|
||||||
File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar"));
|
File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar"));
|
||||||
if (files.length == 0) {
|
// We expect the latest version to be already there, due to the download in TextureUtil#<init>
|
||||||
new File(Fawe.platform().getDirectory() + "/" + Settings.settings().PATHS.TEXTURES + "/")
|
if (files == null || files.length == 0) {
|
||||||
.mkdirs();
|
LOGGER.error("No version jar found in {}. Delete the named folder and restart your server to download the " +
|
||||||
try (BufferedInputStream in = new BufferedInputStream(
|
"missing assets.", folder.getPath());
|
||||||
new URL("https://piston-data.mojang.com/v1/objects/c0898ec7c6a5a2eaa317770203a1554260699994/client.jar")
|
LOGGER.error(
|
||||||
.openStream());
|
"If no asset jar is created, please do so manually by creating a `FastAsyncWorldEdit/textures` " +
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(
|
"folder with a `.minecraft/versions` jar or mods in it.");
|
||||||
Fawe.platform().getDirectory() + "/" + Settings.settings().PATHS.TEXTURES + "/1.19.2.jar")) {
|
|
||||||
byte[] dataBuffer = new byte[1024];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
|
|
||||||
fileOutputStream.write(dataBuffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
fileOutputStream.close();
|
|
||||||
files = folder.listFiles((dir, name) -> name.endsWith(".jar"));
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error(
|
|
||||||
"Could not download version jar. Please do so manually by creating a `FastAsyncWorldEdit/textures` " +
|
|
||||||
"folder with a `.minecraft/versions` jar or mods in it.");
|
|
||||||
LOGGER.error("If the file exists, please make sure the server has read access to the directory.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ((files.length > 0)) {
|
if (files != null && (files.length > 0)) {
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
ZipFile zipFile = new ZipFile(file);
|
ZipFile zipFile = new ZipFile(file);
|
||||||
|
|
||||||
// Get all the groups in the current jar
|
// Get all the groups in the current jar
|
||||||
// The vanilla textures are in `assets/minecraft`
|
// The vanilla textures are in `assets/minecraft`
|
||||||
// A jar may contain textures for multiple mods
|
// A jar may contain textures for multiple mods
|
||||||
String modelsDir = "assets/%1$s/models/block/%2$s.json";
|
String[] modelsDir = {
|
||||||
|
"assets/%1$s/models/block/%2$s.json",
|
||||||
|
"assets/%1$s/models/item/%2$s.json"
|
||||||
|
};
|
||||||
String texturesDir = "assets/%1$s/textures/%2$s.png";
|
String texturesDir = "assets/%1$s/textures/%2$s.png";
|
||||||
|
|
||||||
Type typeToken = new TypeToken<Map<String, Object>>() {
|
Type typeToken = new TypeToken<Map<String, Object>>() {
|
||||||
@ -859,10 +946,15 @@ public class TextureUtil implements TextureHolder {
|
|||||||
String nameSpace = split.length == 1 ? "" : split[0];
|
String nameSpace = split.length == 1 ? "" : split[0];
|
||||||
|
|
||||||
// Read models
|
// Read models
|
||||||
String modelFileName = String.format(modelsDir, nameSpace, name);
|
ZipEntry entry = null;
|
||||||
ZipEntry entry = getEntry(zipFile, modelFileName);
|
for (final String dir : modelsDir) {
|
||||||
|
String modelFileName = String.format(dir, nameSpace, name);
|
||||||
|
if ((entry = getEntry(zipFile, modelFileName)) != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
LOGGER.error("Cannot find {} in {}", modelFileName, file);
|
LOGGER.error("Cannot find {} in {}", modelsDir, file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1080,7 +1172,8 @@ public class TextureUtil implements TextureHolder {
|
|||||||
if (min == Long.MAX_VALUE) {
|
if (min == Long.MAX_VALUE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return BlockTypes.get(closest);
|
|
||||||
|
return BlockTypesCache.values[closest];
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFileName(String path) {
|
private String getFileName(String path) {
|
||||||
@ -1130,4 +1223,12 @@ public class TextureUtil implements TextureHolder {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record VersionMetadata(String version, String url) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private record HashedResource(String resource, String hash) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -455,7 +455,7 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
|||||||
state = item.getType().getBlockType().getDefaultState();
|
state = item.getType().getBlockType().getDefaultState();
|
||||||
nbt = item.getNbtData();
|
nbt = item.getNbtData();
|
||||||
} else {
|
} else {
|
||||||
BlockType type = BlockTypes.parse(typeString.toLowerCase(Locale.ROOT));
|
BlockType type = BlockTypes.parse(typeString.toLowerCase(Locale.ROOT), context);
|
||||||
|
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
state = type.getDefaultState();
|
state = type.getDefaultState();
|
||||||
|
@ -484,7 +484,12 @@ public class MCEditSchematicReader extends NBTSchematicReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BlockState getBlockState(int id, int data) {
|
private BlockState getBlockState(int id, int data) {
|
||||||
return LegacyMapper.getInstance().getBlockFromLegacy(id, data);
|
BlockState foundBlock = LegacyMapper.getInstance().getBlockFromLegacy(id, data);
|
||||||
|
if (foundBlock == null && data != 0) {
|
||||||
|
// Some schematics contain invalid data values, so try without the data value
|
||||||
|
return LegacyMapper.getInstance().getBlockFromLegacy(id, 0);
|
||||||
|
}
|
||||||
|
return foundBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.command.SuggestInputParseException;
|
|||||||
import com.fastasyncworldedit.core.util.JoinedCharSequence;
|
import com.fastasyncworldedit.core.util.JoinedCharSequence;
|
||||||
import com.fastasyncworldedit.core.util.StringMan;
|
import com.fastasyncworldedit.core.util.StringMan;
|
||||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||||
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -1948,6 +1949,9 @@ public final class BlockTypes {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public static BlockType parse(final String type) throws InputParseException {
|
public static BlockType parse(final String type) throws InputParseException {
|
||||||
|
return parse(type, new ParserContext());
|
||||||
|
}
|
||||||
|
public static BlockType parse(final String type, final ParserContext context) throws InputParseException {
|
||||||
final String inputLower = type.toLowerCase(Locale.ROOT);
|
final String inputLower = type.toLowerCase(Locale.ROOT);
|
||||||
String input = inputLower;
|
String input = inputLower;
|
||||||
|
|
||||||
@ -1958,13 +1962,14 @@ public final class BlockTypes {
|
|||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if (context.isTryingLegacy()) {
|
||||||
try {
|
try {
|
||||||
BlockStateHolder<BlockState> block = LegacyMapper.getInstance().getBlockFromLegacy(input);
|
BlockStateHolder<BlockState> block = LegacyMapper.getInstance().getBlockFromLegacy(input);
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
return block.getBlockType();
|
return block.getBlockType();
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException | IndexOutOfBoundsException ignored) {
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException | IndexOutOfBoundsException ignored) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new SuggestInputParseException("Does not match a valid block type: " + inputLower, inputLower, () -> Stream.of(
|
throw new SuggestInputParseException("Does not match a valid block type: " + inputLower, inputLower, () -> Stream.of(
|
||||||
|
@ -28,7 +28,7 @@ dependencies {
|
|||||||
})
|
})
|
||||||
api("org.apache.logging.log4j:log4j-api")
|
api("org.apache.logging.log4j:log4j-api")
|
||||||
api("org.bstats:bstats-sponge:1.7")
|
api("org.bstats:bstats-sponge:1.7")
|
||||||
testImplementation("org.mockito:mockito-core:4.7.0")
|
testImplementation("org.mockito:mockito-core:4.8.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren