Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-19 09:20:08 +01:00
Merge branch 'main' into v3
# Conflicts: # worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java # worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java
Dieser Commit ist enthalten in:
Commit
fbb8a9b944
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -31,7 +31,6 @@ body:
|
|||||||
- '1.20'
|
- '1.20'
|
||||||
- '1.19.4'
|
- '1.19.4'
|
||||||
- '1.18.2'
|
- '1.18.2'
|
||||||
- '1.17.1'
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
2
.github/workflows/label-merge-conflicts.yaml
vendored
2
.github/workflows/label-merge-conflicts.yaml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Label conflicting PRs
|
- name: Label conflicting PRs
|
||||||
uses: eps1lon/actions-label-merge-conflict@v2.1.0
|
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||||
with:
|
with:
|
||||||
dirtyLabel: "unresolved-merge-conflict"
|
dirtyLabel: "unresolved-merge-conflict"
|
||||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
@ -34,7 +34,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.9.1")
|
var rootVersion by extra("2.9.2")
|
||||||
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("")
|
||||||
@ -83,7 +83,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyCommonConfiguration()
|
applyCommonConfiguration()
|
||||||
val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.4")
|
val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4")
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
supportedVersions.forEach {
|
supportedVersions.forEach {
|
||||||
|
@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT"
|
|||||||
griefprevention = "17.0.0"
|
griefprevention = "17.0.0"
|
||||||
griefdefender = "2.1.0-SNAPSHOT"
|
griefdefender = "2.1.0-SNAPSHOT"
|
||||||
residence = "4.5._13.1"
|
residence = "4.5._13.1"
|
||||||
towny = "0.100.1.20"
|
towny = "0.100.1.23"
|
||||||
plotsquared = "7.3.6"
|
plotsquared = "7.3.6"
|
||||||
|
|
||||||
# Third party
|
# Third party
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binäre Datei nicht angezeigt.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
|
|||||||
|
|
||||||
include("worldedit-libs")
|
include("worldedit-libs")
|
||||||
|
|
||||||
listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach {
|
listOf("1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach {
|
||||||
include("worldedit-bukkit:adapters:adapter-$it")
|
include("worldedit-bukkit:adapters:adapter-$it")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
applyPaperweightAdapterConfiguration()
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
java
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations.all {
|
|
||||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.17.1-R0.1-SNAPSHOT
|
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
|
||||||
compileOnly(libs.paperlib)
|
|
||||||
}
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import net.minecraft.network.chat.ChatType;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stat;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
class PaperweightFakePlayer extends ServerPlayer {
|
|
||||||
|
|
||||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(
|
|
||||||
UUID.nameUUIDFromBytes("worldedit".getBytes()),
|
|
||||||
"[WorldEdit]"
|
|
||||||
);
|
|
||||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
|
||||||
|
|
||||||
PaperweightFakePlayer(ServerLevel world) {
|
|
||||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 position() {
|
|
||||||
return ORIGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void die(DamageSource damagesource) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OptionalInt openMenu(MenuProvider factory) {
|
|
||||||
return OptionalInt.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayClientMessage(Component message, boolean actionBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(Component message, ChatType type, UUID sender) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat, int amount) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTextEdit(SignBlockEntity sign) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
|
||||||
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class PaperweightWorldNativeAccess implements
|
|
||||||
WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
|
||||||
|
|
||||||
private static final int UPDATE = 1;
|
|
||||||
private static final int NOTIFY = 2;
|
|
||||||
|
|
||||||
private final PaperweightAdapter adapter;
|
|
||||||
private final WeakReference<ServerLevel> world;
|
|
||||||
private SideEffectSet sideEffectSet;
|
|
||||||
|
|
||||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
|
|
||||||
this.adapter = adapter;
|
|
||||||
this.world = world;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ServerLevel getWorld() {
|
|
||||||
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
|
||||||
this.sideEffectSet = sideEffectSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LevelChunk getChunk(int x, int z) {
|
|
||||||
return getWorld().getChunk(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
|
|
||||||
int stateId = BlockStateIdAccess.getBlockStateId(state);
|
|
||||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
|
||||||
? Block.stateById(stateId)
|
|
||||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
|
|
||||||
return chunk.getBlockState(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState setBlockState(
|
|
||||||
LevelChunk chunk,
|
|
||||||
BlockPos position,
|
|
||||||
net.minecraft.world.level.block.state.BlockState state
|
|
||||||
) {
|
|
||||||
return chunk.setType(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
|
||||||
net.minecraft.world.level.block.state.BlockState block,
|
|
||||||
BlockPos position
|
|
||||||
) {
|
|
||||||
return Block.updateFromNeighbourShapes(block, getWorld(), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPos getPosition(int x, int y, int z) {
|
|
||||||
return new BlockPos(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateLightingForBlock(BlockPos position) {
|
|
||||||
getWorld().getChunkSource().getLightEngine().checkBlock(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyBlockUpdate(
|
|
||||||
LevelChunk chunk,
|
|
||||||
BlockPos position,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
|
||||||
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChunkTicking(LevelChunk chunk) {
|
|
||||||
return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
|
|
||||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
|
||||||
getWorld().getChunkSource().blockChanged(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyNeighbors(
|
|
||||||
BlockPos pos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
world.updateNeighborsAt(pos, oldState.getBlock());
|
|
||||||
} else {
|
|
||||||
// When we don't want events, manually run the physics without them.
|
|
||||||
Block block = oldState.getBlock();
|
|
||||||
fireNeighborChanged(pos, world, block, pos.west());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.east());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.below());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.above());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.north());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.south());
|
|
||||||
}
|
|
||||||
if (newState.hasAnalogOutputSignal()) {
|
|
||||||
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
|
||||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateNeighbors(
|
|
||||||
BlockPos pos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState,
|
|
||||||
int recursionLimit
|
|
||||||
) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
// a == updateNeighbors
|
|
||||||
// b == updateDiagonalNeighbors
|
|
||||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
CraftWorld craftWorld = world.getWorld();
|
|
||||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
|
||||||
craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()),
|
|
||||||
CraftBlockData.fromData(newState)
|
|
||||||
);
|
|
||||||
world.getCraftServer().getPluginManager().callEvent(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockStateChange(
|
|
||||||
BlockPos pos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.util.ReflectionUtil;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.material.Material;
|
|
||||||
import net.minecraft.world.level.material.PushReaction;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
|
||||||
|
|
||||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
|
||||||
|
|
||||||
private final Block block;
|
|
||||||
private final BlockState blockState;
|
|
||||||
private final Material material;
|
|
||||||
private final boolean isTranslucent;
|
|
||||||
private final CraftBlockData craftBlockData;
|
|
||||||
private final org.bukkit.Material craftMaterial;
|
|
||||||
private final int opacity;
|
|
||||||
private final CompoundTag tile;
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block) {
|
|
||||||
this(block, block.defaultBlockState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
|
||||||
this.block = block;
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.material = blockState.getMaterial();
|
|
||||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
|
||||||
this.craftMaterial = craftBlockData.getMaterial();
|
|
||||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, Refraction.pickName(
|
|
||||||
"properties", "aP"));
|
|
||||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
|
||||||
Refraction.pickName("canOcclude", "n")
|
|
||||||
);
|
|
||||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
|
||||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
|
||||||
BlockPos.ZERO,
|
|
||||||
blockState
|
|
||||||
);
|
|
||||||
tile = tileEntity == null
|
|
||||||
? null
|
|
||||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(() -> tileEntity.save(new net.minecraft.nbt.CompoundTag())));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Block getBlock() {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockState getState() {
|
|
||||||
return blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CraftBlockData getCraftBlockData() {
|
|
||||||
return craftBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() {
|
|
||||||
return blockState.isAir();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFullCube() {
|
|
||||||
return craftMaterial.isOccluding();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpaque() {
|
|
||||||
return material.isSolidBlocking();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPowerSource() {
|
|
||||||
return blockState.isSignalSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid() {
|
|
||||||
return material.isLiquid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getHardness() {
|
|
||||||
return craftBlockData.getState().destroySpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getResistance() {
|
|
||||||
return block.getExplosionResistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getSlipperiness() {
|
|
||||||
return block.getFriction();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightValue() {
|
|
||||||
return blockState.getLightEmission();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightOpacity() {
|
|
||||||
return opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFragileWhenPushed() {
|
|
||||||
return material.getPushReaction() == PushReaction.DESTROY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUnpushable() {
|
|
||||||
return material.getPushReaction() == PushReaction.BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTicksRandomly() {
|
|
||||||
return block.isRandomlyTicking(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMovementBlocker() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBurnable() {
|
|
||||||
return material.isFlammable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isToolRequired() {
|
|
||||||
// Removed in 1.16.1, this is not present in higher versions
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReplacedDuringPlacement() {
|
|
||||||
return material.isReplaceable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTranslucent() {
|
|
||||||
return isTranslucent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasContainer() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTile() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getDefaultTile() {
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMapColor() {
|
|
||||||
// rgb field
|
|
||||||
return material.getColor().col;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,671 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
|
||||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.sk89q.jnbt.Tag;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen.PaperweightRegen;
|
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
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.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
|
|
||||||
import com.sk89q.worldedit.world.RegenOptions;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|
||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.WritableRegistry;
|
|
||||||
import net.minecraft.nbt.IntTag;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.util.StringRepresentable;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
|
||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.util.CraftNamespacedKey;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
private final PaperweightAdapter parent;
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Code that may break between versions of Minecraft
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
|
||||||
private char[] ibdToStateOrdinal = null;
|
|
||||||
private int[] ordinalToIbdID = null;
|
|
||||||
private boolean initialised = false;
|
|
||||||
private Map<String, List<Property<?>>> allBlockProperties = null;
|
|
||||||
|
|
||||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
|
||||||
this.parent = new PaperweightAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String getEntityId(Entity entity) {
|
|
||||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
|
||||||
return resourceLocation == null ? null : resourceLocation.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private synchronized boolean init() {
|
|
||||||
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
|
|
||||||
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
|
|
||||||
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
|
|
||||||
BlockState blockState = BlockTypesCache.states[i];
|
|
||||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
|
|
||||||
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
|
|
||||||
char ordinal = blockState.getOrdinalChar();
|
|
||||||
ibdToStateOrdinal[id] = ordinal;
|
|
||||||
ordinalToIbdID[ordinal] = id;
|
|
||||||
}
|
|
||||||
Map<String, List<Property<?>>> properties = new HashMap<>();
|
|
||||||
try {
|
|
||||||
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
|
|
||||||
Object obj = field.get(null);
|
|
||||||
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Property<?> property;
|
|
||||||
if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
|
|
||||||
property = new BooleanProperty(
|
|
||||||
state.getName(),
|
|
||||||
(List<Boolean>) ImmutableList.copyOf(state.getPossibleValues())
|
|
||||||
);
|
|
||||||
} else if (state instanceof DirectionProperty) {
|
|
||||||
property = new DirectionalProperty(
|
|
||||||
state.getName(),
|
|
||||||
state
|
|
||||||
.getPossibleValues()
|
|
||||||
.stream()
|
|
||||||
.map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
|
||||||
property = new EnumProperty(
|
|
||||||
state.getName(),
|
|
||||||
state
|
|
||||||
.getPossibleValues()
|
|
||||||
.stream()
|
|
||||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
|
|
||||||
property = new IntegerProperty(
|
|
||||||
state.getName(),
|
|
||||||
(List<Integer>) ImmutableList.copyOf(state.getPossibleValues())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
|
|
||||||
.getClass()
|
|
||||||
.getSimpleName());
|
|
||||||
}
|
|
||||||
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
|
|
||||||
if (v == null) {
|
|
||||||
v = new ArrayList<>(Collections.singletonList(property));
|
|
||||||
} else {
|
|
||||||
v.add(property);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
allBlockProperties = ImmutableMap.copyOf(properties);
|
|
||||||
}
|
|
||||||
initialised = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockMaterial getMaterial(BlockType blockType) {
|
|
||||||
Block block = getBlock(blockType);
|
|
||||||
return new PaperweightBlockMaterial(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized BlockMaterial getMaterial(BlockState state) {
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
|
||||||
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Block getBlock(BlockType blockType) {
|
|
||||||
return Registry.BLOCK.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(final Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
|
|
||||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
|
||||||
|
|
||||||
// Read the NBT data
|
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
|
||||||
if (blockEntity != null) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = blockEntity.save(new net.minecraft.nbt.CompoundTag());
|
|
||||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.toBaseBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<SideEffect> getSupportedSideEffects() {
|
|
||||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
|
|
||||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
|
||||||
LevelChunk levelChunk = craftChunk.getHandle();
|
|
||||||
Level level = levelChunk.getLevel();
|
|
||||||
|
|
||||||
BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
|
||||||
LevelChunkSection[] levelChunkSections = levelChunk.getSections();
|
|
||||||
int y4 = y >> 4;
|
|
||||||
LevelChunkSection section = levelChunkSections[y4];
|
|
||||||
|
|
||||||
net.minecraft.world.level.block.state.BlockState existing;
|
|
||||||
if (section == null) {
|
|
||||||
existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
|
||||||
} else {
|
|
||||||
existing = section.getBlockState(x & 15, y & 15, z & 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity
|
|
||||||
|
|
||||||
CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null;
|
|
||||||
if (compoundTag != null || existing instanceof TileEntityBlock) {
|
|
||||||
level.setBlock(blockPos, blockState, 0);
|
|
||||||
// remove tile
|
|
||||||
if (compoundTag != null) {
|
|
||||||
// We will assume that the tile entity was created for us,
|
|
||||||
// though we do not do this on the Forge version
|
|
||||||
BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
|
||||||
if (blockEntity != null) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag);
|
|
||||||
tag.put("x", IntTag.valueOf(x));
|
|
||||||
tag.put("y", IntTag.valueOf(y));
|
|
||||||
tag.put("z", IntTag.valueOf(z));
|
|
||||||
blockEntity.load(tag); // readTagIntoTileEntity - load data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (existing == blockState) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (section == null) {
|
|
||||||
if (blockState.isAir()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
levelChunkSections[y4] = section = new LevelChunkSection(y4 << 4);
|
|
||||||
}
|
|
||||||
levelChunk.setBlockState(blockPos, blockState, false);
|
|
||||||
}
|
|
||||||
if (update) {
|
|
||||||
level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
|
||||||
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
|
||||||
Preconditions.checkNotNull(entity);
|
|
||||||
|
|
||||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
|
||||||
Entity mcEntity = craftEntity.getHandle();
|
|
||||||
|
|
||||||
String id = getEntityId(mcEntity);
|
|
||||||
|
|
||||||
if (id != null) {
|
|
||||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
|
||||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
|
||||||
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
|
|
||||||
PaperweightPlatformAdapter.readEntityIntoTag(mcEntity, minecraftTag);
|
|
||||||
//add Id for AbstractChangeSet to work
|
|
||||||
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
|
|
||||||
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
|
||||||
tags.put("Id", StringBinaryTag.of(id));
|
|
||||||
return CompoundBinaryTag.from(tags);
|
|
||||||
};
|
|
||||||
return new LazyBaseEntity(type, saveTag);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getRichBlockName(BlockType blockType) {
|
|
||||||
return parent.getRichBlockName(blockType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getRichItemName(ItemType itemType) {
|
|
||||||
return parent.getRichItemName(itemType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getRichItemName(BaseItemStack itemStack) {
|
|
||||||
return parent.getRichItemName(itemStack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OptionalInt getInternalBlockStateId(BlockState state) {
|
|
||||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
|
||||||
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
|
|
||||||
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState adapt(BlockData blockData) {
|
|
||||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
|
||||||
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
|
|
||||||
return adapt(ibd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
|
||||||
return BlockTypesCache.states[adaptToChar(blockState)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
|
|
||||||
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
init();
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
} catch (ArrayIndexOutOfBoundsException e1) {
|
|
||||||
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
|
|
||||||
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
|
|
||||||
);
|
|
||||||
return BlockTypesCache.ReservedIDs.AIR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public char ibdIDToOrdinal(int id) {
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
return ibdToStateOrdinal[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] getIbdToStateOrdinal() {
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return ibdToStateOrdinal;
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
return ibdToStateOrdinal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ordinalToIbdID(char ordinal) {
|
|
||||||
if (initialised) {
|
|
||||||
return ordinalToIbdID[ordinal];
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return ordinalToIbdID[ordinal];
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
return ordinalToIbdID[ordinal];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] getOrdinalToIbdID() {
|
|
||||||
if (initialised) {
|
|
||||||
return ordinalToIbdID;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return ordinalToIbdID;
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
return ordinalToIbdID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
|
|
||||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
|
||||||
return material.getCraftBlockData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
|
||||||
ServerLevel nmsWorld = getServerLevel(world);
|
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
|
||||||
if (map != null && map.wasAccessibleSinceLastSave()) {
|
|
||||||
boolean flag = false;
|
|
||||||
// PlayerChunk.d players = map.players;
|
|
||||||
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
|
||||||
*/ Stream.empty();
|
|
||||||
|
|
||||||
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
|
|
||||||
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
|
|
||||||
.forEach(entityPlayer -> {
|
|
||||||
synchronized (chunkPacket) {
|
|
||||||
ClientboundLevelChunkPacket nmsPacket = (ClientboundLevelChunkPacket) chunkPacket.getNativePacket();
|
|
||||||
if (nmsPacket == null) {
|
|
||||||
nmsPacket = mapUtil.create(this, chunkPacket);
|
|
||||||
chunkPacket.setNativePacket(nmsPacket);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
|
|
||||||
entityPlayer.connection.send(nmsPacket);
|
|
||||||
} finally {
|
|
||||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
|
||||||
return getParent().getProperties(blockType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
|
|
||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
|
||||||
return blockState1.hasPostProcess(
|
|
||||||
getServerLevel(world),
|
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
|
||||||
ItemStack stack = new ItemStack(
|
|
||||||
Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
|
||||||
baseItemStack.getAmount()
|
|
||||||
);
|
|
||||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNativeBinary(baseItemStack.getNbt())));
|
|
||||||
return CraftItemStack.asCraftMirror(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preCaptureStates(final ServerLevel serverLevel) {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
|
||||||
serverLevel.captureBlockStates = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
|
||||||
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
|
||||||
serverLevel.captureBlockStates = false;
|
|
||||||
serverLevel.captureTreeGeneration = false;
|
|
||||||
serverLevel.capturedBlockStates.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ServerLevel getServerLevel(final World world) {
|
|
||||||
return ((CraftWorld) world).getHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<>();
|
|
||||||
getServerLevel(world).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
|
|
||||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
|
||||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
|
||||||
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
|
|
||||||
weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
|
|
||||||
return weStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Tag toNative(net.minecraft.nbt.Tag foreign) {
|
|
||||||
return parent.toNative(foreign);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
|
|
||||||
if (foreign instanceof PaperweightLazyCompoundTag) {
|
|
||||||
return ((PaperweightLazyCompoundTag) foreign).get();
|
|
||||||
}
|
|
||||||
return parent.fromNative(foreign);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
|
||||||
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
|
|
||||||
return new PaperweightGetBlocks(world, chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getInternalBiomeId(BiomeType biomeType) {
|
|
||||||
final Registry<Biome> registry = MinecraftServer
|
|
||||||
.getServer()
|
|
||||||
.registryAccess()
|
|
||||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
|
||||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
|
||||||
Biome biome = registry.get(resourceLocation);
|
|
||||||
return registry.getId(biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
|
||||||
WritableRegistry<Biome> biomeRegistry = ((CraftServer) Bukkit.getServer())
|
|
||||||
.getServer()
|
|
||||||
.registryAccess()
|
|
||||||
.ownedRegistryOrThrow(
|
|
||||||
Registry.BIOME_REGISTRY);
|
|
||||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
|
||||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
|
||||||
for (ResourceLocation key : keys) {
|
|
||||||
try {
|
|
||||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return namespacedKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RelighterFactory getRelighterFactory() {
|
|
||||||
try {
|
|
||||||
Class.forName("ca.spottedleaf.starlight.light.StarLightEngine");
|
|
||||||
if (PaperweightStarlightRelighter.isUsable()) {
|
|
||||||
return new PaperweightStarlightRelighterFactory();
|
|
||||||
}
|
|
||||||
} catch (ThreadDeath td) {
|
|
||||||
throw td;
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
|
|
||||||
}
|
|
||||||
return new NMSRelighterFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, List<Property<?>>> getAllProperties() {
|
|
||||||
if (initialised) {
|
|
||||||
return allBlockProperties;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (initialised) {
|
|
||||||
return allBlockProperties;
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
return allBlockProperties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBatchProcessor getTickingPostProcessor() {
|
|
||||||
return new PaperweightPostProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,286 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.Fawe;
|
|
||||||
import com.fastasyncworldedit.core.math.IntPair;
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
|
|
||||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
|
||||||
|
|
||||||
private static final int UPDATE = 1;
|
|
||||||
private static final int NOTIFY = 2;
|
|
||||||
private static final Direction[] NEIGHBOUR_ORDER = {
|
|
||||||
Direction.EAST,
|
|
||||||
Direction.WEST,
|
|
||||||
Direction.DOWN,
|
|
||||||
Direction.UP,
|
|
||||||
Direction.NORTH,
|
|
||||||
Direction.SOUTH
|
|
||||||
};
|
|
||||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
|
||||||
private final WeakReference<Level> level;
|
|
||||||
private final AtomicInteger lastTick;
|
|
||||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
|
||||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
|
||||||
private SideEffectSet sideEffectSet;
|
|
||||||
|
|
||||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
|
||||||
this.paperweightFaweAdapter = paperweightFaweAdapter;
|
|
||||||
this.level = level;
|
|
||||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
|
||||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
|
||||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Level getLevel() {
|
|
||||||
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
|
||||||
this.sideEffectSet = sideEffectSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LevelChunk getChunk(int x, int z) {
|
|
||||||
return getLevel().getChunk(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
|
|
||||||
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
|
|
||||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
|
||||||
? Block.stateById(stateId)
|
|
||||||
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
|
|
||||||
return levelChunk.getBlockState(blockPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
|
|
||||||
LevelChunk levelChunk, BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState
|
|
||||||
) {
|
|
||||||
int currentTick = MinecraftServer.currentTick;
|
|
||||||
if (Fawe.isMainThread()) {
|
|
||||||
return levelChunk.setBlockState(blockPos, blockState,
|
|
||||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
|
||||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
|
||||||
cachedChunksToSend.add(new IntPair(levelChunk.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ()));
|
|
||||||
boolean nextTick = lastTick.get() > currentTick;
|
|
||||||
if (nextTick || cachedChanges.size() >= 1024) {
|
|
||||||
if (nextTick) {
|
|
||||||
lastTick.set(currentTick);
|
|
||||||
}
|
|
||||||
flushAsync(nextTick);
|
|
||||||
}
|
|
||||||
return blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState,
|
|
||||||
BlockPos blockPos
|
|
||||||
) {
|
|
||||||
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPos getPosition(int x, int y, int z) {
|
|
||||||
return new BlockPos(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateLightingForBlock(BlockPos blockPos) {
|
|
||||||
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
|
||||||
// We will assume that the tile entity was created for us,
|
|
||||||
// though we do not do this on the other versions
|
|
||||||
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
|
|
||||||
if (blockEntity == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
|
|
||||||
blockEntity.load((CompoundTag) nativeTag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyBlockUpdate(
|
|
||||||
LevelChunk levelChunk, BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
|
||||||
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChunkTicking(LevelChunk levelChunk) {
|
|
||||||
return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
|
|
||||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
|
||||||
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyNeighbors(
|
|
||||||
BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
Level level = getLevel();
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
level.blockUpdated(blockPos, oldState.getBlock());
|
|
||||||
} else {
|
|
||||||
// When we don't want events, manually run the physics without them.
|
|
||||||
// Un-nest neighbour updating
|
|
||||||
for (Direction direction : NEIGHBOUR_ORDER) {
|
|
||||||
BlockPos shifted = blockPos.relative(direction);
|
|
||||||
level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newState.hasAnalogOutputSignal()) {
|
|
||||||
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateNeighbors(
|
|
||||||
BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState,
|
|
||||||
int recursionLimit
|
|
||||||
) {
|
|
||||||
Level level = getLevel();
|
|
||||||
// a == updateNeighbors
|
|
||||||
// b == updateDiagonalNeighbors
|
|
||||||
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
CraftWorld craftWorld = level.getWorld();
|
|
||||||
if (craftWorld != null) {
|
|
||||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
|
||||||
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
|
||||||
CraftBlockData.fromData(newState)
|
|
||||||
);
|
|
||||||
level.getCraftServer().getPluginManager().callEvent(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
|
|
||||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockStateChange(
|
|
||||||
BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState oldState,
|
|
||||||
net.minecraft.world.level.block.state.BlockState newState
|
|
||||||
) {
|
|
||||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void flushAsync(final boolean sendChunks) {
|
|
||||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
|
||||||
cachedChanges.clear();
|
|
||||||
final Set<IntPair> toSend;
|
|
||||||
if (sendChunks) {
|
|
||||||
toSend = Set.copyOf(cachedChunksToSend);
|
|
||||||
cachedChunksToSend.clear();
|
|
||||||
} else {
|
|
||||||
toSend = Collections.emptySet();
|
|
||||||
}
|
|
||||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
|
||||||
@Override
|
|
||||||
public void run(Object value) {
|
|
||||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
|
||||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
|
||||||
));
|
|
||||||
if (!sendChunks) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (IntPair chunk : toSend) {
|
|
||||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void flush() {
|
|
||||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
|
||||||
@Override
|
|
||||||
public void run(Object value) {
|
|
||||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
|
||||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
|
||||||
));
|
|
||||||
for (IntPair chunk : cachedChunksToSend) {
|
|
||||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (Fawe.isMainThread()) {
|
|
||||||
runnableVal.run();
|
|
||||||
} else {
|
|
||||||
TaskManager.taskManager().sync(runnableVal);
|
|
||||||
}
|
|
||||||
cachedChanges.clear();
|
|
||||||
cachedChunksToSend.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private record CachedChange(
|
|
||||||
LevelChunk levelChunk,
|
|
||||||
BlockPos blockPos,
|
|
||||||
net.minecraft.world.level.block.state.BlockState blockState
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,251 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
|
||||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
|
||||||
|
|
||||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
|
||||||
private final Set<CompoundTag> entities = new HashSet<>();
|
|
||||||
private final char[][] blocks;
|
|
||||||
private final int minHeight;
|
|
||||||
private final int maxHeight;
|
|
||||||
final ServerLevel serverLevel;
|
|
||||||
final LevelChunk levelChunk;
|
|
||||||
private ChunkBiomeContainer chunkBiomeContainer;
|
|
||||||
|
|
||||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
|
||||||
this.levelChunk = levelChunk;
|
|
||||||
this.serverLevel = levelChunk.level;
|
|
||||||
this.minHeight = serverLevel.getMinBuildHeight();
|
|
||||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
|
||||||
this.blocks = new char[getSectionCount()][];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void storeTile(BlockEntity blockEntity) {
|
|
||||||
tiles.put(
|
|
||||||
BlockVector3.at(
|
|
||||||
blockEntity.getBlockPos().getX(),
|
|
||||||
blockEntity.getBlockPos().getY(),
|
|
||||||
blockEntity.getBlockPos().getZ()
|
|
||||||
),
|
|
||||||
new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.save(new net.minecraft.nbt.CompoundTag())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
|
||||||
return tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public CompoundTag getTile(int x, int y, int z) {
|
|
||||||
return tiles.get(BlockVector3.at(x, y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
protected void storeEntity(Entity entity) {
|
|
||||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
|
||||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
|
||||||
PaperweightPlatformAdapter.readEntityIntoTag(entity, compoundTag);
|
|
||||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<CompoundTag> getEntities() {
|
|
||||||
return this.entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getEntity(UUID uuid) {
|
|
||||||
for (CompoundTag tag : entities) {
|
|
||||||
if (uuid.equals(tag.getUUID())) {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCreateCopy() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int setCreateCopy(boolean createCopy) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxY() {
|
|
||||||
return maxHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinY() {
|
|
||||||
return minHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxSectionPosition() {
|
|
||||||
return maxHeight >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinSectionPosition() {
|
|
||||||
return minHeight >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void storeBiomes(ChunkBiomeContainer chunkBiomeContainer) {
|
|
||||||
// The to do one line below is pre-paperweight and needs to be revised
|
|
||||||
// TODO revisit last parameter, BiomeStorage[] *would* be more efficient
|
|
||||||
this.chunkBiomeContainer = new ChunkBiomeContainer(chunkBiomeContainer.biomeRegistry, serverLevel,
|
|
||||||
chunkBiomeContainer.writeBiomes()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeType getBiomeType(int x, int y, int z) {
|
|
||||||
Biome biome = null;
|
|
||||||
if (y == -1) {
|
|
||||||
for (y = serverLevel.getMinBuildHeight(); y <= serverLevel.getMaxBuildHeight(); y += 4) {
|
|
||||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
|
||||||
if (biome != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
|
||||||
}
|
|
||||||
return biome != null ? PaperweightPlatformAdapter.adapt(biome, serverLevel) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeSectionLighting(int layer, boolean sky) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive, int layer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlocks reset() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionCount() {
|
|
||||||
return serverLevel.getSectionsCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void storeSection(int layer, char[] data) {
|
|
||||||
blocks[layer] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
|
||||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
|
||||||
return state.toBaseBlock(this, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSection(int layer) {
|
|
||||||
layer -= getMinSectionPosition();
|
|
||||||
return blocks[layer] != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] load(int layer) {
|
|
||||||
layer -= getMinSectionPosition();
|
|
||||||
if (blocks[layer] == null) {
|
|
||||||
blocks[layer] = new char[4096];
|
|
||||||
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
|
|
||||||
}
|
|
||||||
return blocks[layer];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] loadIfPresent(int layer) {
|
|
||||||
layer -= getMinSectionPosition();
|
|
||||||
return blocks[layer];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(int x, int y, int z) {
|
|
||||||
return BlockTypesCache.states[get(x, y, z)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSkyLight(int x, int y, int z) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getEmittedLight(int x, int y, int z) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] getHeightMap(HeightMapType type) {
|
|
||||||
return new int[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char get(int x, int y, int z) {
|
|
||||||
final int layer = (y >> 4) - getMinSectionPosition();
|
|
||||||
final int index = (y & 15) << 8 | z << 4 | x;
|
|
||||||
return blocks[layer][index];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trim(boolean aggressive) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
|
||||||
|
|
||||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkPacket> {
|
|
||||||
|
|
||||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
|
||||||
fieldX = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
|
||||||
fieldZ = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("x", "b"));
|
|
||||||
fieldBitMask = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("z", "c"));
|
|
||||||
fieldHeightMap = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("availableSections", "d"));
|
|
||||||
fieldChunkData = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
|
||||||
fieldBlockEntities = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("buffer", "g"));
|
|
||||||
fieldFull = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("blockEntitiesTags", "h"));
|
|
||||||
fieldX.setAccessible(true);
|
|
||||||
fieldZ.setAccessible(true);
|
|
||||||
fieldBitMask.setAccessible(true);
|
|
||||||
fieldHeightMap.setAccessible(true);
|
|
||||||
fieldChunkData.setAccessible(true);
|
|
||||||
fieldBlockEntities.setAccessible(true);
|
|
||||||
fieldFull.setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientboundLevelChunkPacket createPacket() {
|
|
||||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,526 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
|
||||||
import com.fastasyncworldedit.core.Fawe;
|
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
|
||||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
|
||||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.SectionPos;
|
|
||||||
import net.minecraft.nbt.NbtUtils;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ChunkMap;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.util.BitStorage;
|
|
||||||
import net.minecraft.util.Unit;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.entity.npc.AbstractVillager;
|
|
||||||
import net.minecraft.world.item.trading.MerchantOffers;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
|
||||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
import net.minecraft.world.level.chunk.LinearPalette;
|
|
||||||
import net.minecraft.world.level.chunk.Palette;
|
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
|
||||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
|
||||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|
||||||
|
|
||||||
public static final Field fieldStorage;
|
|
||||||
public static final Field fieldPalette;
|
|
||||||
public static final Field fieldBits;
|
|
||||||
|
|
||||||
public static final Field fieldBitsPerEntry;
|
|
||||||
|
|
||||||
private static final Field fieldTickingFluidContent;
|
|
||||||
private static final Field fieldTickingBlockCount;
|
|
||||||
private static final Field fieldNonEmptyBlockCount;
|
|
||||||
|
|
||||||
private static final Field fieldBiomes;
|
|
||||||
|
|
||||||
private static final MethodHandle methodGetVisibleChunk;
|
|
||||||
|
|
||||||
private static final Field fieldLock;
|
|
||||||
|
|
||||||
private static final Field fieldGameEventDispatcherSections;
|
|
||||||
private static final MethodHandle methodremoveBlockEntityTicker;
|
|
||||||
|
|
||||||
private static final Field fieldOffers;
|
|
||||||
private static final MerchantOffers OFFERS = new MerchantOffers();
|
|
||||||
|
|
||||||
private static final Field fieldRemove;
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
|
||||||
fieldBits.setAccessible(true);
|
|
||||||
fieldStorage = PalettedContainer.class.getDeclaredField(Refraction.pickName("storage", "c"));
|
|
||||||
fieldStorage.setAccessible(true);
|
|
||||||
fieldPalette = PalettedContainer.class.getDeclaredField(Refraction.pickName("palette", "k"));
|
|
||||||
fieldPalette.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBitsPerEntry = BitStorage.class.getDeclaredField(Refraction.pickName("bits", "c"));
|
|
||||||
fieldBitsPerEntry.setAccessible(true);
|
|
||||||
|
|
||||||
fieldTickingFluidContent = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
|
||||||
fieldTickingFluidContent.setAccessible(true);
|
|
||||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
|
||||||
fieldTickingBlockCount.setAccessible(true);
|
|
||||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
|
||||||
fieldNonEmptyBlockCount.setAccessible(true);
|
|
||||||
|
|
||||||
fieldBiomes = ChunkBiomeContainer.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
|
||||||
fieldBiomes.setAccessible(true);
|
|
||||||
|
|
||||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
|
||||||
"getVisibleChunkIfPresent",
|
|
||||||
"getVisibleChunk"
|
|
||||||
), long.class);
|
|
||||||
getVisibleChunkIfPresent.setAccessible(true);
|
|
||||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
|
||||||
|
|
||||||
if (!PaperLib.isPaper()) {
|
|
||||||
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
|
|
||||||
fieldLock.setAccessible(true);
|
|
||||||
} else {
|
|
||||||
// in paper, the used methods are synchronized properly
|
|
||||||
fieldLock = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName(
|
|
||||||
"gameEventDispatcherSections", "x"));
|
|
||||||
fieldGameEventDispatcherSections.setAccessible(true);
|
|
||||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
|
||||||
Refraction.pickName(
|
|
||||||
"removeBlockEntityTicker",
|
|
||||||
"l"
|
|
||||||
), BlockPos.class
|
|
||||||
);
|
|
||||||
removeBlockEntityTicker.setAccessible(true);
|
|
||||||
methodremoveBlockEntityTicker = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
|
||||||
|
|
||||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
|
||||||
fieldRemove.setAccessible(true);
|
|
||||||
|
|
||||||
fieldOffers = AbstractVillager.class.getDeclaredField(Refraction.pickName("offers", "bU"));
|
|
||||||
fieldOffers.setAccessible(true);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Throwable rethrow) {
|
|
||||||
rethrow.printStackTrace();
|
|
||||||
throw new RuntimeException(rethrow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean setSectionAtomic(
|
|
||||||
LevelChunkSection[] sections,
|
|
||||||
LevelChunkSection expected,
|
|
||||||
LevelChunkSection value,
|
|
||||||
int layer
|
|
||||||
) {
|
|
||||||
if (layer >= 0 && layer < sections.length) {
|
|
||||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no point in having a functional semaphore for paper servers.
|
|
||||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
|
||||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
|
||||||
|
|
||||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
return SEMAPHORE_THREAD_LOCAL.get();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
synchronized (section) {
|
|
||||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
|
||||||
Semaphore currentLock = (Semaphore) fieldLock.get(blocks);
|
|
||||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
|
||||||
return delegateSemaphore;
|
|
||||||
}
|
|
||||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
|
||||||
fieldLock.set(blocks, newLock);
|
|
||||||
return newLock;
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
|
||||||
if (!PaperLib.isPaper()) {
|
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
|
||||||
if (nmsChunk != null) {
|
|
||||||
return nmsChunk;
|
|
||||||
}
|
|
||||||
if (Fawe.isMainThread()) {
|
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
|
||||||
if (nmsChunk != null) {
|
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
|
||||||
}
|
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
|
||||||
if (nmsChunk != null) {
|
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
|
||||||
}
|
|
||||||
// Avoid "async" methods from the main thread.
|
|
||||||
if (Fawe.isMainThread()) {
|
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
|
||||||
try {
|
|
||||||
CraftChunk chunk;
|
|
||||||
try {
|
|
||||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
String world = serverLevel.getWorld().getName();
|
|
||||||
// We've already taken 10 seconds we can afford to wait a little here.
|
|
||||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
|
||||||
if (loaded) {
|
|
||||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
|
||||||
// Retry chunk load
|
|
||||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chunk.getHandle();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
|
||||||
// Ensure chunk is definitely loaded before applying a ticket
|
|
||||||
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
|
||||||
.getChunkSource()
|
|
||||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
|
||||||
try {
|
|
||||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
|
||||||
} catch (Throwable thr) {
|
|
||||||
throw new RuntimeException(thr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
|
||||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
|
||||||
if (chunkHolder == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
|
||||||
// UNLOADED_CHUNK
|
|
||||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
|
||||||
.getTickingChunkFuture()
|
|
||||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
// getChunkAtIfLoadedImmediately is paper only
|
|
||||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
|
||||||
.getChunkSource()
|
|
||||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
|
||||||
}
|
|
||||||
if (optional.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LevelChunk levelChunk = optional.get();
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
ClientboundLevelChunkPacket chunkPacket = new ClientboundLevelChunkPacket(levelChunk);
|
|
||||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(chunkPacket));
|
|
||||||
if (lighting) {
|
|
||||||
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
|
||||||
boolean trustEdges = true;
|
|
||||||
ClientboundLightUpdatePacket packet =
|
|
||||||
new ClientboundLightUpdatePacket(coordIntPair, nmsWorld.getChunkSource().getLightEngine(), null, null,
|
|
||||||
trustEdges
|
|
||||||
);
|
|
||||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
|
||||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
NMS conversion
|
|
||||||
*/
|
|
||||||
public static LevelChunkSection newChunkSection(
|
|
||||||
final int layer,
|
|
||||||
final char[] blocks,
|
|
||||||
boolean fastMode,
|
|
||||||
CachedBukkitAdapter adapter
|
|
||||||
) {
|
|
||||||
return newChunkSection(layer, null, blocks, fastMode, adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LevelChunkSection newChunkSection(
|
|
||||||
final int layer,
|
|
||||||
final Function<Integer, char[]> get,
|
|
||||||
char[] set,
|
|
||||||
boolean fastMode,
|
|
||||||
CachedBukkitAdapter adapter
|
|
||||||
) {
|
|
||||||
if (set == null) {
|
|
||||||
return newChunkSection(layer);
|
|
||||||
}
|
|
||||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
|
||||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
|
||||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
|
||||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
|
||||||
try {
|
|
||||||
int num_palette;
|
|
||||||
final short[] nonEmptyBlockCount = fastMode ? new short[1] : null;
|
|
||||||
if (get == null) {
|
|
||||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, nonEmptyBlockCount);
|
|
||||||
} else {
|
|
||||||
num_palette = createPalette(
|
|
||||||
layer,
|
|
||||||
blockToPalette,
|
|
||||||
paletteToBlock,
|
|
||||||
blocksCopy,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
adapter,
|
|
||||||
nonEmptyBlockCount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// BlockStates
|
|
||||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
|
||||||
if (Settings.settings().PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
|
||||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
|
||||||
} else {
|
|
||||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
|
||||||
}
|
|
||||||
if (bitsPerEntry > 8) {
|
|
||||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
|
|
||||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
|
||||||
|
|
||||||
if (num_palette == 1) {
|
|
||||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
|
||||||
blockStates[i] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, 4096, blockStates);
|
|
||||||
bitArray.fromRaw(blocksCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
LevelChunkSection levelChunkSection = newChunkSection(layer);
|
|
||||||
// set palette & data bits
|
|
||||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks =
|
|
||||||
levelChunkSection.getStates();
|
|
||||||
// private DataPalette<T> h;
|
|
||||||
// protected DataBits a;
|
|
||||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
|
||||||
final BitStorage nmsBits = new BitStorage(bitsPerEntry, 4096, bits);
|
|
||||||
final Palette<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer;
|
|
||||||
if (bitsPerEntry <= 4) {
|
|
||||||
blockStatePalettedContainer = new LinearPalette<>(Block.BLOCK_STATE_REGISTRY, bitsPerEntry, dataPaletteBlocks,
|
|
||||||
NbtUtils::readBlockState
|
|
||||||
);
|
|
||||||
} else if (bitsPerEntry < 9) {
|
|
||||||
blockStatePalettedContainer = new HashMapPalette<>(
|
|
||||||
Block.BLOCK_STATE_REGISTRY,
|
|
||||||
bitsPerEntry,
|
|
||||||
dataPaletteBlocks,
|
|
||||||
NbtUtils::readBlockState,
|
|
||||||
NbtUtils::writeBlockState
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
blockStatePalettedContainer = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set palette if required
|
|
||||||
if (bitsPerEntry < 9) {
|
|
||||||
for (int i = 0; i < num_palette; i++) {
|
|
||||||
final int ordinal = paletteToBlock[i];
|
|
||||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
|
||||||
final BlockState state = BlockTypesCache.states[ordinal];
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
|
||||||
blockStatePalettedContainer.idFor(blockState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
fieldStorage.set(dataPaletteBlocks, nmsBits);
|
|
||||||
fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
|
||||||
fieldBits.set(dataPaletteBlocks, bitsPerEntry);
|
|
||||||
} catch (final IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fastMode) {
|
|
||||||
levelChunkSection.recalcBlockCounts();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
fieldNonEmptyBlockCount.set(levelChunkSection, nonEmptyBlockCount[0]);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return levelChunkSection;
|
|
||||||
} catch (final Throwable e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
|
||||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
|
||||||
Arrays.fill(blockStates, 0);
|
|
||||||
Arrays.fill(blocksCopy, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LevelChunkSection newChunkSection(int layer) {
|
|
||||||
return new LevelChunkSection(layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
|
||||||
fieldTickingFluidContent.setShort(section, (short) 0);
|
|
||||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Biome[] getBiomeArray(ChunkBiomeContainer chunkBiomeContainer) {
|
|
||||||
try {
|
|
||||||
return (Biome[]) fieldBiomes.get(chunkBiomeContainer);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BiomeType adapt(Biome biome, LevelAccessor levelAccessor) {
|
|
||||||
ResourceLocation resourceLocation = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getKey(
|
|
||||||
biome);
|
|
||||||
if (resourceLocation == null) {
|
|
||||||
return levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getId(biome) == -1
|
|
||||||
? BiomeTypes.OCEAN
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
return BiomeTypes.get(resourceLocation.toString().toLowerCase(Locale.ROOT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
|
||||||
try {
|
|
||||||
// Do the method ourselves to avoid trying to reflect generic method parameters
|
|
||||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
|
||||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
|
||||||
if (blockEntity != null) {
|
|
||||||
if (!levelChunk.level.isClientSide) {
|
|
||||||
Block block = beacon.getBlockState().getBlock();
|
|
||||||
if (block instanceof EntityBlock) {
|
|
||||||
GameEventListener gameEventListener = ((EntityBlock) block).getListener(levelChunk.level, beacon);
|
|
||||||
if (gameEventListener != null) {
|
|
||||||
int i = SectionPos.blockToSectionCoord(beacon.getBlockPos().getY());
|
|
||||||
GameEventDispatcher gameEventDispatcher = levelChunk.getEventDispatcher(i);
|
|
||||||
gameEventDispatcher.unregister(gameEventListener);
|
|
||||||
if (gameEventDispatcher.isEmpty()) {
|
|
||||||
try {
|
|
||||||
((Int2ObjectMap<GameEventDispatcher>) fieldGameEventDispatcherSections.get(levelChunk))
|
|
||||||
.remove(i);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldRemove.set(beacon, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methodremoveBlockEntityTicker.invoke(levelChunk, beacon.getBlockPos());
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
|
||||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
|
|
||||||
boolean isVillager = entity instanceof AbstractVillager && !Fawe.isMainThread();
|
|
||||||
boolean unset = false;
|
|
||||||
if (isVillager) {
|
|
||||||
try {
|
|
||||||
if (fieldOffers.get(entity) != null) {
|
|
||||||
fieldOffers.set(entity, OFFERS);
|
|
||||||
unset = true;
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Failed to set offers field to villager to avoid async catcher.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entity.save(compoundTag);
|
|
||||||
if (unset) {
|
|
||||||
try {
|
|
||||||
fieldOffers.set(entity, null);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Failed to set offers field to null again on villager.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,175 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
|
||||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
|
||||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.material.Fluid;
|
|
||||||
import net.minecraft.world.level.material.Fluids;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
|
||||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
|
||||||
// The PostProcessor shouldn't be added, but just in case
|
|
||||||
if (!tickFluid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
|
||||||
layer:
|
|
||||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
|
||||||
char[] set = iChunkSet.loadIfPresent(layer);
|
|
||||||
if (set == null) {
|
|
||||||
// No edit means no need to process
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
char[] get = null;
|
|
||||||
for (int i = 0; i < 4096; i++) {
|
|
||||||
char ordinal = set[i];
|
|
||||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
|
||||||
boolean fromGet = false; // Used for liquids
|
|
||||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
|
||||||
if (get == null) {
|
|
||||||
get = getBlocks.load(layer);
|
|
||||||
}
|
|
||||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
|
||||||
// actually being set
|
|
||||||
if (get == null) {
|
|
||||||
continue layer;
|
|
||||||
}
|
|
||||||
fromGet = true;
|
|
||||||
ordinal = replacedOrdinal = get[i];
|
|
||||||
}
|
|
||||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
|
||||||
continue;
|
|
||||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
|
||||||
if (get == null) {
|
|
||||||
get = getBlocks.load(layer);
|
|
||||||
}
|
|
||||||
replacedOrdinal = get[i];
|
|
||||||
}
|
|
||||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
|
||||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
|
||||||
boolean replacedWasLiquid = false;
|
|
||||||
BlockState replacedState = null;
|
|
||||||
if (!ticking) {
|
|
||||||
// If the block being replaced was not ticking, it cannot be a liquid
|
|
||||||
if (!replacedWasTicking) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// If the block being replaced is not fluid, we do not need to worry
|
|
||||||
if (!(replacedWasLiquid =
|
|
||||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
|
||||||
boolean liquid = state.getMaterial().isLiquid();
|
|
||||||
int x = i & 15;
|
|
||||||
int y = (i >> 8) & 15;
|
|
||||||
int z = (i >> 4) & 15;
|
|
||||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
|
||||||
if (liquid || replacedWasLiquid) {
|
|
||||||
if (liquid) {
|
|
||||||
addFluid(getBlocks.serverLevel, state, position);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
|
||||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
|
||||||
// being ticked anyway. We only need it to be "hit" once.
|
|
||||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Extent construct(final Extent child) {
|
|
||||||
throw new UnsupportedOperationException("Processing only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProcessorScope getScope() {
|
|
||||||
return ProcessorScope.READING_SET_BLOCKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
|
||||||
if (set == null || get == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char ordinal;
|
|
||||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
|
||||||
if (x > 0 && set[i - 1] != reserved) {
|
|
||||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (x < 15 && set[i + 1] != reserved) {
|
|
||||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (z > 0 && set[i - 16] != reserved) {
|
|
||||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (z < 15 && set[i + 16] != reserved) {
|
|
||||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (y > 0 && set[i - 256] != reserved) {
|
|
||||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (y < 15 && set[i + 256] != reserved) {
|
|
||||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private boolean isFluid(char ordinal) {
|
|
||||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
|
||||||
Fluid type;
|
|
||||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
|
||||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
|
||||||
} else {
|
|
||||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
|
||||||
}
|
|
||||||
serverLevel.getLiquidTicks().scheduleTick(
|
|
||||||
position,
|
|
||||||
type,
|
|
||||||
type.getTickDelay(serverLevel)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,113 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
|
||||||
import net.minecraft.server.MCUtil;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.util.Unit;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.IntConsumer;
|
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
|
||||||
|
|
||||||
private static final MethodHandle RELIGHT;
|
|
||||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
|
||||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
|
||||||
|
|
||||||
static {
|
|
||||||
MethodHandle tmp = null;
|
|
||||||
try {
|
|
||||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
tmp = lookup.findVirtual(
|
|
||||||
ThreadedLevelLightEngine.class,
|
|
||||||
"relight",
|
|
||||||
MethodType.methodType(
|
|
||||||
int.class, // return type
|
|
||||||
// params
|
|
||||||
Set.class,
|
|
||||||
Consumer.class,
|
|
||||||
IntConsumer.class
|
|
||||||
)
|
|
||||||
);
|
|
||||||
tmp = MethodHandles.dropReturn(tmp);
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
|
||||||
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
|
|
||||||
}
|
|
||||||
RELIGHT = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
|
||||||
super(serverLevel, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChunkPos createChunkPos(final long chunkKey) {
|
|
||||||
return new ChunkPos(chunkKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long asLong(final int chunkX, final int chunkZ) {
|
|
||||||
return ChunkPos.asLong(chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
|
||||||
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
|
||||||
FAWE_TICKET,
|
|
||||||
chunkPos,
|
|
||||||
LIGHT_LEVEL,
|
|
||||||
Unit.INSTANCE
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isUsable() {
|
|
||||||
return RELIGHT != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void invokeRelight(
|
|
||||||
Set<ChunkPos> coords,
|
|
||||||
Consumer<ChunkPos> chunkCallback,
|
|
||||||
IntConsumer processCallback
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
RELIGHT.invokeExact(
|
|
||||||
serverLevel.getChunkSource().getLightEngine(),
|
|
||||||
coords,
|
|
||||||
chunkCallback, // callback per chunk
|
|
||||||
processCallback // callback for all chunks
|
|
||||||
);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
LOGGER.error("Error occurred on relighting", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allow the server to unload the chunks again.
|
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
|
||||||
*/
|
|
||||||
protected void postProcessChunks(Set<ChunkPos> coords) {
|
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
int x = pos.x;
|
|
||||||
int z = pos.z;
|
|
||||||
if (delay) { // we still need to send the block changes of that chunk
|
|
||||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
|
||||||
}
|
|
||||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
|
||||||
import com.sk89q.worldedit.world.World;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
|
||||||
if (w == null) {
|
|
||||||
return NullRelighter.INSTANCE;
|
|
||||||
}
|
|
||||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt;
|
|
||||||
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.jnbt.LazyCompoundTag;
|
|
||||||
import com.sk89q.jnbt.ListTag;
|
|
||||||
import com.sk89q.jnbt.StringTag;
|
|
||||||
import com.sk89q.jnbt.Tag;
|
|
||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import net.minecraft.nbt.NumericTag;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
|
||||||
|
|
||||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
|
||||||
private CompoundTag compoundTag;
|
|
||||||
|
|
||||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
|
||||||
super(new HashMap<>());
|
|
||||||
this.compoundTagSupplier = compoundTagSupplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
|
||||||
this(() -> compoundTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public net.minecraft.nbt.CompoundTag get() {
|
|
||||||
return compoundTagSupplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Map<String, Tag> getValue() {
|
|
||||||
if (compoundTag == null) {
|
|
||||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
|
||||||
}
|
|
||||||
return compoundTag.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundBinaryTag asBinaryTag() {
|
|
||||||
getValue();
|
|
||||||
return compoundTag.asBinaryTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean containsKey(String key) {
|
|
||||||
return compoundTagSupplier.get().contains(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getByteArray(String key) {
|
|
||||||
return compoundTagSupplier.get().getByteArray(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getByte(String key) {
|
|
||||||
return compoundTagSupplier.get().getByte(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getDouble(String key) {
|
|
||||||
return compoundTagSupplier.get().getDouble(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double asDouble(String key) {
|
|
||||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
|
||||||
if (tag instanceof NumericTag numTag) {
|
|
||||||
return numTag.getAsDouble();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFloat(String key) {
|
|
||||||
return compoundTagSupplier.get().getFloat(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getIntArray(String key) {
|
|
||||||
return compoundTagSupplier.get().getIntArray(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getInt(String key) {
|
|
||||||
return compoundTagSupplier.get().getInt(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int asInt(String key) {
|
|
||||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
|
||||||
if (tag instanceof NumericTag numTag) {
|
|
||||||
return numTag.getAsInt();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public List<Tag> getList(String key) {
|
|
||||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
|
||||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
|
||||||
ArrayList<Tag> list = new ArrayList<>();
|
|
||||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
|
||||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
|
||||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
|
||||||
} else {
|
|
||||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public ListTag getListTag(String key) {
|
|
||||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
|
||||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
|
||||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
|
||||||
}
|
|
||||||
return new ListTag(StringTag.class, Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
|
||||||
ListTag listTag = getListTag(key);
|
|
||||||
if (listTag.getType().equals(listType)) {
|
|
||||||
return (List<T>) listTag.getValue();
|
|
||||||
} else {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getLongArray(String key) {
|
|
||||||
return compoundTagSupplier.get().getLongArray(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLong(String key) {
|
|
||||||
return compoundTagSupplier.get().getLong(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long asLong(String key) {
|
|
||||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
|
||||||
if (tag instanceof NumericTag numTag) {
|
|
||||||
return numTag.getAsLong();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getShort(String key) {
|
|
||||||
return compoundTagSupplier.get().getShort(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString(String key) {
|
|
||||||
return compoundTagSupplier.get().getString(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return compoundTagSupplier.get().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,694 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
|
||||||
import com.fastasyncworldedit.core.Fawe;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
|
||||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightGetBlocks;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
|
||||||
import com.sk89q.worldedit.world.RegenOptions;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.MappedRegistry;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.data.BuiltinRegistries;
|
|
||||||
import net.minecraft.data.worldgen.biome.Biomes;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
|
||||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
||||||
import net.minecraft.util.LinearCongruentialGenerator;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
|
||||||
import net.minecraft.world.level.LevelSettings;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.OverworldBiomeSource;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.UpgradeData;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
|
||||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
|
||||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
|
||||||
import net.minecraft.world.level.levelgen.SimpleRandomSource;
|
|
||||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
|
||||||
import net.minecraft.world.level.levelgen.synth.ImprovedNoise;
|
|
||||||
import net.minecraft.world.level.newbiome.area.Area;
|
|
||||||
import net.minecraft.world.level.newbiome.area.AreaFactory;
|
|
||||||
import net.minecraft.world.level.newbiome.context.BigContext;
|
|
||||||
import net.minecraft.world.level.newbiome.layer.Layer;
|
|
||||||
import net.minecraft.world.level.newbiome.layer.Layers;
|
|
||||||
import net.minecraft.world.level.newbiome.layer.traits.PixelTransformer;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.generator.CustomChunkGenerator;
|
|
||||||
import org.bukkit.generator.BiomeProvider;
|
|
||||||
import org.bukkit.generator.BlockPopulator;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
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;
|
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
import java.util.function.LongFunction;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
private static final Field worldsField;
|
|
||||||
private static final Field paperConfigField;
|
|
||||||
private static final Field generateFlatBedrockField;
|
|
||||||
private static final Field generatorSettingFlatField;
|
|
||||||
private static final Field generatorSettingBaseSupplierField;
|
|
||||||
private static final Field delegateField;
|
|
||||||
private static final Field chunkSourceField;
|
|
||||||
|
|
||||||
//list of chunk stati in correct order without FULL
|
|
||||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
|
||||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
|
||||||
chunkStati.put(
|
|
||||||
ChunkStatus.STRUCTURE_REFERENCES,
|
|
||||||
Concurrency.FULL
|
|
||||||
); // structure refs: radius 8, but only writes to current chunk
|
|
||||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
|
||||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
|
||||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
|
||||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
|
||||||
chunkStati.put(
|
|
||||||
ChunkStatus.LIQUID_CARVERS,
|
|
||||||
Concurrency.NONE
|
|
||||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
|
||||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
|
||||||
chunkStati.put(
|
|
||||||
ChunkStatus.LIGHT,
|
|
||||||
Concurrency.FULL
|
|
||||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
|
||||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
|
||||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
|
||||||
|
|
||||||
try {
|
|
||||||
worldsField = CraftServer.class.getDeclaredField("worlds");
|
|
||||||
worldsField.setAccessible(true);
|
|
||||||
|
|
||||||
Field tmpPaperConfigField;
|
|
||||||
Field tmpFlatBedrockField;
|
|
||||||
try { //only present on paper
|
|
||||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
|
||||||
tmpPaperConfigField.setAccessible(true);
|
|
||||||
|
|
||||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
|
||||||
tmpFlatBedrockField.setAccessible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
tmpPaperConfigField = null;
|
|
||||||
tmpFlatBedrockField = null;
|
|
||||||
}
|
|
||||||
paperConfigField = tmpPaperConfigField;
|
|
||||||
generateFlatBedrockField = tmpFlatBedrockField;
|
|
||||||
|
|
||||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
|
||||||
"settings", "g"));
|
|
||||||
generatorSettingBaseSupplierField.setAccessible(true);
|
|
||||||
|
|
||||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e"));
|
|
||||||
generatorSettingFlatField.setAccessible(true);
|
|
||||||
|
|
||||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
|
||||||
delegateField.setAccessible(true);
|
|
||||||
|
|
||||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "C"));
|
|
||||||
chunkSourceField.setAccessible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//runtime
|
|
||||||
private ServerLevel originalServerWorld;
|
|
||||||
private ServerChunkCache originalChunkProvider;
|
|
||||||
private ServerLevel freshWorld;
|
|
||||||
private ServerChunkCache freshChunkProvider;
|
|
||||||
private LevelStorageSource.LevelStorageAccess session;
|
|
||||||
private StructureManager structureManager;
|
|
||||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
|
||||||
private ChunkGenerator chunkGenerator;
|
|
||||||
|
|
||||||
private Path tempDir;
|
|
||||||
|
|
||||||
private boolean generateFlatBedrock = false;
|
|
||||||
|
|
||||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
|
||||||
super(originalBukkitWorld, region, target, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean prepare() {
|
|
||||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
|
||||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
|
||||||
|
|
||||||
//flat bedrock? (only on paper)
|
|
||||||
if (paperConfigField != null) {
|
|
||||||
try {
|
|
||||||
generateFlatBedrock = generateFlatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
|
||||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean initNewWorld() throws Exception {
|
|
||||||
//world folder
|
|
||||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
|
||||||
|
|
||||||
//prepare for world init (see upstream implementation for reference)
|
|
||||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
|
||||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
|
||||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
|
||||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
|
||||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
|
||||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
|
||||||
|
|
||||||
BiomeProvider biomeProvider = getBiomeProvider();
|
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
|
||||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
|
||||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
|
||||||
: originalOpts;
|
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
|
||||||
"faweregentempworld",
|
|
||||||
originalWorldData.settings.gameType(),
|
|
||||||
originalWorldData.settings.hardcore(),
|
|
||||||
originalWorldData.settings.difficulty(),
|
|
||||||
originalWorldData.settings.allowCommands(),
|
|
||||||
originalWorldData.settings.gameRules(),
|
|
||||||
originalWorldData.settings.getDataPackConfig()
|
|
||||||
);
|
|
||||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
|
||||||
|
|
||||||
//init world
|
|
||||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
|
||||||
server,
|
|
||||||
server.executor,
|
|
||||||
session,
|
|
||||||
newWorldData,
|
|
||||||
originalServerWorld.dimension(),
|
|
||||||
originalServerWorld.dimensionType(),
|
|
||||||
new RegenNoOpWorldLoadListener(),
|
|
||||||
// placeholder. Required for new ChunkProviderServer, but we create and then set it later
|
|
||||||
newOpts.dimensions().get(levelStemResourceKey).generator(),
|
|
||||||
originalServerWorld.isDebug(),
|
|
||||||
seed,
|
|
||||||
ImmutableList.of(),
|
|
||||||
false,
|
|
||||||
environment,
|
|
||||||
generator,
|
|
||||||
biomeProvider
|
|
||||||
) {
|
|
||||||
private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(
|
|
||||||
options
|
|
||||||
.getBiomeType()
|
|
||||||
.getId())) : null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
|
||||||
if (options.hasBiomeType()) {
|
|
||||||
return singleBiome;
|
|
||||||
}
|
|
||||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ);
|
|
||||||
}
|
|
||||||
}).get();
|
|
||||||
freshWorld.noSave = true;
|
|
||||||
removeWorldFromWorldsMap();
|
|
||||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
|
||||||
if (paperConfigField != null) {
|
|
||||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
//generator
|
|
||||||
if (originalChunkProvider.getGenerator() instanceof FlatLevelSource) {
|
|
||||||
FlatLevelGeneratorSettings generatorSettingFlat = (FlatLevelGeneratorSettings) generatorSettingFlatField.get(
|
|
||||||
originalChunkProvider.getGenerator());
|
|
||||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
|
||||||
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) {
|
|
||||||
Supplier<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Supplier<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
|
|
||||||
.get(originalChunkProvider.getGenerator());
|
|
||||||
BiomeSource biomeSource;
|
|
||||||
if (options.hasBiomeType()) {
|
|
||||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options
|
|
||||||
.getBiomeType()
|
|
||||||
.getId())));
|
|
||||||
} else {
|
|
||||||
biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
|
|
||||||
if (biomeSource instanceof OverworldBiomeSource) {
|
|
||||||
biomeSource = fastOverworldBiomeSource(biomeSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, seed, generatorSettingBaseSupplier);
|
|
||||||
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) {
|
|
||||||
chunkGenerator = (ChunkGenerator) delegateField.get(originalChunkProvider.getGenerator());
|
|
||||||
} else {
|
|
||||||
LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (generator != null) {
|
|
||||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
|
||||||
generateConcurrent = generator.isParallelCapable();
|
|
||||||
}
|
|
||||||
|
|
||||||
freshChunkProvider = new ServerChunkCache(
|
|
||||||
freshWorld,
|
|
||||||
session,
|
|
||||||
server.getFixerUpper(),
|
|
||||||
server.getStructureManager(),
|
|
||||||
server.executor,
|
|
||||||
chunkGenerator,
|
|
||||||
freshWorld.spigotConfig.viewDistance,
|
|
||||||
server.forceSynchronousWrites(),
|
|
||||||
new RegenNoOpWorldLoadListener(),
|
|
||||||
(chunkCoordIntPair, state) -> {
|
|
||||||
},
|
|
||||||
() -> server.overworld().getDataStorage()
|
|
||||||
) {
|
|
||||||
// redirect to LevelChunks created in #createChunks
|
|
||||||
@Override
|
|
||||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
|
||||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
|
||||||
if (chunkAccess == null && create) {
|
|
||||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
|
||||||
}
|
|
||||||
return chunkAccess;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
|
||||||
//let's start then
|
|
||||||
structureManager = server.getStructureManager();
|
|
||||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void cleanup() {
|
|
||||||
try {
|
|
||||||
session.close();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
//shutdown chunk provider
|
|
||||||
try {
|
|
||||||
Fawe.instance().getQueueHandler().sync(() -> {
|
|
||||||
try {
|
|
||||||
freshChunkProvider.close(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove world from server
|
|
||||||
try {
|
|
||||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete directory
|
|
||||||
try {
|
|
||||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
|
||||||
return PaperLib.isPaper()
|
|
||||||
? new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, freshWorld) // paper
|
|
||||||
: new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld); // spigot
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
|
||||||
return new LevelChunk(
|
|
||||||
freshWorld,
|
|
||||||
protoChunk,
|
|
||||||
null // we don't want to add entities
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChunkStatusWrap getFullChunkStatus() {
|
|
||||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<BlockPopulator> getBlockPopulators() {
|
|
||||||
return originalServerWorld.getWorld().getPopulators();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
|
||||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
|
||||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
|
||||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
|
||||||
@Override
|
|
||||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
|
||||||
return getChunkAt(x, z);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//util
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void removeWorldFromWorldsMap() {
|
|
||||||
Fawe.instance().getQueueHandler().sync(() -> {
|
|
||||||
try {
|
|
||||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) worldsField.get(Bukkit.getServer());
|
|
||||||
map.remove("faweregentempworld");
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
|
||||||
return switch (env) {
|
|
||||||
case NETHER -> LevelStem.NETHER;
|
|
||||||
case THE_END -> LevelStem.END;
|
|
||||||
default -> LevelStem.OVERWORLD;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
private BiomeSource fastOverworldBiomeSource(BiomeSource biomeSource) throws Exception {
|
|
||||||
Field legacyBiomeInitLayerField = OverworldBiomeSource.class.getDeclaredField(
|
|
||||||
Refraction.pickName("legacyBiomeInitLayer", "i"));
|
|
||||||
legacyBiomeInitLayerField.setAccessible(true);
|
|
||||||
Field largeBiomesField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("largeBiomes", "j"));
|
|
||||||
largeBiomesField.setAccessible(true);
|
|
||||||
Field biomeRegistryField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("biomes", "k"));
|
|
||||||
biomeRegistryField.setAccessible(true);
|
|
||||||
Field areaLazyField = Layer.class.getDeclaredField(Refraction.pickName("area", "b"));
|
|
||||||
areaLazyField.setAccessible(true);
|
|
||||||
Method initAreaFactoryMethod = Layers.class.getDeclaredMethod(
|
|
||||||
Refraction.pickName("getDefaultLayer", "a"),
|
|
||||||
boolean.class,
|
|
||||||
int.class,
|
|
||||||
int.class,
|
|
||||||
LongFunction.class
|
|
||||||
);
|
|
||||||
initAreaFactoryMethod.setAccessible(true);
|
|
||||||
|
|
||||||
//init new WorldChunkManagerOverworld
|
|
||||||
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(biomeSource);
|
|
||||||
boolean largebiomes = largeBiomesField.getBoolean(biomeSource);
|
|
||||||
Registry<Biome> biomeRegistryMojang = (Registry<Biome>) biomeRegistryField.get(biomeSource);
|
|
||||||
Registry<Biome> biomeRegistry;
|
|
||||||
if (options.hasBiomeType()) {
|
|
||||||
Biome biome = BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options.getBiomeType().getId()));
|
|
||||||
biomeRegistry = new MappedRegistry<>(
|
|
||||||
ResourceKey.createRegistryKey(new ResourceLocation("fawe_biomes")),
|
|
||||||
Lifecycle.experimental()
|
|
||||||
);
|
|
||||||
((MappedRegistry) biomeRegistry).registerMapping(0, BuiltinRegistries.BIOME.getResourceKey(biome).get(), biome,
|
|
||||||
Lifecycle.experimental()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
biomeRegistry = biomeRegistryMojang;
|
|
||||||
}
|
|
||||||
|
|
||||||
//replace genLayer
|
|
||||||
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(
|
|
||||||
null,
|
|
||||||
legacyBiomeInitLayer,
|
|
||||||
largebiomes ? 6 : 4,
|
|
||||||
4,
|
|
||||||
(LongFunction) (salt -> new FastWorldGenContextArea(seed, salt))
|
|
||||||
);
|
|
||||||
biomeSource = new FastOverworldBiomeSource(biomeRegistry, new FastGenLayer(factory));
|
|
||||||
|
|
||||||
return biomeSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FastOverworldBiomeSource extends BiomeSource {
|
|
||||||
|
|
||||||
private final Registry<Biome> biomeRegistry;
|
|
||||||
private final boolean isSingleRegistry;
|
|
||||||
private final FastGenLayer fastGenLayer;
|
|
||||||
|
|
||||||
public FastOverworldBiomeSource(
|
|
||||||
Registry<Biome> biomeRegistry,
|
|
||||||
FastGenLayer genLayer
|
|
||||||
) {
|
|
||||||
super(biomeRegistry.stream().collect(Collectors.toList()));
|
|
||||||
this.biomeRegistry = biomeRegistry;
|
|
||||||
this.isSingleRegistry = biomeRegistry.entrySet().size() == 1;
|
|
||||||
this.fastGenLayer = genLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends BiomeSource> codec() {
|
|
||||||
return OverworldBiomeSource.CODEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeSource withSeed(final long seed) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
|
||||||
if (this.isSingleRegistry) {
|
|
||||||
return this.biomeRegistry.byId(0);
|
|
||||||
}
|
|
||||||
return this.fastGenLayer.get(this.biomeRegistry, biomeX, biomeZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FastWorldGenContextArea implements BigContext<FastAreaLazy> {
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
|
||||||
private final ImprovedNoise improvedNoise;
|
|
||||||
private final long magicrandom;
|
|
||||||
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
|
||||||
|
|
||||||
public FastWorldGenContextArea(long seed, long lconst) {
|
|
||||||
this.magicrandom = mix(seed, lconst);
|
|
||||||
this.improvedNoise = new ImprovedNoise(new SimpleRandomSource(seed));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long mix(long seed, long salt) {
|
|
||||||
long l = LinearCongruentialGenerator.next(salt, salt);
|
|
||||||
l = LinearCongruentialGenerator.next(l, salt);
|
|
||||||
l = LinearCongruentialGenerator.next(l, salt);
|
|
||||||
long m = LinearCongruentialGenerator.next(seed, l);
|
|
||||||
m = LinearCongruentialGenerator.next(m, l);
|
|
||||||
m = LinearCongruentialGenerator.next(m, l);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FastAreaLazy createResult(PixelTransformer pixelTransformer) {
|
|
||||||
return new FastAreaLazy(sharedAreaMap, pixelTransformer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initRandom(long x, long z) {
|
|
||||||
long l = this.magicrandom;
|
|
||||||
l = LinearCongruentialGenerator.next(l, x);
|
|
||||||
l = LinearCongruentialGenerator.next(l, z);
|
|
||||||
l = LinearCongruentialGenerator.next(l, x);
|
|
||||||
l = LinearCongruentialGenerator.next(l, z);
|
|
||||||
this.map.put(Thread.currentThread().getId(), l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int nextRandom(int y) {
|
|
||||||
long tid = Thread.currentThread().getId();
|
|
||||||
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
|
||||||
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
|
||||||
this.map.put(tid, LinearCongruentialGenerator.next(e, this.magicrandom));
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImprovedNoise getBiomeNoise() {
|
|
||||||
return this.improvedNoise;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FastGenLayer extends Layer {
|
|
||||||
|
|
||||||
private final FastAreaLazy fastAreaLazy;
|
|
||||||
|
|
||||||
public FastGenLayer(AreaFactory<FastAreaLazy> factory) {
|
|
||||||
super(() -> null);
|
|
||||||
this.fastAreaLazy = factory.make();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Biome get(Registry<Biome> registry, int x, int z) {
|
|
||||||
ResourceKey<Biome> key = Biomes.byId(this.fastAreaLazy.get(x, z));
|
|
||||||
if (key == null) {
|
|
||||||
return registry.get(Biomes.byId(0));
|
|
||||||
}
|
|
||||||
Biome biome = registry.get(key);
|
|
||||||
if (biome == null) {
|
|
||||||
return registry.get(Biomes.byId(0));
|
|
||||||
}
|
|
||||||
return biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private record FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, PixelTransformer transformer) implements Area {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get(int x, int z) {
|
|
||||||
long zx = ChunkPos.asLong(x, z);
|
|
||||||
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
|
||||||
|
|
||||||
private RegenNoOpWorldLoadListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Paper only(?) @Override
|
|
||||||
public void setChunkRadius(int radius) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FastProtoChunk extends ProtoChunk {
|
|
||||||
|
|
||||||
// avoid warning on paper
|
|
||||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, ServerLevel serverLevel) {
|
|
||||||
super(pos, upgradeData, world, serverLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compatibility with spigot
|
|
||||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor) {
|
|
||||||
super(pos, upgradeData, levelHeightAccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean generateFlatBedrock() {
|
|
||||||
return generateFlatBedrock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no one will ever see the entities!
|
|
||||||
@Override
|
|
||||||
public List<CompoundTag> getEntities() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
|
||||||
|
|
||||||
private final ChunkStatus chunkStatus;
|
|
||||||
|
|
||||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
|
||||||
this.chunkStatus = chunkStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int requiredNeighborChunkRadius() {
|
|
||||||
return chunkStatus.getRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String name() {
|
|
||||||
return chunkStatus.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
|
||||||
return chunkStatus.generate(
|
|
||||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
|
||||||
freshWorld,
|
|
||||||
chunkGenerator,
|
|
||||||
structureManager,
|
|
||||||
threadedLevelLightEngine,
|
|
||||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
|
||||||
accessibleChunks
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -12,6 +12,6 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT
|
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.4-R0.1-20240106.182028-62")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.4-R0.1-20240325.123556-143")
|
||||||
compileOnly(libs.paperlib)
|
compileOnly(libs.paperlib)
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,15 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
|||||||
|
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.util.ReflectionUtil;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
import net.minecraft.world.level.EmptyBlockGetter;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.LiquidBlock;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
import net.minecraft.world.level.material.PushReaction;
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||||
|
|
||||||
@ -21,7 +18,6 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
|||||||
|
|
||||||
private final Block block;
|
private final Block block;
|
||||||
private final BlockState blockState;
|
private final BlockState blockState;
|
||||||
private final boolean isTranslucent;
|
|
||||||
private final CraftBlockData craftBlockData;
|
private final CraftBlockData craftBlockData;
|
||||||
private final org.bukkit.Material craftMaterial;
|
private final org.bukkit.Material craftMaterial;
|
||||||
private final int opacity;
|
private final int opacity;
|
||||||
@ -36,11 +32,6 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
|||||||
this.blockState = blockState;
|
this.blockState = blockState;
|
||||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
this.craftBlockData = CraftBlockData.fromData(blockState);
|
||||||
this.craftMaterial = craftBlockData.getMaterial();
|
this.craftMaterial = craftBlockData.getMaterial();
|
||||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
|
|
||||||
Refraction.pickName("properties", "aP"));
|
|
||||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
|
||||||
Refraction.pickName("canOcclude", "n")
|
|
||||||
);
|
|
||||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
||||||
BlockPos.ZERO,
|
BlockPos.ZERO,
|
||||||
@ -75,7 +66,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpaque() {
|
public boolean isOpaque() {
|
||||||
return blockState.isOpaque();
|
return blockState.canOcclude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -85,14 +76,13 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLiquid() {
|
public boolean isLiquid() {
|
||||||
// TODO: Better check ?
|
return !blockState.getFluidState().is(Fluids.EMPTY);
|
||||||
return block instanceof LiquidBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSolid() {
|
public boolean isSolid() {
|
||||||
// TODO: Replace
|
// No access to world -> EmptyBlockGetter
|
||||||
return blockState.isSolid();
|
return blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,7 +148,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTranslucent() {
|
public boolean isTranslucent() {
|
||||||
return isTranslucent;
|
return !blockState.canOcclude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,7 +210,7 @@ tasks {
|
|||||||
versionNumber.set("${project.version}")
|
versionNumber.set("${project.version}")
|
||||||
versionType.set("release")
|
versionType.set("release")
|
||||||
uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar"))
|
uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar"))
|
||||||
gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1"))
|
gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2"))
|
||||||
loaders.addAll(listOf("paper", "spigot"))
|
loaders.addAll(listOf("paper", "spigot"))
|
||||||
changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" +
|
changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" +
|
||||||
"FastAsyncWorldEdit/releases/tag/${project.version}")
|
"FastAsyncWorldEdit/releases/tag/${project.version}")
|
||||||
|
@ -307,9 +307,6 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
|
|||||||
if (!tickFluid) {
|
if (!tickFluid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (Settings.settings().QUEUE.NO_TICK_FASTMODE && fastMode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
|
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
|
||||||
}
|
}
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
@ -327,12 +327,12 @@ public class BukkitWorld extends AbstractWorld {
|
|||||||
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM);
|
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM);
|
||||||
for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
|
for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
|
||||||
if (treeTypeMapping.get(type) == null) {
|
if (treeTypeMapping.get(type) == null) {
|
||||||
LOGGER.error("No TreeType mapping for TreeGenerator.TreeType." + type);
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
|
LOGGER.info("No TreeType mapping for TreeGenerator.TreeType." + type);
|
||||||
LOGGER.info("The above message is displayed because your FAWE version is newer than {}" +
|
LOGGER.info("The above message is displayed because your FAWE version is newer than {}" +
|
||||||
" and contains features of future minecraft versions which do not exist in {} hence the tree type" +
|
" and contains features of future minecraft versions which do not exist in {} hence the tree type" +
|
||||||
"{} is not available. This is not an error. This version will work on your version of Minecraft." +
|
" {} is not available. This is not an error. This version of FAWE will work on your version of " +
|
||||||
"This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type);
|
" Minecraft. This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type);
|
||||||
//FAWE end
|
//FAWE end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ public class HeightBrush implements Brush {
|
|||||||
try {
|
try {
|
||||||
heightMap = ScalableHeightMap.fromPNG(stream);
|
heightMap = ScalableHeightMap.fromPNG(stream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid"));
|
throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid", e.getMessage()));
|
||||||
}
|
}
|
||||||
} else if (clipboard != null) {
|
} else if (clipboard != null) {
|
||||||
heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY);
|
heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY);
|
||||||
|
@ -511,7 +511,7 @@ public class Settings extends Config {
|
|||||||
" - A larger value will use slightly less CPU time",
|
" - A larger value will use slightly less CPU time",
|
||||||
" - A smaller value will reduce memory usage",
|
" - A smaller value will reduce memory usage",
|
||||||
" - A value too small may break some operations (deform?)",
|
" - A value too small may break some operations (deform?)",
|
||||||
" - Values smaller than the configurated parallel-threads are not accepted",
|
" - Values smaller than the configured parallel-threads are not accepted",
|
||||||
" - It is recommended this option be at least 4x greater than parallel-threads"
|
" - It is recommended this option be at least 4x greater than parallel-threads"
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -544,12 +544,6 @@ public class Settings extends Config {
|
|||||||
})
|
})
|
||||||
public boolean POOL = true;
|
public boolean POOL = true;
|
||||||
|
|
||||||
@Comment({
|
|
||||||
"When using fastmode do not bother to tick existing/placed blocks/fluids",
|
|
||||||
"Only works in versions up to 1.17.1"
|
|
||||||
})
|
|
||||||
public boolean NO_TICK_FASTMODE = true;
|
|
||||||
|
|
||||||
public static class PROGRESS {
|
public static class PROGRESS {
|
||||||
|
|
||||||
@Comment({"Display constant titles about the progress of a user's edit",
|
@Comment({"Display constant titles about the progress of a user's edit",
|
||||||
|
@ -53,7 +53,7 @@ public abstract class RichParser<E> extends InputParser<E> implements AliasedPar
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private Function<String, Stream<? extends String>> extractArguments(String input) {
|
private Function<String, Stream<? extends String>> extractArguments(String input, ParserContext context) {
|
||||||
return prefix -> {
|
return prefix -> {
|
||||||
if (input.length() > prefix.length() && input.startsWith(prefix + "[")) {
|
if (input.length() > prefix.length() && input.startsWith(prefix + "[")) {
|
||||||
// input already contains argument(s) -> extract them
|
// input already contains argument(s) -> extract them
|
||||||
@ -65,7 +65,7 @@ public abstract class RichParser<E> extends InputParser<E> implements AliasedPar
|
|||||||
}
|
}
|
||||||
String previous = prefix + builder;
|
String previous = prefix + builder;
|
||||||
// read the suggestions for the last argument
|
// read the suggestions for the last argument
|
||||||
return getSuggestions(strings[strings.length - 1], strings.length - 1)
|
return getSuggestions(strings[strings.length - 1], strings.length - 1, context)
|
||||||
.map(suggestion -> previous + "[" + suggestion);
|
.map(suggestion -> previous + "[" + suggestion);
|
||||||
} else {
|
} else {
|
||||||
return Stream.of(prefix);
|
return Stream.of(prefix);
|
||||||
@ -95,7 +95,7 @@ public abstract class RichParser<E> extends InputParser<E> implements AliasedPar
|
|||||||
public Stream<String> getSuggestions(String input) {
|
public Stream<String> getSuggestions(String input) {
|
||||||
return Arrays.stream(this.prefixes)
|
return Arrays.stream(this.prefixes)
|
||||||
.filter(validPrefix(input))
|
.filter(validPrefix(input))
|
||||||
.flatMap(extractArguments(input));
|
.flatMap(extractArguments(input, new ParserContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,8 +122,25 @@ public abstract class RichParser<E> extends InputParser<E> implements AliasedPar
|
|||||||
* @param argumentInput the already provided input for the argument at the given index.
|
* @param argumentInput the already provided input for the argument at the given index.
|
||||||
* @param index the index of the argument to get suggestions for.
|
* @param index the index of the argument to get suggestions for.
|
||||||
* @return a stream of suggestions matching the given input for the argument at the given index.
|
* @return a stream of suggestions matching the given input for the argument at the given index.
|
||||||
|
*
|
||||||
|
* @deprecated Use the version that takes a {@link ParserContext}, {@link #getSuggestions(String, int, ParserContext)}
|
||||||
*/
|
*/
|
||||||
protected abstract Stream<String> getSuggestions(String argumentInput, int index);
|
@Deprecated
|
||||||
|
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream of suggestions for the argument at the given index.
|
||||||
|
*
|
||||||
|
* @param argumentInput the already provided input for the argument at the given index.
|
||||||
|
* @param index the index of the argument to get suggestions for.
|
||||||
|
* @param context the context which may optionally be provided by a parser.
|
||||||
|
* @return a stream of suggestions matching the given input for the argument at the given index.
|
||||||
|
*/
|
||||||
|
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||||
|
return getSuggestions(argumentInput, index);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the already split arguments.
|
* Parses the already split arguments.
|
||||||
|
@ -97,11 +97,11 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
)),
|
)),
|
||||||
() -> {
|
() -> {
|
||||||
if (full.length() == 1) {
|
if (full.length() == 1) {
|
||||||
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("", context));
|
||||||
}
|
}
|
||||||
return new ArrayList<>(worldEdit
|
return new ArrayList<>(worldEdit
|
||||||
.getMaskFactory()
|
.getMaskFactory()
|
||||||
.getSuggestions(command.toLowerCase(Locale.ROOT)));
|
.getSuggestions(command.toLowerCase(Locale.ROOT), context));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -164,11 +164,11 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
)),
|
)),
|
||||||
() -> {
|
() -> {
|
||||||
if (full.length() == 1) {
|
if (full.length() == 1) {
|
||||||
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("", context));
|
||||||
}
|
}
|
||||||
return new ArrayList<>(worldEdit
|
return new ArrayList<>(worldEdit
|
||||||
.getMaskFactory()
|
.getMaskFactory()
|
||||||
.getSuggestions(command.toLowerCase(Locale.ROOT)));
|
.getSuggestions(command.toLowerCase(Locale.ROOT), context));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package com.fastasyncworldedit.core.extension.factory.parser.pattern;
|
|||||||
|
|
||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
|
||||||
import com.fastasyncworldedit.core.function.pattern.Linear2DBlockPattern;
|
import com.fastasyncworldedit.core.math.random.Linear2DRandom;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.command.util.SuggestionHelper;
|
import com.sk89q.worldedit.command.util.SuggestionHelper;
|
||||||
@ -14,7 +14,6 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
|||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class Linear2DPatternParser extends RichParser<Pattern> {
|
public class Linear2DPatternParser extends RichParser<Pattern> {
|
||||||
@ -59,9 +58,8 @@ public class Linear2DPatternParser extends RichParser<Pattern> {
|
|||||||
zScale = Integer.parseInt(arguments[2]);
|
zScale = Integer.parseInt(arguments[2]);
|
||||||
Preconditions.checkArgument(zScale != 0);
|
Preconditions.checkArgument(zScale != 0);
|
||||||
}
|
}
|
||||||
if (inner instanceof RandomPattern) {
|
if (inner instanceof RandomPattern rp) {
|
||||||
Set<Pattern> patterns = ((RandomPattern) inner).getPatterns();
|
return new RandomPattern(new Linear2DRandom(xScale, zScale), rp);
|
||||||
return new Linear2DBlockPattern(patterns.toArray(new Pattern[0]), xScale, zScale);
|
|
||||||
}
|
}
|
||||||
throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName()
|
throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName()
|
||||||
+ " cannot be used with " + getPrefix()));
|
+ " cannot be used with " + getPrefix()));
|
||||||
|
@ -2,7 +2,7 @@ package com.fastasyncworldedit.core.extension.factory.parser.pattern;
|
|||||||
|
|
||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
|
||||||
import com.fastasyncworldedit.core.function.pattern.Linear3DBlockPattern;
|
import com.fastasyncworldedit.core.math.random.Linear3DRandom;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.command.util.SuggestionHelper;
|
import com.sk89q.worldedit.command.util.SuggestionHelper;
|
||||||
@ -14,7 +14,6 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
|||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class Linear3DPatternParser extends RichParser<Pattern> {
|
public class Linear3DPatternParser extends RichParser<Pattern> {
|
||||||
@ -64,9 +63,8 @@ public class Linear3DPatternParser extends RichParser<Pattern> {
|
|||||||
zScale = Integer.parseInt(arguments[3]);
|
zScale = Integer.parseInt(arguments[3]);
|
||||||
Preconditions.checkArgument(zScale != 0);
|
Preconditions.checkArgument(zScale != 0);
|
||||||
}
|
}
|
||||||
if (inner instanceof RandomPattern) {
|
if (inner instanceof RandomPattern rp) {
|
||||||
Set<Pattern> patterns = ((RandomPattern) inner).getPatterns();
|
return new RandomPattern(new Linear3DRandom(xScale, yScale, zScale), rp);
|
||||||
return new Linear3DBlockPattern(patterns.toArray(new Pattern[0]), xScale, yScale, zScale);
|
|
||||||
}
|
}
|
||||||
throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName()
|
throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName()
|
||||||
+ " cannot be used with " + getPrefix()));
|
+ " cannot be used with " + getPrefix()));
|
||||||
|
@ -19,12 +19,9 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -80,28 +77,18 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||||
Map<Integer, Set<IBatchProcessor>> ordered = new HashMap<>();
|
Map<Integer, List<IBatchProcessor>> ordered = new HashMap<>();
|
||||||
IChunkSet chunkSet = set;
|
IChunkSet chunkSet = set;
|
||||||
for (IBatchProcessor processor : processors) {
|
for (IBatchProcessor processor : processors) {
|
||||||
if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) {
|
if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) {
|
||||||
ordered.merge(
|
ordered.computeIfAbsent(processor.getScope().intValue(), k -> new ArrayList<>())
|
||||||
processor.getScope().intValue(),
|
.add(processor);
|
||||||
new HashSet<>(Collections.singleton(processor)),
|
|
||||||
(existing, theNew) -> {
|
|
||||||
existing.add(processor);
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
chunkSet = processSet(processor, chunk, get, chunkSet);
|
chunkSet = processSet(processor, chunk, get, chunkSet);
|
||||||
}
|
}
|
||||||
if (ordered.size() > 0) {
|
if (!ordered.isEmpty()) {
|
||||||
for (int i = 1; i <= 4; i++) {
|
for (List<IBatchProcessor> processors : ordered.values()) {
|
||||||
Set<IBatchProcessor> processors = ordered.get(i);
|
|
||||||
if (processors == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (IBatchProcessor processor : processors) {
|
for (IBatchProcessor processor : processors) {
|
||||||
chunkSet = processSet(processor, chunk, get, chunkSet);
|
chunkSet = processSet(processor, chunk, get, chunkSet);
|
||||||
if (chunkSet == null) {
|
if (chunkSet == null) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.fastasyncworldedit.core.function.pattern;
|
package com.fastasyncworldedit.core.function.pattern;
|
||||||
|
|
||||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||||
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||||
import com.sk89q.worldedit.internal.expression.Expression;
|
import com.sk89q.worldedit.internal.expression.Expression;
|
||||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||||
@ -57,4 +58,9 @@ public class ExpressionPattern extends AbstractPattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new ExpressionPattern(this.expression.clone());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,15 @@ import com.sk89q.worldedit.function.pattern.Pattern;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static java.lang.Math.floorDiv;
|
import static java.lang.Math.floorDiv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated replaced by {@link com.sk89q.worldedit.function.pattern.RandomPattern}
|
||||||
|
* combined with {@link com.fastasyncworldedit.core.math.random.Linear2DRandom}.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "TODO")
|
||||||
public class Linear2DBlockPattern extends AbstractPattern {
|
public class Linear2DBlockPattern extends AbstractPattern {
|
||||||
|
|
||||||
private final Pattern[] patternsArray;
|
private final Pattern[] patternsArray;
|
||||||
@ -47,4 +54,10 @@ public class Linear2DBlockPattern extends AbstractPattern {
|
|||||||
return patternsArray[index].apply(extent, get, set);
|
return patternsArray[index].apply(extent, get, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new);
|
||||||
|
return new Linear2DBlockPattern(forked, this.xScale, this.zScale);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,15 @@ import com.sk89q.worldedit.function.pattern.Pattern;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static java.lang.Math.floorDiv;
|
import static java.lang.Math.floorDiv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated replaced by {@link com.sk89q.worldedit.function.pattern.RandomPattern}
|
||||||
|
* combined with {@link com.fastasyncworldedit.core.math.random.Linear3DRandom}.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "TODO")
|
||||||
public class Linear3DBlockPattern extends AbstractPattern {
|
public class Linear3DBlockPattern extends AbstractPattern {
|
||||||
|
|
||||||
private final Pattern[] patternsArray;
|
private final Pattern[] patternsArray;
|
||||||
@ -51,4 +58,10 @@ public class Linear3DBlockPattern extends AbstractPattern {
|
|||||||
return patternsArray[index].apply(extent, get, set);
|
return patternsArray[index].apply(extent, get, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new);
|
||||||
|
return new Linear3DBlockPattern(forked, this.xScale, this.yScale, this.zScale);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.fastasyncworldedit.core.function.pattern;
|
package com.fastasyncworldedit.core.function.pattern;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.queue.Filter;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||||
@ -7,6 +8,8 @@ import com.sk89q.worldedit.function.pattern.Pattern;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class LinearBlockPattern extends AbstractPattern implements ResettablePattern {
|
public class LinearBlockPattern extends AbstractPattern implements ResettablePattern {
|
||||||
|
|
||||||
private final Pattern[] patternsArray;
|
private final Pattern[] patternsArray;
|
||||||
@ -15,7 +18,7 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat
|
|||||||
/**
|
/**
|
||||||
* Create a new {@link Pattern} instance
|
* Create a new {@link Pattern} instance
|
||||||
*
|
*
|
||||||
* @param patterns array of patterns to linearly choose from based on x/z coordinates
|
* @param patterns array of patterns to linearly choose from
|
||||||
*/
|
*/
|
||||||
public LinearBlockPattern(Pattern[] patterns) {
|
public LinearBlockPattern(Pattern[] patterns) {
|
||||||
this.patternsArray = patterns;
|
this.patternsArray = patterns;
|
||||||
@ -23,18 +26,14 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseBlock applyBlock(BlockVector3 position) {
|
public BaseBlock applyBlock(BlockVector3 position) {
|
||||||
if (index == patternsArray.length) {
|
index = (index + 1) % patternsArray.length;
|
||||||
index = 0;
|
return patternsArray[index].applyBlock(position);
|
||||||
}
|
|
||||||
return patternsArray[index++].applyBlock(position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
||||||
if (index == patternsArray.length) {
|
index = (index + 1) % patternsArray.length;
|
||||||
index = 0;
|
return patternsArray[index].apply(extent, get, set);
|
||||||
}
|
|
||||||
return patternsArray[index++].apply(extent, get, set);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -42,4 +41,10 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat
|
|||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new);
|
||||||
|
return new LinearBlockPattern(forked);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,4 +43,9 @@ public class MaskedPattern extends AbstractPattern {
|
|||||||
return secondary.apply(extent, get, set);
|
return secondary.apply(extent, get, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new MaskedPattern(this.mask.copy(), this.primary.fork(), this.secondary.fork());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,4 +36,9 @@ public class NoXPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, mutable, set);
|
return pattern.apply(extent, mutable, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new NoXPattern(this.pattern.fork());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,4 +36,9 @@ public class NoYPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, mutable, set);
|
return pattern.apply(extent, mutable, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new NoYPattern(this.pattern.fork());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.fastasyncworldedit.core.function.pattern;
|
package com.fastasyncworldedit.core.function.pattern;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
||||||
|
import com.fastasyncworldedit.core.queue.Filter;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
import com.sk89q.worldedit.function.pattern.AbstractPattern;
|
||||||
@ -36,4 +37,9 @@ public class NoZPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, mutable, set);
|
return pattern.apply(extent, mutable, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new NoZPattern(this.pattern.fork());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,4 +60,9 @@ public class OffsetPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, get, mutable);
|
return pattern.apply(extent, get, mutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new OffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -72,4 +72,9 @@ public class RandomOffsetPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, get, mutable);
|
return pattern.apply(extent, get, mutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new RandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -63,4 +63,11 @@ public class RelativePattern extends AbstractPattern implements ResettablePatter
|
|||||||
origin = null;
|
origin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
RelativePattern forked = new RelativePattern(this.pattern.fork(), this.minY, this.maxY);
|
||||||
|
forked.origin = this.origin; // maintain origin for forks
|
||||||
|
return forked;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -94,4 +94,9 @@ public class SolidRandomOffsetPattern extends AbstractPattern {
|
|||||||
return pattern.apply(extent, get, set);
|
return pattern.apply(extent, get, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new SolidRandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -129,4 +129,9 @@ public class SurfaceRandomOffsetPattern extends AbstractPattern {
|
|||||||
return !block.getBlockType().getMaterial().isMovementBlocker();
|
return !block.getBlockType().getMaterial().isMovementBlocker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new SurfaceRandomOffsetPattern(this.pattern.fork(), this.moves, this.minY, this.maxY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.fastasyncworldedit.core.math.random;
|
||||||
|
|
||||||
|
import static com.fastasyncworldedit.core.math.random.Linear3DRandom.doubleDiv;
|
||||||
|
import static java.lang.Math.floorDiv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SimpleRandom} that deterministically maps coordinates
|
||||||
|
* to values.
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
public class Linear2DRandom implements SimpleRandom {
|
||||||
|
private final int xScale;
|
||||||
|
private final int zScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Linear2DRandom} instance
|
||||||
|
*
|
||||||
|
* @param xScale the scale applied to the x component of a coordinate
|
||||||
|
* @param zScale the scale applied to the z component of a coordinate
|
||||||
|
*/
|
||||||
|
public Linear2DRandom(final int xScale, final int zScale) {
|
||||||
|
this.xScale = xScale;
|
||||||
|
this.zScale = zScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextDouble(final int x, final int y, final int z) {
|
||||||
|
return nextDouble(x, y, z, 1d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextDouble(final int x, final int y, final int z, double bound) {
|
||||||
|
double index = (doubleDiv(x, this.xScale) + doubleDiv(z, this.zScale)) % bound;
|
||||||
|
if (index < 0) {
|
||||||
|
index += bound;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextInt(final int x, final int y, final int z, final int bound) {
|
||||||
|
int index = (floorDiv(x, this.xScale) + floorDiv(z, this.zScale)) % bound;
|
||||||
|
if (index < 0) {
|
||||||
|
index += bound;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package com.fastasyncworldedit.core.math.random;
|
||||||
|
|
||||||
|
import static java.lang.Math.floorDiv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SimpleRandom} that deterministically maps coordinates
|
||||||
|
* to values.
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
public class Linear3DRandom implements SimpleRandom {
|
||||||
|
|
||||||
|
private final int xScale;
|
||||||
|
private final int yScale;
|
||||||
|
private final int zScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Linear3DRandom} instance
|
||||||
|
*
|
||||||
|
* @param xScale the scale applied to the x component of a coordinate
|
||||||
|
* @param yScale the scale applied to the y component of a coordinate
|
||||||
|
* @param zScale the scale applied to the z component of a coordinate
|
||||||
|
*/
|
||||||
|
public Linear3DRandom(final int xScale, final int yScale, final int zScale) {
|
||||||
|
this.xScale = xScale;
|
||||||
|
this.yScale = yScale;
|
||||||
|
this.zScale = zScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextDouble(final int x, final int y, final int z) {
|
||||||
|
return nextDouble(x, y, z, 1d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextDouble(final int x, final int y, final int z, double bound) {
|
||||||
|
double index = (doubleDiv(x, this.xScale) + doubleDiv(y, this.yScale) + doubleDiv(z, this.zScale)) % bound;
|
||||||
|
if (index < 0) {
|
||||||
|
index += bound;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// used to avoid explicit conversion at call site
|
||||||
|
static double doubleDiv(double dividend, double divisor) {
|
||||||
|
// add a minimal value to avoid too many integral values hitting the exact weight of an entry in SimpleRandomCollection
|
||||||
|
return Math.nextUp(dividend) / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextInt(final int x, final int y, final int z, final int bound) {
|
||||||
|
int index = (floorDiv(x, this.xScale) + floorDiv(y, this.yScale) + floorDiv(z, this.zScale)) % bound;
|
||||||
|
if (index < 0) {
|
||||||
|
index += bound;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,20 @@ public interface SimpleRandom {
|
|||||||
*/
|
*/
|
||||||
double nextDouble(int x, int y, int z);
|
double nextDouble(int x, int y, int z);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random double from three integer components.
|
||||||
|
* The generated value is between 0 (inclusive) and {@code bound} (exclusive).
|
||||||
|
*
|
||||||
|
* @param x the first component
|
||||||
|
* @param y the second component
|
||||||
|
* @param z the third component
|
||||||
|
* @param bound upper bound (exclusive)
|
||||||
|
* @return a double between 0 (inclusive) and {@code bound} (exclusive)
|
||||||
|
*/
|
||||||
|
default double nextDouble(int x, int y, int z, double bound) {
|
||||||
|
return nextDouble(x, y, z) * bound;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random integer from three integer components.
|
* Generate a random integer from three integer components.
|
||||||
* The generated value is between 0 (inclusive) and 1 (exclusive)
|
* The generated value is between 0 (inclusive) and 1 (exclusive)
|
||||||
@ -24,8 +38,8 @@ public interface SimpleRandom {
|
|||||||
* @return a random integer between 0 (inclusive) and {@code bound} (exclusive)
|
* @return a random integer between 0 (inclusive) and {@code bound} (exclusive)
|
||||||
*/
|
*/
|
||||||
default int nextInt(int x, int y, int z, int bound) {
|
default int nextInt(int x, int y, int z, int bound) {
|
||||||
double val = nextDouble(x, y, z);
|
double val = nextDouble(x, y, z, bound);
|
||||||
return (int) (val * bound);
|
return (int) val;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -131,9 +131,12 @@ public final class ParallelQueueExtent extends PassthroughExtent {
|
|||||||
|
|
||||||
// Get a pool, to operate on the chunks in parallel
|
// Get a pool, to operate on the chunks in parallel
|
||||||
final int size = Math.min(chunks.size(), Settings.settings().QUEUE.PARALLEL_THREADS);
|
final int size = Math.min(chunks.size(), Settings.settings().QUEUE.PARALLEL_THREADS);
|
||||||
if (size <= 1 && chunksIter.hasNext()) {
|
if (size <= 1) {
|
||||||
|
// if PQE is ever used with PARALLEL_THREADS = 1, or only one chunk is edited, just run sequentially
|
||||||
|
while (chunksIter.hasNext()) {
|
||||||
BlockVector2 pos = chunksIter.next();
|
BlockVector2 pos = chunksIter.next();
|
||||||
getExtent().apply(null, filter, region, pos.getX(), pos.getZ(), full);
|
getExtent().apply(null, filter, region, pos.getX(), pos.getZ(), full);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
|
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
|
||||||
try {
|
try {
|
||||||
|
@ -51,11 +51,10 @@ public class ExtentTraverser<T extends Extent> {
|
|||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public <U> U findAndGet(Class<U> clazz) {
|
public <U extends Extent> U findAndGet(Class<U> clazz) {
|
||||||
ExtentTraverser<Extent> traverser = find(clazz);
|
ExtentTraverser<U> traverser = find(clazz);
|
||||||
return (traverser != null) ? (U) traverser.get() : null;
|
return (traverser != null) ? traverser.get() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -38,6 +38,9 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
@ -56,6 +59,9 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
@ -70,6 +76,7 @@ import java.nio.file.attribute.BasicFileAttributes;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -91,6 +98,10 @@ import static java.lang.System.arraycopy;
|
|||||||
public class MainUtil {
|
public class MainUtil {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
private static final String CURL_USER_AGENT = "curl/8.1.1";
|
||||||
|
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
|
||||||
|
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
public static List<String> filter(String prefix, List<String> suggestions) {
|
public static List<String> filter(String prefix, List<String> suggestions) {
|
||||||
if (prefix.isEmpty()) {
|
if (prefix.isEmpty()) {
|
||||||
@ -519,12 +530,53 @@ public class MainUtil {
|
|||||||
return destFile;
|
return destFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedImage readImage(InputStream in) throws IOException {
|
public static BufferedImage readImage(InputStream stream) throws IOException {
|
||||||
return MainUtil.toRGB(ImageIO.read(in));
|
final ImageInputStream imageStream = ImageIO.createImageInputStream(stream);
|
||||||
|
if (imageStream == null) {
|
||||||
|
throw new IOException("Can't find suitable ImageInputStream");
|
||||||
|
}
|
||||||
|
Iterator<ImageReader> iter = ImageIO.getImageReaders(imageStream);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
throw new IOException("Could not get image reader from stream.");
|
||||||
|
}
|
||||||
|
ImageReader reader = iter.next();
|
||||||
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
reader.setInput(imageStream, true, true);
|
||||||
|
BufferedImage bi;
|
||||||
|
try {
|
||||||
|
bi = reader.read(0, param);
|
||||||
|
} finally {
|
||||||
|
reader.dispose();
|
||||||
|
stream.close();
|
||||||
|
imageStream.close();
|
||||||
|
}
|
||||||
|
return MainUtil.toRGB(bi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedImage readImage(URL url) throws IOException {
|
public static BufferedImage readImage(URL url) throws IOException {
|
||||||
return readImage(url.openStream());
|
try {
|
||||||
|
final URI uri = url.toURI();
|
||||||
|
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).GET();
|
||||||
|
|
||||||
|
if (uri.getHost().equalsIgnoreCase("i.imgur.com")) {
|
||||||
|
requestBuilder = requestBuilder.setHeader("User-Agent", CURL_USER_AGENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HttpResponse<InputStream> response = HTTP_CLIENT.send(
|
||||||
|
requestBuilder.build(),
|
||||||
|
HttpResponse.BodyHandlers.ofInputStream()
|
||||||
|
);
|
||||||
|
try (final InputStream body = response.body()) {
|
||||||
|
if (response.statusCode() > 299) {
|
||||||
|
throw new IOException("Expected 2xx as response code, but received " + response.statusCode());
|
||||||
|
}
|
||||||
|
return readImage(body);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException("request was interrupted", e);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new IOException("failed to parse url to uri reference", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedImage readImage(File file) throws IOException {
|
public static BufferedImage readImage(File file) throws IOException {
|
||||||
|
@ -34,7 +34,7 @@ public final class SimpleRandomCollection<T> implements RandomCollection<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T next(final SimpleRandom random, int x, int y, int z) {
|
public T next(final SimpleRandom random, int x, int y, int z) {
|
||||||
return map.ceilingEntry(random.nextDouble(x, y, z) * this.total).getValue();
|
return map.ceilingEntry(random.nextDouble(x, y, z, this.total)).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.fastasyncworldedit.core.util.gson;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
|
import com.sk89q.worldedit.world.item.ItemTypes;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public final class ItemTypeAdapter implements JsonDeserializer<ItemType> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemType deserialize(JsonElement json, Type type, JsonDeserializationContext cont) throws JsonParseException {
|
||||||
|
JsonObject jsonObject = json.getAsJsonObject();
|
||||||
|
String id = jsonObject.get("id").getAsString();
|
||||||
|
ItemType itemType = ItemTypes.get(id);
|
||||||
|
if (itemType == null) {
|
||||||
|
throw new JsonParseException("Could not parse item type `" + id + "`");
|
||||||
|
}
|
||||||
|
return itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.fastasyncworldedit.core.util.gson;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.sk89q.worldedit.regions.RegionSelector;
|
||||||
|
import com.sk89q.worldedit.regions.selector.RegionSelectorType;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class RegionSelectorAdapter implements JsonDeserializer<RegionSelector>, JsonSerializer<RegionSelector> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionSelector deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
RegionSelectorType regionType = RegionSelectorType.valueOf(json.getAsString());
|
||||||
|
return regionType.createSelector();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(RegionSelector selector, Type type, JsonSerializationContext context) {
|
||||||
|
RegionSelectorType regionType = RegionSelectorType.getForSelector(selector);
|
||||||
|
// Cannot nicely deserialize Fuzzy region type
|
||||||
|
if (regionType == null || regionType == RegionSelectorType.FUZZY) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new JsonPrimitive(regionType.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -125,7 +125,9 @@ public class LocalSession implements TextureHolder {
|
|||||||
private transient int cuiVersion = CUI_VERSION_UNINITIALIZED;
|
private transient int cuiVersion = CUI_VERSION_UNINITIALIZED;
|
||||||
|
|
||||||
// Session related
|
// Session related
|
||||||
private transient RegionSelector selector = new CuboidRegionSelector();
|
//FAWE start - allow saving to session store
|
||||||
|
private RegionSelector selector = new CuboidRegionSelector();
|
||||||
|
//FAWE end
|
||||||
private transient boolean placeAtPos1 = false;
|
private transient boolean placeAtPos1 = false;
|
||||||
//FAWE start
|
//FAWE start
|
||||||
private final transient List<Object> history = Collections.synchronizedList(new LinkedList<>() {
|
private final transient List<Object> history = Collections.synchronizedList(new LinkedList<>() {
|
||||||
@ -771,6 +773,7 @@ public class LocalSession implements TextureHolder {
|
|||||||
checkNotNull(selector);
|
checkNotNull(selector);
|
||||||
selector.setWorld(world);
|
selector.setWorld(world);
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
|
setDirty();
|
||||||
if (hasWorldOverride() && !world.equals(getWorldOverride())) {
|
if (hasWorldOverride() && !world.equals(getWorldOverride())) {
|
||||||
setWorldOverride(null);
|
setWorldOverride(null);
|
||||||
}
|
}
|
||||||
|
@ -755,13 +755,7 @@ public class SelectionCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setDefaultSelector) {
|
if (setDefaultSelector) {
|
||||||
RegionSelectorType found = null;
|
RegionSelectorType found = RegionSelectorType.getForSelector(newSelector);
|
||||||
for (RegionSelectorType type : RegionSelectorType.values()) {
|
|
||||||
if (type.getSelectorClass() == newSelector.getClass()) {
|
|
||||||
found = type;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
session.setDefaultRegionSelector(found);
|
session.setDefaultRegionSelector(found);
|
||||||
|
@ -113,8 +113,7 @@ public class FactoryConverter<T> implements ArgumentConverter<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private ParserContext createContext(InjectedValueAccess context) {
|
||||||
public ConversionResult<T> convert(String argument, InjectedValueAccess context) {
|
|
||||||
Actor actor = context.injectedValue(Key.of(Actor.class))
|
Actor actor = context.injectedValue(Key.of(Actor.class))
|
||||||
.orElseThrow(() -> new IllegalStateException("No actor"));
|
.orElseThrow(() -> new IllegalStateException("No actor"));
|
||||||
LocalSession session = WorldEdit.getInstance().getSessionManager().get(actor);
|
LocalSession session = WorldEdit.getInstance().getSessionManager().get(actor);
|
||||||
@ -139,6 +138,13 @@ public class FactoryConverter<T> implements ArgumentConverter<T> {
|
|||||||
contextTweaker.accept(parserContext);
|
contextTweaker.accept(parserContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return parserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConversionResult<T> convert(String argument, InjectedValueAccess context) {
|
||||||
|
ParserContext parserContext = createContext(context);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return SuccessfulConversion.fromSingle(
|
return SuccessfulConversion.fromSingle(
|
||||||
factoryExtractor.apply(worldEdit).parseFromInput(argument, parserContext)
|
factoryExtractor.apply(worldEdit).parseFromInput(argument, parserContext)
|
||||||
@ -150,7 +156,9 @@ public class FactoryConverter<T> implements ArgumentConverter<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input, InjectedValueAccess context) {
|
public List<String> getSuggestions(String input, InjectedValueAccess context) {
|
||||||
return factoryExtractor.apply(worldEdit).getSuggestions(input);
|
ParserContext parserContext = createContext(context);
|
||||||
|
|
||||||
|
return factoryExtractor.apply(worldEdit).getSuggestions(input, parserContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,13 +127,13 @@ public final class MaskFactory extends AbstractFactory<Mask> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input, final ParserContext parserContext) {
|
||||||
final String[] split = input.split(" ");
|
final String[] split = input.split(" ");
|
||||||
if (split.length > 1) {
|
if (split.length > 1) {
|
||||||
String prev = input.substring(0, input.lastIndexOf(" ")) + " ";
|
String prev = input.substring(0, input.lastIndexOf(" ")) + " ";
|
||||||
return super.getSuggestions(split[split.length - 1]).stream().map(s -> prev + s).collect(Collectors.toList());
|
return super.getSuggestions(split[split.length - 1], parserContext).stream().map(s -> prev + s).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
return super.getSuggestions(input);
|
return super.getSuggestions(input, parserContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,6 +64,7 @@ import com.sk89q.worldedit.world.entity.EntityType;
|
|||||||
import com.sk89q.worldedit.world.entity.EntityTypes;
|
import com.sk89q.worldedit.world.entity.EntityTypes;
|
||||||
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -337,9 +338,10 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
|
|||||||
return SuggestionHelper.getBlockPropertySuggestions(blockType, props);
|
return SuggestionHelper.getBlockPropertySuggestions(blockType, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException {
|
private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException {
|
||||||
//FAWE start
|
//FAWE start
|
||||||
String[] blockAndExtraData = input.trim().split("\\|");
|
String[] blockAndExtraData = input.trim().split("(?<!^)\\|");
|
||||||
blockAndExtraData[0] = woolMapper(blockAndExtraData[0]);
|
blockAndExtraData[0] = woolMapper(blockAndExtraData[0]);
|
||||||
Map<Property<?>, Object> blockStates = new HashMap<>();
|
Map<Property<?>, Object> blockStates = new HashMap<>();
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
@ -235,4 +235,11 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FAWE - stateful pattern
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
return new ForgetfulExtentBuffer(extent, mask.copy());
|
||||||
|
}
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ import com.sk89q.worldedit.extent.buffer.ExtentBuffer;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,4 +66,12 @@ public class ExtentBufferedCompositePattern extends AbstractExtentPattern {
|
|||||||
return lastBlock;
|
return lastBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FAWE - stateful pattern
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
final Pattern[] forkedPatterns = Arrays.stream(patterns).map(Pattern::fork).toArray(Pattern[]::new);
|
||||||
|
return new ExtentBufferedCompositePattern(getExtent(), forkedPatterns);
|
||||||
|
}
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,11 @@ public interface Pattern extends Filter {
|
|||||||
apply(block, block, block);
|
apply(block, block, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Pattern fork() { // covariant return type
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +66,12 @@ public class RandomPattern extends AbstractPattern {
|
|||||||
this.weights = new ArrayList<>(parent.weights);
|
this.weights = new ArrayList<>(parent.weights);
|
||||||
this.collection = RandomCollection.of(weights);
|
this.collection = RandomCollection.of(weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RandomPattern(SimpleRandom random, List<RandomCollection.Weighted<Pattern>> weights) {
|
||||||
|
this.random = random;
|
||||||
|
this.weights = weights;
|
||||||
|
this.collection = RandomCollection.of(weights);
|
||||||
|
}
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,6 +109,14 @@ public class RandomPattern extends AbstractPattern {
|
|||||||
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
||||||
return collection.next(this.random, get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set);
|
return collection.next(this.random, get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern fork() {
|
||||||
|
List<RandomCollection.Weighted<Pattern>> newWeights = new ArrayList<>();
|
||||||
|
this.weights.forEach((w) -> newWeights.add(new RandomCollection.Weighted<>(w.value().fork(), w.weight())));
|
||||||
|
return new RandomPattern(this.random, newWeights);
|
||||||
|
}
|
||||||
|
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,16 @@ import com.sk89q.worldedit.world.block.BlockType;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static com.sk89q.worldedit.blocks.Blocks.resolveProperties;
|
import static com.sk89q.worldedit.blocks.Blocks.resolveProperties;
|
||||||
|
|
||||||
public class StateApplyingPattern extends AbstractExtentPattern {
|
public class StateApplyingPattern extends AbstractExtentPattern {
|
||||||
|
|
||||||
private final Map<String, String> states;
|
private final Map<String, String> states;
|
||||||
private final Map<BlockType, Map<Property<Object>, Object>> cache = Maps.newHashMap();
|
//FAWE - avoid race conditions
|
||||||
|
private final Map<BlockType, Map<Property<Object>, Object>> cache = new ConcurrentHashMap<>();
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
public StateApplyingPattern(Extent extent, Map<String, String> statesToSet) {
|
public StateApplyingPattern(Extent extent, Map<String, String> statesToSet) {
|
||||||
super(extent);
|
super(extent);
|
||||||
|
@ -24,6 +24,7 @@ import com.sk89q.worldedit.WorldEdit;
|
|||||||
import com.sk89q.worldedit.antlr.ExpressionLexer;
|
import com.sk89q.worldedit.antlr.ExpressionLexer;
|
||||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||||
import com.sk89q.worldedit.internal.expression.invoke.ExpressionCompiler;
|
import com.sk89q.worldedit.internal.expression.invoke.ExpressionCompiler;
|
||||||
|
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
import org.antlr.v4.runtime.CommonTokenStream;
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
@ -199,7 +200,9 @@ public class Expression implements Cloneable {
|
|||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
public Expression clone() {
|
public Expression clone() {
|
||||||
return new Expression(initialExpression, new HashSet<>(providedSlots));
|
Expression expression = new Expression(initialExpression, new HashSet<>(providedSlots));
|
||||||
|
expression.setEnvironment(getEnvironment().clone());
|
||||||
|
return expression;
|
||||||
}
|
}
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ package com.sk89q.worldedit.internal.expression;
|
|||||||
/**
|
/**
|
||||||
* Represents a way to access blocks in a world. Has to accept non-rounded coordinates.
|
* Represents a way to access blocks in a world. Has to accept non-rounded coordinates.
|
||||||
*/
|
*/
|
||||||
public interface ExpressionEnvironment {
|
public interface ExpressionEnvironment extends Cloneable {
|
||||||
|
|
||||||
int getBlockType(double x, double y, double z);
|
int getBlockType(double x, double y, double z);
|
||||||
|
|
||||||
@ -36,4 +36,7 @@ public interface ExpressionEnvironment {
|
|||||||
|
|
||||||
int getBlockDataRel(double x, double y, double z);
|
int getBlockDataRel(double x, double y, double z);
|
||||||
|
|
||||||
|
// FAWE start
|
||||||
|
ExpressionEnvironment clone();
|
||||||
|
// FAWE end
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,14 @@ public abstract class AbstractFactory<E> {
|
|||||||
throw new NoMatchException(Caption.of("worldedit.error.no-match", TextComponent.of(input)));
|
throw new NoMatchException(Caption.of("worldedit.error.no-match", TextComponent.of(input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input) {
|
||||||
|
return getSuggestions(input, new ParserContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSuggestions(String input, ParserContext context) {
|
||||||
return parsers.stream().flatMap(
|
return parsers.stream().flatMap(
|
||||||
p -> p.getSuggestions(input)
|
p -> p.getSuggestions(input, context)
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +45,22 @@ public abstract class InputParser<E> {
|
|||||||
* Gets a stream of suggestions of input to this parser.
|
* Gets a stream of suggestions of input to this parser.
|
||||||
*
|
*
|
||||||
* @return a stream of suggestions
|
* @return a stream of suggestions
|
||||||
|
* @deprecated Use the version that takes a {@link ParserContext}, {@link #getSuggestions(String, ParserContext)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Stream<String> getSuggestions(String input) {
|
public Stream<String> getSuggestions(String input) {
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a stream of suggestions of input to this parser.
|
||||||
|
*
|
||||||
|
* @param input The string input
|
||||||
|
* @param context The parser context
|
||||||
|
*
|
||||||
|
* @return a stream of suggestions
|
||||||
|
*/
|
||||||
|
public Stream<String> getSuggestions(String input, ParserContext context) {
|
||||||
|
return getSuggestions(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,14 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.regions.selector;
|
package com.sk89q.worldedit.regions.selector;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.regions.selector.FuzzyRegionSelector;
|
||||||
|
import com.fastasyncworldedit.core.regions.selector.PolyhedralRegionSelector;
|
||||||
import com.sk89q.worldedit.regions.RegionSelector;
|
import com.sk89q.worldedit.regions.RegionSelector;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enum of default region selector types.
|
* An enum of default region selector types.
|
||||||
*/
|
*/
|
||||||
@ -32,7 +38,21 @@ public enum RegionSelectorType {
|
|||||||
SPHERE(SphereRegionSelector.class),
|
SPHERE(SphereRegionSelector.class),
|
||||||
ELLIPSOID(EllipsoidRegionSelector.class),
|
ELLIPSOID(EllipsoidRegionSelector.class),
|
||||||
POLYGON(Polygonal2DRegionSelector.class),
|
POLYGON(Polygonal2DRegionSelector.class),
|
||||||
CONVEX_POLYHEDRON(ConvexPolyhedralRegionSelector.class);
|
CONVEX_POLYHEDRON(ConvexPolyhedralRegionSelector.class),
|
||||||
|
//FAWE start
|
||||||
|
POLYHEDRAL(PolyhedralRegionSelector.class),
|
||||||
|
FUZZY(FuzzyRegionSelector.class);
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
|
//FAWE start
|
||||||
|
private static final Map<Class<? extends RegionSelector>, RegionSelectorType> VALUE_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (RegionSelectorType type : values()) {
|
||||||
|
VALUE_MAP.put(type.getSelectorClass(), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
private final Class<? extends RegionSelector> selectorClass;
|
private final Class<? extends RegionSelector> selectorClass;
|
||||||
|
|
||||||
@ -40,6 +60,19 @@ public enum RegionSelectorType {
|
|||||||
this.selectorClass = selectorClass;
|
this.selectorClass = selectorClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FAWE start
|
||||||
|
/**
|
||||||
|
* Get a {@link RegionSelectorType} for the given {@link RegionSelector}
|
||||||
|
*
|
||||||
|
* @param selector Region selector to get type enum for
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static RegionSelectorType getForSelector(RegionSelector selector) {
|
||||||
|
return VALUE_MAP.get(selector.getClass());
|
||||||
|
}
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the selector class.
|
* Get the selector class.
|
||||||
*
|
*
|
||||||
|
@ -28,6 +28,8 @@ import com.sk89q.worldedit.math.Vector3;
|
|||||||
|
|
||||||
public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
|
public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
|
||||||
|
|
||||||
|
private static final Vector3 BLOCK_CENTER_OFFSET = Vector3.at(0.5, 0.5, 0.5);
|
||||||
|
|
||||||
private final Vector3 unit;
|
private final Vector3 unit;
|
||||||
private final Vector3 zero2;
|
private final Vector3 zero2;
|
||||||
//FAWE start - MutableVector3
|
//FAWE start - MutableVector3
|
||||||
@ -42,7 +44,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
|
|||||||
public WorldEditExpressionEnvironment(Extent extent, Vector3 unit, Vector3 zero) {
|
public WorldEditExpressionEnvironment(Extent extent, Vector3 unit, Vector3 zero) {
|
||||||
this.extent = extent;
|
this.extent = extent;
|
||||||
this.unit = unit;
|
this.unit = unit;
|
||||||
this.zero2 = zero.add(0.5, 0.5, 0.5);
|
this.zero2 = zero.add(BLOCK_CENTER_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockVector3 toWorld(double x, double y, double z) {
|
public BlockVector3 toWorld(double x, double y, double z) {
|
||||||
@ -83,7 +85,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public int getBlockDataRel(double x, double y, double z) {
|
public int getBlockDataRel(double x, double y, double z) {
|
||||||
return extent.getBlock(toWorld(x, y, z)).getBlockType().getLegacyCombinedId() & 0xF;
|
return extent.getBlock(toWorldRel(x, y, z).toBlockPoint()).getBlockType().getLegacyCombinedId() & 0xF;
|
||||||
}
|
}
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
@ -94,10 +96,13 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment {
|
|||||||
public Vector3 toWorldRel(double x, double y, double z) {
|
public Vector3 toWorldRel(double x, double y, double z) {
|
||||||
return current.add(x, y, z);
|
return current.add(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorldEditExpressionEnvironment clone() {
|
||||||
|
return new WorldEditExpressionEnvironment(extent, unit, zero2.subtract(BLOCK_CENTER_OFFSET));
|
||||||
|
}
|
||||||
//FAWe end
|
//FAWe end
|
||||||
|
|
||||||
public void setCurrentBlock(Vector3 current) {
|
public void setCurrentBlock(Vector3 current) {
|
||||||
this.current = current;
|
this.current = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,14 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.util.gson;
|
package com.sk89q.worldedit.util.gson;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.util.gson.ItemTypeAdapter;
|
||||||
|
import com.fastasyncworldedit.core.util.gson.RegionSelectorAdapter;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
|
import com.sk89q.worldedit.regions.RegionSelector;
|
||||||
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for Google's GSON library.
|
* Utility methods for Google's GSON library.
|
||||||
@ -41,6 +45,10 @@ public final class GsonUtil {
|
|||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter());
|
gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter());
|
||||||
gsonBuilder.registerTypeAdapter(BlockVector3.class, new BlockVectorAdapter());
|
gsonBuilder.registerTypeAdapter(BlockVector3.class, new BlockVectorAdapter());
|
||||||
|
//FAWE start
|
||||||
|
gsonBuilder.registerTypeAdapter(RegionSelector.class, new RegionSelectorAdapter());
|
||||||
|
gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter());
|
||||||
|
//FAWE end
|
||||||
return gsonBuilder;
|
return gsonBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ public class ItemType implements RegistryItem, Keyed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
private int internalId;
|
private transient int internalId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInternalId(int internalId) {
|
public void setInternalId(int internalId) {
|
||||||
|
@ -114,6 +114,11 @@ class BaseExpressionTest {
|
|||||||
public int getBlockDataRel(double x, double y, double z) {
|
public int getBlockDataRel(double x, double y, double z) {
|
||||||
return (int) y * 100;
|
return (int) y * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionEnvironment clone() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return expression.evaluate();
|
return expression.evaluate();
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren