Skip to content

Commit 765ad33

Browse files
committed
Add support for 1.21.1, 1.21.3, remove support for 1.21
1 parent aaf2390 commit 765ad33

File tree

7 files changed

+293
-11
lines changed

7 files changed

+293
-11
lines changed

gradle/libs.versions.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ cloud-build-logic = "0.0.3"
33
ktlint = "0.50.0"
44
checkstyle = "10.12.5"
55
kotlin = "1.9.20"
6-
shadow = "8.1.1"
7-
paperweight = "1.7.1"
6+
shadow = "9.0.0-beta4"
7+
paperweight = "1.7.7"
88
run-paper = "2.3.0"
99
pluginyml = "0.6.0"
1010

@@ -54,6 +54,6 @@ paperweight-userdev = { id = "io.papermc.paperweight.userdev", version.ref = "pa
5454
cloud-buildLogic-spotless = { id = "org.incendo.cloud-build-logic.spotless", version.ref = "cloud-build-logic" }
5555
cloud-buildLogic-rootProject-publishing = { id = "org.incendo.cloud-build-logic.publishing.root-project", version.ref = "cloud-build-logic" }
5656
cloud-buildLogic-rootProject-spotless = { id = "org.incendo.cloud-build-logic.spotless.root-project", version.ref = "cloud-build-logic" }
57-
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
57+
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
5858
run-paper = { id = "xyz.jpenilla.run-paper", version.ref = "run-paper" }
5959
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }

hyperverse-core/build.gradle.kts

