diff --git a/gradle.properties b/gradle.properties index bb600dbd..5dac5a12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,11 @@ # Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx2G -# Fabric Properties -minecraft_version=1.18.2-rc1 -loader_version=0.12.8 +minecraft_version=1.18.2 +loader_version=0.14.8 -# Fabric api -fabric_version=0.42.4+1.18 +#Fabric api +fabric_version=0.57.0+1.18.2 # Mod Properties mod_version=1.21.0 diff --git a/src/main/java/org/kilocraft/essentials/mixin/accessor/ServerChunkCacheAccessor.java b/src/main/java/org/kilocraft/essentials/mixin/accessor/ServerChunkCacheAccessor.java index 3a2ecf41..69d2687c 100644 --- a/src/main/java/org/kilocraft/essentials/mixin/accessor/ServerChunkCacheAccessor.java +++ b/src/main/java/org/kilocraft/essentials/mixin/accessor/ServerChunkCacheAccessor.java @@ -14,6 +14,8 @@ public interface ServerChunkCacheAccessor { DistanceManager getDistanceManager(); @Invoker("getVisibleChunkIfPresent") - ChunkHolder getVisibleChunkIfPresent(long pos); - + ChunkHolder callGetVisibleChunkIfPresent(long pos); + + @Invoker + boolean callRunDistanceManagerUpdates(); } diff --git a/src/main/java/org/kilocraft/essentials/patch/ChunkManager.java b/src/main/java/org/kilocraft/essentials/patch/ChunkManager.java index 62f5e8c5..7023ce49 100644 --- a/src/main/java/org/kilocraft/essentials/patch/ChunkManager.java +++ b/src/main/java/org/kilocraft/essentials/patch/ChunkManager.java @@ -1,10 +1,13 @@ package org.kilocraft.essentials.patch; +import java.util.concurrent.CompletableFuture; + import net.minecraft.core.BlockPos; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; @@ -12,6 +15,9 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; import org.kilocraft.essentials.mixin.accessor.ServerChunkCacheAccessor; public final class ChunkManager { @@ -80,7 +86,7 @@ public static ChunkHolder getChunkHolder(LevelReader world, BlockPos pos) { } public static ChunkHolder getChunkHolder(ServerChunkCache manager, int x, int z) { - return ((ServerChunkCacheAccessor) manager).getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); + return ((ServerChunkCacheAccessor) manager).callGetVisibleChunkIfPresent(ChunkPos.asLong(x, z)); } /** @@ -94,4 +100,18 @@ public static DistanceManager getTicketManager(LevelReader world) { public static DistanceManager getTicketManager(ServerChunkCache manager) { return ((ServerChunkCacheAccessor) manager).getDistanceManager(); } + + public static CompletableFuture> asyncChunkLoad(TicketType ticket, ServerLevel level, ChunkPos pos, T instance) { + final ServerChunkCache chunkManager = level.getChunkSource(); + final DistanceManager manager = getTicketManager(level); + manager.addRegionTicket(ticket, pos, 3, instance); + ((ServerChunkCacheAccessor) chunkManager).callRunDistanceManagerUpdates(); + final ChunkHolder chunkHolder = getChunkHolder(chunkManager, pos.x, pos.z); + if (chunkHolder == null) { + throw new IllegalStateException("Chunk not there when requested"); + } + final CompletableFuture> future = chunkHolder.getEntityTickingChunkFuture(); + future.whenCompleteAsync((unused, throwable) -> manager.removeRegionTicket(ticket, pos, 3, instance), level.getServer()); + return future; + } } diff --git a/src/main/java/org/kilocraft/essentials/util/commands/teleport/RtpCommand.java b/src/main/java/org/kilocraft/essentials/util/commands/teleport/RtpCommand.java index b6a2ad75..1e987b37 100644 --- a/src/main/java/org/kilocraft/essentials/util/commands/teleport/RtpCommand.java +++ b/src/main/java/org/kilocraft/essentials/util/commands/teleport/RtpCommand.java @@ -5,6 +5,8 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.datafixers.util.Unit; +import com.mojang.realmsclient.util.task.OpenServerTask; import org.jetbrains.annotations.Nullable; import org.kilocraft.essentials.api.KiloEssentials; import org.kilocraft.essentials.api.command.ArgumentSuggestions; @@ -24,6 +26,7 @@ import org.kilocraft.essentials.util.registry.RegistryUtils; import org.kilocraft.essentials.util.text.Texter; +import java.util.Optional; import java.util.Random; import java.util.function.Predicate; import net.minecraft.commands.CommandSourceStack; @@ -32,7 +35,10 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; +import net.minecraft.network.Connection; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.TicketType; @@ -90,8 +96,7 @@ private void attemptTeleport(CommandSourceStack src, OnlineUser targetUser, Serv ServerPlayer target = targetUser.asPlayer(); targetUser.sendLangMessage("command.rtp.loading"); // Add a custom ticket to gradually preload chunks - targetWorld.getChunkSource().addRegionTicket(TicketType.create("rtp", Integer::compareTo, 300), new ChunkPos(blockPos), 1, target.getId()); // Lag reduction - this.teleport(src, targetUser, targetWorld, blockPos.mutable(), 0); + this.teleport(src, targetUser, targetWorld, blockPos.mutable(), 0, target.getId()); } else { targetUser.sendLangMessage("command.rtp.failed.invalid_biome"); } @@ -124,36 +129,44 @@ private BlockPos getBlockPos(ServerLevel world) { } return null; } - - private void teleport(final CommandSourceStack src, final OnlineUser targetUser, final ServerLevel world, final BlockPos.MutableBlockPos pos, int attempts) { - // Check every ~4 ticks - AbstractScheduler.start(175, () -> { - final ChunkAccess chunk = ChunkManager.getChunkIfLoaded(world, pos); - if (chunk == null) { - if (attempts > 70) { - targetUser.sendLangMessage("command.rtp.failed.too_slow"); - } else { - this.teleport(src, targetUser, world, pos, attempts + 1); - } + + private static final TicketType ASYNC_TELEPORT = TicketType.create("rtp", Integer::compareTo, 300); + + private void teleport(final CommandSourceStack src, final OnlineUser targetUser, final ServerLevel world, final BlockPos.MutableBlockPos pos, int attempts, int id) { + ChunkManager.asyncChunkLoad(ASYNC_TELEPORT, world, new ChunkPos(pos), id).whenCompleteAsync((either, throwable) -> { + if (throwable != null) { + targetUser.sendLangMessage("command.rtp.err", throwable); + return; } else { - try { - ServerPlayer target = targetUser.asPlayer(); - if (target != null) { - pos.setY(this.getY(world, pos.getX(), world.getMaxBuildHeight(), pos.getZ())); - target.teleportTo(world, pos.getX(), pos.getY(), pos.getZ(), target.getYRot(), target.getXRot()); - if (!PERMISSION_CHECK_IGNORE_LIMIT.test(src)) { - targetUser.getPreferences().set(RTP_LEFT, targetUser.getPreference(RTP_LEFT) - 1); - } - Biome biome = world.getBiome(pos).value(); - final ResourceLocation identifier = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(biome); - assert identifier != null; - targetUser.sendLangMessage("command.rtp.success", identifier.getPath(), pos.getX(), pos.getY(), pos.getZ(), RegistryUtils.dimensionToName(world.dimension())); + Optional right = either.right(); + if(right.isPresent()) { + targetUser.sendLangMessage("command.rtp.err", right.get()); + return; + } + } + + Connection connection = targetUser.getConnection(); + if (connection == null || !connection.isConnected()) { + return; + } + + try { + ServerPlayer target = targetUser.asPlayer(); + if (target != null) { + pos.setY(this.getY(world, pos.getX(), world.getMaxBuildHeight(), pos.getZ())); + target.teleportTo(world, pos.getX(), pos.getY(), pos.getZ(), target.getYRot(), target.getXRot()); + if (!PERMISSION_CHECK_IGNORE_LIMIT.test(src)) { + targetUser.getPreferences().set(RTP_LEFT, targetUser.getPreference(RTP_LEFT) - 1); } - } catch (InsecureDestinationException e) { - targetUser.sendLangMessage("command.rtp.failed.unsafe"); + Biome biome = world.getBiome(pos).value(); + final ResourceLocation identifier = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(biome); + assert identifier != null; + targetUser.sendLangMessage("command.rtp.success", identifier.getPath(), pos.getX(), pos.getY(), pos.getZ(), RegistryUtils.dimensionToName(world.dimension())); } + } catch (InsecureDestinationException e) { + targetUser.sendLangMessage("command.rtp.failed.unsafe"); } - }); + }, runnable -> world.getServer().tell(new TickTask(0, runnable))); } private int getY(BlockGetter blockView, int x, int maxY, int z) throws InsecureDestinationException { diff --git a/src/main/resources/assets/lang/en_us.properties b/src/main/resources/assets/lang/en_us.properties index 2af393ab..4b72ae08 100644 --- a/src/main/resources/assets/lang/en_us.properties +++ b/src/main/resources/assets/lang/en_us.properties @@ -149,6 +149,7 @@ command.rtp.failed.too_slow=RTP failed, because chunks did not load! command.rtp.failed.unsafe=RTP failed, because the target location is unsafe! command.rtp.success=Successfully teleported to %s at %s %s %s in %s! command.rtp.get=%s has %s RTPs left +command.rtp.err=Error while loading chunks %s command.sethome.confirmation_message=Hold on! You are about to override an existing Home!\nAre you sure you want to continue? command.sethome.limit=Limit reached! You can't set any more Homes! command.sethome.other=Set the Home %s for %s