generated from AriasCreations/vsmodtemplate
286 lines
No EOL
9.5 KiB
C#
286 lines
No EOL
9.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using AriasServerUtils;
|
|
using Microsoft.Win32.SafeHandles;
|
|
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();
|
|
private static List<RTPChunk> ChunkChecks = 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
|
|
/// </summary>
|
|
/// <param name="isp">The player to be teleported</param>
|
|
/// <returns>A safe position if able to be found</returns>
|
|
public static PlayerPosition GetSafePosition(RTPData data, RTPPosition position)
|
|
{
|
|
PlayerPosition PPos = data.LastPosition.GetPlayerPosition();
|
|
BlockPos check = data.LastPosition.GetBlockPos();
|
|
var BA = ServerUtilities.API.World.BlockAccessor;
|
|
check.Y = 1;
|
|
int height = BA.GetTerrainMapheightAt(check);
|
|
if (height >= 0 && height <= 20) return null;
|
|
check.Y = height + 1;
|
|
PPos.Y = height + 1;
|
|
return PPos;
|
|
}
|
|
|
|
/// <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, 10, PlayerPosition.from(isp.Entity));
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fired automatically by the internal game timer. This function will handle checking for a RTP Location
|
|
///
|
|
/// NOTE: This function will only cause the chunks in question to be force loaded long enough to check their blocks and make sure it is safe to land there.
|
|
/// </summary>
|
|
internal static void HandleRTPChecking()
|
|
{
|
|
foreach (var chunk in ChunkChecks)
|
|
{
|
|
chunk.Wait--;
|
|
if (chunk.Wait <= 0)
|
|
{
|
|
ChunkChecks.Remove(chunk);
|
|
break;
|
|
}
|
|
}
|
|
// We want to now loop over the entire cache list.
|
|
// We'll then generate a position to check
|
|
// We'll also then check the loaded status of the chunk
|
|
foreach (var rtp in RTPCache)
|
|
{
|
|
// Check for any chunks still being checked.
|
|
int num = ChunkChecks.Select(x => x.rtp.player.PlayerUID == rtp.player.PlayerUID).Count();
|
|
if (num > 0) continue;
|
|
|
|
|
|
if (rtp.NumTriesRemaining <= 0)
|
|
{
|
|
// Send failure message to the player
|
|
ServerUtilities.SendMessageTo(rtp.player, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-fail"));
|
|
// This check needs to be removed from the queue
|
|
RTPCache.Remove(rtp);
|
|
HandleRTPChecking();
|
|
return; // We modified the list, so abort the loop.
|
|
}
|
|
|
|
// Get the world handle, then get chunk size
|
|
var worldManager = ServerUtilities.API.WorldManager;
|
|
var chunkSize = worldManager.ChunkSize;
|
|
var worldSize = new Vec3i(worldManager.MapSizeX, worldManager.MapSizeY, worldManager.MapSizeZ);
|
|
|
|
// Generate a new position
|
|
var position = rtp.MakeNewPosition(worldSize);
|
|
ServerUtilities.SendMessageTo(rtp.player, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-progress", rtp.NumTriesRemaining));
|
|
|
|
// Generate a chunk load check object.
|
|
RTPChunk chunk = new RTPChunk();
|
|
chunk.ChunkX = position.x / chunkSize;
|
|
chunk.ChunkZ = position.z / chunkSize;
|
|
chunk.dim = position.dimension;
|
|
chunk.rtp = rtp;
|
|
|
|
// Log the request
|
|
ChunkChecks.Add(chunk);
|
|
|
|
// Check if the chunk's coordinates are loaded
|
|
var cxCheck = ServerUtilities.API.World.IsFullyLoadedChunk(position.GetBlockPos());
|
|
if (cxCheck)
|
|
{
|
|
// Process the check here, no need to load
|
|
var posX = GetSafePosition(rtp, position);
|
|
if (posX == null)
|
|
{
|
|
// Let this get checked again
|
|
//ServerUtilities.API.Logger.Notification("position null: resume search");
|
|
}
|
|
else
|
|
{
|
|
|
|
// Found! Perform teleport and remove the RTP Check
|
|
RTPCache.Remove(rtp);
|
|
posX.Merge(rtp.player.Entity);
|
|
|
|
ServerUtilities.SendMessageTo(rtp.player, Lang.Get($"{ServerUtilities.MOD_ID}:rtp", GetDistance(new Vec2i(rtp.StartPosition.X, rtp.StartPosition.Z), new Vec2i(posX.X, posX.Z))));
|
|
|
|
//ServerUtilities.API.Logger.Notification("position found");
|
|
}
|
|
|
|
ChunkChecks.Remove(chunk);
|
|
}
|
|
else
|
|
{
|
|
// Load the chunk
|
|
worldManager.LoadChunkColumnPriority(chunk.ChunkX, chunk.ChunkZ);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void ChunkLoaded(Vec2i chunkCoord, IWorldChunk[] chunks)
|
|
{
|
|
// Check if this is even a valid check
|
|
var num = ChunkChecks.Where(x => x.ChunkX == chunkCoord.X && x.ChunkZ == chunkCoord.Y).Count();
|
|
if (num == 0) return;
|
|
|
|
|
|
// Get the chunk from the stack
|
|
var chunk = ChunkChecks.Where(x => x.ChunkX == chunkCoord.X && x.ChunkZ == chunkCoord.Y).First();
|
|
|
|
// Attempt to find a landing point.
|
|
var data = chunk.rtp;
|
|
var pos = GetSafePosition(data, data.LastPosition);
|
|
|
|
if (pos == null)
|
|
{
|
|
// Let this get checked again
|
|
//ServerUtilities.API.Logger.Notification("position null: resume search");
|
|
}
|
|
else
|
|
{
|
|
// Found! Perform teleport and remove the RTP Check
|
|
RTPCache.Remove(data);
|
|
pos.Merge(data.player.Entity);
|
|
|
|
ServerUtilities.SendMessageTo(data.player, Lang.Get($"{ServerUtilities.MOD_ID}:rtp", GetDistance(new Vec2i(data.StartPosition.X, data.StartPosition.Z), new Vec2i(pos.X, pos.Z))));
|
|
|
|
//ServerUtilities.API.Logger.Notification("position found");
|
|
}
|
|
|
|
// Remove this check
|
|
ChunkChecks.Remove(chunk);
|
|
}
|
|
}
|
|
|
|
public class RTPChunk
|
|
{
|
|
public int ChunkX;
|
|
public int ChunkZ;
|
|
public int dim;
|
|
public RTPData rtp;
|
|
public int Wait = 5;
|
|
}
|
|
|
|
public class RTPData
|
|
{
|
|
public IServerPlayer player;
|
|
public int NumTriesRemaining;
|
|
public int MaxDistance;
|
|
public PlayerPosition StartPosition;
|
|
public RTPPosition LastPosition;
|
|
|
|
public RTPData(IServerPlayer isp, int maxDistance, int tries, PlayerPosition playerPosition)
|
|
{
|
|
MaxDistance = maxDistance;
|
|
player = isp;
|
|
NumTriesRemaining = tries;
|
|
StartPosition = playerPosition;
|
|
}
|
|
|
|
public RTPPosition MakeNewPosition(Vec3i mapSize)
|
|
{
|
|
NumTriesRemaining--;
|
|
LastPosition = new RTPPosition((int)player.Entity.Pos.X, (int)player.Entity.Pos.Z, MaxDistance, player.Entity.Pos.Dimension, mapSize, player);
|
|
|
|
return LastPosition;
|
|
}
|
|
}
|
|
|
|
public class RTPPosition
|
|
{
|
|
public int x;
|
|
public int y;
|
|
public int z;
|
|
|
|
public int dimension;
|
|
|
|
public RTPPosition(int x, int z, int maxDist, int dim, Vec3i mapSize, IServerPlayer player)
|
|
{
|
|
|
|
int worldx = mapSize.X / 2;
|
|
int worldz = mapSize.Z / 2;
|
|
|
|
if (maxDist > worldx)
|
|
{
|
|
ServerUtilities.SendMessageTo(player, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-capped", maxDist, (worldx)));
|
|
maxDist = worldx / 2;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public PlayerPosition GetPlayerPosition()
|
|
{
|
|
return new PlayerPosition
|
|
{
|
|
X = x,
|
|
Y = y,
|
|
Dimension = dimension,
|
|
Z = z
|
|
};
|
|
}
|
|
|
|
public BlockPos GetBlockPos()
|
|
{
|
|
return new BlockPos(new Vec3i(x, y, z), dimension);
|
|
}
|
|
} |