Skip to content

Commit 632b693

Browse files
authored
feat: implement command to fix block connections (#2746)
* feat: implement command to fix block connections - closes #313 * set SETUP to true on setup * Add (untested) processing capability and second passes where appropriate * Minor refactor, add various javadocs * Minor adjustments * Better chest handling * Utilise thread extends when able * Add strings, changes to fastmode/side effect handling * Cleanup * Add for 1.21 * Adjustment for tall flowers - add capability to forcefully override block updates * Move to Fawe/LinTag * fix: adjustments, fix certain upside-down blocks * Add for 1.21.3
1 parent afec252 commit 632b693

File tree

69 files changed

+4667
-128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+4667
-128
lines changed

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -857,12 +857,12 @@ private ResourceKey<LevelStem> getWorldDimKey(Environment env) {
857857
}
858858

859859
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
860-
SideEffect.NEIGHBORS,
861-
SideEffect.LIGHTING,
862-
SideEffect.VALIDATION,
863-
SideEffect.ENTITY_AI,
864-
SideEffect.EVENTS,
865-
SideEffect.UPDATE
860+
//FAWE start - FAWE-supported side effects
861+
SideEffect.HISTORY,
862+
SideEffect.HEIGHTMAPS,
863+
SideEffect.LIGHTING,
864+
SideEffect.NEIGHBORS
865+
//FAWE end
866866
);
867867

