VSMod_AriasServerUtils/AriasServerUtils/RTPFactory.cs
2025-03-07 14:10:41 -07:00

216 lines
No EOL
7 KiB
C#

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<RTPData> 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));
*/
/// <summary>
/// This function searches for a safe position, honoring the max RTP distance in the global configuration
/// </summary>
/// <param name="isp">The player to be teleported</param>
/// <returns>A random position +/- max distance from current position.</returns>
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
}
/// <summary>
/// This function will schedule a task to perform an RTP.
/// </summary>
/// <param name="isp">Player to be teleported</param>
/// <param name="maxDistance">Max distance +/- the current position.</param>
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);
}
}