Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,38 @@ Cardboard is an implementation of the popular Bukkit/Spigot/Paper Modding API fo
Fabric version chart:
| Support | Minecraft | Git Branch | Dev Status |
|----------|------------------|-------------|------------|
| ✅ | Fabric 26.1.2 | ver/26.1 | Active |
| ✅ | Fabric 26.2 | ver/26.2 | Active |
| ✅ | Fabric 26.1.2 | ver/26.1 | Low |
| ✅ | Fabric 1.21.11 | ver/1.21.11 | Low |
| ✅ | Fabric 1.21.1 | ver/1.21 | Low |
| &#x274C; | <= 1.20 | | |

See [Supported Versions](https://github.com/CardboardPowered/cardboard/wiki/Supported-Versions) for more details. & [View Downloads](https://cardboardpowered.org/download/)

## Building

Requires JDK 25 (auto-provisioned by the Gradle toolchain resolver) and Java 21+ at runtime.

```
./gradlew build
```

The mod jar is produced at `build/libs/Cardboard-<version>.jar`. Drop it into a
Fabric server's `mods/` folder alongside [Fabric API](https://modrinth.com/mod/fabric-api)
and iCommonLib; Bukkit plugins then go in `plugins/`.

### 26.2 notes

Minecraft 26.2 moved the entity and block-entity constants out of their type
classes (`EntityType.X` -> `EntityTypes.X`, `BlockEntityType.X` ->
`BlockEntityTypes.X`), stripped the colour metadata from `ChatFormatting` in
favour of `world.scores.TeamColor`, and ships Adventure 5. Cardboard on this
branch targets Paper-API `26.2.build.110-stable` and Adventure `5.2.0`.

## About this fork
This repository is derived from [CardboardPowered/cardboard](https://github.com/CardboardPowered/cardboard)
and carries its history and license. The `ver/26.2` branch adds Minecraft 26.2 support.

## License
We inherit the license from Paper. See [Paper's License](https://github.com/PaperMC/Paper/blob/master/LICENSE.md) for full details.
SrgLib is also licensed under MIT.
Expand Down
27 changes: 14 additions & 13 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ configurations.all {
}

// Paper-API
def PAPER_VER_A = "26.1.2.build.69-stable" // "1.21.11-R0.1-SNAPSHOT"
def PAPER_VER_B = "26.1.2.build.69-stable" // "1.21.11-R0.1-20260511.115010-91" // "1.21.11-R0.1-20260120.191825-59"
def PAPER_VER_A = "26.2.build.110-stable" // "26.1.2.build.69-stable"
def PAPER_VER_B = "26.2.build.110-stable" // "26.1.2.build.69-stable"

def PAPER_API_VER = "io.papermc.paper:paper-api:" + PAPER_VER_B

Expand All @@ -75,9 +75,9 @@ dependencies {
// javax.annotation-api-1.3.2.jar
// iCommon-Fabric-1.21.11_unobfuscated.jar

compileOnly fileTree(dir: 'libs', include: 'iCommon-Fabric-26.1.2.jar')
compileOnly fileTree(dir: 'libs', include: 'iCommon-Fabric-26.2.jar')

implementation fileTree(dir: 'libs', include: 'iCommon-Fabric-26.1.2.jar')
implementation fileTree(dir: 'libs', include: 'iCommon-Fabric-26.2.jar')

// TODO: Maven repo this
compileOnly fileTree(dir: 'libs', include: 'icommon-configuration-1.0.jar')
Expand All @@ -102,11 +102,11 @@ dependencies {
implementation(PAPER_API_VER)

implementation("net.md-5:bungeecord-chat:1.21-R0.2")
implementation("net.kyori:adventure-api:4.25.0")
implementation("net.kyori:adventure-text-serializer-gson:4.25.0")
implementation("net.kyori:adventure-text-serializer-legacy:4.25.0")
implementation("net.kyori:adventure-text-serializer-plain:4.25.0")
implementation("net.kyori:adventure-text-minimessage:4.25.0")
implementation("net.kyori:adventure-api:5.2.0")
implementation("net.kyori:adventure-text-serializer-gson:5.2.0")
implementation("net.kyori:adventure-text-serializer-legacy:5.2.0")
implementation("net.kyori:adventure-text-serializer-plain:5.2.0")
implementation("net.kyori:adventure-text-minimessage:5.2.0")

// compileOnly fileTree(dir: 'libs', include: 'commons-lang-2.6.jar')
// compileOnly group: "org.jetbrains", name: "annotations", version: "18.0.0"
Expand Down Expand Up @@ -188,9 +188,9 @@ processResources {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json"
if(System.env.BUILD_NUMBER){
expand "version": System.env.BUILD_NUMBER
}
// Always expand: leaving the literal ${version} in fabric.mod.json makes
// Fabric Loader warn that the version is unparseable on every start.
expand "version": (System.env.BUILD_NUMBER ?: project.mod_version)
}

from(sourceSets.main.resources.srcDirs) {
Expand Down Expand Up @@ -230,7 +230,7 @@ shadowJar {
modrinth {
token = System.getenv("MODRINTH") // This is the default. Remember to have the MODRINTH_TOKEN environment variable set or else this will fail, or set it to whatever you want - just make sure it stays private!
projectId = "MLYQ9VGP"
versionNumber = "26.1-" + System.env.BUILD_NUMBER
versionNumber = project.mod_version + "-" + System.env.BUILD_NUMBER
versionType = "beta"
uploadFile = jar
gameVersions = ["26.1.2"]
Expand Down Expand Up @@ -261,3 +261,4 @@ publishing {
// }
//}
}

13 changes: 9 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ org.gradle.jvmargs=-Xmx2G
# fabric_version=0.139.4+1.21.11_unobfuscated

# 26.1.2
minecraft_version=26.1.2
loader_version=0.19.2
fabric_version=0.149.1+26.1.2
# minecraft_version=26.1.2
# loader_version=0.19.2
# fabric_version=0.149.1+26.1.2

# 26.2
minecraft_version=26.2
loader_version=0.19.3
fabric_version=0.156.0+26.2

# Mod Properties
mod_version = 26.1.2
mod_version = 26.2
maven_group = org.cardboardpowered
archives_base_name = Cardboard

Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Binary file not shown.
7 changes: 7 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ pluginManagement {
}
}

// Auto-provision the JDK 25 toolchain this build requires (build.gradle pins
// languageVersion 25). Without this, Gradle fails on machines that only have a
// different JDK installed instead of downloading a matching one.
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}

//include ':iCommon',':iCommon-API',':iCommon-Fabric-1.20.2'
//project(':iCommon').projectDir = new File("C:/Users/isaia/mods/iCommonLib")
//project(':iCommon-API').projectDir = new File("C:/Users/isaia/mods/iCommonLib/iCommon-API")
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,23 @@ public PathResult findPath(Entity target) {
return path != null ? new PaperPathResult(path) : null;
}


// 26.2: Pathfinder gained a max-distance overload
@Override
public PathResult findPath(Location loc, int maxDistance) {
Preconditions.checkArgument(loc != null, "Location cant be nil");
Path path = this.entity.getNavigation().createPath(
new net.minecraft.core.BlockPos(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()), maxDistance);
return path != null ? new PaperPathResult(path) : null;
}


// 26.2: Pathfinder declares max-distance overloads for both Entity and Location
@Override
public PathResult findPath(Entity target, int maxDistance) {
Preconditions.checkArgument(target != null, "Target cant be nil");
Path path = this.entity.getNavigation().createPath(((CraftEntity) target).getHandle(), maxDistance);
return path != null ? new PaperPathResult(path) : null;
}

}
26 changes: 15 additions & 11 deletions src/main/java/io/papermc/paper/adventure/AdventureCodecs.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,19 @@ public String getSerializedName() {
}

public static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
he -> switch (he.action()) {
case OPEN_URL -> OPEN_URL_CLICK_EVENT_TYPE;
case OPEN_FILE -> OPEN_FILE_CLICK_EVENT_TYPE;
case RUN_COMMAND -> RUN_COMMAND_CLICK_EVENT_TYPE;
case SUGGEST_COMMAND -> SUGGEST_COMMAND_CLICK_EVENT_TYPE;
case CHANGE_PAGE -> CHANGE_PAGE_CLICK_EVENT_TYPE;
case COPY_TO_CLIPBOARD -> COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
case SHOW_DIALOG -> SHOW_DIALOG_CLICK_EVENT_TYPE;
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
he -> {
// 26.2: Adventure 5 turned ClickEvent.Action from an enum into a generic
// interface with singleton instances, so this can no longer be a switch.
final ClickEvent.Action<?> a = he.action();
if (a == ClickEvent.Action.OPEN_URL) return OPEN_URL_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.OPEN_FILE) return OPEN_FILE_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.RUN_COMMAND) return RUN_COMMAND_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.SUGGEST_COMMAND) return SUGGEST_COMMAND_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.CHANGE_PAGE) return CHANGE_PAGE_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.COPY_TO_CLIPBOARD) return COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.SHOW_DIALOG) return SHOW_DIALOG_CLICK_EVENT_TYPE;
if (a == ClickEvent.Action.CUSTOM) return CUSTOM_CLICK_EVENT_TYPE;
throw new IllegalArgumentException("Unknown ClickEvent.Action: " + a);
};

