using System; using System.Collections.Generic; using System.Data; using System.Numerics; using AriasServerUtils; using Vintagestory.API.Common; using Vintagestory.API.Common.Entities; using Vintagestory.API.Config; using Vintagestory.API.MathTools; using Vintagestory.API.Server; using Vintagestory.GameContent; namespace AriasServerUtils; public class RTPFactory { private static List RTPCache = new(); /* if (pPos == null) { ps.ActiveCooldowns.Add(CooldownType.RTP, (TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.RTP)) / 2) + TimeUtil.GetUnixEpochTimestamp()); ServerUtilities.MarkDirty(); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-fail")); return TextCommandResult.Success(); } Vec2i origin = new((int)isp.Entity.Pos.X, (int)isp.Entity.Pos.Z); Vec2i npos = new(pPos.X, pPos.Z); float distance = RTPFactory.GetDistance(origin, npos); pPos.Merge(isp.Entity); ps.ActiveCooldowns.Add(CooldownType.RTP, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.RTP)) + TimeUtil.GetUnixEpochTimestamp()); ServerUtilities.MarkDirty(); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp", distance)); */ /// /// This function searches for a safe position, honoring the max RTP distance in the global configuration /// /// The player to be teleported /// A random position +/- max distance from current position. public static PlayerPosition GetRandomPosition(IServerPlayer isp, int maxDistance) { Random rng = ServerUtilities.rng; EntityPos vPos = isp.Entity.Pos; BlockPos bPos = new BlockPos(isp.Entity.Pos.Dimension); IServerWorldAccessor iswa = isp.Entity.World as IServerWorldAccessor; int tries = 10000; PlayerPosition PPos = PlayerPosition.from(isp.Entity); PlayerPosition original = PlayerPosition.from(isp.Entity); while (tries-- > 0) { int ixMax = (int)vPos.X + maxDistance; int ixMin = (int)vPos.X - maxDistance; int izMax = (int)vPos.Z + maxDistance; int izMin = (int)vPos.Z - maxDistance; // Generate random X and Z within max RTP distance bPos.X = (int)vPos.X + rng.Next(ixMin, ixMax); bPos.Z = (int)vPos.Z + rng.Next(izMin, izMax); bPos.Y = 255; Block lastAboveLast = null; Block lastBlock = null; Block curBlock; // Ensure the chunk is loaded before accessing blocks int chunkX = bPos.X / 32; int chunkY = bPos.Y / 32; int chunkZ = bPos.Z / 32; var chunk = iswa.ChunkProvider.GetUnpackedChunkFast(chunkX, chunkY, chunkZ); if (chunk == null) { // Chunk doesn't exist, or isn't loaded. // Teleport the player to the position up in the sky. That way the chunk does get loaded and we can proceed. PPos.X = bPos.X; PPos.Y = bPos.Y; PPos.Z = bPos.Z; PPos.Merge(isp.Entity); } // Scan downwards to find a valid landing spot for (int i = 255; i > 0; i--) { bPos.Y = i; if (i / 32 != chunkY) { chunkY = i / 32; chunk = iswa.ChunkProvider.GetUnpackedChunkFast(chunkX, chunkY, chunkZ); if (chunk == null) { PPos.Y = i; PPos.Merge(isp.Entity); } } curBlock = iswa.BlockAccessor.GetBlock(bPos); if (curBlock.MatterState == EnumMatterState.Solid) { if (lastBlock != null && lastBlock.BlockMaterial == EnumBlockMaterial.Air && lastAboveLast != null && lastAboveLast.BlockMaterial == EnumBlockMaterial.Air) { // Found a valid spot: curBlock is solid, lastBlock & lastAboveLast are gas (air) PPos.X = bPos.X; PPos.Y = bPos.Y + 1; PPos.Z = bPos.Z; ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-found", tries)); return PPos; } } lastAboveLast = lastBlock; lastBlock = curBlock; } } original.Merge(isp.Entity); return null; // Return null if no valid position is found after retries } /// /// This function will schedule a task to perform an RTP. /// /// Player to be teleported /// Max distance +/- the current position. public static void TryRTP(IServerPlayer isp, int maxDistance) { var data = new RTPData(isp, maxDistance, 1000, PlayerPosition.from(isp)); RTPCache.Add(data); } public static float GetDistance(Vec2i pos1, Vec2i pos2) { return MathF.Sqrt(MathF.Pow(pos2.X - pos1.X, 2) + MathF.Pow(pos2.Y - pos1.Y, 2)); } internal static void ChunkColumnGenerated(Vec2i chunkCoord, IWorldChunk[] chunks) { throw new NotImplementedException(); } } public class RTPData { public IServerPlayer player; public int NumTriesRemaining; public int MaxDistance; public PlayerPosition StartPosition; public RTPData(IServerPlayer isp, int maxDistance, int tries, PlayerPosition playerPosition) { MaxDistance = maxDistance; player = isp; NumTriesRemaining = tries; StartPosition = playerPosition; } public RTPPosition MakeNewPosition() { NumTriesRemaining--; return new RTPPosition((int)player.Entity.Pos.X, (int)player.Entity.Pos.Z, MaxDistance, player.Entity.Pos.Dimension); } } public class RTPPosition { int x; int y; int z; int dimension; public RTPPosition(int x, int z, int maxDist, int dim) { int minX = x - maxDist; int maxX = x + maxDist; int minZ = z - maxDist; int maxZ = z + maxDist; this.x = ServerUtilities.rng.Next(minX, maxX); this.y = 1; this.z = ServerUtilities.rng.Next(minZ, maxZ); this.dimension = dim; } PlayerPosition GetPlayerPosition() { return new PlayerPosition { X = x, Y = y, Dimension = dimension, Z = z }; } BlockPos GetBlockPos() { return new BlockPos(new Vec3i(x, y, z), dimension); } }