Skip to content

Commit eada057

Browse files
authored
Merge pull request #106 from Snewmy/main
Add support for 1.21.5, 1.21.6, and 1.21.7.
2 parents 8578374 + 0faa6ea commit eada057

File tree

11 files changed

+920
-4
lines changed

11 files changed

+920
-4
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ktlint = "0.50.0"
44
checkstyle = "10.12.5"
55
kotlin = "1.9.20"
66
shadow = "9.0.0-beta8"
7-
paperweight = "2.0.0-beta.14"
7+
paperweight = "2.0.0-beta.17"
88
run-paper = "2.3.1"
99
pluginyml = "0.6.0"
1010

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-8.12.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

hyperverse-core/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ dependencies {
4848
runtimeOnly(projects.hyperverseNms121)
4949
runtimeOnly(projects.hyperverseNms1213)
5050
runtimeOnly(projects.hyperverseNms1214)
51+
runtimeOnly(projects.hyperverseNms1215)
52+
runtimeOnly(projects.hyperverseNms1216)
53+
runtimeOnly(projects.hyperverseNms1217)
5154
}
5255

5356
bukkit {
@@ -107,6 +110,9 @@ tasks {
107110
exclude(project(":hyperverse-nms-1-21"))
108111
exclude(project(":hyperverse-nms-1-21-3"))
109112
exclude(project(":hyperverse-nms-1-21-4"))
113+
exclude(project(":hyperverse-nms-1-21-5"))
114+
exclude(project(":hyperverse-nms-1-21-6"))
115+
exclude(project(":hyperverse-nms-1-21-7"))
110116
}
111117
mergeServiceFiles()
112118

@@ -144,6 +150,6 @@ tasks {
144150

145151
runServer {
146152
java.toolchain.languageVersion.set(JavaLanguageVersion.of(21))
147-
minecraftVersion("1.21.4")
153+
minecraftVersion("1.21.7")
148154
}
149155
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste
9696
Version.parseMinecraft("1.20.6"),
9797
Version.parseMinecraft("1.21.1"),
9898
Version.parseMinecraft("1.21.3"),
99-
Version.parseMinecraft("1.21.4")
99+
Version.parseMinecraft("1.21.4"),
100+
Version.parseMinecraft("1.21.5"),
101+
Version.parseMinecraft("1.21.6"),
102+
Version.parseMinecraft("1.21.7")
100103
);
101104

102105
private WorldManager worldManager;
Lines changed: 22 additions & 0 deletions
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.5-R0.1-SNAPSHOT")
15+
compileOnly(projects.hyperverseNmsCommon)
16+
}
17+
18+
tasks {
19+
assemble {
20+
dependsOn(reobfJar)
21+
}
22+
}
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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_5;
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.IntTag;
36+
import net.minecraft.nbt.ListTag;
37+
import net.minecraft.nbt.NbtAccounter;
38+
import net.minecraft.nbt.NbtIo;
39+
import net.minecraft.nbt.NumericTag;
40+
import net.minecraft.nbt.StringTag;
41+
import net.minecraft.resources.ResourceKey;
42+
import net.minecraft.server.level.ServerLevel;
43+
import net.minecraft.server.level.ServerPlayer;
44+
import net.minecraft.world.entity.Entity;
45+
import net.minecraft.world.level.Level;
46+
import net.minecraft.world.level.border.WorldBorder;
47+
import net.minecraft.world.level.dimension.DimensionType;
48+
import net.minecraft.world.level.entity.EntityLookup;
49+
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
50+
import net.minecraft.world.level.portal.PortalForcer;
51+
import net.minecraft.world.phys.Vec3;
52+
import org.apache.logging.log4j.core.Filter;
53+
import org.apache.logging.log4j.core.Logger;
54+
import org.apache.logging.log4j.core.filter.RegexFilter;
55+
import org.bukkit.Bukkit;
56+
import org.bukkit.Location;
57+
import org.bukkit.World;
58+
import org.bukkit.craftbukkit.CraftWorld;
59+
import org.bukkit.craftbukkit.entity.CraftEntity;
60+
import org.bukkit.craftbukkit.entity.CraftPlayer;
61+
import org.bukkit.entity.Player;
62+
import org.bukkit.event.player.PlayerRespawnEvent;
63+
import org.jetbrains.annotations.NotNull;
64+
import org.jetbrains.annotations.Nullable;
65+
import org.incendo.hyperverse.util.HyperConfigShouldGroupProfiles;
66+
import org.incendo.hyperverse.util.NMS;
67+
68+
@SuppressWarnings("unused")
69+
public class NMSImpl implements NMS {
70+
71+
private final TaskChainFactory taskFactory;
72+
private Field entitySectionManager;
73+
private Field entityLookup;
74+
private org.apache.logging.log4j.core.Logger worldServerLogger;
75+
76+
@Inject public NMSImpl(final TaskChainFactory taskFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) {
77+
this.taskFactory = taskFactory;
78+
if (hyperConfiguration) {
79+
try {
80+
final Field field = ServerLevel.class.getDeclaredField("LOGGER");
81+
field.setAccessible(true);
82+
this.worldServerLogger = (Logger) field.get(null);
83+
} catch (final Exception e) {
84+
e.printStackTrace();
85+
}
86+
try {
87+
final RegexFilter regexFilter = RegexFilter
88+
.createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false,
89+
Filter.Result.DENY, Filter.Result.ACCEPT);
90+
this.worldServerLogger.addFilter(regexFilter);
91+
} catch (IllegalAccessException e) {
92+
e.printStackTrace();
93+
}
94+
}
95+
}
96+
97+
@Override @Nullable public Location getOrCreateNetherPortal(@NotNull final org.bukkit.entity.Entity entity,
98+
@NotNull final Location origin) {
99+
final ServerLevel worldServer = Objects.requireNonNull(((CraftWorld) origin.getWorld()).getHandle());
100+
final PortalForcer portalTravelAgent = Objects.requireNonNull(worldServer.getPortalForcer());
101+
final Entity nmsEntity = Objects.requireNonNull(((CraftEntity) entity).getHandle());
102+
final BlockPos blockPosition = new BlockPos(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ());
103+
final WorldBorder worldBorder = worldServer.getWorldBorder();
104+
Optional<BlockPos> existingPortalPosition = Objects.requireNonNull(portalTravelAgent, "travel agent")
105+
.findClosestPortalPosition(Objects.requireNonNull(blockPosition, "position"), worldBorder,128);
106+
if (existingPortalPosition.isPresent()) {
107+
BlockPos bottomLeft = existingPortalPosition.get();
108+
return new Location(origin.getWorld(), bottomLeft.getX(), bottomLeft.getY(), bottomLeft.getZ());
109+
}
110+
Optional<BlockUtil.FoundRectangle> createdPortal = portalTravelAgent.createPortal(blockPosition,
111+
nmsEntity.getDirection().getAxis(), nmsEntity,
112+
16);
113+
if (createdPortal.isEmpty()) {
114+
return null;
115+
}
116+
final BlockUtil.FoundRectangle rectangle = createdPortal.get();
117+
return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1D, rectangle.minCorner.getY() - 1D,
118+
rectangle.minCorner.getZ() + 1D);
119+
}
120+
121+
@Override @Nullable public Location getDimensionSpawn(@NotNull final Location origin) {
122+
if (Objects.requireNonNull(origin.getWorld()).getEnvironment()
123+
== World.Environment.THE_END) {
124+
return new Location(origin.getWorld(), 100, 50, 0);
125+
}
126+
return origin.getWorld().getSpawnLocation();
127+
}
128+
129+
@Override
130+
public void writePlayerData(@NotNull final Player player, @NotNull final Path file) {
131+
final CompoundTag playerTag = new CompoundTag();
132+
final net.minecraft.world.entity.player.Player entityPlayer = ((CraftPlayer) player).getHandle();
133+
entityPlayer.save(playerTag);
134+
135+
// Handle the Optional return type of getCompound
136+
CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag());
137+
// If "hyperverse" didn't exist, we need to add it to playerTag
138+
playerTag.put("hyperverse", hyperverse);
139+
140+
hyperverse.putLong("writeTime", System.currentTimeMillis());
141+
hyperverse.putString("version", Bukkit.getPluginManager().getPlugin("Hyperverse").getDescription().getVersion());
142+
143+
taskFactory.newChain().async(() -> {
144+
try (final OutputStream outputStream = Files.newOutputStream(file)) {
145+
NbtIo.writeCompressed(playerTag, outputStream);
146+
} catch (final Exception e) {
147+
e.printStackTrace();
148+
}
149+
}).execute();
150+
}
151+
152+
@Override
153+
public void readPlayerData(@NotNull final Player player, @NotNull final Path file, @NotNull final Runnable whenDone) {
154+
final Location originLocation = player.getLocation().clone();
155+
taskFactory.newChain().asyncFirst(() -> {
156+
try (final InputStream inputStream = Files.newInputStream(file)) {
157+
return Optional.of(NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()));
158+
} catch (final Exception e) {
159+
e.printStackTrace();
160+
}
161+
return Optional.empty();
162+
}).syncLast((optionalCompound) -> {
163+
if (!optionalCompound.isPresent()) {
164+
return;
165+
}
166+
final CompoundTag compound = (CompoundTag) optionalCompound.get();
167+
PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> {
168+
// Health and hunger don't update properly, so we give them a little help
169+
final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health)
170+
final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food)
171+
172+
// Restore bed spawn
173+
final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world
174+
final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX());
175+
final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY());
176+
final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ());
177+
final Location spawnLocation = new Location(Bukkit.getWorld(spawnWorld), spawnX, spawnY, spawnZ);
178+
179+
final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle();
180+
181+
// We re-write the extra Bukkit data as to not mess up the profile
182+
((CraftPlayer) player).setExtraData(compound);
183+
// Set the position to the player's current position
184+
Vec3 pos = entityPlayer.position();
185+
compound.put("Pos", doubleList(pos.x, pos.y, pos.z));
186+
// Set the world to the player's current world
187+
compound.putString("world", player.getWorld().getName());
188+
// Store persistent values
189+
((CraftPlayer) player).storeBukkitValues(compound);
190+
191+
// We start by doing a total reset
192+
entityPlayer.reset();
193+
entityPlayer.load(compound);
194+
195+
entityPlayer.effectsDirty = true;
196+
entityPlayer.onUpdateAbilities();
197+
player.teleport(originLocation);
198+
199+
final ServerLevel worldServer = ((CraftWorld) originLocation.getWorld()).getHandle();
200+
final DimensionType dimensionManager = worldServer.dimensionType();
201+
202+
// Prevent annoying message
203+
// Spigot-obf = decouple()
204+
entityPlayer.unRide();
205+
worldServer.removePlayerImmediately(entityPlayer, Entity.RemovalReason.CHANGED_DIMENSION);
206+
// worldServer.removePlayer above should remove the player from the map, but that doesn't always happen
207+
try {
208+
if (this.entitySectionManager == null) {
209+
this.entitySectionManager = worldServer.getClass().getDeclaredField("entityManager");
210+
this.entitySectionManager.setAccessible(true);
211+
}
212+
final PersistentEntitySectionManager<Entity> esm = (PersistentEntitySectionManager<Entity>) this.entitySectionManager.get(worldServer);
213+
if (this.entityLookup == null) {
214+
this.entityLookup = esm.getClass().getDeclaredField("visibleEntityStorage");
215+
}
216+
final EntityLookup<Entity> lookup = (EntityLookup<Entity>) this.entityLookup.get(esm);
217+
lookup.remove(entityPlayer);
218+
} catch (final NoSuchFieldException | IllegalAccessException e) {
219+
e.printStackTrace();
220+
}
221+
222+
// pre 1.18 code = PlayerList#moveToWorld
223+
entityPlayer.server.getPlayerList().remove(entityPlayer);
224+
worldServer.getServer().getPlayerList().respawn(entityPlayer, true,
225+
Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN, originLocation);
226+
227+
// Apply health and foodLevel
228+
player.setHealth(health);
229+
player.setFoodLevel(foodLevel);
230+
player.setPortalCooldown(40);
231+
player.setBedSpawnLocation(spawnLocation, true);
232+
});
233+
}).execute(whenDone);
234+
}
235+
236+
@Override
237+
@Nullable
238+
public Location findBedRespawn(@NotNull final Location spawnLocation) {
239+
final CraftWorld craftWorld = (CraftWorld) spawnLocation.getWorld();
240+
if (craftWorld == null) {
241+
return null;
242+
}
243+
244+
// Create a BlockPos for the spawn location
245+
BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ());
246+
247+
// Get the dimension ResourceKey from the ServerLevel
248+
ServerLevel serverLevel = craftWorld.getHandle();
249+
ResourceKey<Level> dimension = serverLevel.dimension();
250+
251+
// Create a RespawnConfig instance with the required arguments
252+
ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig(
253+
dimension, // The dimension of the world
254+
blockPos, // The position to check for respawn
255+
0.0f, // Yaw (rotation angle), default to 0
256+
true // Forced flag, set to true to match previous behavior
257+
);
258+
259+
// Call the method with the correct arguments
260+
return ServerPlayer.findRespawnAndUseSpawnBlock(
261+
serverLevel,
262+
respawnConfig,
263+
false // Assuming the boolean is respawnAfterDeath
264+
)
265+
.map(ServerPlayer.RespawnPosAngle::position)
266+
.map(pos -> new Location(spawnLocation.getWorld(), pos.x(), pos.y(), pos.z()))
267+
.orElse(null);
268+
}
269+
270+
private static ListTag doubleList(final double... values) {
271+
final ListTag tagList = new ListTag();
272+
for (final double d : values) {
273+
tagList.add(DoubleTag.valueOf(d));
274+
}
275+
return tagList;
276+
}
277+
278+
}
Lines changed: 22 additions & 0 deletions
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.6-R0.1-SNAPSHOT")
15+
compileOnly(projects.hyperverseNmsCommon)
16+
}
17+
18+
tasks {
19+
assemble {
20+
dependsOn(reobfJar)
21+
}
22+
}

0 commit comments

Comments
 (0)