diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml index 93587ed76..cbfa9eafa 100644 --- a/.github/workflows/upload-release-assets.yml +++ b/.github/workflows/upload-release-assets.yml @@ -26,3 +26,23 @@ jobs: files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar' repo-token: ${{ secrets.GITHUB_TOKEN }} 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 diff --git a/build.gradle.kts b/build.gradle.kts index 7ea9ffb46..069db1c3e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 revision: String by extra("") var buildNumber by extra("") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 689fb110a..b6a7f4236 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ griefprevention = "16.18" griefdefender = "2.1.0-SNAPSHOT" mcore = "7.0.1" residence = "4.5._13.1" -towny = "0.98.3.5" +towny = "0.98.3.8" redprotect = "1.9.6" # Third party @@ -38,7 +38,7 @@ text = "3.0.4" piston = "0.5.7" # Tests -mockito = "4.7.0" +mockito = "4.8.0" # Gradle plugins pluginyml = "0.5.2" diff --git a/steamwarci.yml b/steamwarci.yml index b851e38b3..06489e900 100644 --- a/steamwarci.yml +++ b/steamwarci.yml @@ -3,4 +3,4 @@ build: - "./gradlew --stop" 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" diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java index 4b446d620..3ea9df730 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java @@ -473,7 +473,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static List getEntities(LevelChunk chunk) { - return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ)); + return chunk.level.entityManager.getEntities(chunk.getPos()); } } diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java index 6ea695d8c..3d9694af9 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java @@ -79,6 +79,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.OptionalLong; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -217,10 +218,10 @@ public class PaperweightRegen extends Regenerator 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 { diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java index 214484948..eae01a685 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java @@ -213,10 +213,9 @@ public class PaperweightRegen extends Regenerator getEntities(org.bukkit.World world) { - // Quickly add each entity to a list copy. - List mcEntities = new ArrayList<>(); - ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add); - - List list = new ArrayList<>(); - mcEntities.forEach((mcEnt) -> { - org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity(); - if (bukkitEntity.isValid()) { - list.add(bukkitEntity); - } - - }); - return list; - } - @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java index 3c543d2c7..9ee467201 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java @@ -676,7 +676,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.entityManager.getEntityGetter().get(uuid); + Entity entity = nmsWorld.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java index aa87aceea..307a52d77 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java @@ -278,20 +278,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return; } ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ); - // UNLOADED_CHUNK - Optional optional = ((Either) chunkHolder - .getTickingChunkFuture() - .getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); + LevelChunk levelChunk; if (PaperLib.isPaper()) { // getChunkAtIfLoadedImmediately is paper only - optional = optional.or(() -> Optional.ofNullable(nmsWorld + levelChunk = nmsWorld .getChunkSource() - .getChunkAtIfLoadedImmediately(chunkX, chunkZ))); + .getChunkAtIfLoadedImmediately(chunkX, chunkZ); + } else { + levelChunk = ((Optional) ((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; } - LevelChunk levelChunk = optional.get(); TaskManager.taskManager().task(() -> { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { @@ -589,7 +590,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static List 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 { diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java index 44e579e90..e97f78247 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java @@ -214,10 +214,9 @@ public class PaperweightRegen extends Regenerator getEntities(Region region) { - //FAWE start - allow async entity retrieval - List ents = WorldEditPlugin.getInstance().getBukkitImplAdapter().getEntities(getWorld()); - //FAWE end + World world = getWorld(); + List ents = world.getEntities(); List entities = new ArrayList<>(); for (Entity ent : ents) { if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) { @@ -157,9 +156,7 @@ public class BukkitWorld extends AbstractWorld { @Override public List getEntities() { List list = new ArrayList<>(); - //FAWE start - allow async entity retrieval - for (Entity entity : WorldEditPlugin.getInstance().getBukkitImplAdapter().getEntities(getWorld())) { - //FAWE end + for (Entity entity : getWorld().getEntities()) { list.add(BukkitAdapter.adapt(entity)); } return list; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index acdf9fad7..f87fd5afa 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -71,16 +71,25 @@ public class DiskOptimizedClipboard extends LinearClipboard { private boolean canHaveBiomes = true; 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. */ public DiskOptimizedClipboard(Region region, UUID uuid) { this( region.getDimensions(), - MainUtil.getFile( - Fawe.instance() != null ? Fawe.platform().getDirectory() : new File("."), - Settings.settings().PATHS.CLIPBOARD + File.separator + uuid + ".bd" - ) + createTmpFile() ); setOffset(region.getMinimumPoint()); setOrigin(region.getMinimumPoint()); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SaturatePattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SaturatePattern.java index 48c411750..0c42c814a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SaturatePattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SaturatePattern.java @@ -66,7 +66,7 @@ public class SaturatePattern extends AbstractPattern { } int newColor = TextureUtil.multiplyColor(currentColor, color); BlockType newBlock = util.getNearestBlock(newColor); - if (newBlock.equals(type)) { + if (newBlock == null || newBlock.equals(type)) { return false; } return set.setBlock(extent, newBlock.getDefaultState()); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TextureUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TextureUtil.java index 21771867a..f6f896f56 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TextureUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TextureUtil.java @@ -5,6 +5,9 @@ import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.SingleFilterBlock; import com.fastasyncworldedit.core.util.image.ImageUtil; 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.stream.JsonReader; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -24,19 +27,26 @@ import org.apache.logging.log4j.Logger; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.lang.reflect.Type; import java.net.URL; 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.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.HexFormat; import java.util.List; import java.util.Locale; 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(254, "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]; protected int[] blockColors = new int[BlockTypes.size()]; protected long[] blockDistance = new long[BlockTypes.size()]; @@ -352,17 +364,43 @@ public class TextureUtil implements TextureHolder { try { LOGGER.info("Downloading asset jar from Mojang, please wait..."); 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") - .openStream()); - FileOutputStream fileOutputStream = new FileOutputStream( - 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); + + try { + VersionMetadata metadata = getLatestVersion(); + LOGGER.info("Latest release version is {}", metadata.version()); + HashedResource resource = getLatestClientJarUrl(metadata); + + Path out = Path.of( + Fawe.platform().getDirectory().getPath(), + 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) { LOGGER.error( "Could not download version jar. Please do so manually by creating a `FastAsyncWorldEdit/textures` " + @@ -388,7 +426,7 @@ public class TextureUtil implements TextureHolder { HashSet blocks = new HashSet<>(); for (int typeId = 0; typeId < ids.length; typeId++) { if (ids[typeId]) { - blocks.add(BlockTypes.get(typeId)); + blocks.add(BlockTypesCache.values[typeId]); } } return fromBlocks(blocks); @@ -406,7 +444,7 @@ public class TextureUtil implements TextureHolder { TextureUtil tu = Fawe.instance().getTextureUtil(); for (int typeId : tu.getValidBlockIds()) { - BlockType block = BlockTypes.get(typeId); + BlockType block = BlockTypesCache.values[typeId]; extent.init(0, 0, 0, block.getDefaultState().toBaseBlock()); if (mask.test(extent)) { blocks.add(block); @@ -555,6 +593,66 @@ public class TextureUtil implements TextureHolder { 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 public TextureUtil getTextureUtil() { return this; @@ -586,7 +684,7 @@ public class TextureUtil implements TextureHolder { if (min == Long.MAX_VALUE) { return null; } - return BlockTypes.get(closest); + return BlockTypesCache.values[closest]; } /** @@ -615,7 +713,7 @@ public class TextureUtil implements TextureHolder { if (min == Long.MAX_VALUE) { 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[1] = BlockTypes.get(closest[1]); + layerBuffer[0] = BlockTypesCache.values[closest[0]]; + layerBuffer[1] = BlockTypesCache.values[closest[1]]; return layerBuffer; } @@ -806,36 +904,25 @@ public class TextureUtil implements TextureHolder { if (folder.exists()) { // Get all the jar files File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar")); - if (files.length == 0) { - 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") - .openStream()); - FileOutputStream fileOutputStream = new FileOutputStream( - 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."); - } + // We expect the latest version to be already there, due to the download in TextureUtil# + if (files == null || files.length == 0) { + LOGGER.error("No version jar found in {}. Delete the named folder and restart your server to download the " + + "missing assets.", folder.getPath()); + LOGGER.error( + "If no asset jar is created, please do so manually by creating a `FastAsyncWorldEdit/textures` " + + "folder with a `.minecraft/versions` jar or mods in it."); } - if ((files.length > 0)) { + if (files != null && (files.length > 0)) { for (File file : files) { ZipFile zipFile = new ZipFile(file); // Get all the groups in the current jar // The vanilla textures are in `assets/minecraft` // 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"; Type typeToken = new TypeToken>() { @@ -859,10 +946,15 @@ public class TextureUtil implements TextureHolder { String nameSpace = split.length == 1 ? "" : split[0]; // Read models - String modelFileName = String.format(modelsDir, nameSpace, name); - ZipEntry entry = getEntry(zipFile, modelFileName); + ZipEntry entry = null; + for (final String dir : modelsDir) { + String modelFileName = String.format(dir, nameSpace, name); + if ((entry = getEntry(zipFile, modelFileName)) != null) { + break; + } + } if (entry == null) { - LOGGER.error("Cannot find {} in {}", modelFileName, file); + LOGGER.error("Cannot find {} in {}", modelsDir, file); continue; } @@ -1080,7 +1172,8 @@ public class TextureUtil implements TextureHolder { if (min == Long.MAX_VALUE) { return null; } - return BlockTypes.get(closest); + + return BlockTypesCache.values[closest]; } 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) { + + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index e37e5b0fa..4d8523f84 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -455,7 +455,7 @@ public class DefaultBlockParser extends InputParser { state = item.getType().getBlockType().getDefaultState(); nbt = item.getNbtData(); } else { - BlockType type = BlockTypes.parse(typeString.toLowerCase(Locale.ROOT)); + BlockType type = BlockTypes.parse(typeString.toLowerCase(Locale.ROOT), context); if (type != null) { state = type.getDefaultState(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java index 760598ad5..aebef3e9a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java @@ -484,7 +484,12 @@ public class MCEditSchematicReader extends NBTSchematicReader { } 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 diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index 152c4df9b..47e083809 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.command.SuggestInputParseException; import com.fastasyncworldedit.core.util.JoinedCharSequence; import com.fastasyncworldedit.core.util.StringMan; import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.world.registry.LegacyMapper; import javax.annotation.Nullable; @@ -1948,6 +1949,9 @@ public final class BlockTypes { */ 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); String input = inputLower; @@ -1958,13 +1962,14 @@ public final class BlockTypes { if (result != null) { return result; } - - try { - BlockStateHolder block = LegacyMapper.getInstance().getBlockFromLegacy(input); - if (block != null) { - return block.getBlockType(); + if (context.isTryingLegacy()) { + try { + BlockStateHolder block = LegacyMapper.getInstance().getBlockFromLegacy(input); + if (block != null) { + 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( diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index 20b9d7b68..befd85873 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:4.7.0") + testImplementation("org.mockito:mockito-core:4.8.0") } <<<<<<< HEAD