diff --git a/AriasServerUtils/ASUModSystem.cs b/AriasServerUtils/ASUModSystem.cs
index 0755aac..b7bc9a2 100644
--- a/AriasServerUtils/ASUModSystem.cs
+++ b/AriasServerUtils/ASUModSystem.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
+using Vintagestory.API.Common.CommandAbbr;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.MathTools;
@@ -137,6 +138,14 @@ namespace AriasServerUtils
.WithDescription("Percentage of players required to sleep before sleeping through the night")
.HandleWith(Events.HandleUpdateASUPSP)
.EndSubCommand()
+ .BeginSubCommand("rtp")
+ .RequiresPrivilege(Privilege.controlserver)
+ .WithArgs(
+ parsers.Int("maxDistance")
+ )
+ .WithDescription("Update RTP Max block distance. Plus and/or minus this distance from player current position")
+ .HandleWith(Events.HandleUpdateASURTPMax)
+ .EndSubCommand()
.EndSubCommand()
.BeginSubCommand("help")
.RequiresPrivilege(Privilege.chat)
diff --git a/AriasServerUtils/EventHandler.cs b/AriasServerUtils/EventHandler.cs
index 1df0360..9dcb867 100644
--- a/AriasServerUtils/EventHandler.cs
+++ b/AriasServerUtils/EventHandler.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using Vintagestory.API.Common;
using Vintagestory.API.Config;
+using Vintagestory.API.MathTools;
using Vintagestory.API.Server;
namespace AriasServerUtils
@@ -41,6 +42,23 @@ namespace AriasServerUtils
internal static TextCommandResult HandleRTP(TextCommandCallingArgs args)
{
+ if (args.Caller is IServerPlayer isp)
+ {
+ ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-search"));
+
+ PlayerPosition pPos = RTPFactory.GetRandomPosition(isp);
+ if (pPos == null)
+ {
+ ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-fail"));
+ }
+ 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);
+
+ ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp", distance));
+
+ }
return TextCommandResult.Success();
}
@@ -349,5 +367,18 @@ namespace AriasServerUtils
return TextCommandResult.Success();
}
+
+ internal static TextCommandResult HandleUpdateASURTPMax(TextCommandCallingArgs args)
+ {
+ if (args[0] is int maxDist)
+ {
+ ServerUtilities.config.MaxRTPBlockDistance = maxDist;
+ ServerUtilities.MarkDirty();
+
+ return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
+ }
+
+ return TextCommandResult.Success();
+ }
}
}
\ No newline at end of file
diff --git a/AriasServerUtils/Globals.cs b/AriasServerUtils/Globals.cs
index 223b195..98f125c 100644
--- a/AriasServerUtils/Globals.cs
+++ b/AriasServerUtils/Globals.cs
@@ -18,6 +18,7 @@ namespace AriasServerUtils
public bool onlyAdminsCreateWarps { get; set; } = true;
public int MaxBackCache { get; set; } = 10;
public int PlayerSleepingPercentage { get; set; } = 50;
+ public int MaxRTPBlockDistance { get; set; } = 5000;
public PlayerPosition Spawn { get; set; }
diff --git a/AriasServerUtils/RTPFactory.cs b/AriasServerUtils/RTPFactory.cs
new file mode 100644
index 0000000..2018d31
--- /dev/null
+++ b/AriasServerUtils/RTPFactory.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Data;
+using System.Numerics;
+using AriasServerUtils;
+using Vintagestory.API.Common;
+using Vintagestory.API.Common.Entities;
+using Vintagestory.API.MathTools;
+using Vintagestory.API.Server;
+using Vintagestory.GameContent;
+
+public class RTPFactory
+{
+ ///
+ /// 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)
+ {
+ Random rng = new Random();
+ EntityPos vPos = isp.Entity.Pos;
+ BlockPos bPos = new BlockPos(isp.Entity.Pos.Dimension);
+ IServerWorldAccessor iswa = isp.Entity.World as IServerWorldAccessor;
+
+ int tries = 5;
+ PlayerPosition PPos = PlayerPosition.from(isp.Entity);
+
+ while (tries-- > 0)
+ {
+ // Generate random X and Z within max RTP distance
+ bPos.X = (int)vPos.X + rng.Next(-ServerUtilities.config.MaxRTPBlockDistance, ServerUtilities.config.MaxRTPBlockDistance);
+ bPos.Z = (int)vPos.Z + rng.Next(-ServerUtilities.config.MaxRTPBlockDistance, ServerUtilities.config.MaxRTPBlockDistance);
+ bPos.Y = 255;
+
+ Block lastAboveLast = null;
+ Block lastBlock = null;
+ Block curBlock;
+
+ // Scan downwards to find a valid landing spot
+ for (int i = 255; i > 50; i--)
+ {
+ bPos.Y = i;
+ curBlock = iswa.BlockAccessor.GetBlock(bPos);
+
+ if (curBlock != null && !curBlock.IsLiquid() && curBlock.MatterState == EnumMatterState.Solid)
+ {
+ if (lastBlock != null && lastBlock.MatterState == EnumMatterState.Gas &&
+ lastAboveLast != null && lastAboveLast.MatterState == EnumMatterState.Gas)
+ {
+ // 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;
+
+ return PPos;
+ }
+ }
+
+ lastAboveLast = lastBlock;
+ lastBlock = curBlock;
+ }
+ }
+
+ return null; // Return null if no valid position is found after retries
+ }
+ 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));
+ }
+
+}
\ No newline at end of file
diff --git a/AriasServerUtils/assets/ariasserverutils/lang/en.json b/AriasServerUtils/assets/ariasserverutils/lang/en.json
index eb944c0..52606b7 100644
--- a/AriasServerUtils/assets/ariasserverutils/lang/en.json
+++ b/AriasServerUtils/assets/ariasserverutils/lang/en.json
@@ -34,6 +34,7 @@
"rtp-search": "Searching for a random position...",
"rtp": "You have been teleported [{0}] blocks away!",
+ "rtp-fail": "Giving up on RTP search. No valid position could be found. Try again later",
"cmd-cooldown": "[{0}] is currently on cooldown. You can use this command again in [{1}]"
}