generated from AriasCreations/vsmodtemplate
Implement the RTP refactor
This commit is contained in:
parent
89e4835216
commit
6e0dbb361a
3 changed files with 114 additions and 79 deletions
|
@ -76,10 +76,11 @@ namespace AriasServerUtils
|
||||||
api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown);
|
api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown);
|
||||||
api.Event.Timer(OnCheckModDirty, 20);
|
api.Event.Timer(OnCheckModDirty, 20);
|
||||||
api.Event.Timer(OnCheckPlayerCooldowns, 1);
|
api.Event.Timer(OnCheckPlayerCooldowns, 1);
|
||||||
|
api.Event.Timer(RTPFactory.HandleRTPChecking, 1);
|
||||||
api.Event.PlayerDeath += OnPlayerDeath;
|
api.Event.PlayerDeath += OnPlayerDeath;
|
||||||
api.Event.PlayerJoin += OnPlayerJoin;
|
api.Event.PlayerJoin += OnPlayerJoin;
|
||||||
api.Event.PlayerDisconnect += OnPlayerDC;
|
api.Event.PlayerDisconnect += OnPlayerDC;
|
||||||
api.Event.ChunkColumnLoaded += RTPFactory.ChunkColumnGenerated;
|
api.Event.ChunkColumnLoaded += RTPFactory.ChunkLoaded;
|
||||||
//api.Event.PlayerLeave += OnPlayerDC;
|
//api.Event.PlayerLeave += OnPlayerDC;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using AriasServerUtils;
|
using AriasServerUtils;
|
||||||
using Vintagestory.API.Common;
|
using Vintagestory.API.Common;
|
||||||
|
@ -15,6 +16,7 @@ public class RTPFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
private static List<RTPData> RTPCache = new();
|
private static List<RTPData> RTPCache = new();
|
||||||
|
private static List<RTPChunk> ChunkChecks = new();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (pPos == null)
|
if (pPos == null)
|
||||||
|
@ -41,68 +43,36 @@ public class RTPFactory
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function searches for a safe position, honoring the max RTP distance in the global configuration
|
/// This function searches for a safe position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isp">The player to be teleported</param>
|
/// <param name="isp">The player to be teleported</param>
|
||||||
/// <returns>A random position +/- max distance from current position.</returns>
|
/// <returns>A safe position if able to be found</returns>
|
||||||
public static PlayerPosition GetRandomPosition(IServerPlayer isp, int maxDistance)
|
public static PlayerPosition GetSafePosition(RTPData data, RTPPosition position)
|
||||||
{
|
{
|
||||||
Random rng = ServerUtilities.rng;
|
Random rng = ServerUtilities.rng;
|
||||||
|
IServerPlayer isp = data.player;
|
||||||
EntityPos vPos = isp.Entity.Pos;
|
EntityPos vPos = isp.Entity.Pos;
|
||||||
BlockPos bPos = new BlockPos(isp.Entity.Pos.Dimension);
|
BlockPos bPos = new BlockPos(isp.Entity.Pos.Dimension);
|
||||||
IServerWorldAccessor iswa = isp.Entity.World as IServerWorldAccessor;
|
IServerWorldAccessor iswa = isp.Entity.World as IServerWorldAccessor;
|
||||||
|
|
||||||
int tries = 10000;
|
int tries = 10000;
|
||||||
PlayerPosition PPos = PlayerPosition.from(isp.Entity);
|
PlayerPosition PPos = PlayerPosition.from(isp.Entity);
|
||||||
PlayerPosition original = 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
|
// Generate random X and Z within max RTP distance
|
||||||
bPos.X = (int)vPos.X + rng.Next(ixMin, ixMax);
|
bPos.X = position.x;
|
||||||
bPos.Z = (int)vPos.Z + rng.Next(izMin, izMax);
|
bPos.Z = position.z;
|
||||||
bPos.Y = 255;
|
bPos.Y = 255;
|
||||||
|
|
||||||
Block lastAboveLast = null;
|
Block lastAboveLast = null;
|
||||||
Block lastBlock = null;
|
Block lastBlock = null;
|
||||||
Block curBlock;
|
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
|
// Scan downwards to find a valid landing spot
|
||||||
for (int i = 255; i > 0; i--)
|
for (int i = 255; i > 0; i--)
|
||||||
{
|
{
|
||||||
bPos.Y = 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);
|
curBlock = iswa.BlockAccessor.GetBlock(bPos);
|
||||||
|
|
||||||
if (curBlock.MatterState == EnumMatterState.Solid)
|
if (curBlock.MatterState == EnumMatterState.Solid)
|
||||||
|
@ -124,9 +94,6 @@ public class RTPFactory
|
||||||
lastAboveLast = lastBlock;
|
lastAboveLast = lastBlock;
|
||||||
lastBlock = curBlock;
|
lastBlock = curBlock;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
original.Merge(isp.Entity);
|
|
||||||
|
|
||||||
return null; // Return null if no valid position is found after retries
|
return null; // Return null if no valid position is found after retries
|
||||||
}
|
}
|
||||||
|
@ -138,7 +105,7 @@ public class RTPFactory
|
||||||
/// <param name="maxDistance">Max distance +/- the current position.</param>
|
/// <param name="maxDistance">Max distance +/- the current position.</param>
|
||||||
public static void TryRTP(IServerPlayer isp, int maxDistance)
|
public static void TryRTP(IServerPlayer isp, int maxDistance)
|
||||||
{
|
{
|
||||||
var data = new RTPData(isp, maxDistance, 1000, PlayerPosition.from(isp));
|
var data = new RTPData(isp, maxDistance, 100, PlayerPosition.from(isp.Entity));
|
||||||
RTPCache.Add(data);
|
RTPCache.Add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,10 +114,75 @@ public class RTPFactory
|
||||||
return MathF.Sqrt(MathF.Pow(pos2.X - pos1.X, 2) + MathF.Pow(pos2.Y - pos1.Y, 2));
|
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)
|
/// <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()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
// 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;
|
||||||
|
|
||||||
|
// Generate a new position
|
||||||
|
var position = rtp.MakeNewPosition();
|
||||||
|
|
||||||
|
// Get the world handle, then get chunk size
|
||||||
|
var worldManager = ServerUtilities.API.WorldManager;
|
||||||
|
var chunkSize = worldManager.ChunkSize;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Load the chunk
|
||||||
|
worldManager.LoadChunkColumnForDimension(chunk.ChunkX, chunk.ChunkZ, chunk.dim);
|
||||||
|
|
||||||
|
// Log the request
|
||||||
|
ChunkChecks.Add(chunk);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ChunkLoaded(Vec2i chunkCoord, IWorldChunk[] chunks)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Found! Perform teleport and remove the RTP Check
|
||||||
|
RTPCache.Remove(data);
|
||||||
|
pos.Merge(data.player.Entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove this check
|
||||||
|
ChunkChecks.Remove(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RTPChunk
|
||||||
|
{
|
||||||
|
public int ChunkX;
|
||||||
|
public int ChunkZ;
|
||||||
|
public int dim;
|
||||||
|
public RTPData rtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RTPData
|
public class RTPData
|
||||||
|
@ -159,6 +191,7 @@ public class RTPData
|
||||||
public int NumTriesRemaining;
|
public int NumTriesRemaining;
|
||||||
public int MaxDistance;
|
public int MaxDistance;
|
||||||
public PlayerPosition StartPosition;
|
public PlayerPosition StartPosition;
|
||||||
|
public RTPPosition LastPosition;
|
||||||
|
|
||||||
public RTPData(IServerPlayer isp, int maxDistance, int tries, PlayerPosition playerPosition)
|
public RTPData(IServerPlayer isp, int maxDistance, int tries, PlayerPosition playerPosition)
|
||||||
{
|
{
|
||||||
|
@ -171,18 +204,19 @@ public class RTPData
|
||||||
public RTPPosition MakeNewPosition()
|
public RTPPosition MakeNewPosition()
|
||||||
{
|
{
|
||||||
NumTriesRemaining--;
|
NumTriesRemaining--;
|
||||||
|
LastPosition = new RTPPosition((int)player.Entity.Pos.X, (int)player.Entity.Pos.Z, MaxDistance, player.Entity.Pos.Dimension);
|
||||||
|
|
||||||
return new RTPPosition((int)player.Entity.Pos.X, (int)player.Entity.Pos.Z, MaxDistance, player.Entity.Pos.Dimension);
|
return LastPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RTPPosition
|
public class RTPPosition
|
||||||
{
|
{
|
||||||
int x;
|
public int x;
|
||||||
int y;
|
public int y;
|
||||||
int z;
|
public int z;
|
||||||
|
|
||||||
int dimension;
|
public int dimension;
|
||||||
|
|
||||||
public RTPPosition(int x, int z, int maxDist, int dim)
|
public RTPPosition(int x, int z, int maxDist, int dim)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"modid": "ariasserverutils",
|
"modid": "ariasserverutils",
|
||||||
"name": "Aria's Server Utilities",
|
"name": "Aria's Server Utilities",
|
||||||
"authors": ["zontreck"],
|
"authors": ["zontreck"],
|
||||||
"description": "A collection of server utilities\n\nBuild Date: 03-07-2025 @ 1:32 PM MST",
|
"description": "A collection of server utilities\n\nBuild Date: 03-10-2025 @ 10:01 AM MST",
|
||||||
"version": "1.0.6-dev.8",
|
"version": "1.0.6-dev.9",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"game": ""
|
"game": ""
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue