Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with Forge+Bukkit environment #781

Merged
merged 13 commits into from
Jan 6, 2025
11 changes: 6 additions & 5 deletions .github/workflows/build_and_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v2
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
Expand All @@ -20,15 +20,16 @@ jobs:
- name: Set up git submodules
run: git submodule init && git submodule update
- name: Set up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 1.8
distribution: 'temurin'
java-version: '8'
- name: Build with Gradle
env:
sonatypeUsername: ${{ secrets.OSSRH_USERNAME }}
sonatypePassword: ${{ secrets.OSSRH_PASSWORD }}
run: ./gradlew build publish
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: Compiled jars
path: build/libs/*
12 changes: 12 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ repositories {
maven {
setUrl("https://repo.spongepowered.org/maven")
}
maven {
setUrl("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
}

dependencies {
Expand All @@ -130,6 +133,7 @@ dependencies {

compileOnly(sourceSets["optifine_dummy"].output)

compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT") // Spigot API - used for Bukkit sided mixins
testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-junit:2.0.0.0")
testImplementation("it.ozimov:java7-hamcrest-matchers:1.3.0")
Expand Down Expand Up @@ -172,6 +176,14 @@ mixinGen {
defaultCompatibilityLevel = "JAVA_8"
defaultMinVersion = "0.7.10"

config("core_sided.bukkit") {
required = false
conformVisibility = true
}
config("core_sided.vanilla") {
required = false
conformVisibility = true
}
config("core") {
required = true
conformVisibility = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package io.github.opencubicchunks.cubicchunks.core.asm.coremod;

import io.github.opencubicchunks.cubicchunks.core.util.PlatformCompatUtils;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.fml.common.Loader;
Expand Down Expand Up @@ -104,6 +105,12 @@ public String getAccessTransformerClass() {
public static void initMixin() {
MixinBootstrap.init();
Mixins.addConfiguration("cubicchunks.mixins.core.json");
if (PlatformCompatUtils.isHybridEnv()) {
Mixins.addConfiguration("cubicchunks.mixins.core_sided.bukkit.json");
System.out.println("Running in Forge+Bukkit hybrid environment, using compatibility mixins");
} else {
Mixins.addConfiguration("cubicchunks.mixins.core_sided.vanilla.json");
}
Mixins.addConfiguration("cubicchunks.mixins.fixes.json");
Mixins.addConfiguration("cubicchunks.mixins.selectable.json");
Mixins.addConfiguration("cubicchunks.mixins.noncritical.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ public abstract class MixinChunk_Column {
if (cachedCube != null && cachedCube.getY() == cubeY) {
return cachedCube;
}
return getWorld().getCubeCache().getLoadedCube(x, cubeY, z);
return getCubicWorld().getCubeCache().getLoadedCube(x, cubeY, z);
}


public ICube chunk$getCube(int cubeY) {
if (cachedCube != null && cachedCube.getY() == cubeY) {
return cachedCube;
}
return getWorld().getCubeCache().getCube(x, cubeY, z);
return getCubicWorld().getCubeCache().getCube(x, cubeY, z);
}


Expand Down Expand Up @@ -138,7 +138,7 @@ public abstract class MixinChunk_Column {
}

@Unique @SuppressWarnings({"unchecked", "AddedMixinMembersNamePattern"})
public <T extends World & ICubicWorldInternal> T getWorld() {
public <T extends World & ICubicWorldInternal> T getCubicWorld() {
HaHaWTH marked this conversation as resolved.
Show resolved Hide resolved
return (T) this.world;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,25 +225,6 @@ private void cubicChunkColumn_construct(World world, int x, int z, CallbackInfo
Arrays.fill(getBiomeArray(), (byte) -1);
}

@ModifyConstant(method = "<init>(Lnet/minecraft/world/World;II)V", constant = @Constant(intValue = 16),
slice = @Slice(to = @At(
value = "FIELD",
target = "Lnet/minecraft/world/chunk/Chunk;storageArrays:[Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;",
opcode = Opcodes.PUTFIELD
)),
allow = 1, require = 1)
private int modifySectionArrayLength(int sixteen, World worldIn, int x, int z) {
if (worldIn == null) {
// Some mods construct chunks with null world, ignore them
return sixteen;
}
if (!((ICubicWorld) worldIn).isCubicWorld()) {
IMinMaxHeight y = (IMinMaxHeight) worldIn;
return Coords.blockToCube(y.getMaxHeight()) - Coords.blockToCube(y.getMinHeight());
}
return sixteen;
}

@Redirect(method = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/world/chunk/ChunkPrimer;II)V",
at = @At(
value = "FIELD",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,17 @@
*/
package io.github.opencubicchunks.cubicchunks.core.asm.mixin.core.common;

