generated from AriasCreations/vsmodtemplate
Compare commits
61 commits
Author | SHA1 | Date | |
---|---|---|---|
a88807045e | |||
ac8ac62d27 | |||
bd12021575 | |||
45b024654a | |||
419c05dbac | |||
9715975a48 | |||
7fb2d38c3d | |||
b00e56fdf7 | |||
12d10a9a3c | |||
e56a123cb8 | |||
4c585f647e | |||
7ca713e42a | |||
268834b434 | |||
11f52de3f7 | |||
37a1c8d361 | |||
3ab3dc099f | |||
d2b92f95c5 | |||
720dafea87 | |||
9a8b9d1545 | |||
c609dde0ee | |||
caf12dfade | |||
bda8e49ee1 | |||
cf7084c164 | |||
c69e1de64b | |||
5f99672a9b | |||
3712399a03 | |||
6598eb44da | |||
74d85c57e1 | |||
aad44f4c45 | |||
40f8eb4048 | |||
6e0dbb361a | |||
89e4835216 | |||
70fe822b0a | |||
2f1ac319de | |||
1b6586c9e8 | |||
29cf4e21b7 | |||
c5c605d057 | |||
700de94ffe | |||
cd00e620c3 | |||
f86030dc61 | |||
67df350a04 | |||
a3eee94f7e | |||
2efa532059 | |||
9f7edc64c3 | |||
a9150cf322 | |||
54e8d8a4d5 | |||
65ec65bfc9 | |||
85f728babf | |||
107f502736 | |||
e53f26717f | |||
7120b4083a | |||
403f18c020 | |||
51a71fee24 | |||
86ff08b9e7 | |||
aedef3317c | |||
740801bd96 | |||
1f5458b8b1 | |||
3e0896f5f9 | |||
1f43ffaa6c | |||
737c51989b | |||
e776a6bb9e |
15 changed files with 1833 additions and 318 deletions
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
|
@ -8,12 +8,12 @@
|
||||||
"name": "Launch Client (Debug)",
|
"name": "Launch Client (Debug)",
|
||||||
"type": "coreclr",
|
"type": "coreclr",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${env:VINTAGE_STORY}/Vintagestory.exe",
|
"program": ".game/Vintagestory.exe",
|
||||||
"linux": {
|
"linux": {
|
||||||
"program": "${env:VINTAGE_STORY}/Vintagestory"
|
"program": ".game/Vintagestory"
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"program": "${env:VINTAGE_STORY}/Vintagestory"
|
"program": ".game/Vintagestory"
|
||||||
},
|
},
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
// "--openWorld" , "modding test world",
|
// "--openWorld" , "modding test world",
|
||||||
"--tracelog",
|
"--tracelog",
|
||||||
"--addModPath",
|
"--addModPath",
|
||||||
"${workspaceFolder}/ModTemplate/bin/Debug/Mods"
|
"${workspaceFolder}/AriasServerUtils/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": "${env:VINTAGE_STORY}/VintagestoryServer.exe",
|
"program": ".game/VintagestoryServer.exe",
|
||||||
"linux": {
|
"linux": {
|
||||||
"program": "${env:VINTAGE_STORY}/VintagestoryServer"
|
"program": ".game/VintagestoryServer"
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"program": "${env:VINTAGE_STORY}/VintagestoryServer"
|
"program": ".game/VintagestoryServer"
|
||||||
},
|
},
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
"args": [
|
"args": [
|
||||||
"--tracelog",
|
"--tracelog",
|
||||||
"--addModPath",
|
"--addModPath",
|
||||||
"${workspaceFolder}/ModTemplate/bin/Debug/Mods"
|
"${workspaceFolder}/AriasServerUtils/bin/Debug/Mods"
|
||||||
],
|
],
|
||||||
"console": "internalConsole",
|
"console": "internalConsole",
|
||||||
"stopAtEntry": false
|
"stopAtEntry": false
|
||||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"dotnet.defaultSolution": "ModTemplate.sln",
|
"dotnet.defaultSolution": "AriasServerUtils.sln",
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"server-*.txt": "log",
|
"server-*.txt": "log",
|
||||||
"client-*.txt": "log"
|
"client-*.txt": "log"
|
||||||
|
|
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
|
@ -9,7 +9,7 @@
|
||||||
"build",
|
"build",
|
||||||
"-c",
|
"-c",
|
||||||
"Debug",
|
"Debug",
|
||||||
"${workspaceFolder}/ModTemplate/ModTemplate.csproj"
|
"${workspaceFolder}/AriasServerUtils/AriasServerUtils.csproj"
|
||||||
],
|
],
|
||||||
"problemMatcher": "$msCompile"
|
"problemMatcher": "$msCompile"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,12 @@ 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
|
||||||
{
|
{
|
||||||
|
@ -12,7 +16,32 @@ 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, string.Join(", ", new string[] { "setspawn", "spawn", "delspawn", "sethome", "home", "delhome", "homes", "restoreinv", "asu" })));
|
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" })));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -23,6 +52,35 @@ 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";
|
||||||
|
@ -61,9 +119,18 @@ 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))
|
||||||
{
|
{
|
||||||
data.Homes[homeName].Location.Merge(isp.Entity);
|
Home home = data.Homes[homeName];
|
||||||
|
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"));
|
||||||
}
|
}
|
||||||
|
@ -71,6 +138,11 @@ 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();
|
||||||
|
@ -130,14 +202,20 @@ 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);
|
data.Homes[homeName] = Home.MakeHome(args.Caller.Player.Entity, homeName, withMount: withMount);
|
||||||
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-set"));
|
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:home-set"));
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -176,9 +254,23 @@ 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,17 +280,30 @@ namespace AriasServerUtils
|
||||||
internal static TextCommandResult HandleUpdateASUBypass(TextCommandCallingArgs args)
|
internal static TextCommandResult HandleUpdateASUBypass(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (args[1] is bool bypass)
|
if (args[0] is bool bypass)
|
||||||
{
|
{
|
||||||
ServerUtilities.config.AdminsBypassMaxHomes = bypass;
|
ServerUtilities.config.AdminsBypassMaxHomes = bypass;
|
||||||
ServerUtilities.MarkDirty();
|
ServerUtilities.MarkDirty();
|
||||||
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
|
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -206,10 +311,382 @@ namespace AriasServerUtils
|
||||||
ServerUtilities.config.MaxHomes = maxHomes;
|
ServerUtilities.config.MaxHomes = maxHomes;
|
||||||
ServerUtilities.MarkDirty();
|
ServerUtilities.MarkDirty();
|
||||||
|
|
||||||
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
|
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", maxHomes));
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
187
AriasServerUtils/Globals.cs
Normal file
187
AriasServerUtils/Globals.cs
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
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 = 6;
|
||||||
|
|
||||||
|
|
||||||
|
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>();
|
||||||
|
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 Dictionary<string, int> costs = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
/// <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(string 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
19
AriasServerUtils/ModSystems/ASUClient.cs
Normal file
19
AriasServerUtils/ModSystems/ASUClient.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
602
AriasServerUtils/ModSystems/ASUServer.cs
Normal file
602
AriasServerUtils/ModSystems/ASUServer.cs
Normal file
|
@ -0,0 +1,602 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,8 +31,15 @@ namespace AriasServerUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Merge(Entity entity)
|
public void Merge(Entity entity, bool unmount = true)
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
@ -78,6 +85,7 @@ 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>
|
||||||
|
@ -87,11 +95,13 @@ 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)
|
public static Home MakeHome(Entity player, string homeName, bool withMount = false)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
|
|
286
AriasServerUtils/RTPFactory.cs
Normal file
286
AriasServerUtils/RTPFactory.cs
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
131
AriasServerUtils/RustyGearUtils.cs
Normal file
131
AriasServerUtils/RustyGearUtils.cs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
72
AriasServerUtils/TimeUtil.cs
Normal file
72
AriasServerUtils/TimeUtil.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,33 @@
|
||||||
"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}; Admins can bypass max homes: {1}\n{2}",
|
"help": "All Aria's Server Utilities Commands: \n\nMax Homes: {0}; \nAdmins can bypass max homes: {1}\nMax back positions: {2}\n\n{3}",
|
||||||
|
|
||||||
"updatedconfig": "[ASU] server config updated"
|
"updatedconfig": "[ASU] server config updated with the new value: {0}",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: 01-18-2025 @ 03:18 PM",
|
"description": "A collection of server utilities\n\nBuild Date: 06-05-2025 @ 4:44 PM MST",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0-dev.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"game": ""
|
"game": ""
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue