Compare commits

...

19 commits

Author SHA1 Message Date
a88807045e Implement money utility APIs
Part of #6
2025-06-05 16:44:48 -07:00
ac8ac62d27 Fix serialization of home mount flag
Part of #6
2025-06-05 12:41:22 -07:00
bd12021575 Some refactoring work
Add command aliases
Begin adding the sethomemount command

Part of #6
2025-06-05 12:18:07 -07:00
45b024654a Ship 1.0.11 with the farmland feature 2025-05-07 12:21:23 -07:00
419c05dbac Add farmland drop 2025-05-07 12:15:23 -07:00
9715975a48 Release 1.0.10 2025-05-03 14:23:18 -07:00
7fb2d38c3d Fix: #5 2025-05-03 14:21:28 -07:00
b00e56fdf7 Ensure time accel is disabled when world is fully loaded.
Part of #5
2025-05-03 14:02:17 -07:00
12d10a9a3c Attempt to fix time accel on server
Part of #5
2025-05-03 13:57:36 -07:00
e56a123cb8 #5: Add debug to player sleeping percentage 2025-05-03 12:53:44 -07:00
4c585f647e Remove old deprecated code in RTPFactory 2025-05-03 12:40:50 -07:00
7ca713e42a Fix: #4 2025-05-03 12:36:01 -07:00
268834b434 Patch for 1.20.9 2025-04-24 11:35:01 -07:00
11f52de3f7 Bump version 1.0.8 2025-03-11 01:05:50 -07:00
37a1c8d361 Issue a hotfix for a small issue that happens when no players are online. 2025-03-11 01:05:41 -07:00
3ab3dc099f Adds a initial basic version of player sleeping percentage. 2025-03-11 00:55:21 -07:00
d2b92f95c5 Change method of time acceleration 2025-03-11 00:37:46 -07:00
720dafea87 Implement experimental player sleeping percentage handling 2025-03-10 18:57:27 -07:00
9a8b9d1545 Begin to add the player sleeping percentage system 2025-03-10 17:17:25 -07:00
9 changed files with 579 additions and 123 deletions

View file

