Compare commits

..

No commits in common. "master" and "1.0.1" have entirely different histories.

15 changed files with 318 additions and 1880 deletions

16
.vscode/launch.json vendored
View file

@ -8,12 +8,12 @@
"name": "Launch Client (Debug)", "name": "Launch Client (Debug)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"program": ".game/Vintagestory.exe", "program": "${env:VINTAGE_STORY}/Vintagestory.exe",
"linux": { "linux": {
"program": ".game/Vintagestory" "program": "${env:VINTAGE_STORY}/Vintagestory"
}, },
"osx": { "osx": {
"program": ".game/Vintagestory" "program": "${env:VINTAGE_STORY}/Vintagestory"
}, },
"preLaunchTask": "build", "preLaunchTask": "build",
"args": [ "args": [
@ -21,7 +21,7 @@
// "--openWorld" , "modding test world", // "--openWorld" , "modding test world",
"--tracelog", "--tracelog",
"--addModPath", "--addModPath",
"${workspaceFolder}/AriasServerUtils/bin/Debug/Mods" "${workspaceFolder}/ModTemplate/bin/Debug/Mods"
], ],
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false "stopAtEntry": false
@ -30,18 +30,18 @@
"name": "Launch Server", "name": "Launch Server",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"program": ".game/VintagestoryServer.exe", "program": "${env:VINTAGE_STORY}/VintagestoryServer.exe",
"linux": { "linux": {
"program": ".game/VintagestoryServer" "program": "${env:VINTAGE_STORY}/VintagestoryServer"
}, },
"osx": { "osx": {
"program": ".game/VintagestoryServer" "program": "${env:VINTAGE_STORY}/VintagestoryServer"
}, },
"preLaunchTask": "build", "preLaunchTask": "build",
"args": [ "args": [
"--tracelog", "--tracelog",
"--addModPath", "--addModPath",
"${workspaceFolder}/AriasServerUtils/bin/Debug/Mods" "${workspaceFolder}/ModTemplate/bin/Debug/Mods"
], ],
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false "stopAtEntry": false

View file

@ -1,5 +1,5 @@
{ {
"dotnet.defaultSolution": "AriasServerUtils.sln", "dotnet.defaultSolution": "ModTemplate.sln",
"files.associations": { "files.associations": {
"server-*.txt": "log", "server-*.txt": "log",
"client-*.txt": "log" "client-*.txt": "log"

2
.vscode/tasks.json vendored
View file

@ -9,7 +9,7 @@
"build", "build",
"-c", "-c",
"Debug", "Debug",
"${workspaceFolder}/AriasServerUtils/AriasServerUtils.csproj" "${workspaceFolder}/ModTemplate/ModTemplate.csproj"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },

View file

@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.IO;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Server;
using Vintagestory.GameContent;
namespace AriasServerUtils
{
public class ServerUtilities : ModSystem
{
public static string MOD_ID = "ariasserverutils";
public static ASUModConfig config = new ASUModConfig();
private static ICoreServerAPI API;
private static bool bDirty = false;
internal static Dictionary<string, PlayerInventory> backupInventory = new Dictionary<string, PlayerInventory>();
internal static Dictionary<string, PlayerStorage> mPlayerData = new Dictionary<string, PlayerStorage>();
internal static string[] saveInvTypes = new string[] {
GlobalConstants.hotBarInvClassName,
GlobalConstants.backpackInvClassName,
GlobalConstants.craftingInvClassName,
GlobalConstants.mousecursorInvClassName,
GlobalConstants.characterInvClassName
};
/// <summary>
/// Method to register all mod blocks
/// </summary>
/// <param name="api"></param>
private void RegisterBlocks(ICoreAPI api)
{
api.Logger.Notification("Begin registering block classes for Aria's Server Utils...");
api.Logger.Notification("Block Classes have been registered for Aria's Server Utils!");
}
private void RegisterBlockEntities(ICoreAPI api)
{
}
// Called on server and client
public override void Start(ICoreAPI api)
{
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
RegisterBlocks(api);
RegisterBlockEntities(api);
}
public override void StartServerSide(ICoreServerAPI api)
{
API = api;
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
api.Event.ServerRunPhase(EnumServerRunPhase.GameReady, OnGameReady);
api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown);
api.Event.Timer(OnCheckModDirty, 20);
api.Event.PlayerDeath += OnPlayerDeath;
api.Event.PlayerJoin += OnPlayerJoin;
api.Event.PlayerDisconnect += OnPlayerDC;
//api.Event.PlayerLeave += OnPlayerDC;
api.ChatCommands.Create("setspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleSetSpawn);
api.ChatCommands.Create("spawn").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSpawn);
api.ChatCommands.Create("delspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleClearSpawn);
//api.ChatCommands.Create("test_death").RequiresPlayer().RequiresPrivilege(Privilege.controlserver).HandleWith(TestDeath);
var parsers = api.ChatCommands.Parsers;
api.ChatCommands.Create("restoreinv").RequiresPlayer().WithArgs(parsers.OnlinePlayer("player")).HandleWith(Events.HandleReturnItems).WithDescription("Returns items to a player in the event of a problem").RequiresPrivilege(Privilege.controlserver);
api.ChatCommands.Create("sethome").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Creates a home").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSetHome);
api.ChatCommands.Create("home").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Teleports you to home").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleGoHome);
api.ChatCommands.Create("delhome").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Deletes a home entry").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleDelHome);
api.ChatCommands.Create("homes").RequiresPlayer().WithDescription("Lists your homes").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleListHomes);
api.ChatCommands.Create("asu")
.RequiresPrivilege(Privilege.chat)
.BeginSubCommand("update")
.BeginSubCommand("maxhomes")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Int("maxHomes")
)
.WithDescription("Updates the maximum number of homes")
.HandleWith(Events.HandleUpdateASUMaxHomes)
.EndSubCommand()
.BeginSubCommand("adminhomes")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Bool("adminsBypass")
)
.WithDescription("Updates the flag deciding whether admins can bypass max number of homes")
.HandleWith(Events.HandleUpdateASUBypass)
.EndSubCommand()
.WithDescription("Updates the ASU mod configuration")
.EndSubCommand()
.BeginSubCommand("help")
.RequiresPlayer()
.RequiresPrivilege(Privilege.chat)
.HandleWith(Events.HandleASU)
.WithDescription("Lists all Aria's Server Utils commands")
.EndSubCommand();
}
private void OnPlayerDC(IServerPlayer byPlayer)
{
OnCheckModDirty();
mPlayerData.Remove(byPlayer.PlayerName);
}
private void OnPlayerJoin(IServerPlayer byPlayer)
{
API.Logger.Notification($"[ASU] {Lang.Get($"{MOD_ID}:playerjoin")}");
PlayerStorage data = API.LoadModConfig<PlayerStorage>(GetConfigurationFile(byPlayer.PlayerName, ModConfigType.World));
if (data == null) data = new PlayerStorage();
mPlayerData[byPlayer.PlayerName] = data;
}
public static PlayerStorage GetPlayerData(IServerPlayer player)
{
if (mPlayerData.ContainsKey(player.PlayerName))
{
return mPlayerData[player.PlayerName];
}
else
{
return new PlayerStorage();
}
}
private TextCommandResult TestDeath(TextCommandCallingArgs args)
{
if (args.Caller.Player is IServerPlayer isp) OnPlayerDeath(isp, null);
return TextCommandResult.Success();
}
private void OnPlayerDeath(IServerPlayer player, DamageSource damageSource)
{
PlayerInventory inv = new PlayerInventory();
var invMgr = player.InventoryManager;
var iBackpackSlotNum = 0;
foreach (var type in saveInvTypes)
{
foreach (var stack in invMgr.GetOwnInventory(type))
{
if (iBackpackSlotNum >= 4)
{
continue;
}
if (type == GlobalConstants.backpackInvClassName)
{
iBackpackSlotNum++;
}
if (stack.Empty) continue;
if (stack.Inventory.ClassName == GlobalConstants.characterInvClassName)
{
if (stack.Itemstack.ItemAttributes?["protectionModifiers"].Exists ?? false)
{
inv.Items.Add(stack.Itemstack.Clone());
}
}
else
inv.Items.Add(stack.Itemstack.Clone());
API.Logger.Notification($"SAVED STORAGE ITEM TYPE: {stack.Itemstack}");
}
}
backupInventory[player.PlayerName] = inv;
}
private void OnCheckModDirty()
{
if (bDirty)
{
//API.Logger.Notification(Lang.Get($"{MOD_ID}:timer"));
bDirty = false;
SaveGlobalConfig();
SavePlayerData();
}
}
private void SavePlayerData()
{
foreach (var data in mPlayerData)
{
API.StoreModConfig<PlayerStorage>(data.Value, GetConfigurationFile(data.Key, ModConfigType.World));
}
}
private void OnShutdown()
{
// Mod Shutdown //
// Handle any remaining tasks before shutdown
API.Logger.Notification(Lang.Get($"{MOD_ID}:halt"));
OnCheckModDirty();
}
public void SaveGlobalConfig()
{
API.StoreModConfig<ASUModConfig>(config, GetConfigurationFile("global", ModConfigType.Global));
}
private void OnGameReady()
{
// Mod Setup Info //
// -> Step 1. Load Mod Global Config <-
config = API.LoadModConfig<ASUModConfig>(GetConfigurationFile("global", ModConfigType.Global));
if (config == null) config = new ASUModConfig();
}
public string GetConfigurationFile(string sName, ModConfigType type)
{
if (type == ModConfigType.Global)
{
return $"ariaserverconfig/{sName}.json";
}
else if (type == ModConfigType.World)
{
return $"ariaserverconfig/{GetWorldName()}/{sName}.json";
}
else return $"ariaserverconfig/global.json";
}
/// <summary>
/// This function is used to mark the mod's global config, and all loaded player configs as dirty. They will be flushed to disk, then the dirty flag will be cleared.
/// </summary>
public static void MarkDirty()
{
bDirty = true;
}
public string GetWorldName()
{
string[] lName = API.WorldManager.CurrentWorldName.Split(Path.DirectorySeparatorChar);
string sName = lName[lName.Length - 1];
return sName.Substring(0, sName.Length - 6);
}
public override void StartClientSide(ICoreClientAPI api)
{
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
}
public static void SendMessageTo(IServerPlayer player, string sMsg)
{
player.SendMessage(0, sMsg, EnumChatType.CommandSuccess);
}
}
}