868868
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
* WorldEdit, a Minecraft world manipulation toolkit
3+
* Copyright (C) sk89q <http://www.sk89q.com>
4+
* Copyright (C) WorldEdit team and contributors
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
21+
22+
import com.fastasyncworldedit.core.internal.exception.FaweException;
23+
import com.sk89q.worldedit.EditSession;
24+
import com.sk89q.worldedit.MaxChangedBlocksException;
25+
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
26+
import com.sk89q.worldedit.bukkit.adapter.Refraction;
27+
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightFaweAdapter;
28+
import com.sk89q.worldedit.extent.Extent;
29+
import com.sk89q.worldedit.internal.util.LogManagerCompat;
30+
import com.sk89q.worldedit.world.block.BlockTypes;
31+
import net.minecraft.core.BlockPos;
32+
import net.minecraft.core.Direction;
33+
import net.minecraft.nbt.CompoundTag;
34+
import net.minecraft.server.level.ServerLevel;
35+
import net.minecraft.tags.FluidTags;
36+
import net.minecraft.world.entity.Entity;
37+
import net.minecraft.world.level.WorldGenLevel;
38+
import net.minecraft.world.level.block.entity.BlockEntity;
39+
import net.minecraft.world.level.block.state.BlockState;
40+
import net.minecraft.world.level.material.FluidState;
41+
import net.minecraft.world.phys.AABB;
42+
import org.apache.logging.log4j.Logger;
43+
import org.jetbrains.annotations.Nullable;
44+
45+
import java.lang.reflect.InvocationHandler;
46+
import java.lang.reflect.Method;
47+
import java.lang.reflect.Proxy;
48+
import java.util.ArrayList;
49+
import java.util.Arrays;
50+
51+
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
52+
53+
private static final Logger LOGGER = LogManagerCompat.getLogger();
54+
55+
// FAWE start - extent not EditSession
56+
private final Extent editSession;
57+
//FAWE end
58+
private final ServerLevel serverLevel;
59+
//FAWE start - use FAWE adapter
60+
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
61+
.getInstance()
62+
.getBukkitImplAdapter());
63+
//FAWE end
64+
//FAWE start - force error if method not caught by this instance
65+
private final boolean errorOnPassthrough;
66+
//FAWE end
67+
68+
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
69+
this.editSession = editSession;
70+
this.serverLevel = serverLevel;
71+
//FAWE start
72+
this.errorOnPassthrough = false;
73+
//FAWE end
74+
}
75+
76+
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
77+
return (WorldGenLevel) Proxy.newProxyInstance(
78+
serverLevel.getClass().getClassLoader(),
79+
serverLevel.getClass().getInterfaces(),
80+
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
81+
);
82+
}
83+
84+
//FAWE start - force error if method not caught by this instance
85+
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
86+
this.editSession = extent;
87+
this.serverLevel = serverLevel;
88+
this.errorOnPassthrough = errorOnPassthrough;
89+
}
90+
91+
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
92+
return (WorldGenLevel) Proxy.newProxyInstance(
93+
serverLevel.getClass().getClassLoader(),
94+
serverLevel.getClass().getInterfaces(),
95+
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
96+
);
97+
}
98+
//FAWE end
99+
100+
@Nullable
101+
private BlockEntity getBlockEntity(BlockPos blockPos) {
102+
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
103+
if (tileEntity == null) {
104+
return null;
105+
}
106+
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
107+
newEntity.load((CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
108+
blockPos.getX(),
109+
blockPos.getY(),
110+
blockPos.getZ()
111+
).getNbtReference().getValue()));
112+
113+
return newEntity;
114+
}
115+
116+
private BlockState getBlockState(BlockPos blockPos) {
117+
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
118+
}
119+
120+
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
121+
try {
122+
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
123+
} catch (MaxChangedBlocksException e) {
124+
throw new RuntimeException(e);
125+
}
126+
}
127+
128+
private boolean removeBlock(BlockPos blockPos, boolean bl) {
129+
try {
130+
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
131+
} catch (MaxChangedBlocksException e) {
132+
throw new RuntimeException(e);
133+
}
134+
}
135+
136+
private FluidState getFluidState(BlockPos pos) {
137+
return getBlockState(pos).getFluidState();
138+
}
139+
140+
private boolean isWaterAt(BlockPos pos) {
141+
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
142+
}
143+
144+
@Override
145+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
146+
//FAWE start - cannot use switch where method names are equal
147+
String methodName = method.getName();
148+
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
149+
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
150+
// getBlockState
151+
return getBlockState(blockPos);
152+
}
153+
}
154+
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
155+
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
156+
// getBlockEntity
157+
return getBlockEntity(blockPos);
158+
}
159+
}
160+
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
161+
methodName)) {
162+
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
163+
// setBlock
164+
return setBlock(blockPos, blockState);
165+
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
166+
// removeBlock (and also matches destroyBlock)
167+
return removeBlock(blockPos, bl);
168+
}
169+
}
170+
//FAWE start
171+
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
172+
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
173+
return getFluidState(blockPos);
174+
}
175+
}
176+
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
177+
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
178+
return isWaterAt(blockPos);
179+
}
180+
}
181+
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
182+
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
183+
return new ArrayList<>();
184+
}
185+
}
186+
// Specific passthroughs that we want to allow
187+
// net.minecraft.world.level.BlockAndTintGetter
188+
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
189+
return method.invoke(this.serverLevel, args);
190+
}
191+
// net.minecraft.world.level.LevelHeightAccessor
192+
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
193+
if (args.length == 0) {
194+
return method.invoke(this.serverLevel, args);
195+
}
196+
}
197+
// net.minecraft.world.level.SignalGetter
198+
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
199+
if (args.length == 1 && args[0] instanceof BlockPos) {
200+
return method.invoke(this.serverLevel, args);
201+
}
202+
}
203+
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
204+
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
205+
return method.invoke(this.serverLevel, args);
206+
}
207+
}
208+
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
209+
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
210+
return method.invoke(this.serverLevel, args);
211+
}
212+
}
213+
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
214+
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
215+
return method.invoke(this.serverLevel, args);
216+
}
217+
}
218+
//FAWE start - force error if method not caught by this instance
219+
if (errorOnPassthrough) {
220+
LOGGER.error(
221+
"""
222+
Attempted passthough of method {}.
223+
Method argument types: {}
224+
Method argument values: {}
225+
""",
226+
method.getName(),
227+
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
228+
Arrays.stream(args).map(Object::toString).toList()
229+
);
230+
throw new FaweException("Method required passthrough.");
231+
}
232+
//FAWE end
233+
234+
return method.invoke(this.serverLevel, args);
235+
}
236+
237+
}

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
55
import com.fastasyncworldedit.core.FaweCache;
66
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
7+
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
78
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
89
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
910
import com.fastasyncworldedit.core.queue.IBatchProcessor;
@@ -13,6 +14,7 @@
1314
import com.google.common.base.Preconditions;
1415
import com.google.common.collect.ImmutableList;
1516
import com.google.common.collect.ImmutableMap;
17+
import com.google.common.collect.Sets;
1618
import com.sk89q.jnbt.Tag;
1719
import com.sk89q.worldedit.blocks.BaseItemStack;
1820
import com.sk89q.worldedit.bukkit.BukkitAdapter;
@@ -21,6 +23,7 @@
2123
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
2224
import com.sk89q.worldedit.entity.BaseEntity;
2325
import com.sk89q.worldedit.extent.Extent;
26+
import com.sk89q.worldedit.function.mask.BlockTypeMask;
2427
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
2528
import com.sk89q.worldedit.internal.util.LogManagerCompat;
2629
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
@@ -33,7 +36,6 @@
3336
import com.sk89q.worldedit.registry.state.Property;
3437
import com.sk89q.worldedit.util.Direction;
3538
import com.sk89q.worldedit.util.SideEffect;
36-
import com.sk89q.worldedit.util.SideEffectSet;
3739
import com.sk89q.worldedit.util.formatting.text.Component;
3840
import com.sk89q.worldedit.world.RegenOptions;
3941
import com.sk89q.worldedit.world.biome.BiomeType;
@@ -284,9 +286,16 @@ public BaseBlock getFullBlock(final Location location) {
284286
return state.toBaseBlock();
285287
}
286288

289+
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
290+
SideEffect.HISTORY,
291+
SideEffect.HEIGHTMAPS,
292+
SideEffect.LIGHTING,
293+
SideEffect.NEIGHBORS
294+
);
295+
287296
@Override
288297
public Set<SideEffect> getSupportedSideEffects() {
289-
return SideEffectSet.defaults().getSideEffectsToApply();
298+
return SUPPORTED_SIDE_EFFECTS;
290299
}
291300

292301
@Override
@@ -434,6 +443,10 @@ public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
434443
return material.getCraftBlockData();
435444
}
436445

446+
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
447+
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
448+
}
449+
437450
@Override
438451
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
439452
ServerLevel nmsWorld = getServerLevel(world);
@@ -602,6 +615,11 @@ public IBatchProcessor getTickingPostProcessor() {
602615
return new PaperweightPostProcessor();
603616
}
604617

618+
@Override
619+
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
620+
return new PaperweightPlacementStateProcessor(extent, mask, region);
621+
}
622+
605623
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
606624
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
607625
try {

0 commit comments

Comments
 (0)