@ -3,10 +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.MathTools;
using Vintagestory.API.Server; using Vintagestory.API.Server;
using Vintagestory.API.Util; using Vintagestory.API.Util;
using Vintagestory.GameContent;
namespace AriasServerUtils namespace AriasServerUtils
{ {
@ -125,8 +127,10 @@ namespace AriasServerUtils
if (data.Homes.ContainsKey(homeName)) if (data.Homes.ContainsKey(homeName))
{ {
Home home = data.Homes[homeName];
ServerUtilities.NewBackCacheForPlayer(isp); ServerUtilities.NewBackCacheForPlayer(isp);
data.Homes[homeName].Location.Merge(isp.Entity);
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"));
} }
@ -198,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
{ {
@ -274,7 +284,7 @@ namespace AriasServerUtils
{ {
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();
@ -288,7 +298,7 @@ namespace AriasServerUtils
ServerUtilities.config.MaxBackCache = max; ServerUtilities.config.MaxBackCache = max;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", max));
} }
return TextCommandResult.Success(); return TextCommandResult.Success();
@ -301,7 +311,7 @@ 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();
@ -310,19 +320,32 @@ namespace AriasServerUtils
internal static TextCommandResult HandleUpdateASUMgrWarps(TextCommandCallingArgs args) internal static TextCommandResult HandleUpdateASUMgrWarps(TextCommandCallingArgs args)
{ {
if (args[0] is bool mgr) if (args[0] is bool mgr)
{
ServerUtilities.config.onlyAdminsCreateWarps = mgr; ServerUtilities.config.onlyAdminsCreateWarps = mgr;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", mgr));
}
else ServerUtilities.config.onlyAdminsCreateWarps = true; else ServerUtilities.config.onlyAdminsCreateWarps = true;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", true));
} }
internal static TextCommandResult HandleUpdateASUPSP(TextCommandCallingArgs args) internal static TextCommandResult HandleUpdateASUPSP(TextCommandCallingArgs args)
{ {
if (args[0] is int psp) ServerUtilities.config.PlayerSleepingPercentage = psp; if (args[0] is int psp)
ServerUtilities.MarkDirty(); {
ServerUtilities.config.PlayerSleepingPercentage = psp;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", psp));
}
return TextCommandResult.Success();
} }
internal static TextCommandResult HandleWarp(TextCommandCallingArgs args) internal static TextCommandResult HandleWarp(TextCommandCallingArgs args)
@ -341,13 +364,11 @@ namespace AriasServerUtils
if (ServerUtilities.serverWarps.warps.ContainsKey(name)) if (ServerUtilities.serverWarps.warps.ContainsKey(name))
{ {
Warp warp = ServerUtilities.serverWarps.warps[name];
ServerUtilities.NewBackCacheForPlayer(isp); ServerUtilities.NewBackCacheForPlayer(isp);
ServerUtilities.serverWarps.warps[name].Location.Merge(isp.Entity); warp.Location.Merge(isp.Entity, unmount: !warp.CanHaveMount);
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-set", name));
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()); data.ActiveCooldowns.Add(CooldownType.Warp, TimeUtil.DecodeTimeNotation(ServerUtilities.config.Cooldowns.Get(CooldownType.Warp)) + TimeUtil.GetUnixEpochTimestamp());
@ -430,7 +451,7 @@ namespace AriasServerUtils
ServerUtilities.config.MaxRTPBlockDistance = maxDist; ServerUtilities.config.MaxRTPBlockDistance = maxDist;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", maxDist));
} }
return TextCommandResult.Success(); return TextCommandResult.Success();
@ -443,7 +464,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Back] = CD; ServerUtilities.config.Cooldowns[CooldownType.Back] = CD;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
} }
else else
{ {
@ -461,7 +482,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Warp] = CD; ServerUtilities.config.Cooldowns[CooldownType.Warp] = CD;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
} }
else else
{ {
@ -479,7 +500,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Home] = CD; ServerUtilities.config.Cooldowns[CooldownType.Home] = CD;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
} }
else else
{ {
@ -497,7 +518,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Spawn] = CD; ServerUtilities.config.Cooldowns[CooldownType.Spawn] = CD;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
} }
else else
{ {
@ -515,7 +536,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.RTP] = CD; ServerUtilities.config.Cooldowns[CooldownType.RTP] = CD;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
} }
else else
{ {
@ -541,7 +562,7 @@ namespace AriasServerUtils
// Update the bypass // Update the bypass
ServerUtilities.config.AdminsBypassCooldowns = bypass; ServerUtilities.config.AdminsBypassCooldowns = bypass;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
} }
else return TextCommandResult.Success(); else return TextCommandResult.Success();
} }
@ -553,11 +574,43 @@ namespace AriasServerUtils
// Update the flag // Update the flag
ServerUtilities.config.AdminsBypassRTPMaxDistance = bypass; ServerUtilities.config.AdminsBypassRTPMaxDistance = bypass;
ServerUtilities.MarkDirty(); ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig")); return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
} }
else return TextCommandResult.Success(); 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) internal static TextCommandResult HandleListCooldowns(TextCommandCallingArgs args)
{ {
string sReturn = "SERVER COOLDOWN SETTINGS\n"; string sReturn = "SERVER COOLDOWN SETTINGS\n";
@ -577,5 +630,63 @@ namespace AriasServerUtils
} }
return TextCommandResult.Success(sReturn); return TextCommandResult.Success(sReturn);
} }
internal static TextCommandResult HandleSleepyDebug(TextCommandCallingArgs args)
{
EntityBehaviorTiredness sleepy = args.Caller.Entity.GetBehavior<EntityBehaviorTiredness>();
if (sleepy != null)
{
sleepy.Tiredness = 100;
}
return TextCommandResult.Success();
}
internal static void CheckBreakFarmland(IServerPlayer byPlayer, BlockSelection blockSel, ref float dropQuantityMultiplier, ref EnumHandling handling)
{
if (!ServerUtilities.config.EnableFarmlandDrop)
{
return; // Default behavior
}
if (blockSel.Block is BlockFarmland farmland)
{
BlockEntityFarmland beFarmland = farmland.GetBlockEntity<BlockEntityFarmland>(blockSel.Position);
string farmlandType = blockSel.Block.LastCodePart();
if (ServerUtilities.config.EnableFarmlandDowngrade)
{
switch (farmlandType)
{
case "verylow":
{ // barren
break; // Can't downgrade further
}
case "low":
{
farmlandType = "verylow";
break;
}
case "medium":
{
farmlandType = "low";
break;
}
case "compost":
{ // high
farmlandType = "medium";
break;
}
case "high":
{ // Terra preta
farmlandType = "compost";
break;
}
}
}
byPlayer.Entity.World.SpawnItemEntity(new ItemStack(byPlayer.Entity.World.GetBlock(new AssetLocation($"soil-{farmlandType}-none"))), blockSel.Position.ToVec3d().Add(0.5, 0.5, 0.5));
}
}
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using Vintagestory.API.Common;
using Vintagestory.API.Server; using Vintagestory.API.Server;
namespace AriasServerUtils namespace AriasServerUtils
@ -22,7 +23,7 @@ namespace AriasServerUtils
{ CooldownType.RTP, "30s" }, { CooldownType.RTP, "30s" },
{ CooldownType.Back, "5s" } { CooldownType.Back, "5s" }
}; };
private static readonly int CURRENT_VERSION = 5; private static readonly int CURRENT_VERSION = 6;
public int Version { get; set; } = 0; public int Version { get; set; } = 0;
@ -36,11 +37,25 @@ namespace AriasServerUtils
public int MaxRTPBlockDistance { get; set; } = 50000; public int MaxRTPBlockDistance { get; set; } = 50000;
public Dictionary<CooldownType, string> Cooldowns { get; set; } = new Dictionary<CooldownType, string>(); 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() public Dictionary<CooldownType, string> GetDefaultCooldowns()
{ {
return m_defaultCD; return m_defaultCD;
} }
/// <summary>
/// Performs some checks against known possible invalid values and sets them to sane values.
/// </summary>
public void SanityCheck() public void SanityCheck()
{ {
foreach (var cd in GetDefaultCooldowns()) foreach (var cd in GetDefaultCooldowns())
@ -79,14 +94,16 @@ namespace AriasServerUtils
public PlayerPosition Location; public PlayerPosition Location;
public string CreatedBy; public string CreatedBy;
public DateTime CreatedAt; public DateTime CreatedAt;
public bool CanHaveMount = false;
public static Warp Create(IServerPlayer player) public static Warp Create(IServerPlayer player, bool withMount = false)
{ {
Warp warp = new Warp(); Warp warp = new Warp();
warp.Location = PlayerPosition.from(player.Entity); warp.Location = PlayerPosition.from(player.Entity);
warp.CreatedBy = player.PlayerName; warp.CreatedBy = player.PlayerName;
warp.CreatedAt = DateTime.Now; warp.CreatedAt = DateTime.Now;
warp.CanHaveMount = withMount;
return warp; return warp;
} }
@ -98,6 +115,41 @@ namespace AriasServerUtils
public Dictionary<string, Warp> warps = new Dictionary<string, Warp>(); 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 public class BackCaches
{ {

View 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;
}
}

View file

@ -7,6 +7,7 @@ using Vintagestory.API.Common;
using Vintagestory.API.Common.CommandAbbr; using Vintagestory.API.Common.CommandAbbr;
using Vintagestory.API.Common.Entities; using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config; using Vintagestory.API.Config;
using Vintagestory.API.Datastructures;
using Vintagestory.API.MathTools; using Vintagestory.API.MathTools;
using Vintagestory.API.Server; using Vintagestory.API.Server;
using Vintagestory.API.Util; using Vintagestory.API.Util;
@ -28,15 +29,14 @@ namespace AriasServerUtils
internal static Warps serverWarps = new Warps(); internal static Warps serverWarps = new Warps();
internal static Random rng = new Random((int)TimeUtil.GetUnixEpochTimestamp()); internal static Random rng = new Random((int)TimeUtil.GetUnixEpochTimestamp());
internal bool isFirstStart = true;
internal static string[] saveInvTypes = new string[] { List<EntityAgent> SleepingPlayers { get; set; } = new();
GlobalConstants.hotBarInvClassName, float OriginalSpeed { get; set; } = 0.0f;
GlobalConstants.backpackInvClassName, public double Hours { get; private set; } = 0.0;
GlobalConstants.craftingInvClassName, bool Sleeping { get; set; } = false;
GlobalConstants.mousecursorInvClassName, public IServerNetworkChannel ServerNetworkChannel { get; private set; }
GlobalConstants.characterInvClassName
};
/// <summary> /// <summary>
/// Method to register all mod blocks /// Method to register all mod blocks
@ -56,6 +56,14 @@ namespace AriasServerUtils
{ {
} }
public override bool ShouldLoad(EnumAppSide side)
{
return side == EnumAppSide.Server;
}
// Called on server and client // Called on server and client
public override void Start(ICoreAPI api) public override void Start(ICoreAPI api)
{ {
@ -74,30 +82,29 @@ namespace AriasServerUtils
api.Event.ServerRunPhase(EnumServerRunPhase.GameReady, OnGameReady); api.Event.ServerRunPhase(EnumServerRunPhase.GameReady, OnGameReady);
api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown); api.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, OnShutdown);
api.Event.Timer(OnCheckModDirty, 20);
api.Event.Timer(OnCheckPlayerCooldowns, 1);
api.Event.Timer(RTPFactory.HandleRTPChecking, 1);
api.Event.PlayerDeath += OnPlayerDeath; api.Event.PlayerDeath += OnPlayerDeath;
api.Event.PlayerJoin += OnPlayerJoin; api.Event.PlayerJoin += OnPlayerJoin;
api.Event.PlayerDisconnect += OnPlayerDC; api.Event.PlayerDisconnect += OnPlayerDC;
api.Event.ChunkColumnLoaded += RTPFactory.ChunkLoaded; api.Event.ChunkColumnLoaded += RTPFactory.ChunkLoaded;
api.Event.BreakBlock += Events.CheckBreakFarmland;
//api.Event.PlayerLeave += OnPlayerDC; //api.Event.PlayerLeave += OnPlayerDC;
api.ChatCommands.Create("setspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleSetSpawn); api.ChatCommands.Create("setspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleSetSpawn).WithAlias("ss");
api.ChatCommands.Create("spawn").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSpawn); api.ChatCommands.Create("spawn").RequiresPrivilege(Privilege.chat).HandleWith(Events.HandleSpawn).WithAlias("s");
api.ChatCommands.Create("delspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleClearSpawn); api.ChatCommands.Create("delspawn").RequiresPrivilege(Privilege.controlserver).HandleWith(Events.HandleClearSpawn).WithAlias("ds");
//api.ChatCommands.Create("test_death").RequiresPlayer().RequiresPrivilege(Privilege.controlserver).HandleWith(TestDeath); //api.ChatCommands.Create("test_death").RequiresPlayer().RequiresPrivilege(Privilege.controlserver).HandleWith(TestDeath);
var parsers = api.ChatCommands.Parsers; 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("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("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); 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); 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); 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") api.ChatCommands.Create("asu")
.RequiresPrivilege(Privilege.chat) .RequiresPrivilege(Privilege.chat)
@ -168,6 +175,18 @@ namespace AriasServerUtils
.WithDescription("Update RTP Max block distance. Plus and/or minus this distance from player current position (Default is 50000)") .WithDescription("Update RTP Max block distance. Plus and/or minus this distance from player current position (Default is 50000)")
.HandleWith(Events.HandleUpdateASURTPMax) .HandleWith(Events.HandleUpdateASURTPMax)
.EndSubCommand() .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") .BeginSubCommand("cooldowns")
.WithDescription("Commands related to all the various cooldowns") .WithDescription("Commands related to all the various cooldowns")
.BeginSubCommand("back") .BeginSubCommand("back")
@ -221,18 +240,188 @@ namespace AriasServerUtils
.RequiresPrivilege(Privilege.chat) .RequiresPrivilege(Privilege.chat)
.HandleWith(Events.HandleASU) .HandleWith(Events.HandleASU)
.WithDescription("Lists all Aria's Server Utils commands") .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(); .EndSubCommand();
api.ChatCommands.Create("setwarp").RequiresPlayer().RequiresPrivilege(Privilege.chat).WithDescription("Creates a new server warp").WithArgs(parsers.OptionalWord("name")).HandleWith(Events.HandleWarpUpdate); 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); 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); 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); 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); 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("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); 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() private void OnCheckPlayerCooldowns()
@ -306,42 +495,10 @@ namespace AriasServerUtils
private void OnPlayerDeath(IServerPlayer player, DamageSource damageSource) private void OnPlayerDeath(IServerPlayer player, DamageSource damageSource)
{ {
PlayerInventory inv = new PlayerInventory(); PlayerInventory inv = RustyGearUtils.GetAllItems(player);
var invMgr = player.InventoryManager;
NewBackCacheForPlayer(player); NewBackCacheForPlayer(player);
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; backupInventory[player.PlayerName] = inv;
} }
@ -395,6 +552,13 @@ namespace AriasServerUtils
// -> Step 3. Load Mod Warps <- // -> Step 3. Load Mod Warps <-
serverWarps = API.LoadModConfig<Warps>(GetConfigurationFile("warps", ModConfigType.Global)); serverWarps = API.LoadModConfig<Warps>(GetConfigurationFile("warps", ModConfigType.Global));
if (serverWarps == null) serverWarps = new Warps(); 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) public string GetConfigurationFile(string sName, ModConfigType type)