View file

@ -3,12 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Vintagestory.API.Common; using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config; using Vintagestory.API.Config;
using Vintagestory.API.MathTools;
using Vintagestory.API.Server; using Vintagestory.API.Server;
using Vintagestory.API.Util;
using Vintagestory.GameContent;
namespace AriasServerUtils namespace AriasServerUtils
{ {
@ -16,32 +12,7 @@ namespace AriasServerUtils
{ {
internal static TextCommandResult HandleASU(TextCommandCallingArgs args) internal static TextCommandResult HandleASU(TextCommandCallingArgs args)
{ {
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:help", ServerUtilities.config.MaxHomes, ServerUtilities.config.AdminsBypassMaxHomes, ServerUtilities.config.MaxBackCache, string.Join(", ", new string[] { "setspawn", "spawn", "delspawn", "sethome", "home", "delhome", "homes", "restoreinv", "asu", "warp", "setwarp", "delwarp", "warps", "back", "rtp", "listcooldowns" }))); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:help", ServerUtilities.config.MaxHomes, ServerUtilities.config.AdminsBypassMaxHomes, string.Join(", ", new string[] { "setspawn", "spawn", "delspawn", "sethome", "home", "delhome", "homes", "restoreinv", "asu" })));
}
internal static TextCommandResult HandleBack(TextCommandCallingArgs args)
{
PlayerStorage ps = ServerUtilities.GetPlayerData(args.Caller.Player as IServerPlayer);
if (ps.ActiveCooldowns.ContainsKey(CooldownType.Back))
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:cmd-cooldown", "/back", TimeUtil.EncodeTimeNotation(ps.ActiveCooldowns.Get(CooldownType.Back) - TimeUtil.GetUnixEpochTimestamp())));
}
PlayerPosition pos = ServerUtilities.backCaches.ReadAndPopNewestPosition(args.Caller.Player.PlayerName);
if (pos == null)
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:back-no"));
}
else
{
// Go back to old position
pos.Merge(args.Caller.Player.Entity);
ps.ActiveCooldowns.Add(CooldownType.Back, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.Back)) + TimeUtil.GetUnixEpochTimestamp());
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:back"));
}
} }
internal static TextCommandResult HandleClearSpawn(TextCommandCallingArgs args) internal static TextCommandResult HandleClearSpawn(TextCommandCallingArgs args)
@ -52,35 +23,6 @@ namespace AriasServerUtils
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:rmspawn")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:rmspawn"));
} }
internal static TextCommandResult HandleRTP(TextCommandCallingArgs args)
{
if (args.Caller.Player is IServerPlayer isp)
{
int maxDistance = ServerUtilities.config.MaxRTPBlockDistance;
if (args[0] is int ix)
{
if (ix == -1) ix = maxDistance;
if (ix > maxDistance && !(ServerUtilities.config.AdminsBypassRTPMaxDistance && isp.HasPrivilege(Privilege.controlserver)))
{
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-capped", ix, maxDistance));
}
else maxDistance = ix;
}
PlayerStorage ps = ServerUtilities.GetPlayerData(isp);
if (ps.ActiveCooldowns.ContainsKey(CooldownType.RTP))
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:cmd-cooldown", "/rtp", TimeUtil.EncodeTimeNotation(ps.ActiveCooldowns.Get(CooldownType.RTP) - TimeUtil.GetUnixEpochTimestamp())));
}
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:rtp-search"));
RTPFactory.TryRTP(isp, maxDistance: maxDistance);
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleDelHome(TextCommandCallingArgs args) internal static TextCommandResult HandleDelHome(TextCommandCallingArgs args)
{ {
string homeName = "default"; string homeName = "default";
@ -119,18 +61,9 @@ namespace AriasServerUtils
if (args.Caller.Player is IServerPlayer isp) if (args.Caller.Player is IServerPlayer isp)
{ {
PlayerStorage data = ServerUtilities.GetPlayerData(isp); PlayerStorage data = ServerUtilities.GetPlayerData(isp);
if (data.ActiveCooldowns.ContainsKey(CooldownType.Home))
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:cmd-cooldown", "/home", TimeUtil.EncodeTimeNotation(data.ActiveCooldowns.Get(CooldownType.Home) - TimeUtil.GetUnixEpochTimestamp())));
}
if (data.Homes.ContainsKey(homeName)) if (data.Homes.ContainsKey(homeName))
{ {
Home home = data.Homes[homeName]; data.Homes[homeName].Location.Merge(isp.Entity);
ServerUtilities.NewBackCacheForPlayer(isp);
home.Location.Merge(isp.Entity, unmount: !home.CanHaveMount);
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-tp")); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-tp"));
} }
@ -138,11 +71,6 @@ namespace AriasServerUtils
{ {
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-no")); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-no"));
} }
data.ActiveCooldowns.Add(CooldownType.Home, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.Home)) + TimeUtil.GetUnixEpochTimestamp());
ServerUtilities.MarkDirty();
} }
return TextCommandResult.Success(); return TextCommandResult.Success();
@ -202,20 +130,14 @@ namespace AriasServerUtils
if (args.Caller.Player is IServerPlayer isp) if (args.Caller.Player is IServerPlayer isp)
{ {
bool withMount = false;
if (args.Command.Name == "sethomemount")
{
// Check for the gears and pay here, or show error.
withMount = true;
}
bool bypass = isOp && ServerUtilities.config.AdminsBypassMaxHomes; bool bypass = isOp && ServerUtilities.config.AdminsBypassMaxHomes;
var data = ServerUtilities.GetPlayerData(isp); var data = ServerUtilities.GetPlayerData(isp);
if (bypass || data.Homes.Count < ServerUtilities.config.MaxHomes || data.Homes.ContainsKey(homeName)) if (bypass || data.Homes.Count < ServerUtilities.config.MaxHomes || data.Homes.ContainsKey(homeName))
{ {
data.Homes[homeName] = Home.MakeHome(args.Caller.Player.Entity, homeName, withMount: withMount); data.Homes[homeName] = Home.MakeHome(args.Caller.Player.Entity, homeName);
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-set")); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-set"));
} }
else else
{ {
@ -254,23 +176,9 @@ namespace AriasServerUtils
{ {
if (args.Caller.Player is IServerPlayer isp) if (args.Caller.Player is IServerPlayer isp)
{ {
PlayerStorage data = ServerUtilities.GetPlayerData(isp);
if (data.ActiveCooldowns.ContainsKey(CooldownType.Spawn))
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:cmd-cooldown", "/spawn", TimeUtil.EncodeTimeNotation(data.ActiveCooldowns.Get(CooldownType.Spawn) - TimeUtil.GetUnixEpochTimestamp())));
}
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:tp-spawn")); ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:tp-spawn"));
ServerUtilities.NewBackCacheForPlayer(isp);
ServerUtilities.config.Spawn.Merge(args.Caller.Player.Entity); ServerUtilities.config.Spawn.Merge(args.Caller.Player.Entity);
data.ActiveCooldowns.Add(CooldownType.Spawn, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.Spawn)) + TimeUtil.GetUnixEpochTimestamp());
ServerUtilities.MarkDirty();
} }
} }
@ -280,30 +188,17 @@ namespace AriasServerUtils
internal static TextCommandResult HandleUpdateASUBypass(TextCommandCallingArgs args) internal static TextCommandResult HandleUpdateASUBypass(TextCommandCallingArgs args)
{ {
if (args[0] is bool bypass) if (args[1] is bool bypass)
{ {
ServerUtilities.config.AdminsBypassMaxHomes = bypass; ServerUtilities.config.AdminsBypassMaxHomes = bypass;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass)); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
} }
return TextCommandResult.Success(); return TextCommandResult.Success();
} }
internal static TextCommandResult HandleUpdateASUMaxBack(TextCommandCallingArgs args)
{
if (args[0] is int max)
{
ServerUtilities.config.MaxBackCache = max;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", max));
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleUpdateASUMaxHomes(TextCommandCallingArgs args) internal static TextCommandResult HandleUpdateASUMaxHomes(TextCommandCallingArgs args)
{ {
if (args[0] is int maxHomes) if (args[0] is int maxHomes)
@ -311,382 +206,10 @@ namespace AriasServerUtils
ServerUtilities.config.MaxHomes = maxHomes; ServerUtilities.config.MaxHomes = maxHomes;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", maxHomes)); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
} }
return TextCommandResult.Success(); return TextCommandResult.Success();
} }
internal static TextCommandResult HandleUpdateASUMgrWarps(TextCommandCallingArgs args)
{
if (args[0] is bool mgr)
{
ServerUtilities.config.onlyAdminsCreateWarps = mgr;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", mgr));
}
else ServerUtilities.config.onlyAdminsCreateWarps = true;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", true));
}
internal static TextCommandResult HandleUpdateASUPSP(TextCommandCallingArgs args)
{
if (args[0] is int psp)
{
ServerUtilities.config.PlayerSleepingPercentage = psp;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", psp));
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleWarp(TextCommandCallingArgs args)
{
string name = "default";
if (args.ArgCount > 0) name = args[0] as string ?? "default";
if (args.Caller.Player is IServerPlayer isp)
{
PlayerStorage data = ServerUtilities.GetPlayerData(isp);
if (data.ActiveCooldowns.ContainsKey(CooldownType.Warp))
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:cmd-cooldown", "/warp", TimeUtil.EncodeTimeNotation(data.ActiveCooldowns.Get(CooldownType.Warp) - TimeUtil.GetUnixEpochTimestamp())));
}
if (ServerUtilities.serverWarps.warps.ContainsKey(name))
{
Warp warp = ServerUtilities.serverWarps.warps[name];
ServerUtilities.NewBackCacheForPlayer(isp);
warp.Location.Merge(isp.Entity, unmount: !warp.CanHaveMount);
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-tp", name));
data.ActiveCooldowns.Add(CooldownType.Warp, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.Warp)) + TimeUtil.GetUnixEpochTimestamp());
ServerUtilities.MarkDirty();
}
else
{
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-fail", name));
}
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleWarpDelete(TextCommandCallingArgs args)
{
string name = "default";
if (args.ArgCount > 0) name = args[0] as string ?? "default";
if (args.Caller.Player is IServerPlayer isp)
{
if (isp.HasPrivilege(Privilege.controlserver) || !ServerUtilities.config.onlyAdminsCreateWarps)
{
ServerUtilities.serverWarps.warps.Remove(name);
ServerUtilities.MarkDirty();
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-del"));
}
else
{
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-no"));
}
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleWarpList(TextCommandCallingArgs args)
{
List<string> warps = new List<string>();
foreach (string id in ServerUtilities.serverWarps.warps.Keys)
{
warps.Add(id);
}
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:warp-list", warps.Count, string.Join(", ", warps)));
}
internal static TextCommandResult HandleWarpUpdate(TextCommandCallingArgs args)
{
string name = "default";
if (args.ArgCount > 0) name = args[0] as string ?? "default";
if (args.Caller.Player is IServerPlayer isp)
{
if (isp.HasPrivilege(Privilege.controlserver) || !ServerUtilities.config.onlyAdminsCreateWarps)
{
ServerUtilities.serverWarps.warps[name] = Warp.Create(isp);
ServerUtilities.MarkDirty();
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-set", name));
}
else
{
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-no"));
}
}
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", maxDist));
}
return TextCommandResult.Success();
}
internal static TextCommandResult HandleUpdateASUCDBack(TextCommandCallingArgs args)
{
if (args[0] is string CD)
{
ServerUtilities.config.Cooldowns[CooldownType.Back] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
ServerUtilities.config.Cooldowns[CooldownType.Back] = "5s";
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
}
internal static TextCommandResult HandleUpdateASUCDWarp(TextCommandCallingArgs args)
{
if (args[0] is string CD)
{
ServerUtilities.config.Cooldowns[CooldownType.Warp] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
ServerUtilities.config.Cooldowns[CooldownType.Warp] = "10s";
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
}
internal static TextCommandResult HandleUpdateASUCDHome(TextCommandCallingArgs args)
{
if (args[0] is string CD)
{
ServerUtilities.config.Cooldowns[CooldownType.Home] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
ServerUtilities.config.Cooldowns[CooldownType.Home] = "5s";
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
}
internal static TextCommandResult HandleUpdateASUCDSpawn(TextCommandCallingArgs args)
{
if (args[0] is string CD)
{
ServerUtilities.config.Cooldowns[CooldownType.Spawn] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
ServerUtilities.config.Cooldowns[CooldownType.Spawn] = "5s";
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
}
internal static TextCommandResult HandleUpdateASUCDRTP(TextCommandCallingArgs args)
{
if (args[0] is string CD)
{
ServerUtilities.config.Cooldowns[CooldownType.RTP] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
ServerUtilities.config.Cooldowns[CooldownType.RTP] = "30s";
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
}
internal static TextCommandResult HandleUpdateASUCDReset(TextCommandCallingArgs args)
{
ServerUtilities.config.Cooldowns = ServerUtilities.config.GetDefaultCooldowns();
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:config-value-reset"));
}
internal static TextCommandResult HandleUpdateASUBypassCD(TextCommandCallingArgs args)
{
if (args[0] is bool bypass)
{
// Update the bypass
ServerUtilities.config.AdminsBypassCooldowns = bypass;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
}
else return TextCommandResult.Success();
}
internal static TextCommandResult HandleUpdateASUBypassRTPMaxDist(TextCommandCallingArgs args)
{
if (args[0] is bool bypass)
{
// Update the flag
ServerUtilities.config.AdminsBypassRTPMaxDistance = bypass;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
}
else return TextCommandResult.Success();
}
internal static TextCommandResult HandleUpdateASUFarmlandDowngrade(TextCommandCallingArgs args)
{
if (args[0] is bool downgrade)
{
// Update the flag
ServerUtilities.config.EnableFarmlandDowngrade = downgrade;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", downgrade));
}
else
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:farmland-downgrade", ServerUtilities.config.EnableFarmlandDowngrade));
}
}
internal static TextCommandResult HandleUpdateASUFarmlandDrop(TextCommandCallingArgs args)
{
if (args[0] is bool drop)
{
// Update the flag
ServerUtilities.config.EnableFarmlandDrop = drop;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", drop));
}
else
{
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:farmland-drop", ServerUtilities.config.EnableFarmlandDrop));
}
}
internal static TextCommandResult HandleListCooldowns(TextCommandCallingArgs args)
{
string sReturn = "SERVER COOLDOWN SETTINGS\n";
foreach (var cd in ServerUtilities.config.Cooldowns)
{
sReturn += $"{cd.Key}: {cd.Value}\n";
}
if (args.Caller.Player is IServerPlayer isp)
{
sReturn += "\nYour active cooldowns:";
foreach (var cd in ServerUtilities.GetPlayerData(isp).ActiveCooldowns)
{
long remain = cd.Value - TimeUtil.GetUnixEpochTimestamp();
string sCDVal = TimeUtil.EncodeTimeNotation(remain);
sReturn += $"{cd.Key}: {sCDVal}\n";
}
}
return TextCommandResult.Success(sReturn);
}
internal static TextCommandResult HandleSleepyDebug(TextCommandCallingArgs args)
{
EntityBehaviorTiredness sleepy = args.Caller.Entity.GetBehavior<EntityBehaviorTiredness>();
if (sleepy != null)
{
sleepy.Tiredness = 100;
}
return TextCommandResult.Success();
}
internal static void CheckBreakFarmland(IServerPlayer byPlayer, BlockSelection blockSel, ref float dropQuantityMultiplier, ref EnumHandling handling)
{
if (!ServerUtilities.config.EnableFarmlandDrop)
{
return; // Default behavior
}
if (blockSel.Block is BlockFarmland farmland)
{
BlockEntityFarmland beFarmland = farmland.GetBlockEntity<BlockEntityFarmland>(blockSel.Position);
string farmlandType = blockSel.Block.LastCodePart();
if (ServerUtilities.config.EnableFarmlandDowngrade)
{
switch (farmlandType)
{
case "verylow":
{ // barren
break; // Can't downgrade further
}
case "low":
{
farmlandType = "verylow";
break;
}
case "medium":
{
farmlandType = "low";
break;
}
case "compost":
{ // high
farmlandType = "medium";
break;
}
case "high":
{ // Terra preta
farmlandType = "compost";
break;
}
}
}
byPlayer.Entity.World.SpawnItemEntity(new ItemStack(byPlayer.Entity.World.GetBlock(new AssetLocation($"soil-{farmlandType}-none"))), blockSel.Position.ToVec3d().Add(0.5, 0.5, 0.5));
}
}
} }
} }

View file

@ -1,234 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Runtime.ConstrainedExecution;
using Vintagestory.API.Common;
using Vintagestory.API.Server;
namespace AriasServerUtils
{
public enum ModConfigType
{
Global,
World
}
[Serializable]
public class ASUModConfig
{
private readonly static Dictionary<CooldownType, string> m_defaultCD = new Dictionary<CooldownType, string>{
{ CooldownType.Home, "5s" },
{ CooldownType.Warp, "10s" },
{ CooldownType.Spawn, "5s" },
{ CooldownType.RTP, "30s" },
{ CooldownType.Back, "5s" }
};
private static readonly int CURRENT_VERSION = 7;
public int Version { get; set; } = 0;
public int MaxHomes { get; set; } = 20;
public bool AdminsBypassMaxHomes { get; set; } = true;
public bool onlyAdminsCreateWarps { get; set; } = true;
public bool AdminsBypassCooldowns { get; set; } = true;
public bool AdminsBypassRTPMaxDistance { get; set; } = false;
public int MaxBackCache { get; set; } = 10;
public int PlayerSleepingPercentage { get; set; } = 50;
public int MaxRTPBlockDistance { get; set; } = 50000;
public Dictionary<CooldownType, string> Cooldowns { get; set; } = new Dictionary<CooldownType, string>();
public Costs costs { get; set; } = new Costs();
/// <summary>
/// If true, attempts to downgrade the soil quality when breaking farmland.
/// </summary>
public bool EnableFarmlandDowngrade { get; set; } = false;
/// <summary>
/// If true, farmland will drop as soil when broken.
/// </summary>
public bool EnableFarmlandDrop { get; set; } = true;
public Dictionary<CooldownType, string> GetDefaultCooldowns()
{
return m_defaultCD;
}
/// <summary>
/// Performs some checks against known possible invalid values and sets them to sane values.
/// </summary>
public void SanityCheck()
{
foreach (var cd in GetDefaultCooldowns())
{
if (!Cooldowns.ContainsKey(cd.Key))
{
Cooldowns.Add(cd.Key, cd.Value);
ServerUtilities.MarkDirty();
}
}
if (Version < CURRENT_VERSION)
{
Version = CURRENT_VERSION;
ServerUtilities.MarkDirty(); // This is here to ensure that the config gets saved when there is a update. Whenever a new field is added to config, the CURRENT_VERSION should get bumped so that this SanityCheck can properly work
}
// Sanity check costs config
bool checkCostDefaults = false;
if (costs == null)
{
costs = new Costs(); // Initialize a brand new costs object
checkCostDefaults = true;
}
else if (costs.costs.Count != Costs.defaults.Count) checkCostDefaults = true;
if (checkCostDefaults)
{
foreach (var def in Costs.defaults)
{
if (!costs.costs.ContainsKey(def.Key))
{
costs.costs.Add(def.Key, def.Value);
}
}
ServerUtilities.MarkDirty();
}
}
public PlayerPosition Spawn { get; set; }
}
[Serializable]
public enum CooldownType
{
Home, // Default: 5s
Warp, // Default 10s
Spawn, // Default 5s
RTP, // Default 30s
Back // Default 5s
}
[Serializable]
public class Warp
{
public PlayerPosition Location;
public string CreatedBy;
public DateTime CreatedAt;
public bool CanHaveMount = false;
public static Warp Create(IServerPlayer player, bool withMount = false)
{
Warp warp = new Warp();
warp.Location = PlayerPosition.from(player.Entity);
warp.CreatedBy = player.PlayerName;
warp.CreatedAt = DateTime.Now;
warp.CanHaveMount = withMount;
return warp;
}
}
[Serializable]
public class Warps
{
public Dictionary<string, Warp> warps = new Dictionary<string, Warp>();
}
/// <summary>
/// This contains per command costs, as well as helper functions that can refund a player, or check if the player has the required balance, or payment
///
/// </summary>
[Serializable]
public class Costs
{
public enum CostType
{
sethome,
home,
delhome,
sethomemount,
warp,
setwarp,
delwarp,
spawn
}
public Dictionary<CostType, int> costs = new Dictionary<CostType, int>();
public static readonly Dictionary<CostType, int> defaults = new Dictionary<CostType, int>()
{
{ CostType.sethome, 0 },
{ CostType.home, 0 },
{ CostType.delhome, 0 },
{ CostType.sethomemount, 10 },
{ CostType.setwarp, 5 },
{ CostType.warp, 1 },
{ CostType.delwarp, 5 },
{ CostType.spawn, 0 }
};
/// <summary>
/// Checks if the player has the balance required to use the command
/// </summary>
/// <param name="cmd">The command the player is trying to use</param>
/// <param name="player">The player</param>
/// <returns>True if the player has the required balance</returns>
public bool PlayerHasBalance(CostType cmd, EntityPlayer player)
{
int gears = 0;
int required = 0;
if (costs.ContainsKey(cmd))
{
// Get the cost for that command
required = costs[cmd];
}
// Scan the player inventory, check gears
if (gears >= required) return true;
return false;
}
}
public class BackCaches
{
private const int MaxCacheSize = 10;
private readonly Dictionary<string, LinkedList<PlayerPosition>> playerCache = new();
public void AddPosition(string playerName, PlayerPosition position)
{
if (!playerCache.TryGetValue(playerName, out var positions))
{
positions = new LinkedList<PlayerPosition>();
playerCache[playerName] = positions;
}
if (positions.Count >= MaxCacheSize)
{
positions.RemoveFirst(); // Remove the oldest position
}
positions.AddLast(position.Clone()); // Add the new position
}
public PlayerPosition ReadAndPopNewestPosition(string playerName)
{
if (playerCache.TryGetValue(playerName, out var positions) && positions.Count > 0)
{
var newestPosition = positions.Last.Value;
positions.RemoveLast(); // Remove the newest position
return newestPosition;
}
return null; // Return null if no positions are available
}
}
}

View file

@ -0,0 +1,20 @@
using System;
namespace AriasServerUtils
{
public enum ModConfigType
{
Global,
World
}
[Serializable]
public class ASUModConfig
{
public int MaxHomes { get; set; } = 20;
public bool AdminsBypassMaxHomes { get; set; } = true;
public PlayerPosition Spawn { get; set; }
}
}

View file

@ -1,19 +0,0 @@
using System;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
public class ASUModClient : ModSystem
{
public static ICoreClientAPI CAPI;
bool accel = false;
public override bool ShouldLoad(EnumAppSide forSide)
{
return forSide == EnumAppSide.Client;
}
public override void StartClientSide(ICoreClientAPI api)
{
CAPI = api;
}
}

View file

@ -1,602 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
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.Datastructures;
using Vintagestory.API.MathTools;
using Vintagestory.API.Server;
using Vintagestory.API.Util;
using Vintagestory.GameContent;
namespace AriasServerUtils
{
public class ServerUtilities : ModSystem
{
public static string MOD_ID = "ariasserverutils";
public static ASUModConfig config = new ASUModConfig();
internal static ICoreServerAPI API;
private static bool bDirty = false;
internal static Dictionary<string, PlayerInventory> backupInventory = new Dictionary<string, PlayerInventory>();
internal static Dictionary<string, PlayerStorage> mPlayerData = new Dictionary<string, PlayerStorage>();
internal static BackCaches backCaches = new BackCaches();
internal static Warps serverWarps = new Warps();
internal static Random rng = new Random((int)TimeUtil.GetUnixEpochTimestamp());
internal bool isFirstStart = true;
List<EntityAgent> SleepingPlayers { get; set; } = new();
float OriginalSpeed { get; set; } = 0.0f;
public double Hours { get; private set; } = 0.0;
bool Sleeping { get; set; } = false;
public IServerNetworkChannel ServerNetworkChannel { get; private set; }
/// <summary>
/// Method to register all mod blocks
/// </summary>
/// <param name="api"></param>
private void RegisterBlocks(ICoreAPI api)
{
api.Logger.Notification("Begin registering block classes for Aria's Server Utils...");
api.Logger.Notification("Block Classes have been registered for Aria's Server Utils!");
}
private void RegisterBlockEntities(ICoreAPI api)
{
}
public override bool ShouldLoad(EnumAppSide side)
{
return side == EnumAppSide.Server;
}
// Called on server and client
public override void Start(ICoreAPI api)
{
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
RegisterBlocks(api);
RegisterBlockEntities(api);
}
public override void StartServerSide(ICoreServerAPI api)
{
API = api;
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
api.Event.ServerRunPhase(EnumServerRunPhase.GameReady, OnGameReady);
api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown);
api.Event.PlayerDeath += OnPlayerDeath;
api.Event.PlayerJoin += OnPlayerJoin;
api.Event.PlayerDisconnect += OnPlayerDC;
api.Event.ChunkColumnLoaded += RTPFactory.ChunkLoaded;
api.Event.BreakBlock += Events.CheckBreakFarmland;
//api.Event.PlayerLeave += OnPlayerDC;
api.ChatCommands.Create("setspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleSetSpawn).WithAlias("ss");
api.ChatCommands.Create("spawn").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSpawn).WithAlias("s");
api.ChatCommands.Create("delspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleClearSpawn).WithAlias("ds");
//api.ChatCommands.Create("test_death").RequiresPlayer().RequiresPrivilege(Privilege.controlserver).HandleWith(TestDeath);
var parsers = api.ChatCommands.Parsers;
api.ChatCommands.Create("restoreinv").RequiresPlayer().WithArgs(parsers.OnlinePlayer("player")).HandleWith(Events.HandleReturnItems).WithDescription("Returns items to a player in the event of a problem").RequiresPrivilege(Privilege.controlserver);
api.ChatCommands.Create("sethome").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Creates a home").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSetHome).WithAlias("sh");
api.ChatCommands.Create("home").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Teleports you to home").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleGoHome).WithAlias("h");
api.ChatCommands.Create("delhome").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Deletes a home entry").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleDelHome).WithAlias("dh");
api.ChatCommands.Create("homes").RequiresPlayer().WithDescription("Lists your homes").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleListHomes).WithAlias("lh");
api.ChatCommands.Create("sethomemount").RequiresPlayer().WithArgs(parsers.OptionalWord("name")).WithDescription("Create a home with the ability to take a mount with you. Costs [undefined] gears.").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSetHome).WithAlias("shm", "shb");
api.ChatCommands.Create("asu")
.RequiresPrivilege(Privilege.chat)
.BeginSubCommand("update")
.BeginSubCommand("maxhomes")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Int("maxHomes")
)
.WithDescription("Updates the maximum number of homes")
.HandleWith(Events.HandleUpdateASUMaxHomes)
.EndSubCommand()
.BeginSubCommand("adminhomes")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Bool("adminsBypass")
)
.WithDescription("Updates the flag deciding whether admins can bypass max number of homes")
.HandleWith(Events.HandleUpdateASUBypass)
.EndSubCommand()
.WithDescription("Updates the ASU mod configuration")
.BeginSubCommand("onlyAdminManageWarps")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Bool("manageWarps")
)
.WithDescription("DANGER: This updates the flag allowing anybody to create warps, even non-admins. It is recommended to leave this alone. The default is true/on/yes")
.HandleWith(Events.HandleUpdateASUMgrWarps)
.EndSubCommand()
.BeginSubCommand("adminBypassCooldown")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Bool("bypass")
)
.WithDescription("This flag controls whether admins can or can not bypass the cooldown system (Default: true)")
.HandleWith(Events.HandleUpdateASUBypassCD)
.EndSubCommand()
.BeginSubCommand("adminsBypassRTPMaxDist")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Bool("bypass")
)
.WithDescription("This flag would allow admins to go further than the max server RTP distance when manually specifying a distance to RTP (Default: false)")
.HandleWith(Events.HandleUpdateASUBypassRTPMaxDist)
.EndSubCommand()
.BeginSubCommand("maxback")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.OptionalInt("max")
)
.WithDescription("Max number of back positions cached for players")
.HandleWith(Events.HandleUpdateASUMaxBack)
.EndSubCommand()
.BeginSubCommand("playerSleepingPercentage")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.OptionalIntRange("psp", 1, 100, 50)
)
.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 (Default is 50000)")
.HandleWith(Events.HandleUpdateASURTPMax)
.EndSubCommand()
.BeginSubCommand("farmlandDowngrade")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(parsers.OptionalBool("downgrade"))
.WithDescription("Enables or disables farmland downgrade when breaking farmland")
.HandleWith(Events.HandleUpdateASUFarmlandDowngrade)
.EndSubCommand()
.BeginSubCommand("farmlandDrop")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(parsers.OptionalBool("drop"))
.WithDescription("Enables or disables dropping soil when breaking farmland")
.HandleWith(Events.HandleUpdateASUFarmlandDrop)
.EndSubCommand()
.BeginSubCommand("cooldowns")
.WithDescription("Commands related to all the various cooldowns")
.BeginSubCommand("back")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Word("cooldown")
)
.WithDescription("Updates the cooldown time on /back (Default is 5s)")
.HandleWith(Events.HandleUpdateASUCDBack)
.EndSubCommand()
.BeginSubCommand("warp")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Word("cooldown")
)
.WithDescription("Updates the cooldown time on /warp (Default is 10s)")
.HandleWith(Events.HandleUpdateASUCDWarp)
.EndSubCommand()
.BeginSubCommand("home")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Word("cooldown")
)
.WithDescription("Updates the cooldown time on /home (Default is 5s)")
.HandleWith(Events.HandleUpdateASUCDHome)
.EndSubCommand()
.BeginSubCommand("spawn")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Word("cooldown")
)
.WithDescription("Updates the cooldown time on /spawn (Default is 5s)")
.HandleWith(Events.HandleUpdateASUCDSpawn)
.EndSubCommand()
.BeginSubCommand("rtp")
.RequiresPrivilege(Privilege.controlserver)
.WithArgs(
parsers.Word("cooldown")
)
.WithDescription("Updates the cooldown time on /rtp (Default is 30s)")
.HandleWith(Events.HandleUpdateASUCDRTP)
.EndSubCommand()
.BeginSubCommand("reset")
.RequiresPrivilege(Privilege.controlserver)
.WithDescription("Resets all cooldowns to default values")
.HandleWith(Events.HandleUpdateASUCDReset)
.EndSubCommand()
.EndSubCommand()
.EndSubCommand()
.BeginSubCommand("help")
.RequiresPrivilege(Privilege.chat)
.HandleWith(Events.HandleASU)
.WithDescription("Lists all Aria's Server Utils commands")
.EndSubCommand()
.BeginSubCommand("test")
.RequiresPlayer()
.RequiresPrivilege(Privilege.controlserver)
.BeginSubCommand("sleep")
.RequiresPlayer()
.RequiresPrivilege(Privilege.controlserver)
.HandleWith(TestSleep)
.EndSubCommand()
.BeginSubCommand("sleepy")
.RequiresPlayer()
.RequiresPrivilege(Privilege.controlserver)
.HandleWith(Events.HandleSleepyDebug)
.EndSubCommand()
.EndSubCommand();
api.ChatCommands.Create("setwarp").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Creates a new server warp").WithArgs(parsers.OptionalWord("name")).HandleWith(Events.HandleWarpUpdate).WithAlias("sw");
api.ChatCommands.Create("warp").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Warp to the specified server warp").WithArgs(parsers.OptionalWord("name")).HandleWith(Events.HandleWarp).WithAlias("w");
api.ChatCommands.Create("delwarp").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Deletes the specified warp").WithArgs(parsers.OptionalWord("name")).HandleWith(Events.HandleWarpDelete).WithAlias("dw");
api.ChatCommands.Create("warps").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Lists all server warps").HandleWith(Events.HandleWarpList).WithAlias("lw");
api.ChatCommands.Create("back").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Returns you to the last location you were at").HandleWith(Events.HandleBack).WithAlias("b");
api.ChatCommands.Create("rtp").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithArgs(parsers.OptionalInt("maxDist", defaultValue: -1)).WithDescription("Seeks a position possibly thousands of blocks away to teleport to.").HandleWith(Events.HandleRTP);
api.ChatCommands.Create("listcooldowns").RequiresPrivilege(Privilege.chat).WithDescription("Lists the cooldown settings on the server, as well as your own active cooldowns if applicable.").HandleWith(Events.HandleListCooldowns).WithAlias("lc");
}
private TextCommandResult TestSleep(TextCommandCallingArgs args)
{
// Initiate the sleep process
// Basically run all the same commands as we would on a player in bed
Events.HandleSleepyDebug(args);
OriginalSpeed = API.World.Calendar.CalendarSpeedMul;
if (args.Caller.Player is IServerPlayer isp)
{
Hours = API.World.Calendar.TotalHours;
SleepingPlayers.Add(isp.Entity);
API.Logger.Notification($"Game Hours: {API.World.Calendar.TotalHours}, Difference: {API.World.Calendar.TotalHours - Hours}");
EntityAgent Agent = isp.Entity;
EntityBehaviorTiredness ebt = Agent.GetBehavior<EntityBehaviorTiredness>();
ebt.IsSleeping = true;
ebt.Tiredness = 100;
Sleeping = true;
API.World.Calendar.SetTimeSpeedModifier("asu_psp", 1000);
}
return TextCommandResult.Success($"Test initiated, original calendar multiplier: '{OriginalSpeed}'");
}
private TextCommandResult TestCalendarSpeed(TextCommandCallingArgs args)
{
if (args.Caller.Player is IServerPlayer isp)
{
EntityAgent agent = isp.Entity;
EntityBehaviorTiredness ebt = agent.GetBehavior("tiredness") as EntityBehaviorTiredness;
SendMessageTo(isp, $"- Current calendar speed: {API.World.Calendar.CalendarSpeedMul}");
SendMessageTo(isp, $"- Total Hours: {API.World.Calendar.TotalHours}");
SendMessageTo(isp, $"- Tiredness: {ebt.Tiredness}");
if (OriginalSpeed == 0)
{
// Apply multiplier
OriginalSpeed = 0.5f;
ebt.IsSleeping = true;
API.World.Calendar.SetTimeSpeedModifier("asu_psp", 1000);
SendMessageTo(isp, "Applied calendar speed multiplier");
}
else
{
// Unapply multiplier
OriginalSpeed = 0;
ebt.IsSleeping = false;
API.World.Calendar.RemoveTimeSpeedModifier("asu_psp");
SendMessageTo(isp, "Restored default calendar speed");
}
}
return TextCommandResult.Success();
}
private void OnCheckSleepingPlayers()
{
if (API.Side == EnumAppSide.Client) return; // This must only ever be called on the server!
if (isFirstStart)
{
API.World.Calendar.RemoveTimeSpeedModifier("asu_psp");
isFirstStart = false;
}
if (Sleeping)
{
//API.Logger.Notification($"Game Hours: {API.World.Calendar.TotalHours}, Difference: {API.World.Calendar.TotalHours - Hours}");
if (API.World.Calendar.TotalHours - Hours >= 6)
{
Sleeping = false;
foreach (var player in SleepingPlayers)
{
EntityBehaviorTiredness ebt = player.GetBehavior<EntityBehaviorTiredness>();
ebt.IsSleeping = false;
ebt.Tiredness = 0;
}
SleepingPlayers.Clear();
API.World.Calendar.RemoveTimeSpeedModifier("asu_psp");
//API.Logger.Notification("Stopping PSP Time Acceleration");
}
return;
}
if (config.PlayerSleepingPercentage == 100) return; // Normal behavior
// Iterate over all players, get their entity, check if mounted on a bed.
// If mounted on a bed, check tiredness
int TotalOnline = API.World.AllOnlinePlayers.Length;
if (TotalOnline == 0) return; // No one on, just abort the checks.
int TotalInBed = 0;
List<BlockEntityBed> BEBs = new();
foreach (var player in API.World.AllOnlinePlayers)
{
EntityAgent ePlay = player.Entity;
if (ePlay.MountedOn is BlockEntityBed beb)
{
BEBs.Add(beb);
TotalInBed++;
//API.Logger.Notification($"Bed found for player {player.PlayerName}");
}
if (ePlay.MountedOn == null)
{
//API.Logger.Notification($"No bed found for player {player.PlayerName}");
if (SleepingPlayers.Contains(ePlay))
{
EntityBehaviorTiredness ebt = ePlay.GetBehavior<EntityBehaviorTiredness>();
if (ebt != null)
ebt.IsSleeping = false;
}
}
}
if (Sleeping) return; // Abort
SleepingPlayers.Clear();
int Percentage = TotalInBed * 100 / TotalOnline;
//API.Logger.Notification($"Percentage of players in bed: {Percentage}, Required percentage: {config.PlayerSleepingPercentage}");
if (Percentage >= config.PlayerSleepingPercentage)
{
API.World.Calendar.SetTimeSpeedModifier("asu_psp", 1000);
// Call the API to make sleep happen
foreach (var bed in BEBs)
{
if (bed.MountedBy != null) SleepingPlayers.Add(bed.MountedBy);
// Start sleep
EntityBehaviorTiredness EBT = bed.MountedBy.GetBehavior<EntityBehaviorTiredness>();
EBT.IsSleeping = true;
//API.Logger.Notification("Starting PSP Time Acceleration");
bed.MountedBy.TryUnmount(); // Stand up. We cant trigger the real sleep phase, but all code for starting time accel has been executed.
}
// Get current calendar speed
Hours = API.World.Calendar.TotalHours;
Sleeping = true;
}
}
private void OnCheckPlayerCooldowns()
{
foreach (var cdEntry in ServerUtilities.mPlayerData)
{
// Obtain the IServerPlayer instance for this player.
IServerPlayer player = API.Server.Players.First(x => x.PlayerName == cdEntry.Key);
if (player.HasPrivilege(Privilege.controlserver) && ServerUtilities.config.AdminsBypassCooldowns)
{
cdEntry.Value.ActiveCooldowns.Clear(); // Problem solved.
}
List<CooldownType> toRemove = new();
foreach (var cd in cdEntry.Value.ActiveCooldowns)
{
if (cd.Value < TimeUtil.GetUnixEpochTimestamp())
{
toRemove.Add(cd.Key);
}
}
foreach (var item in toRemove)
{
cdEntry.Value.ActiveCooldowns.Remove(item);
}
if (toRemove.Count > 0) MarkDirty();
}
}
public static void NewBackCacheForPlayer(IServerPlayer player)
{
backCaches.AddPosition(player.PlayerName, PlayerPosition.from(player.Entity));
}
private void OnPlayerDC(IServerPlayer byPlayer)
{
OnCheckModDirty();
mPlayerData.Remove(byPlayer.PlayerName);
}
private void OnPlayerJoin(IServerPlayer byPlayer)
{
API.Logger.Notification($"[ASU] {Lang.Get($"{MOD_ID}:playerjoin")}");
PlayerStorage data = API.LoadModConfig<PlayerStorage>(GetConfigurationFile(byPlayer.PlayerName, ModConfigType.World));
if (data == null) data = new PlayerStorage();
mPlayerData[byPlayer.PlayerName] = data;
}
public static PlayerStorage GetPlayerData(IServerPlayer player)
{
if (mPlayerData.ContainsKey(player.PlayerName))
{
return mPlayerData[player.PlayerName];
}
else
{
return new PlayerStorage();
}
}
private TextCommandResult TestDeath(TextCommandCallingArgs args)
{
if (args.Caller.Player is IServerPlayer isp) OnPlayerDeath(isp, null);
return TextCommandResult.Success();
}
private void OnPlayerDeath(IServerPlayer player, DamageSource damageSource)
{
PlayerInventory inv = RustyGearUtils.GetAllItems(player);
NewBackCacheForPlayer(player);
backupInventory[player.PlayerName] = inv;
}
private void OnCheckModDirty()
{
if (bDirty)
{
//API.Logger.Notification(Lang.Get($"{MOD_ID}:timer"));
bDirty = false;
SaveGlobalConfig();
SavePlayerData();
}
}
private void SavePlayerData()
{
foreach (var data in mPlayerData)
{
API.StoreModConfig<PlayerStorage>(data.Value, GetConfigurationFile(data.Key, ModConfigType.World));
}
}
private void OnShutdown()
{
// Mod Shutdown //
// Handle any remaining tasks before shutdown
API.Logger.Notification(Lang.Get($"{MOD_ID}:halt"));
OnCheckModDirty();
}
public void SaveGlobalConfig()
{
API.StoreModConfig<ASUModConfig>(config, GetConfigurationFile("global", ModConfigType.Global));
API.StoreModConfig<Warps>(serverWarps, GetConfigurationFile("warps", ModConfigType.Global));
}
private void OnGameReady()
{
// Mod Setup Info //
// -> Step 1. Load Mod Global Config <-
config = API.LoadModConfig<ASUModConfig>(GetConfigurationFile("global", ModConfigType.Global));
if (config == null) config = new ASUModConfig();
// Step 2. Check if config needs Migrations
config.SanityCheck();
// -> Step 3. Load Mod Warps <-
serverWarps = API.LoadModConfig<Warps>(GetConfigurationFile("warps", ModConfigType.Global));
if (serverWarps == null) serverWarps = new Warps();
API.Event.Timer(OnCheckModDirty, 20);
API.Event.Timer(OnCheckPlayerCooldowns, 1);
API.Event.Timer(OnCheckSleepingPlayers, 5);
API.Event.Timer(RTPFactory.HandleRTPChecking, 1);
}
public string GetConfigurationFile(string sName, ModConfigType type)
{
if (type == ModConfigType.Global)
{
return $"ariaserverconfig/{sName}.json";
}
else if (type == ModConfigType.World)
{
return $"ariaserverconfig/{GetWorldName()}/{sName}.json";
}
else return $"ariaserverconfig/global.json";
}
/// <summary>
/// This function is used to mark the mod's global config, and all loaded player configs as dirty. They will be flushed to disk, then the dirty flag will be cleared.
/// </summary>
public static void MarkDirty()
{
bDirty = true;
}
public string GetWorldName()
{
string[] lName = API.WorldManager.CurrentWorldName.Split(Path.DirectorySeparatorChar);
string sName = lName[lName.Length - 1];
return sName.Substring(0, sName.Length - 6);
}
public override void StartClientSide(ICoreClientAPI api)
{
api.Logger.Notification(Lang.Get($"{MOD_ID}:start"));
}
public static void SendMessageTo(IServerPlayer player, string sMsg)
{
player.SendMessage(0, sMsg, EnumChatType.CommandSuccess);
}
}
}

