Skip to content
Merged
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
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ cloud-build-logic = "0.0.3"
ktlint = "0.50.0"
checkstyle = "10.12.5"
kotlin = "1.9.20"
shadow = "9.0.0-beta4"
paperweight = "1.7.7"
run-paper = "2.3.0"
shadow = "9.0.0-beta8"
paperweight = "2.0.0-beta.14"
run-paper = "2.3.1"
pluginyml = "0.6.0"

# Platforms
minecraft = "1.17.1-R0.1-SNAPSHOT"
minecraft = "1.20.6-R0.1-SNAPSHOT"

# Libraries
cloud = "1.8.4"
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
7 changes: 6 additions & 1 deletion hyperverse-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ dependencies {
runtimeOnly(projects.hyperverseNms1213) {
targetConfiguration = "reobf"
}
runtimeOnly(projects.hyperverseNms1214) {
targetConfiguration = "reobf"
}
}

bukkit {
Expand Down Expand Up @@ -126,6 +129,8 @@ tasks {
exclude(project(":hyperverse-nms-1-20"))
exclude(project(":hyperverse-nms-1-20-6"))
exclude(project(":hyperverse-nms-1-21"))
exclude(project(":hyperverse-nms-1-21-3"))
exclude(project(":hyperverse-nms-1-21-4"))
}
mergeServiceFiles()

Expand Down Expand Up @@ -163,6 +168,6 @@ tasks {

runServer {
java.toolchain.languageVersion.set(JavaLanguageVersion.of(21))
minecraftVersion("1.21")
minecraftVersion("1.21.4")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste
Version.parseMinecraft("1.20.4"),
Version.parseMinecraft("1.20.6"),
Version.parseMinecraft("1.21.1"),
Version.parseMinecraft("1.21.3")
Version.parseMinecraft("1.21.3"),
Version.parseMinecraft("1.21.4")
);

private WorldManager worldManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import com.google.gson.JsonParser;
import com.google.inject.Inject;
import io.leangen.geantyref.TypeToken;
import org.apache.commons.lang.StringUtils;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
import org.bukkit.Location;
Expand Down Expand Up @@ -1277,7 +1276,7 @@ public void doPlugin(final CommandSender sender) {
final PluginDescriptionFile description = plugin.getDescription();
Stream.of(
"<gold>Plugin Version:</gold> <gray>" + description.getVersion() + "</gray>",
"<gold>Author(s):</gold> <gray>" + StringUtils.join(description.getAuthors(), ", ") + "</gray>",
"<gold>Author(s):</gold> <gray>" + String.join(", ", description.getAuthors()) + "</gray>",
"<gold>Website:</gold> <gray><hover:show_text:\"<gray>Click to open</gray>\"><click:open_url:https://hyperver.se>https://hyperver.se</click></hover></gray>"
).forEach(msg ->
MessageUtil.sendMessage(sender, new Message("plugin.internal", "<dark_gray>[</dark_gray><gold>Hyperverse</gold"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
Expand Down Expand Up @@ -1179,7 +1178,7 @@ public void doPlugin(final CommandSender sender) {
final PluginDescriptionFile description = plugin.getDescription();
Stream.of(
"<gold>Plugin Version:</gold> <gray>" + description.getVersion() + "</gray>",
"<gold>Author(s):</gold> <gray>" + StringUtils.join(description.getAuthors(), ", ") + "</gray>",
"<gold>Author(s):</gold> <gray>" + String.join(", ", description.getAuthors()) + "</gray>",
"<gold>Website:</gold> <gray><hover:show_text:\"<gray>Click to open</gray>\"><click:open_url:https://hyperver.se>https://hyperver.se</click></hover></gray>"
).forEach(msg ->
MessageUtil.sendMessage(sender, new Message("plugin.internal", "<dark_gray>[</dark_gray><gold>Hyperverse</gold"
Expand Down
22 changes: 22 additions & 0 deletions hyperverse-nms-1-21-4/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
id("hyperverse.base-conventions")
alias(libs.plugins.paperweight.userdev)
}

indra {
javaVersions {
minimumToolchain(21)
target(21)
}
}

dependencies {
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
compileOnly(projects.hyperverseNmsCommon)
}

tasks {
assemble {
dependsOn(reobfJar)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
//
// Hyperverse - A minecraft world management plugin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

package org.incendo.hyperverse.platform.v1_21_4;

import co.aikar.taskchain.TaskChainFactory;
import com.google.inject.Inject;
import io.papermc.lib.PaperLib;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;

import net.minecraft.BlockUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.entity.EntityLookup;
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import net.minecraft.world.level.portal.PortalForcer;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.filter.RegexFilter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.incendo.hyperverse.util.HyperConfigShouldGroupProfiles;
import org.incendo.hyperverse.util.NMS;

@SuppressWarnings("unused")
public class NMSImpl implements NMS {

private final TaskChainFactory taskFactory;
private Field entitySectionManager;
private Field entityLookup;
private org.apache.logging.log4j.core.Logger worldServerLogger;

@Inject public NMSImpl(final TaskChainFactory taskFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) {
this.taskFactory = taskFactory;
if (hyperConfiguration) {
try {
final Field field = ServerLevel.class.getDeclaredField("LOGGER");
field.setAccessible(true);
this.worldServerLogger = (Logger) field.get(null);
} catch (final Exception e) {
e.printStackTrace();
}
try {
final RegexFilter regexFilter = RegexFilter
.createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false,
Filter.Result.DENY, Filter.Result.ACCEPT);
this.worldServerLogger.addFilter(regexFilter);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

@Override @Nullable public Location getOrCreateNetherPortal(@NotNull final org.bukkit.entity.Entity entity,
@NotNull final Location origin) {
final ServerLevel worldServer = Objects.requireNonNull(((CraftWorld) origin.getWorld()).getHandle());
final PortalForcer portalTravelAgent = Objects.requireNonNull(worldServer.getPortalForcer());
final Entity nmsEntity = Objects.requireNonNull(((CraftEntity) entity).getHandle());
final BlockPos blockPosition = new BlockPos(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ());
final WorldBorder worldBorder = worldServer.getWorldBorder();
Optional<BlockPos> existingPortalPosition = Objects.requireNonNull(portalTravelAgent, "travel agent")
.findClosestPortalPosition(Objects.requireNonNull(blockPosition, "position"), worldBorder,128);
if (existingPortalPosition.isPresent()) {
BlockPos bottomLeft = existingPortalPosition.get();
return new Location(origin.getWorld(), bottomLeft.getX(), bottomLeft.getY(), bottomLeft.getZ());
}
Optional<BlockUtil.FoundRectangle> createdPortal = portalTravelAgent.createPortal(blockPosition,
nmsEntity.getDirection().getAxis(), nmsEntity,
16);
if (createdPortal.isEmpty()) {
return null;
}
final BlockUtil.FoundRectangle rectangle = createdPortal.get();
return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1D, rectangle.minCorner.getY() - 1D,
rectangle.minCorner.getZ() + 1D);
}

@Override @Nullable public Location getDimensionSpawn(@NotNull final Location origin) {
if (Objects.requireNonNull(origin.getWorld()).getEnvironment()
== World.Environment.THE_END) {
return new Location(origin.getWorld(), 100, 50, 0);
}
return origin.getWorld().getSpawnLocation();
}

@Override public void writePlayerData(@NotNull final Player player, @NotNull final Path file) {
final CompoundTag playerTag = new CompoundTag();
final net.minecraft.world.entity.player.Player entityPlayer = ((CraftPlayer) player).getHandle();
entityPlayer.save(playerTag);

if (!playerTag.contains("hyperverse")) {
playerTag.put("hyperverse", new CompoundTag());
}
final CompoundTag hyperverse = playerTag.getCompound("hyperverse");
hyperverse.putLong("writeTime", System.currentTimeMillis());
hyperverse.putString("version", Bukkit.getPluginManager().getPlugin("Hyperverse").getDescription().getVersion());

taskFactory.newChain().async(() -> {
try (final OutputStream outputStream = Files.newOutputStream(file)) {
NbtIo.writeCompressed(playerTag, outputStream);
} catch (final Exception e) {
e.printStackTrace();
}
}).execute();
}

@Override public void readPlayerData(@NotNull final Player player, @NotNull final Path file, @NotNull final Runnable whenDone) {
final Location originLocation = player.getLocation().clone();
taskFactory.newChain().asyncFirst(() -> {
try (final InputStream inputStream = Files.newInputStream(file)) {
return Optional.of(NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()));
} catch (final Exception e) {
e.printStackTrace();
}
return Optional.empty();
}).syncLast((optionalCompound) -> {
if (!optionalCompound.isPresent()) {
return;
}
final CompoundTag compound = (CompoundTag) optionalCompound.get();
PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> {
// Health and hunger don't update properly, so we
// give them a little help
final float health = compound.getFloat("Health");
final int foodLevel = compound.getInt("foodLevel");

// Restore bed spawn
final String spawnWorld = compound.getString("SpawnWorld");
final int spawnX = compound.getInt("SpawnX");
final int spawnY = compound.getInt("SpawnY");
final int spawnZ = compound.getInt("SpawnZ");
final Location spawnLocation = new Location(Bukkit.getWorld(spawnWorld), spawnX,
spawnY, spawnZ);

final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle();

// We re-write the extra Bukkit data as to not
// mess up the profile
((CraftPlayer) player).setExtraData(compound);
// Set the position to the player's current position
Vec3 pos = entityPlayer.position();
compound.put("Pos", doubleList(pos.x, pos.y, pos.z));
// Set the world to the player's current world
compound.putString("world", player.getWorld().getName());
// Store persistent values
((CraftPlayer) player).storeBukkitValues(compound);

// We start by doing a total reset
entityPlayer.reset();
entityPlayer.load(compound);

entityPlayer.effectsDirty = true;
entityPlayer.onUpdateAbilities();
player.teleport(originLocation);

final ServerLevel worldServer = ((CraftWorld) originLocation.getWorld()).getHandle();
final DimensionType dimensionManager = worldServer.dimensionType();

// Prevent annoying message
// Spigot-obf = decouple()
entityPlayer.unRide();
worldServer.removePlayerImmediately(entityPlayer, Entity.RemovalReason.CHANGED_DIMENSION);
// worldServer.removePlayer above should remove the player from the
// map, but that doesn't always happen. This is a last effort
// attempt to prevent the annoying "Force re-added" message
// from appearing
try {
if (this.entitySectionManager == null) {
this.entitySectionManager = worldServer.getClass().getDeclaredField("entityManager");
this.entitySectionManager.setAccessible(true);
}
final PersistentEntitySectionManager<Entity> esm = (PersistentEntitySectionManager<Entity>) this.entitySectionManager.get(worldServer);
if (this.entityLookup == null) {
this.entityLookup = esm.getClass().getDeclaredField("visibleEntityStorage");
}
final EntityLookup<Entity> lookup = (EntityLookup<Entity>) this.entityLookup.get(esm);
lookup.remove(entityPlayer);
} catch (final NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}

// pre 1.18 code = PlayerList#moveToWorld
entityPlayer.server.getPlayerList().remove(entityPlayer);
worldServer.getServer().getPlayerList().respawn(entityPlayer, true,
Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN, originLocation);

// Apply health and foodLevel
player.setHealth(health);
player.setFoodLevel(foodLevel);
player.setPortalCooldown(40);
player.setBedSpawnLocation(spawnLocation, true);
});
}).execute(whenDone);
}

@Override @Nullable public Location findBedRespawn(@NotNull final Location spawnLocation) {
final CraftWorld craftWorld = (CraftWorld) spawnLocation.getWorld();
if (craftWorld == null) {
return null;
}

return ServerPlayer.findRespawnAndUseSpawnBlock(craftWorld.getHandle(), new BlockPos(spawnLocation.getBlockX(),
spawnLocation.getBlockY(), spawnLocation.getBlockZ()), 0, true, false)
.map(ServerPlayer.RespawnPosAngle::position)
.map(pos -> new Location(spawnLocation.getWorld(), pos.x(), pos.y(), pos.z()))
.orElse(null);
}

private static ListTag doubleList(final double... values) {
final ListTag tagList = new ListTag();
for (final double d : values) {
tagList.add(DoubleTag.valueOf(d));
}
return tagList;
}

}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ include(":hyperverse-nms-1-20")
include(":hyperverse-nms-1-20-6")
include(":hyperverse-nms-1-21")
include(":hyperverse-nms-1-21-3")
include(":hyperverse-nms-1-21-4")
Loading