Skip to content

Commit 57b5ce5

Browse files
YaossgInitAuther97
andauthored
Validate ServerLevel via IWorldBridge. (#1951) (#2023)
* Experimental: better support for logic world initial * Use checkActual to validate ServerLevel. (#1951) * Fix build failure * No local variable with type ServerLevel --------- Co-authored-by: InitAuther97 <initauther97@outlook.com>
1 parent 114d4b7 commit 57b5ce5

File tree

22 files changed

+216
-212
lines changed

22 files changed

+216
-212
lines changed
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
package io.izzel.arclight.common.bridge.core.world;
22

3+
import io.izzel.arclight.common.mod.util.DistValidate;
34
import net.minecraft.server.level.ServerLevel;
5+
import net.minecraft.world.level.LevelAccessor;
46

57
public interface IWorldBridge {
8+
static IWorldBridge from(LevelAccessor world) {
9+
final var result = (IWorldBridge) world;
10+
return result.arclight$isActual() ? result : null;
11+
}
612

7-
ServerLevel bridge$getMinecraftWorld();
13+
default boolean arclight$isActual() {
14+
return DistValidate.isValid((LevelAccessor) this);
15+
}
16+
17+
default ServerLevel bridge$getMinecraftWorld() {
18+
throw new UnsupportedOperationException(String.format("No server level found for %s.\n This is likely because the specified world is not a ServerLevelAccessor and thus it shouldn't be a logic world.\n Otherwise it is a bug of Arclight.", getClass().getName()));
19+
}
820
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.izzel.arclight.common.mixin.bukkit;
2+
3+
import io.izzel.arclight.common.mod.server.world.ArclightWorldConfig;
4+
import io.izzel.arclight.mixin.Decorate;
5+
import io.izzel.arclight.mixin.DecorationOps;
6+
import org.spigotmc.SpigotWorldConfig;
7+
import org.spongepowered.asm.mixin.Final;
8+
import org.spongepowered.asm.mixin.Mixin;
9+
import org.spongepowered.asm.mixin.Shadow;
10+
import org.spongepowered.asm.mixin.injection.At;
11+
12+
@Mixin(value = SpigotWorldConfig.class, remap = false)
13+
public class SpigotWorldConfigMixin {
14+
15+
@Shadow @Final private String worldName;
16+
17+
@SuppressWarnings("StringEquality")
18+
@Decorate(method = "log", inject = true, at = @At("HEAD"))
19+
private void arclight$skipLog(String content) throws Throwable {
20+
if (worldName == ArclightWorldConfig.DEFAULT_MARKER) {
21+
DecorationOps.cancel().invoke();
22+
return;
23+
}
24+
DecorationOps.blackhole().invoke();
25+
}
26+
}

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/fluid/LavaFluidMixin.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,15 @@ public void randomTick(Level world, BlockPos pos, FluidState state, RandomSource
8888

8989
@Decorate(method = "spreadTo", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/LevelAccessor;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;I)Z"))
9090
private boolean arclight$blockFromTo(LevelAccessor world, BlockPos pos, BlockState newState, int flags) throws Throwable {
91-
// Already checked in callBlockFormEvent
92-
final var event = ArclightEventFactory.callBlockFormEvent(((IWorldBridge) world).bridge$getMinecraftWorld(), pos, newState, flags, null);
93-
if (event != null) {
94-
if (event.isCancelled()) {
95-
return (boolean) DecorationOps.cancel().invoke();
91+
if (IWorldBridge.from(world) instanceof IWorldBridge bridge) {
92+
// Already checked in callBlockFormEvent
93+
final var event = ArclightEventFactory.callBlockFormEvent(bridge.bridge$getMinecraftWorld(), pos, newState, flags, null);
94+
if (event != null) {
95+
if (event.isCancelled()) {
96+
return (boolean) DecorationOps.cancel().invoke();
97+
}
98+
newState = ((CraftBlockState) event.getNewState()).getHandle();
9699
}
97-
newState = ((CraftBlockState) event.getNewState()).getHandle();
98100
}
99101
return (boolean) DecorationOps.callsite().invoke(world, pos, newState, flags);
100102
}

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/server/level/ServerLevelMixin.java

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import io.izzel.arclight.common.bridge.core.inventory.IInventoryBridge;
99
import io.izzel.arclight.common.bridge.core.server.MinecraftServerBridge;
1010
import io.izzel.arclight.common.bridge.core.world.ExplosionBridge;
11-
import io.izzel.arclight.common.bridge.core.world.IWorldBridge;
1211
import io.izzel.arclight.common.bridge.core.world.level.levelgen.flat.FlatLevelGeneratorSettingsBridge;
1312
import io.izzel.arclight.common.bridge.core.world.server.ServerChunkProviderBridge;
1413
import io.izzel.arclight.common.bridge.core.world.server.ServerWorldBridge;
@@ -73,6 +72,7 @@
7372
import org.bukkit.Bukkit;
7473
import org.bukkit.Location;
7574
import org.bukkit.World;
75+
import org.bukkit.craftbukkit.v.CraftWorld;
7676
import org.bukkit.craftbukkit.v.block.CraftBlockState;
7777
import org.bukkit.craftbukkit.v.entity.CraftHumanEntity;
7878
import org.bukkit.craftbukkit.v.event.CraftEventFactory;
@@ -157,7 +157,6 @@ public ResourceKey<LevelStem> getTypeKey() {
157157
craftBridge.bridge$offerGeneratorCache(worldInfo.getLevelName(), gen);
158158
craftBridge.bridge$offerBiomeProviderCache(worldInfo.getLevelName(), biomeProvider);
159159
arclight$constructor(server, backgroundExecutor, levelSave, worldInfo, dimension, levelStem, statusListener, isDebug, seed, specialSpawners, shouldBeTicking, seq);
160-
bridge$getWorld();
161160
}
162161

163162
// Support custom chunk generator; in consistency with CraftBukkit
@@ -168,51 +167,62 @@ public ResourceKey<LevelStem> getTypeKey() {
168167
@Decorate(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/LevelStem;generator()Lnet/minecraft/world/level/chunk/ChunkGenerator;"))
169168
private ChunkGenerator arclight$initChunkGenerator(LevelStem instance, @Local(ordinal = -1) MinecraftServer server, @Local(ordinal = -1) ServerLevelData worldInfo) throws Throwable {
170169
// Pulling up world info init since level info is used when selecting ChunkGenerator.
171-
if (worldInfo instanceof PrimaryLevelData primary) {
172-
this.K = primary;
170+
if (arclight$isActual()) {
171+
if (worldInfo instanceof PrimaryLevelData primary) {
172+
this.K = primary;
173+
} else {
174+
// damn spigot again
175+
this.K = DelegateWorldInfo.wrap(worldInfo);
176+
}
173177
} else {
174-
// damn spigot again
175-
this.K = DelegateWorldInfo.wrap(worldInfo);
178+
this.K = null;
176179
}
177180

178-
var craftBridge = (CraftServerBridge) (Object) ((MinecraftServerBridge) server).bridge$getServer();
179-
180-
this.biomeProvider = craftBridge.bridge$consumeBiomeProviderCache(worldInfo.getLevelName());
181-
this.generator = craftBridge.bridge$consumeGeneratorCache(worldInfo.getLevelName());
182-
this.environment = craftBridge.bridge$consumeEnvironmentCache(worldInfo.getLevelName());
183-
184-
if (this.environment == null) {
185-
// Select world environment for vanilla/mod world creation
186-
if (instance.type().is(LevelStem.OVERWORLD.location())) {
187-
this.environment = World.Environment.NORMAL;
188-
} else if (instance.type().is(LevelStem.NETHER.location())) {
189-
this.environment = World.Environment.NETHER;
190-
} else if (instance.type().is(LevelStem.END.location())) {
191-
this.environment = World.Environment.THE_END;
192-
} else {
193-
// Don't use CUSTOM; it's not even supported in Multiverse
194-
// this.environment = World.Environment.CUSTOM;
195-
this.environment = World.Environment.NORMAL;
181+
if (arclight$isActual()) {
182+
var craftBridge = (CraftServerBridge) (Object) ((MinecraftServerBridge) server).bridge$getServer();
183+
184+
this.biomeProvider = craftBridge.bridge$consumeBiomeProviderCache(worldInfo.getLevelName());
185+
this.generator = craftBridge.bridge$consumeGeneratorCache(worldInfo.getLevelName());
186+
this.environment = craftBridge.bridge$consumeEnvironmentCache(worldInfo.getLevelName());
187+
188+
if (this.environment == null) {
189+
// Select world environment for vanilla/mod world creation
190+
if (instance.type().is(LevelStem.OVERWORLD.location())) {
191+
this.environment = World.Environment.NORMAL;
192+
} else if (instance.type().is(LevelStem.NETHER.location())) {
193+
this.environment = World.Environment.NETHER;
194+
} else if (instance.type().is(LevelStem.END.location())) {
195+
this.environment = World.Environment.THE_END;
196+
} else {
197+
// Don't use CUSTOM; it's not even supported in Multiverse
198+
// this.environment = World.Environment.CUSTOM;
199+
this.environment = World.Environment.NORMAL;
200+
}
196201
}
197202
}
198203

199-
// Data needed by getWorld() are all initialized for possible creating CraftWorld.
200-
// CraftBukkit start: select custom chunk generator
201204
ChunkGenerator raw = (ChunkGenerator) DecorationOps.callsite().invoke(instance);
202-
if (biomeProvider != null) {
203-
BiomeSource biomeSource = new CustomWorldChunkManager(getWorld(), biomeProvider, getServer().registryAccess().registryOrThrow(Registries.BIOME));
204-
if (raw instanceof NoiseBasedChunkGenerator noise) {
205-
raw = new NoiseBasedChunkGenerator(biomeSource, noise.settings);
206-
} else if (raw instanceof FlatLevelSource flat) {
207-
raw = new FlatLevelSource(((FlatLevelGeneratorSettingsBridge)flat.settings()).bridge$withBiomeSource(biomeSource));
208-
} else {
209-
ArclightServer.LOGGER.warn("Level {} has unknown customized generator -- requested biome provider won't be satisfied.", this.serverLevelData.getLevelName());
205+
if (arclight$isActual()) {
206+
// Data needed by getWorld() are all initialized for possible creating CraftWorld.
207+
// CraftBukkit start: select custom chunk generator
208+
if (biomeProvider != null) {
209+
BiomeSource biomeSource = new CustomWorldChunkManager(getWorld(), biomeProvider, getServer().registryAccess().registryOrThrow(Registries.BIOME));
210+
if (raw instanceof NoiseBasedChunkGenerator noise) {
211+
raw = new NoiseBasedChunkGenerator(biomeSource, noise.settings);
212+
} else if (raw instanceof FlatLevelSource flat) {
213+
raw = new FlatLevelSource(((FlatLevelGeneratorSettingsBridge) flat.settings()).bridge$withBiomeSource(biomeSource));
214+
} else {
215+
ArclightServer.LOGGER.warn("Level {} has unknown customized generator -- requested biome provider won't be satisfied.", this.serverLevelData.getLevelName());
216+
}
210217
}
218+
if (generator != null) {
219+
raw = new CustomChunkGenerator((ServerLevel) (Object) this, raw, generator);
220+
}
221+
// CraftBukkit end
222+
223+
// Now we create the CraftWorld
224+
this.world = new CraftWorld((ServerLevel) (Object) this, generator, biomeProvider, environment);
211225
}
212-
if (generator != null) {
213-
raw = new CustomChunkGenerator((ServerLevel)(Object) this, raw, generator);
214-
}
215-
// CraftBukkit end
216226
return raw;
217227
}
218228

@@ -225,7 +235,7 @@ public ResourceKey<LevelStem> getTypeKey() {
225235
private void arclight$init(MinecraftServer minecraftServer, Executor backgroundExecutor, LevelStorageSource.LevelStorageAccess levelSave, ServerLevelData worldInfo, ResourceKey<Level> dimension, LevelStem levelStem, ChunkProgressListener statusListener, boolean isDebug, long seed, List<CustomSpawner> specialSpawners, boolean shouldBeTicking, RandomSequences seq, CallbackInfo ci) {
226236
this.pvpMode = minecraftServer.isPvpAllowed();
227237
this.convertable = levelSave;
228-
if (this.dragonFight == null && this.environment == World.Environment.THE_END) {
238+
if (arclight$isActual() && this.dragonFight == null && this.environment == World.Environment.THE_END) {
229239
this.dragonFight = new EndDragonFight((ServerLevel)(Object) this, K.worldGenOptions().seed(), K.endDragonFightData());
230240
}
231241
var typeKey = ((LevelStorageSourceBridge.LevelStorageAccessBridge) levelSave).bridge$getTypeKey();
@@ -251,9 +261,11 @@ public ResourceKey<LevelStem> getTypeKey() {
251261
this.uuid = WorldUUID.getUUID(levelSave.getDimensionPath(this.dimension()).toFile());
252262
((ServerChunkProviderBridge) this.chunkSource).bridge$setViewDistance(spigotConfig.viewDistance);
253263
((ServerChunkProviderBridge) this.chunkSource).bridge$setSimulationDistance(spigotConfig.simulationDistance);
254-
((WorldInfoBridge) this.K).bridge$setWorld((ServerLevel) (Object) this);
255-
var data = this.getDataStorage().computeIfAbsent(LevelPersistentData.factory(), "bukkit_pdc");
256-
this.bridge$getWorld().readBukkitValues(data.getTag());
264+
if (arclight$isActual()) {
265+
((WorldInfoBridge) this.K).bridge$setWorld((ServerLevel) (Object) this);
266+
var data = this.getDataStorage().computeIfAbsent(LevelPersistentData.factory(), "bukkit_pdc");
267+
this.bridge$getWorld().readBukkitValues(data.getTag());
268+
}
257269
}
258270

259271
@Inject(method = "saveLevelData", at = @At("RETURN"))
@@ -439,7 +451,7 @@ public <T extends ParticleOptions> int sendParticles(T type, double posX, double
439451
return;
440452
}
441453
CreatureSpawnEvent.SpawnReason spawnReason = reason == null ? CreatureSpawnEvent.SpawnReason.DEFAULT : reason;
442-
if (!DistValidate.isValid(this)) {
454+
if (!arclight$isActual()) {
443455
return;
444456
}
445457
((EntityBridge) entityIn).arclight$onAddedToLevel();

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/LivingEntityMixin.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -687,12 +687,14 @@ private boolean checkTotemDeathProtection(DamageSource damageSourceIn) {
687687

688688
@Decorate(method = "createWitherRose", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;I)Z"))
689689
private boolean arclight$fireWitherRoseForm(Level level, BlockPos pos, BlockState newState, int flags) throws Throwable {
690-
final var event = ArclightEventFactory.callBlockFormEvent(((IWorldBridge) level).bridge$getMinecraftWorld(), pos, newState, flags, null);
691-
if (event != null) {
692-
if (event.isCancelled()) {
693-
return false;
690+
if (IWorldBridge.from(level) instanceof IWorldBridge bridge) {
691+
final var event = ArclightEventFactory.callBlockFormEvent(bridge.bridge$getMinecraftWorld(), pos, newState, flags, null);
692+
if (event != null) {
693+
if (event.isCancelled()) {
694+
return false;
695+
}
696+
newState = ((CraftBlockState) event.getNewState()).getHandle();
694697
}
695-
newState = ((CraftBlockState) event.getNewState()).getHandle();
696698
}
697699
return (boolean) DecorationOps.callsite().invoke(level, pos, newState, flags);
698700
}

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/ai/brain/BrainUtilMixin.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.izzel.arclight.common.mixin.core.world.entity.ai.brain;
22

3-
import io.izzel.arclight.common.bridge.core.entity.EntityBridge;
43
import net.minecraft.world.entity.LivingEntity;
54
import net.minecraft.world.entity.ai.behavior.BehaviorUtils;
65
import net.minecraft.world.entity.item.ItemEntity;
@@ -26,7 +25,7 @@ public class BrainUtilMixin {
2625
@Inject(method = "throwItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;F)V", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD,
2726
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"))
2827
private static void arclight$entityDropItem(LivingEntity entity, ItemStack p_217135_, Vec3 p_217136_, Vec3 p_217137_, float p_217138_, CallbackInfo ci, double d0, ItemEntity itemEntity) {
29-
EntityDropItemEvent event = new EntityDropItemEvent(((EntityBridge) entity).bridge$getBukkitEntity(), (Item) ((EntityBridge) itemEntity).bridge$getBukkitEntity());
28+
EntityDropItemEvent event = new EntityDropItemEvent(entity.bridge$getBukkitEntity(), (Item) itemEntity.bridge$getBukkitEntity());
3029
Bukkit.getPluginManager().callEvent(event);
3130
if (event.isCancelled()) {
3231
ci.cancel();

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/ai/goal/BreakDoorGoalMixin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.spongepowered.asm.mixin.injection.At;
99
import org.spongepowered.asm.mixin.injection.Inject;
1010
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
1112

1213
@Mixin(BreakDoorGoal.class)
1314
public abstract class BreakDoorGoalMixin extends DoorInteractGoal {
@@ -17,7 +18,7 @@ public BreakDoorGoalMixin(Mob entityIn) {
1718
}
1819

1920
@Inject(method = "tick", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;removeBlock(Lnet/minecraft/core/BlockPos;Z)Z"))
20-
public void arclight$breakDoor(CallbackInfo ci) {
21+
private void arclight$breakDoor(CallbackInfo ci) {
2122
if (CraftEventFactory.callEntityBreakDoorEvent(this.mob, this.doorPos).isCancelled()) {
2223
this.start();
2324
ci.cancel();

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/SnowGolemMixin.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import net.minecraft.sounds.SoundSource;
99
import net.minecraft.world.damagesource.DamageSources;
1010
import org.bukkit.craftbukkit.v.block.CraftBlockState;
11-
import org.bukkit.craftbukkit.v.event.CraftEventFactory;
1211
import org.spongepowered.asm.mixin.Mixin;
1312
import org.spongepowered.asm.mixin.injection.At;
1413
import org.spongepowered.asm.mixin.injection.Inject;
@@ -31,12 +30,14 @@ public abstract class SnowGolemMixin extends PathfinderMobMixin {
3130

3231
@Decorate(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;setBlockAndUpdate(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z"))
3332
private boolean arclight$blockForm(Level world, BlockPos pos, BlockState newState) throws Throwable {
34-
final var event = ArclightEventFactory.callBlockFormEvent(((IWorldBridge) world).bridge$getMinecraftWorld(), pos, newState, 3, (SnowGolem)(Object) this);
35-
if (event != null) {
36-
if (event.isCancelled()) {
37-
return false;
33+
if (IWorldBridge.from(world) instanceof IWorldBridge bridge) {
34+
final var event = ArclightEventFactory.callBlockFormEvent(bridge.bridge$getMinecraftWorld(), pos, newState, 3, (SnowGolem) (Object) this);
35+
if (event != null) {
36+
if (event.isCancelled()) {
37+
return false;
38+
}
39+
newState = ((CraftBlockState) event.getNewState()).getHandle();
3840
}
39-
newState = ((CraftBlockState) event.getNewState()).getHandle();
4041
}
4142
return (boolean) DecorationOps.callsite().invoke(world, pos, newState);
4243
}

arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/item/BlockItemMixin.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.izzel.arclight.common.mixin.core.world.item;
22

33
import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge;
4+
import io.izzel.arclight.common.bridge.core.world.IWorldBridge;
45
import io.izzel.arclight.common.mod.util.DistValidate;
56
import net.minecraft.core.BlockPos;
67
import net.minecraft.server.level.ServerLevel;
@@ -47,8 +48,8 @@ public abstract class BlockItemMixin {
4748
org.bukkit.block.BlockState state = arclight$state;
4849
arclight$state = null;
4950
BlockPos pos = context1.getClickedPos();
50-
if (state != null && DistValidate.isValid(context)) {
51-
org.bukkit.event.block.BlockPlaceEvent placeEvent = CraftEventFactory.callBlockPlaceEvent((ServerLevel) context1.getLevel(), context1.getPlayer(), context1.getHand(), state, pos.getX(), pos.getY(), pos.getZ());
51+
if (IWorldBridge.from(context1.getLevel()) instanceof IWorldBridge bridge && state != null) {
52+
org.bukkit.event.block.BlockPlaceEvent placeEvent = CraftEventFactory.callBlockPlaceEvent(bridge.bridge$getMinecraftWorld(), context1.getPlayer(), context1.getHand(), state, pos.getX(), pos.getY(), pos.getZ());
5253
if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
5354
state.update(true, false);
5455
if ((Object) this instanceof SolidBucketItem) {

0 commit comments

Comments
 (0)