import io.github.opencubicchunks.cubicchunks.api.util.Coords;
import io.github.opencubicchunks.cubicchunks.core.entity.ICubicEntityTracker;
import io.github.opencubicchunks.cubicchunks.core.server.ICubicPlayerList;
import io.github.opencubicchunks.cubicchunks.core.server.PlayerCubeMap;
import io.github.opencubicchunks.cubicchunks.api.world.ICubicWorld;
import io.github.opencubicchunks.cubicchunks.core.asm.mixin.ICubicWorldInternal;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import javax.annotation.ParametersAreNonnullByDefault;

Expand All @@ -56,18 +48,6 @@ public abstract class MixinPlayerList implements ICubicPlayerList {
@Shadow @Final private MinecraftServer server;
protected int verticalViewDistance = -1;

@Redirect(method = "playerLoggedOut",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;markDirty()V", ordinal = 0),
require = 1)
private void setChunkModifiedOnPlayerLoggedOut(Chunk chunkIn, EntityPlayerMP playerIn) {
ICubicWorldInternal world = (ICubicWorldInternal) playerIn.getServerWorld();
if (world.isCubicWorld()) {
world.getCubeFromCubeCoords(playerIn.chunkCoordX, playerIn.chunkCoordY, playerIn.chunkCoordZ).markDirty();
} else {
((World) world).getChunk(playerIn.chunkCoordX, playerIn.chunkCoordZ).markDirty();
}
}

@Override public int getVerticalViewDistance() {
return verticalViewDistance < 0 ? viewDistance : verticalViewDistance;
}
Expand All @@ -88,25 +68,4 @@ private void setChunkModifiedOnPlayerLoggedOut(Chunk chunkIn, EntityPlayerMP pla
}
}
}

@Inject(method = "recreatePlayerEntity", at = @At(value = "INVOKE",
target = "Lnet/minecraft/world/gen/ChunkProviderServer;provideChunk(II)Lnet/minecraft/world/chunk/Chunk;"))
private void createPlayerChunk(EntityPlayerMP playerIn, int dimension, boolean conqueredEnd, CallbackInfoReturnable<EntityPlayerMP> cir) {
if (!((ICubicWorld) playerIn.world).isCubicWorld()) {
return;
}
for (int dCubeY = -8; dCubeY <= 8; dCubeY++) {
((ICubicWorld) playerIn.world).getCubeFromBlockCoords(playerIn.getPosition().up(Coords.cubeToMinBlock(dCubeY)));
}
}

