Skip to content
This repository was archived by the owner on Nov 24, 2022. It is now read-only.
Open
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
9 changes: 4 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ServerChunkCacheAccessor {
DistanceManager getDistanceManager();

@Invoker("getVisibleChunkIfPresent")
ChunkHolder getVisibleChunkIfPresent(long pos);

ChunkHolder callGetVisibleChunkIfPresent(long pos);

@Invoker
boolean callRunDistanceManagerUpdates();
}
22 changes: 21 additions & 1 deletion src/main/java/org/kilocraft/essentials/patch/ChunkManager.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
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;
import net.minecraft.world.level.block.Blocks;
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 {
Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -94,4 +100,18 @@ public static DistanceManager getTicketManager(LevelReader world) {
public static DistanceManager getTicketManager(ServerChunkCache manager) {
return ((ServerChunkCacheAccessor) manager).getDistanceManager();
}

public static <T> CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> asyncChunkLoad(TicketType<T> 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<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> future = chunkHolder.getEntityTickingChunkFuture();
future.whenCompleteAsync((unused, throwable) -> manager.removeRegionTicket(ticket, pos, 3, instance), level.getServer());
return future;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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<Integer> 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<ChunkHolder.ChunkLoadingFailure> 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 {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/lang/en_us.properties
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ command.rtp.failed.too_slow=<red>RTP failed, because chunks did not load!
command.rtp.failed.unsafe=<red>RTP failed, because the target location is unsafe!
command.rtp.success=<yellow>Successfully teleported to <gold>%s <yellow>at <light_purple>%s <dark_purple>%s <light_purple>%s <yellow>in <aqua>%s<yellow>!
command.rtp.get=<gold>%s<yellow> has <gold>%s<yellow> RTPs left
command.rtp.err=Error while loading chunks %s
command.sethome.confirmation_message=<yellow><bold>Hold on!<reset><yellow> You are about to override an existing Home!\n<yellow>Are you sure you want to continue?
command.sethome.limit=<red>Limit reached! You can't set any more Homes!
command.sethome.other=<green>Set the Home <gold>%s <green>for <aqua>%s
Expand Down