static final Codec<ClickEvent> CLICK_EVENT_CODEC = CLICK_EVENT_TYPE_CODEC.dispatch("action", GET_CLICK_EVENT_TYPE, ClickEventType::codec);
Expand Down Expand Up @@ -471,7 +475,7 @@ public String getSerializedName() {
}
}

static final MapCodec<NBTComponent<?, ?>> NBT_COMPONENT_MAP_CODEC = mapCodec((instance) -> {
static final MapCodec<NBTComponent<?>> NBT_COMPONENT_MAP_CODEC = mapCodec((instance) -> {
return instance.group(
Codec.STRING.fieldOf("nbt").forGetter(NBTComponent::nbtPath),
Codec.BOOL.lenientOptionalFieldOf("interpret", false).forGetter(NBTComponent::interpret),
Expand Down Expand Up @@ -506,7 +510,7 @@ public String getSerializedName() {
static final ComponentType<ObjectComponent> OBJECT = new ComponentType<>(OBJECT_COMPONENT_MAP_CODEC, ObjectComponent.class::isInstance, "object");
static final ComponentType<ScoreComponent> SCORE = new ComponentType<>(SCORE_COMPONENT_MAP_CODEC, ScoreComponent.class::isInstance, "score");
static final ComponentType<SelectorComponent> SELECTOR = new ComponentType<>(SELECTOR_COMPONENT_MAP_CODEC, SelectorComponent.class::isInstance, "selector");
static final ComponentType<NBTComponent<?, ?>> NBT = new ComponentType<>(NBT_COMPONENT_MAP_CODEC, NBTComponent.class::isInstance, "nbt");
static final ComponentType<NBTComponent<?>> NBT = new ComponentType<>(NBT_COMPONENT_MAP_CODEC, NBTComponent.class::isInstance, "nbt");

static Codec<Component> createCodec(final Codec<Component> selfCodec) {
final ExtraCodecs.LateBoundIdMapper<String, MapCodec<? extends Component>> lateBoundIdMapper = new ExtraCodecs.LateBoundIdMapper<>();
Expand Down
15 changes: 9 additions & 6 deletions src/main/java/io/papermc/paper/adventure/PaperAdventure.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.util.Codec;
Expand Down Expand Up @@ -114,7 +113,7 @@ public final class PaperAdventure {
.build();
public static final AttributeKey<Locale> LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger
@Deprecated
public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build();
// 26.2: Adventure 5 removed PlainComponentSerializer; PlainTextComponentSerializer replaces it.
//public static final ANSIComponentSerializer ANSI_SERIALIZER = ANSIComponentSerializer.builder().flattener(FLATTENER).build();
private static final TagParser<Tag> NBT_PARSER = TagParser.create(NbtOps.INSTANCE);
public static final Codec<Tag, String, CommandSyntaxException, RuntimeException> NBT_CODEC = new Codec<>() {
Expand Down Expand Up @@ -431,12 +430,14 @@ public record DataComponentValueImpl<T>(com.mojang.serialization.Codec<T> codec,

// Colors

// 26.2: ChatFormatting no longer carries colour data; net.minecraft.world.scores.TeamColor does.
public static @NotNull TextColor asAdventure(final ChatFormatting formatting) {
final Integer color = formatting.getColor();
if (color == null) {
final net.minecraft.world.scores.TeamColor teamColor =
net.minecraft.world.scores.TeamColor.byName(formatting.name());
if (teamColor == null) {
throw new IllegalArgumentException("Not a valid color");
}
return TextColor.color(color);
return TextColor.color(teamColor.rgb());
}

public static @Nullable ChatFormatting asVanilla(final TextColor color) {
Expand All @@ -448,7 +449,9 @@ public record DataComponentValueImpl<T>(com.mojang.serialization.Codec<T> codec,
@org.jspecify.annotations.Nullable
public static ChatFormatting getByHexValue(int color) {
for (ChatFormatting value : ChatFormatting.values()) {
if (value.getColor() != null && value.getColor() == color) {
final net.minecraft.world.scores.TeamColor tc =
net.minecraft.world.scores.TeamColor.byName(value.name());
if (tc != null && tc.rgb() == color) {
return value;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ public boolean isConnected() {
return getPlayer().isConnected();
}

}

// 26.2: new on PlayerCommonConnection
@Override
public String getClientBrandName() {
// captured from the minecraft:brand custom payload by ServerCommonPacketListenerImplMixin_Brand
return ((org.cardboardpowered.bridge.server.network.ClientBrandBridge) this.handle).cardboard_getClientBrand();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ public static void bootstrap() {
register(DataComponents.TOOLTIP_DISPLAY, PaperTooltipDisplay::new);
register(DataComponents.WEAPON, PaperWeapon::new);
register(DataComponents.BLOCKS_ATTACKS, PaperBlocksAttacks::new);
register(DataComponents.SULFUR_CUBE_CONTENT, io.papermc.paper.datacomponent.item.PaperSulfurCubeContent::new); // 26.2: new component
register(DataComponents.PIERCING_WEAPON, PaperPiercingWeapon::new);
register(DataComponents.KINETIC_WEAPON, PaperKineticWeapon::new);
register(DataComponents.ATTACK_RANGE, PaperAttackRange::new);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,12 @@ public io.papermc.paper.datacomponent.item.AttackRange.Builder attackRange() {
// TODO Auto-generated method stub
return null;
}
}

// 26.2: new component type bridge
@Override
public io.papermc.paper.datacomponent.item.SulfurCubeContent sulfurCubeContent(org.bukkit.inventory.ItemStack stack) {
return new PaperSulfurCubeContent(net.minecraft.world.item.component.SulfurCubeContent.ofNonEmpty(
org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(stack)));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.Optional;
import net.minecraft.advancements.criterion.DataComponentMatchers;
import net.minecraft.advancements.predicates.DataComponentMatchers;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.AdventureModePredicate;
import org.bukkit.craftbukkit.util.Handleable;
Expand All @@ -35,12 +35,12 @@ public List<BlockPredicate> predicates() {

static final class BuilderImpl implements ItemAdventurePredicate.Builder {

private final List<net.minecraft.advancements.criterion.BlockPredicate> predicates = new ObjectArrayList<>();
private final List<net.minecraft.advancements.predicates.BlockPredicate> predicates = new ObjectArrayList<>();
private boolean showInTooltip = true;

@Override
public ItemAdventurePredicate.Builder addPredicate(BlockPredicate predicate) {
this.predicates.add(new net.minecraft.advancements.criterion.BlockPredicate(Optional.ofNullable(predicate.blocks()).map(blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), blocks)), Optional.empty(), Optional.empty(), DataComponentMatchers.ANY));
this.predicates.add(new net.minecraft.advancements.predicates.BlockPredicate(Optional.ofNullable(predicate.blocks()).map(blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), blocks)), Optional.empty(), Optional.empty(), DataComponentMatchers.ANY));
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.papermc.paper.datacomponent.item;

import net.minecraft.world.item.component.SulfurCubeContent;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.Handleable;

// 26.2: SULFUR_CUBE_CONTENT is a new data component type.
public record PaperSulfurCubeContent(SulfurCubeContent impl)
implements io.papermc.paper.datacomponent.item.SulfurCubeContent, Handleable<SulfurCubeContent> {

@Override
public SulfurCubeContent getHandle() {
return this.impl;
}

@Override
public org.bukkit.inventory.ItemStack absorbedItem() {
// ItemStackTemplate -> ItemStack; apply an empty patch to materialise the stack
return CraftItemStack.asCraftMirror(
this.impl.absorbedBlockItemStack().apply(net.minecraft.core.component.DataComponentPatch.EMPTY));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ public net.minecraft.world.item.component.WritableBookContent getHandle() {
return this.impl;
}

// 26.2: Adventure 5 BookLike
@Override
public net.kyori.adventure.inventory.Book asBook() {
java.util.List<net.kyori.adventure.text.Component> bookPages = new java.util.ArrayList<>();
for (Filtered<String> page : this.pages()) {
bookPages.add(net.kyori.adventure.text.Component.text(page.raw()));
}
return net.kyori.adventure.inventory.Book.book(
net.kyori.adventure.text.Component.empty(),
net.kyori.adventure.text.Component.empty(),
bookPages);
}

@Override
public @Unmodifiable List<Filtered<String>> pages() {
return MCUtil.transformUnmodifiable(this.impl.pages(), input -> Filtered.of(input.raw(), input.filtered().orElse(null)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ public net.minecraft.world.item.component.WrittenBookContent getHandle() {
return this.impl;
}

// 26.2: Adventure 5 BookLike
@Override
public net.kyori.adventure.inventory.Book asBook() {
java.util.List<net.kyori.adventure.text.Component> bookPages = new java.util.ArrayList<>();
for (net.minecraft.server.network.Filterable<net.minecraft.network.chat.Component> page : this.impl.pages()) {
bookPages.add(io.papermc.paper.adventure.PaperAdventure.asAdventure(page.raw()));
}
return net.kyori.adventure.inventory.Book.book(
net.kyori.adventure.text.Component.text(this.impl.title().raw()),
net.kyori.adventure.text.Component.text(this.impl.author()),
bookPages);
}

@Override
public Filtered<String> title() {
return Filtered.of(this.impl.title().raw(), this.impl.title().filtered().orElse(null));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/papermc/paper/entity/PaperBucketable.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

public interface PaperBucketable extends Bucketable {

net.minecraft.world.entity.animal.Bucketable getHandle();
net.minecraft.world.entity.Bucketable getHandle();

@Override
default boolean isFromBucket() {
Expand Down
Loading