@ModifyConstant(method = "recreatePlayerEntity",
constant = @Constant(doubleValue = 256))
private double getMaxHeight(double _256, EntityPlayerMP playerIn, int dimension, boolean conqueredEnd) {
// +/- 8 chunks around the original position are loaded because of an inject above
if (!playerIn.world.isBlockLoaded(new BlockPos(playerIn))) {
return Double.NEGATIVE_INFINITY;
}
return ((ICubicWorld) playerIn.world).getMaxHeight();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import io.github.opencubicchunks.cubicchunks.core.world.cube.Cube;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.profiler.Profiler;
import net.minecraft.tileentity.TileEntity;
Expand Down Expand Up @@ -143,6 +144,13 @@ public abstract class MixinWorld implements ICubicWorldInternal {

@Shadow public abstract void setLightFor(EnumSkyBlock type, BlockPos pos, int lightValue);

/*
* This shadow method is used by MixinWorldServer, place in here for Bukkit compatibility.
* As World#spawnEntity method is not getting overridden in CraftBukkit WorldServer class,
* shadowing spawnEntity in WorldServer will break Bukkit compatibility.
*/
@Shadow public abstract boolean spawnEntity(Entity entityIn);
HaHaWTH marked this conversation as resolved.
Show resolved Hide resolved

protected void initCubicWorld(IntRange heightRange, IntRange generationRange) {
((ICubicWorldSettings) worldInfo).setCubic(true);
// Set the world height boundaries to their highest and lowest values respectively
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import io.github.opencubicchunks.cubicchunks.core.server.PlayerCubeMap;
import io.github.opencubicchunks.cubicchunks.core.server.SpawnCubes;
import io.github.opencubicchunks.cubicchunks.core.server.VanillaNetworkHandler;
import io.github.opencubicchunks.cubicchunks.core.util.CompatHandler;
import io.github.opencubicchunks.cubicchunks.core.util.world.CubeSplitTickList;
import io.github.opencubicchunks.cubicchunks.core.util.world.CubeSplitTickSet;
import io.github.opencubicchunks.cubicchunks.core.world.CubeWorldEntitySpawner;
Expand Down Expand Up @@ -90,7 +91,6 @@
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

/**
Expand All @@ -117,15 +117,15 @@ public abstract class MixinWorldServer extends MixinWorld implements ICubicWorld

@Shadow protected abstract void playerCheckLight();

@Shadow public abstract boolean spawnEntity(Entity entityIn);

@Shadow public abstract boolean addWeatherEffect(Entity entityIn);

@Shadow @Mutable @Final private Set<NextTickListEntry> pendingTickListEntriesHashSet;
@Shadow @Mutable @Final private List<NextTickListEntry> pendingTickListEntriesThisTick;

@Shadow public abstract PlayerChunkMap getPlayerChunkMap();

@Shadow protected abstract boolean canAddEntity(Entity entityIn);

@Override public void initCubicWorldServer(IntRange heightRange, IntRange generationRange) {
super.initCubicWorld(heightRange, generationRange);
this.isCubicWorld = true;
Expand Down Expand Up @@ -368,7 +368,9 @@ private void tickColumn(boolean raining, boolean thundering, Chunk chunk) {
skeletonHorse.setTrap(true);
skeletonHorse.setGrowingAge(0);
skeletonHorse.setPosition((double) strikePos.getX(), (double) strikePos.getY(), (double) strikePos.getZ());
this.spawnEntity(skeletonHorse);
if (this.canAddEntity(skeletonHorse)) {
this.spawnEntity(skeletonHorse);
}
this.addWeatherEffect(new EntityLightningBolt((World) (Object) this,
(double) strikePos.getX(), (double) strikePos.getY(), (double) strikePos.getZ(), true));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,6 @@ private void isBlockLoaded(BlockPos pos, boolean allowEmpty, CallbackInfoReturna
}
}

@Redirect(method = "spawnEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;isChunkLoaded(IIZ)Z"))
private boolean spawnEntity_isChunkLoaded(World world, int chunkX, int chunkZ, boolean allowEmpty, Entity ent) {
assert this == (Object) world;
if (isCubicWorld()) {
return this.isBlockLoaded(new BlockPos(cubeToMinBlock(chunkX), ent.posY, cubeToMinBlock(chunkZ)), allowEmpty);
} else {
return this.isChunkLoaded(chunkX, chunkZ, allowEmpty);
}
}

@Redirect(method = "updateEntityWithOptionalForce",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;isChunkLoaded(IIZ)Z", ordinal = 0))
private boolean updateEntityWithOptionalForce_isChunkLoaded0(World world, int chunkX, int chunkZ, boolean allowEmpty, Entity ent, boolean force) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* This file is part of Cubic Chunks Mod, licensed under the MIT License (MIT).
*
* Copyright (c) 2015-2021 OpenCubicChunks
* Copyright (c) 2015-2021 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.github.opencubicchunks.cubicchunks.core.asm.mixin.core_sided.bukkit.common;

import io.github.opencubicchunks.cubicchunks.api.util.Coords;
import io.github.opencubicchunks.cubicchunks.api.world.ICubicWorld;
import io.github.opencubicchunks.cubicchunks.api.world.IMinMaxHeight;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Slice;

@Mixin(value = Chunk.class, priority = 999)
public abstract class MixinChunk_Cubes_Bukkit_Sided {

/*
* Spigot added some more constants before the storageArrays array is set, so we need to adjust the slice.
*/
@ModifyConstant(method = "<init>(Lnet/minecraft/world/World;II)V", constant = @Constant(intValue = 16),
slice = @Slice(
from = @At(
value = "FIELD",
target = "Lnet/minecraft/world/chunk/Chunk;neighbors:I", // Add from to avoid expected changes
remap = false
),
to = @At(
value = "FIELD",
target = "Lnet/minecraft/world/chunk/Chunk;storageArrays:[Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;",
opcode = Opcodes.PUTFIELD
)
)
)
private int modifySectionArrayLength(int sixteen, World worldIn, int x, int z) {
if (worldIn == null) {
// Some mods construct chunks with null world, ignore them
return sixteen;
}
if (!((ICubicWorld) worldIn).isCubicWorld()) {
IMinMaxHeight y = (IMinMaxHeight) worldIn;
return Coords.blockToCube(y.getMaxHeight()) - Coords.blockToCube(y.getMinHeight());
}
return sixteen;
}
}
Loading