View file

@ -31,15 +31,8 @@ namespace AriasServerUtils
} }
public void Merge(Entity entity, bool unmount = true) public void Merge(Entity entity)
{ {
if (entity is EntityPlayer player)
{
if (unmount && player.MountedOn.Entity != null)
{
player.TryUnmount();
}
}
entity.TeleportTo(new BlockPos(X, Y, Z, Dimension)); entity.TeleportTo(new BlockPos(X, Y, Z, Dimension));
entity.Pos.SetYaw(Yaw); entity.Pos.SetYaw(Yaw);
entity.Pos.Pitch = Pitch; entity.Pos.Pitch = Pitch;
@ -85,7 +78,6 @@ namespace AriasServerUtils
public class PlayerStorage public class PlayerStorage
{ {
public Dictionary<string, Home> Homes = new Dictionary<string, Home>(); public Dictionary<string, Home> Homes = new Dictionary<string, Home>();
public Dictionary<CooldownType, long> ActiveCooldowns = new();
} }
/// <summary> /// <summary>
@ -95,13 +87,11 @@ namespace AriasServerUtils
public class Home public class Home
{ {
public PlayerPosition Location { get; set; } public PlayerPosition Location { get; set; }
public bool CanHaveMount = false;
public static Home MakeHome(Entity player, string homeName, bool withMount = false) public static Home MakeHome(Entity player, string homeName)
{ {
Home home = new Home(); Home home = new Home();
home.Location = PlayerPosition.from(player); home.Location = PlayerPosition.from(player);
home.CanHaveMount = withMount;
return home; return home;

View file

@ -1,286 +0,0 @@
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);
}
}

View file

@ -1,131 +0,0 @@
using System;
using AriasServerUtils;
using Vintagestory.API.Common;
using Vintagestory.API.Config;
using Vintagestory.API.Datastructures;
using Vintagestory.API.Server;
using Vintagestory.API.Util;
public static class RustyGearUtils
{
internal static string[] saveInvTypes = new string[] {
GlobalConstants.hotBarInvClassName,
GlobalConstants.backpackInvClassName,
GlobalConstants.craftingInvClassName,
GlobalConstants.mousecursorInvClassName,
GlobalConstants.characterInvClassName
};
// Replace with the correct code if it's different
private const string RustyGearCode = "currency-rustygear";
/// <summary>
/// Counts the total number of rusty gears in the player's inventory.
/// </summary>
public static int CountRustyGears(IServerPlayer player)
{
int total = 0;
player.Entity.WalkInventory((slot) =>
{
if (slot is ItemSlotCreative || !(slot.Inventory is InventoryBasePlayer)) return true;
total += CurrencyValuePerItem(slot) * slot.StackSize;
return true;
});
return total;
}
private static int CurrencyValuePerItem(ItemSlot slot)
{
JsonObject obj = slot.Itemstack?.Collectible?.Attributes?["currency"];
if (obj != null && obj.Exists)
{
JsonObject v = obj["value"];
return v.Exists ? v.AsInt(0) : 0;
}
return 0;
}
/// <summary>
/// Attempts to subtract a specific number of rusty gears from the player's inventory.
/// </summary>
/// <param name="player">The player.</param>
/// <param name="amount">How many gears to remove.</param>
/// <returns>True if the full amount was successfully removed, false if not enough gears.</returns>
public static bool SubtractRustyGears(IServerPlayer player, int amount)
{
// Check if the player has enough rusty gears
int currentAmount = CountRustyGears(player);
if (currentAmount < amount) return false;
// Subtract the specified amount of rusty gears from the player's inventory
player.Entity.WalkInventory((slot) =>
{
if (slot is ItemSlotCreative || !(slot.Inventory is InventoryBasePlayer)) return true;
int value = CurrencyValuePerItem(slot) * slot.StackSize;
if (value > 0 && slot.StackSize > 0)
{
// Calculate the amount of rusty gears to remove from this slot
int amountToRemove = Math.Min(value, amount);
// If the slot size is less than or equal to the amount to remove, set the slot's itemstack to null
if (slot.StackSize <= amountToRemove) slot.Itemstack = null;
else
{
// Otherwise, subtract the amount to remove from the slot size and decrement the total amount
slot.Itemstack.StackSize -= amountToRemove;
amount -= amountToRemove;
}
}
// If the total amount has been removed, return true
if (amount <= 0) return true; // we're done
return true;
});
// If the player's inventory still contains rusty gears, they don't have enough to remove
return false;
}
public static PlayerInventory GetAllItems(IServerPlayer player)
{
PlayerInventory inv = new PlayerInventory();
var invMgr = player.InventoryManager;
var iBackpackSlotNum = 0;
foreach (var type in saveInvTypes)
{
foreach (var stack in invMgr.GetOwnInventory(type))
{
if (iBackpackSlotNum >= 4)
{
continue;
}
if (type == GlobalConstants.backpackInvClassName)
{
iBackpackSlotNum++;
}
if (stack.Empty) continue;
if (stack.Inventory.ClassName == GlobalConstants.characterInvClassName)
{
if (stack.Itemstack.ItemAttributes?["protectionModifiers"].Exists ?? false)
{
inv.Items.Add(stack.Itemstack.Clone());
}
}
else
inv.Items.Add(stack.Itemstack.Clone());
}
}
return inv;
}
}

View file

@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace AriasServerUtils
{
public static class TimeUtil
{
private static readonly Dictionary<char, int> TimeUnits = new()
{
{ 's', 1 }, // seconds
{ 'm', 60 }, // minutes
{ 'h', 3600 }, // hours
{ 'd', 86400 }, // days
{ 'w', 604800 }, // weeks
{ 'M', 2592000 }, // months (approx. 30 days)
{ 'y', 31536000 } // years (365 days)
};
public static long DecodeTimeNotation(string input)
{
if (string.IsNullOrWhiteSpace(input)) return 0;
long totalSeconds = 0;
int number = 0;
foreach (char c in input)
{
if (char.IsDigit(c))
{
number = number * 10 + (c - '0');
}
else if (TimeUnits.TryGetValue(c, out int unitSeconds))
{
totalSeconds += number * unitSeconds;
number = 0;
}
else
{
throw new FormatException($"Invalid time unit: {c}");
}
}
return totalSeconds;
}
public static string EncodeTimeNotation(long seconds)
{
if (seconds <= 0) return "0s";
var result = new List<string>();
foreach (var (unit, unitSeconds) in TimeUnits.OrderByDescending(u => u.Value))
{
long value = seconds / unitSeconds;
if (value > 0)
{
result.Add($"{value}{unit}");
seconds %= unitSeconds;
}
}
return string.Join("", result);
}
public static long GetUnixEpochTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
}
}

View file

@ -18,33 +18,7 @@
"home-del": "Home deleted", "home-del": "Home deleted",
"home-list": "You have [{0}] home(s)\n\n{1}", "home-list": "You have [{0}] home(s)\n\n{1}",
"help": "All Aria's Server Utilities Commands: \n\nMax Homes: {0}; \nAdmins can bypass max homes: {1}\nMax back positions: {2}\n\n{3}", "help": "All Aria's Server Utilities Commands: \n\nMax Homes: {0}; Admins can bypass max homes: {1}\n{2}",
"updatedconfig": "[ASU] server config updated with the new value: {0}", "updatedconfig": "[ASU] server config updated"
"config-value-reset": "[ASU] server config value reset to default",
"farmland-downgrade": "The current farmland downgrade setting is {0}",
"farmland-drop": "The current farmland drop setting is {0}",
"warp-tp": "Teleported to warp [{0}]",
"warp-set": "Warp [{0}] created!",
"warp-no": "You lack permissions to manage a warp",
"warp-fail": "Warp [{0}] does not exist",
"warp-del": "Warp [{0}] deleted",
"warp-list": "There are [{0}] total warps\n\n{1}",
"back-no": "There's no position to go back to",
"back": "You've been taken back to your last position",
"rtp-search": "Searching for a random position...",
"rtp-progress": "Still searching for a random position... [{0}] tries remaining...",
"rtp": "You have been teleported [{0}] blocks away!",
"rtp-found": "Found a valid landing position after {0} tries.",
"rtp-fail": "Giving up on RTP search. No valid position could be found. Try again later",
"rtp-capped": "The distance you tried to go [{0}] is greater than the maximum allowable by the server [{1}]",
"cmd-cooldown": "[{0}] is currently on cooldown. You can use this command again in [{1}]",
"psp": "[ASU] PSP Starting... you do not need to stay in bed",
"psp-ending": "[ASU] PSP Complete"
} }

View file

@ -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: 06-07-2025 @ 1:18 AM MST", "description": "A collection of server utilities\n\nBuild Date: 01-18-2025 @ 03:18 PM",
"version": "1.1.0-dev.4", "version": "1.0.1",
"dependencies": { "dependencies": {
"game": "" "game": ""
} }