+9-6
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,25 @@ dependencies {
4444
implementation("net.kyori:adventure-text-minimessage:4.14.0")
4545

4646
implementation(projects.hyperverseNmsUnsupported)
47-
runtimeOnly(project(":hyperverse-nms-1-17")) {
47+
runtimeOnly(projects.hyperverseNms117) {
4848
targetConfiguration = "reobf"
4949
}
50-
runtimeOnly(project(":hyperverse-nms-1-18")) {
50+
runtimeOnly(projects.hyperverseNms118) {
5151
targetConfiguration = "reobf"
5252
}
53-
runtimeOnly(project(":hyperverse-nms-1-19")) {
53+
runtimeOnly(projects.hyperverseNms119) {
5454
targetConfiguration = "reobf"
5555
}
56-
runtimeOnly(project(":hyperverse-nms-1-20")) {
56+
runtimeOnly(projects.hyperverseNms120) {
5757
targetConfiguration = "reobf"
5858
}
59-
runtimeOnly(project(":hyperverse-nms-1-20-6")) {
59+
runtimeOnly(projects.hyperverseNms1206) {
6060
targetConfiguration = "reobf"
6161
}
62-
runtimeOnly(project(":hyperverse-nms-1-21")) {
62+
runtimeOnly(projects.hyperverseNms121) {
63+
targetConfiguration = "reobf"
64+
}
65+
runtimeOnly(projects.hyperverseNms1213) {
6366
targetConfiguration = "reobf"
6467
}
6568
}

hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste
9898
Version.parseMinecraft("1.19.4"),
9999
Version.parseMinecraft("1.20.4"),
100100
Version.parseMinecraft("1.20.6"),
101-
Version.parseMinecraft("1.21")
101+
Version.parseMinecraft("1.21.1"),
102+
Version.parseMinecraft("1.21.3")
102103
);
103104

104105
private WorldManager worldManager;
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id("hyperverse.base-conventions")
3+
alias(libs.plugins.paperweight.userdev)
4+
}
5+
6+
indra {
7+
javaVersions {
8+
minimumToolchain(21)
9+
target(21)
10+
}
11+
}
12+
13+
dependencies {
14+
paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT")
15+
compileOnly(projects.hyperverseNmsCommon)
16+
}
17+
18+
tasks {
19+
assemble {
20+
dependsOn(reobfJar)
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
//
2+
// Hyperverse - A minecraft world management plugin
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
//
17+
18+
package org.incendo.hyperverse.platform.v1_21_3;
19+
20+
import co.aikar.taskchain.TaskChainFactory;
21+
import com.google.inject.Inject;
22+
import io.papermc.lib.PaperLib;
23+
import java.io.InputStream;
24+
import java.io.OutputStream;
25+
import java.lang.reflect.Field;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.util.Objects;
29+
import java.util.Optional;
30+
31+
import net.minecraft.BlockUtil;
32+
import net.minecraft.core.BlockPos;
33+
import net.minecraft.nbt.CompoundTag;
34+
import net.minecraft.nbt.DoubleTag;
35+
import net.minecraft.nbt.ListTag;
36+
import net.minecraft.nbt.NbtAccounter;
37+
import net.minecraft.nbt.NbtIo;
38+
import net.minecraft.server.level.ServerLevel;
39+
import net.minecraft.server.level.ServerPlayer;
40+
import net.minecraft.world.entity.Entity;
41+
import net.minecraft.world.level.border.WorldBorder;
42+
import net.minecraft.world.level.dimension.DimensionType;
43+
import net.minecraft.world.level.entity.EntityLookup;
44+
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
45+
import net.minecraft.world.level.portal.PortalForcer;
46+
import net.minecraft.world.phys.Vec3;
47+
import org.apache.logging.log4j.core.Filter;
48+
import org.apache.logging.log4j.core.Logger;
49+
import org.apache.logging.log4j.core.filter.RegexFilter;
50+
import org.bukkit.Bukkit;
51+
import org.bukkit.Location;
52+
import org.bukkit.World;
53+
import org.bukkit.craftbukkit.CraftWorld;
54+
import org.bukkit.craftbukkit.entity.CraftEntity;
55+
import org.bukkit.craftbukkit.entity.CraftPlayer;
56+
import org.bukkit.entity.Player;
57+
import org.bukkit.event.player.PlayerRespawnEvent;
58+
import org.jetbrains.annotations.NotNull;
59+
import org.jetbrains.annotations.Nullable;
60+
import org.incendo.hyperverse.util.HyperConfigShouldGroupProfiles;
61+
import org.incendo.hyperverse.util.NMS;
62+
63+
@SuppressWarnings("unused")
64+
public class NMSImpl implements NMS {
65+
66+
private final TaskChainFactory taskFactory;
67+
private Field entitySectionManager;
68+
private Field entityLookup;
69+
private org.apache.logging.log4j.core.Logger worldServerLogger;
70+
71+
@Inject public NMSImpl(final TaskChainFactory taskFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) {
72+
this.taskFactory = taskFactory;
73+
if (hyperConfiguration) {
74+
try {
75+
final Field field = ServerLevel.class.getDeclaredField("LOGGER");
76+
field.setAccessible(true);
77+
this.worldServerLogger = (Logger) field.get(null);
78+
} catch (final Exception e) {
79+
e.printStackTrace();
80+
}
81+
try {
82+
final RegexFilter regexFilter = RegexFilter
83+
.createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false,
84+
Filter.Result.DENY, Filter.Result.ACCEPT);
85+
this.worldServerLogger.addFilter(regexFilter);
86+
} catch (IllegalAccessException e) {
87+
e.printStackTrace();
88+
}
89+
}
90+
}
91+
92+
@Override @Nullable public Location getOrCreateNetherPortal(@NotNull final org.bukkit.entity.Entity entity,
93+
@NotNull final Location origin) {
94+
final ServerLevel worldServer = Objects.requireNonNull(((CraftWorld) origin.getWorld()).getHandle());
95+
final PortalForcer portalTravelAgent = Objects.requireNonNull(worldServer.getPortalForcer());
96+
final Entity nmsEntity = Objects.requireNonNull(((CraftEntity) entity).getHandle());
97+
final BlockPos blockPosition = new BlockPos(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ());
98+
final WorldBorder worldBorder = worldServer.getWorldBorder();
99+
Optional<BlockPos> existingPortalPosition = Objects.requireNonNull(portalTravelAgent, "travel agent")
100+
.findClosestPortalPosition(Objects.requireNonNull(blockPosition, "position"), worldBorder,128);
101+
if (existingPortalPosition.isPresent()) {
102+
BlockPos bottomLeft = existingPortalPosition.get();
103+
return new Location(origin.getWorld(), bottomLeft.getX(), bottomLeft.getY(), bottomLeft.getZ());
104+
}
105+
Optional<BlockUtil.FoundRectangle> createdPortal = portalTravelAgent.createPortal(blockPosition,
106+
nmsEntity.getDirection().getAxis(), nmsEntity,
107+
16);
108+
if (createdPortal.isEmpty()) {
109+
return null;
110+
}
111+
final BlockUtil.FoundRectangle rectangle = createdPortal.get();
112+
return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1D, rectangle.minCorner.getY() - 1D,
113+
rectangle.minCorner.getZ() + 1D);
114+
}
115+
116+
@Override @Nullable public Location getDimensionSpawn(@NotNull final Location origin) {
117+
if (Objects.requireNonNull(origin.getWorld()).getEnvironment()
118+
== World.Environment.THE_END) {
119+
return new Location(origin.getWorld(), 100, 50, 0);
120+
}
121+
return origin.getWorld().getSpawnLocation();
122+
}
123+
124+
@Override public void writePlayerData(@NotNull final Player player, @NotNull final Path file) {
125+
final CompoundTag playerTag = new CompoundTag();
126+
final net.minecraft.world.entity.player.Player entityPlayer = ((CraftPlayer) player).getHandle();
127+
entityPlayer.save(playerTag);
128+
129+
if (!playerTag.contains("hyperverse")) {
130+
playerTag.put("hyperverse", new CompoundTag());
131+
}
132+
final CompoundTag hyperverse = playerTag.getCompound("hyperverse");
133+
hyperverse.putLong("writeTime", System.currentTimeMillis());
134+
hyperverse.putString("version", Bukkit.getPluginManager().getPlugin("Hyperverse").getDescription().getVersion());
135+
136+
taskFactory.newChain().async(() -> {
137+
try (final OutputStream outputStream = Files.newOutputStream(file)) {
138+
NbtIo.writeCompressed(playerTag, outputStream);
139+
} catch (final Exception e) {
140+
e.printStackTrace();
141+
}
142+
}).execute();
143+
}
144+
145+
@Override public void readPlayerData(@NotNull final Player player, @NotNull final Path file, @NotNull final Runnable whenDone) {
146+
final Location originLocation = player.getLocation().clone();
147+
taskFactory.newChain().asyncFirst(() -> {
148+
try (final InputStream inputStream = Files.newInputStream(file)) {
149+
return Optional.of(NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()));
150+
} catch (final Exception e) {
151+
e.printStackTrace();
152+
}
153+
return Optional.empty();
154+
}).syncLast((optionalCompound) -> {
155+
if (!optionalCompound.isPresent()) {
156+
return;
157+
}
158+
final CompoundTag compound = (CompoundTag) optionalCompound.get();
159+
PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> {
160+
// Health and hunger don't update properly, so we
161+
// give them a little help
162+
final float health = compound.getFloat("Health");
163+
final int foodLevel = compound.getInt("foodLevel");
164+
165+
// Restore bed spawn
166+
final String spawnWorld = compound.getString("SpawnWorld");
167+
final int spawnX = compound.getInt("SpawnX");
168+
final int spawnY = compound.getInt("SpawnY");
169+
final int spawnZ = compound.getInt("SpawnZ");
170+
final Location spawnLocation = new Location(Bukkit.getWorld(spawnWorld), spawnX,
171+
spawnY, spawnZ);
172+
173+
final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle();
174+
175+
// We re-write the extra Bukkit data as to not
176+
// mess up the profile
177+
((CraftPlayer) player).setExtraData(compound);
178+
// Set the position to the player's current position
179+
Vec3 pos = entityPlayer.position();
180+
compound.put("Pos", doubleList(pos.x, pos.y, pos.z));
181+
// Set the world to the player's current world
182+
compound.putString("world", player.getWorld().getName());
183+
// Store persistent values
184+
((CraftPlayer) player).storeBukkitValues(compound);
185+
186+
// We start by doing a total reset
187+
entityPlayer.reset();
188+
entityPlayer.load(compound);
189+
190+
entityPlayer.effectsDirty = true;
191+
entityPlayer.onUpdateAbilities();
192+
player.teleport(originLocation);
193+
194+
final ServerLevel worldServer = ((CraftWorld) originLocation.getWorld()).getHandle();
195+
final DimensionType dimensionManager = worldServer.dimensionType();
196+
197+
// Prevent annoying message
198+
// Spigot-obf = decouple()
199+
entityPlayer.unRide();
200+
worldServer.removePlayerImmediately(entityPlayer, Entity.RemovalReason.CHANGED_DIMENSION);
201+
// worldServer.removePlayer above should remove the player from the
202+
// map, but that doesn't always happen. This is a last effort
203+
// attempt to prevent the annoying "Force re-added" message
204+
// from appearing
205+
try {
206+
if (this.entitySectionManager == null) {
207+
this.entitySectionManager = worldServer.getClass().getDeclaredField("entityManager");
208+
this.entitySectionManager.setAccessible(true);
209+
}
210+
final PersistentEntitySectionManager<Entity> esm = (PersistentEntitySectionManager<Entity>) this.entitySectionManager.get(worldServer);
211+
if (this.entityLookup == null) {
212+
this.entityLookup = esm.getClass().getDeclaredField("visibleEntityStorage");
213+
}
214+
final EntityLookup<Entity> lookup = (EntityLookup<Entity>) this.entityLookup.get(esm);
215+
lookup.remove(entityPlayer);
216+
} catch (final NoSuchFieldException | IllegalAccessException e) {
217+
e.printStackTrace();
218+
}
219+
220+
// pre 1.18 code = PlayerList#moveToWorld
221+
entityPlayer.server.getPlayerList().remove(entityPlayer);
222+
worldServer.getServer().getPlayerList().respawn(entityPlayer, true,
223+
Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN, originLocation);
224+
225+
// Apply health and foodLevel
226+
player.setHealth(health);
227+
player.setFoodLevel(foodLevel);
228+
player.setPortalCooldown(40);
229+
player.setBedSpawnLocation(spawnLocation, true);
230+
});
231+
}).execute(whenDone);
232+
}
233+
234+
@Override @Nullable public Location findBedRespawn(@NotNull final Location spawnLocation) {
235+
final CraftWorld craftWorld = (CraftWorld) spawnLocation.getWorld();
236+
if (craftWorld == null) {
237+
return null;
238+
}
239+
240+
return ServerPlayer.findRespawnAndUseSpawnBlock(craftWorld.getHandle(), new BlockPos(spawnLocation.getBlockX(),
241+
spawnLocation.getBlockY(), spawnLocation.getBlockZ()), 0, true, false)
242+
.map(ServerPlayer.RespawnPosAngle::position)
243+
.map(pos -> new Location(spawnLocation.getWorld(), pos.x(), pos.y(), pos.z()))
244+
.orElse(null);
245+
}
246+
247+
private static ListTag doubleList(final double... values) {
248+
final ListTag tagList = new ListTag();
249+
for (final double d : values) {
250+
tagList.add(DoubleTag.valueOf(d));
251+
}
252+
return tagList;
253+
}
254+
255+
}

hyperverse-nms-1-21/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ indra {
1111
}
1212

1313
dependencies {
14-
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT")
14+
paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
1515
compileOnly(projects.hyperverseNmsCommon)
1616
}
1717

settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ include(":hyperverse-nms-1-19")
1919
include(":hyperverse-nms-1-20")
2020
include(":hyperverse-nms-1-20-6")
2121
include(":hyperverse-nms-1-21")
22+
include(":hyperverse-nms-1-21-3")

0 commit comments

Comments
 (0)