Skip to content

Commit 9497850

Browse files
committed
chore(26.1.1): add 26.1.1 support
1 parent a9e66a9 commit 9497850

11 files changed

Lines changed: 247 additions & 15 deletions

File tree

build.gradle.kts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@ import java.nio.charset.Charset
33
plugins {
44
java
55
`maven-publish`
6-
alias(libs.plugins.paperweight.userdev)
76
alias(libs.plugins.gradleup.shadow)
7+
alias(libs.plugins.paperweight.userdev) apply false
88
}
99

1010
val isSnapshot: Boolean = project.hasProperty("snapshot") || System.getProperty("snapshot")?.toBoolean() == true
11-
val stable = "1.2.0"
11+
val stable = "1.2.1"
1212

1313
allprojects {
1414
apply(plugin = "java")
1515
apply(plugin = "maven-publish")
1616
apply(plugin = "com.gradleup.shadow")
17-
apply(plugin = "io.papermc.paperweight.userdev")
1817

1918
group = "me.outspending.biomesapi"
2019
version = if (isSnapshot) "$stable-${gitShortCommitHash()}" else stable
@@ -29,19 +28,18 @@ allprojects {
2928

3029
dependencies {
3130
val libs = rootProject.libs
32-
paperweight.paperDevBundle(libs.versions.minecraft.v1.m21.r7)
3331
implementation(libs.google.guava)
3432
compileOnly(libs.kyori.adventure)
3533
compileOnly(libs.kyori.minimessage)
3634
}
3735

38-
java {
39-
toolchain.languageVersion = JavaLanguageVersion.of(21)
40-
}
41-
4236
tasks.withType<JavaCompile> {
4337
options.encoding = "UTF-8"
4438
}
39+
40+
java {
41+
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
42+
}
4543
}
4644

4745
gradle.taskGraph.whenReady {

gradle/libs.versions.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
[versions]
2-
paperweight-userdev = "2.0.0-beta.19"
2+
paperweight-userdev = "2.0.0-beta.21"
33
gradleup-shadow = "9.0.0-beta9"
44
kyori-adventure = "4.17.0"
55
protocollib = "5.4.0"
66
packetevents = "2.11.1"
77
google-guava = "33.5.0-jre"
8-
minecraft-v1-m21-r7 = "1.21.11-R0.1-SNAPSHOT"
8+
minecraft-v1-m21-r11 = "1.21.11-R0.1-SNAPSHOT"
9+
minecraft-v26-m1-r1 = "26.1.1.build.+"
910
run-paper = "2.3.1"
1011
plugin-yml-bukkit = "0.7.1"
1112

@@ -20,4 +21,5 @@ kyori-adventure = { group = "net.kyori", name = "adventure-api", version.ref = "
2021
kyori-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "kyori-adventure" }
2122
protocollib = { group = "net.dmulloy2", name = "ProtocolLib", version.ref = "protocollib" }
2223
packetevents = { group = "com.github.retrooper", name = "packetevents-spigot", version.ref = "packetevents" }
23-
google-guava = { group = "com.google.guava", name = "guava", version.ref = "google-guava" }
24+
google-guava = { group = "com.google.guava", name = "guava", version.ref = "google-guava" }
25+
paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "minecraft-v1-m21-r11" }

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

main/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
plugins {
2+
alias(libs.plugins.paperweight.userdev)
3+
}
4+
15
val minecraft = ":minecraft"
26
val minecraftProjects = project(minecraft)
37
.subprojects
@@ -12,6 +16,7 @@ dependencies {
1216
for (project in minecraftProjects) {
1317
implementation(project(path = "${minecraft}:${project}"))
1418
}
19+
paperweight.paperDevBundle(libs.versions.minecraft.v1.m21.r11)
1520
}
1621

1722
java {

main/src/main/java/me/outspending/biomesapi/unsafe/UnsafeNMSHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package me.outspending.biomesapi.unsafe;
22

33
import me.outspending.biomesapi.UnsafeNMS_v1_21_11;
4+
import me.outspending.biomesapi.UnsafeNMS_v26_1_1;
45
import me.outspending.biomesapi.annotations.AsOf;
56
import me.outspending.biomesapi.exceptions.UnknownNMSVersionException;
67
import org.bukkit.Bukkit;
@@ -50,7 +51,8 @@ static void init() {
5051

5152
String version = Bukkit.getMinecraftVersion();
5253
switch (version) {
53-
case "1.21.11", "1.21.10" -> NMS_VERSION = new UnsafeNMS_v1_21_11();
54+
case "1.21.11", "1.21.10", "1.21.9" -> NMS_VERSION = new UnsafeNMS_v1_21_11();
55+
case "26.1.1", "26.1" -> NMS_VERSION = new UnsafeNMS_v26_1_1();
5456
default -> throw new UnknownNMSVersionException("The version " + version + " is not supported by BiomesAPI. Make sure you are up-to-date with the latest version of BiomesAPI.");
5557
}
5658
}

minecraft/1_21_11/build.gradle.kts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
plugins {
2+
alias(libs.plugins.paperweight.userdev)
3+
}
4+
15
dependencies {
26
compileOnly(project(":minecraft:Wrapper"))
3-
}
7+
paperweight.paperDevBundle(libs.versions.minecraft.v1.m21.r11)
8+
}
9+
10+
//java {
11+
// toolchain.languageVersion = JavaLanguageVersion.of(21)
12+
//}

minecraft/26_1_1/build.gradle.kts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
plugins {
2+
alias(libs.plugins.paperweight.userdev)
3+
}
4+
5+
dependencies {
6+
compileOnly(project(":minecraft:Wrapper"))
7+
paperweight.paperDevBundle(libs.versions.minecraft.v26.m1.r1)
8+
}
9+
10+
java {
11+
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
12+
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package me.outspending.biomesapi;
2+
3+
import com.google.common.base.Preconditions;
4+
import me.outspending.biomesapi.unsafe.UnsafeNMS;
5+
import net.minecraft.core.Holder;
6+
import net.minecraft.resources.Identifier;
7+
import net.minecraft.world.level.biome.Biome;
8+
import org.bukkit.Chunk;
9+
10+
import java.util.Optional;
11+
import java.util.concurrent.CompletableFuture;
12+
13+
import net.minecraft.core.MappedRegistry;
14+
import net.minecraft.core.Registry;
15+
import net.minecraft.core.registries.Registries;
16+
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
17+
import net.minecraft.resources.ResourceKey;
18+
import net.minecraft.server.dedicated.DedicatedServer;
19+
import net.minecraft.world.level.chunk.LevelChunk;
20+
import net.minecraft.world.level.chunk.status.ChunkStatus;
21+
import net.minecraft.world.level.lighting.LevelLightEngine;
22+
import org.bukkit.Bukkit;
23+
import org.bukkit.Location;
24+
import org.bukkit.NamespacedKey;
25+
import org.bukkit.craftbukkit.CraftChunk;
26+
import org.bukkit.craftbukkit.CraftServer;
27+
import org.bukkit.craftbukkit.entity.CraftPlayer;
28+
import org.bukkit.entity.Player;
29+
import org.bukkit.plugin.Plugin;
30+
import org.jetbrains.annotations.NotNull;
31+
import org.jetbrains.annotations.Nullable;
32+
33+
import java.lang.reflect.Field;
34+
import java.lang.reflect.InvocationTargetException;
35+
import java.lang.reflect.Method;
36+
import java.util.List;
37+
import java.util.function.Supplier;
38+
39+
/**
40+
* Implementation of UnsafeNMS for Minecraft version 26.1.1.
41+
* This class provides methods to interact with the Minecraft server's internal mechanics,
42+
* such as updating chunks and managing the biome registry.
43+
*
44+
* @author Jsinco
45+
* @version 1.2.1
46+
* @since 1.2.1
47+
*/
48+
public class UnsafeNMS_v26_1_1 implements UnsafeNMS {
49+
50+
/**
51+
* Retrieves the registry for a given key.
52+
*
53+
* @param key The key for the registry to retrieve.
54+
* @return The registry associated with the given key.
55+
*/
56+
private static <T> MappedRegistry<@NotNull T> getRegistry(ResourceKey<@NotNull Registry<@NotNull T>> key) {
57+
DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer();
58+
return (MappedRegistry<@NotNull T>) server.registryAccess().lookup(key).orElseThrow();
59+
}
60+
61+
/**
62+
* Updates a list of chunks asynchronously.
63+
* For each chunk, it creates a packet with the chunk's data and sends it to all players within the chunk's view distance.
64+
*
65+
* @param chunks The chunks to update.
66+
*/
67+
@Override
68+
public void updateChunks(@NotNull List<CompletableFuture<Chunk>> chunks, @Nullable Plugin plugin) {
69+
CompletableFuture.runAsync(() -> {
70+
for (CompletableFuture<Chunk> chunkFuture : chunks) {
71+
chunkFuture.thenAccept(chunk -> {
72+
if (plugin != null) {
73+
Bukkit.getRegionScheduler().run(plugin, chunk.getWorld(), chunk.getX(), chunk.getZ(), task -> {
74+
doUpdateChunk(chunk);
75+
});
76+
} else {
77+
doUpdateChunk(chunk);
78+
}
79+
}).exceptionally(ex -> {
80+
ex.printStackTrace();
81+
return null;
82+
});
83+
}
84+
}).exceptionally(ex -> {
85+
ex.printStackTrace();
86+
return null;
87+
});
88+
}
89+
90+
private void doUpdateChunk(Chunk chunk) {
91+
LevelChunk levelChunk = (LevelChunk) ((CraftChunk) chunk).getHandle(ChunkStatus.BIOMES);
92+
LevelLightEngine levelLightEngine = levelChunk.getLevel().getLightEngine();
93+
94+
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(levelChunk, levelLightEngine, null, null, true);
95+
for (Player player : getPlayersInDistance(chunk)) {
96+
((CraftPlayer) player).getHandle().connection.send(packet);
97+
}
98+
}
99+
100+
/**
101+
* Locks or unlocks the biome registry.
102+
* It uses reflection to access the private boolean field in the registry class and sets its value.
103+
*
104+
* @param lock true to lock the biome registry, false to unlock it.
105+
*/
106+
@Override
107+
public void biomeRegistryLock(boolean lock) {
108+
MappedRegistry<@NotNull Biome> biomes = getRegistry(Registries.BIOME);
109+
try {
110+
Class<?> registryClass = biomes.getClass();
111+
Field field = registryClass.getDeclaredField("frozen");
112+
field.setAccessible(true);
113+
field.setBoolean(biomes, lock);
114+
} catch (NoSuchFieldException | IllegalAccessException e) {
115+
throw new RuntimeException("Failed to change biome registry lock state", e);
116+
}
117+
}
118+
119+
/**
120+
* Unlocks the registry, performs an operation supplied by the supplier, and then freezes the registry.
121+
* This method is useful for performing operations on the registry that require it to be unlocked.
122+
*
123+
* @param supplier The supplier that provides the operation to perform on the unlocked registry.
124+
*/
125+
@Override
126+
public void unlockRegistry(@NotNull Supplier<?> supplier) {
127+
MappedRegistry<@NotNull Biome> registry = getRegistry(Registries.BIOME);
128+
biomeRegistryLock(false);
129+
supplier.get();
130+
131+
try {
132+
// Use reflection to set the 'allTags' field to an unbound TagSet
133+
Class<?> registryClass = registry.getClass();
134+
Field field = registryClass.getDeclaredField("allTags");
135+
field.setAccessible(true);
136+
Class<?> tagSetClass = Class.forName("net.minecraft.core.MappedRegistry$TagSet");
137+
Method unboundMethod = tagSetClass.getDeclaredMethod("unbound");
138+
unboundMethod.setAccessible(true);
139+
Object unboundTagSet = unboundMethod.invoke(null);
140+
field.set(registry, unboundTagSet);
141+
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException | InvocationTargetException |
142+
NoSuchMethodException e) {
143+
e.printStackTrace();
144+
}
145+
registry.freeze();
146+
}
147+
148+
/**
149+
* Retrieves the biome registry from the Minecraft server.
150+
* This method gets the server instance from the Bukkit API, accesses the registry of the server,
151+
* and retrieves the biome registry. If the biome registry cannot be retrieved, it throws a RuntimeException.
152+
*
153+
* @return The biome registry from the Minecraft server.
154+
* @throws RuntimeException if the biome registry cannot be retrieved.
155+
*/
156+
@Override
157+
public @NotNull Registry<@NotNull Biome> getRegistry() {
158+
159+
return ((CraftServer) Bukkit.getServer()).getServer()
160+
.registryAccess()
161+
.lookup(Registries.BIOME)
162+
.orElseThrow(() -> new RuntimeException("Could not retrieve biome registry"));
163+
}
164+
165+
@Override
166+
public void updateBiome(@NotNull Location minLoc, @NotNull Location maxLoc, @NotNull NamespacedKey namespacedKey) {
167+
CompletableFuture.runAsync(() -> {
168+
169+
String namespace = namespacedKey.getNamespace();
170+
String path = namespacedKey.getKey();
171+
172+
ResourceKey<@NotNull Biome> biomeKey = ResourceKey.create(Registries.BIOME, Identifier.fromNamespaceAndPath(namespace, path));
173+
Optional<Holder.Reference<@NotNull Biome>> biomeOptional = getRegistry().get(biomeKey);
174+
175+
Preconditions.checkArgument(biomeOptional.isPresent(), "Biome with namespace " + namespace + ":" + path + " does not exist");
176+
177+
Holder<@NotNull Biome> biome = biomeOptional.orElseThrow();
178+
179+
for (int x = minLoc.getBlockX(); x <= maxLoc.getBlockX(); x++) {
180+
for (int y = minLoc.getBlockY(); y <= maxLoc.getBlockY(); y++) {
181+
for (int z = minLoc.getBlockZ(); z <= maxLoc.getBlockZ(); z++) {
182+
final int finalX = x;
183+
final int finalY = y;
184+
final int finalZ = z;
185+
186+
minLoc.getWorld().getChunkAtAsync(x, z).thenAccept(chunk -> {
187+
LevelChunk levelChunk = (LevelChunk) ((CraftChunk) chunk).getHandle(ChunkStatus.BIOMES);
188+
levelChunk.setNoiseBiome(finalX, finalY, finalZ, biome);
189+
});
190+
}
191+
}
192+
}
193+
194+
});
195+
}
196+
197+
}
198+

minecraft/Wrapper/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
compileOnly(rootProject.libs.paper.api)
3+
}

settings.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ rootProject.name = "BiomesAPI"
77
include("main")
88
include("test-plugin")
99
include("minecraft:Wrapper")
10-
include("minecraft:1_21_11")
10+
include("minecraft:1_21_11")
11+
include("minecraft:26_1_1")

0 commit comments

Comments
 (0)