View file

@ -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;
@ -88,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;

View file

@ -59,42 +59,6 @@ public class RTPFactory
check.Y = height + 1; check.Y = height + 1;
PPos.Y = height + 1; PPos.Y = height + 1;
return PPos; return PPos;
int Y = 255;
bool lastBlockAir = true;
bool lastLastBlockAir = true;
bool curBlockAir = true;
bool safe = false;
for (Y = 255; Y > 1; Y--)
{
// Manually scan downwards
check.Y = Y;
var current = BA.GetBlock(check);
if (current.BlockMaterial != EnumBlockMaterial.Air)
{
curBlockAir = false;
}
lastLastBlockAir = lastBlockAir;
lastBlockAir = curBlockAir;
if (!curBlockAir && lastBlockAir && lastLastBlockAir)
{
// We found a safe spot to land
check.Y++;
safe = true;
break;
}
}
if (!safe) return null;
PPos.Y = check.Y;
return PPos;
} }
/// <summary> /// <summary>

View 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;
}
}

View file

@ -20,9 +20,12 @@
"help": "All Aria's Server Utilities Commands: \n\nMax Homes: {0}; \nAdmins can bypass max homes: {1}\nMax back positions: {2}\n\n{3}", "help": "All Aria's Server Utilities Commands: \n\nMax Homes: {0}; \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", "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-tp": "Teleported to warp [{0}]",
"warp-set": "Warp [{0}] created!", "warp-set": "Warp [{0}] created!",
"warp-no": "You lack permissions to manage a warp", "warp-no": "You lack permissions to manage a warp",
@ -40,5 +43,8 @@
"rtp-fail": "Giving up on RTP search. No valid position could be found. Try again later", "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}]", "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}]" "cmd-cooldown": "[{0}] is currently on cooldown. You can use this command again in [{1}]",
"psp": "[ASU] PSP Starting... you do not need to stay in bed",
"psp-ending": "[ASU] PSP Complete"
} }

View file

@ -3,8 +3,8 @@
"modid": "ariasserverutils", "modid": "ariasserverutils",
"name": "Aria's Server Utilities", "name": "Aria's Server Utilities",
"authors": ["zontreck"], "authors": ["zontreck"],
"description": "A collection of server utilities\n\nBuild Date: 03-10-2025 @ 12:04 PM MST", "description": "A collection of server utilities\n\nBuild Date: 06-05-2025 @ 4:44 PM MST",
"version": "1.0.6", "version": "1.1.0-dev.3",
"dependencies": { "dependencies": {
"game": "" "game": ""
} }