Compare commits

...

16 commits

7 changed files with 370 additions and 60 deletions

View file

@ -7,6 +7,7 @@ using Vintagestory.API.Config;
using Vintagestory.API.MathTools;
using Vintagestory.API.Server;
using Vintagestory.API.Util;
using Vintagestory.GameContent;
namespace AriasServerUtils
{
@ -274,7 +275,7 @@ namespace AriasServerUtils
{
ServerUtilities.config.AdminsBypassMaxHomes = bypass;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", bypass));
}
return TextCommandResult.Success();
@ -288,7 +289,7 @@ namespace AriasServerUtils
ServerUtilities.config.MaxBackCache = max;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", max));
}
return TextCommandResult.Success();
@ -301,7 +302,7 @@ namespace AriasServerUtils
ServerUtilities.config.MaxHomes = maxHomes;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", maxHomes));
}
return TextCommandResult.Success();
@ -310,19 +311,32 @@ namespace AriasServerUtils
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"));
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();
if (args[0] is int psp)
{
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)
@ -344,7 +358,7 @@ namespace AriasServerUtils
ServerUtilities.NewBackCacheForPlayer(isp);
ServerUtilities.serverWarps.warps[name].Location.Merge(isp.Entity);
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-set", name));
ServerUtilities.SendMessageTo(isp, Lang.Get($"{ServerUtilities.MOD_ID}:warp-tp", name));
@ -430,7 +444,7 @@ namespace AriasServerUtils
ServerUtilities.config.MaxRTPBlockDistance = maxDist;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", maxDist));
}
return TextCommandResult.Success();
@ -443,7 +457,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Back] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
@ -461,7 +475,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Warp] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
@ -479,7 +493,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Home] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
@ -497,7 +511,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.Spawn] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
@ -515,7 +529,7 @@ namespace AriasServerUtils
ServerUtilities.config.Cooldowns[CooldownType.RTP] = CD;
ServerUtilities.MarkDirty();
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig"));
return TextCommandResult.Success(Lang.Get($"{ServerUtilities.MOD_ID}:updatedconfig", CD));
}
else
{
@ -541,7 +555,7 @@ namespace AriasServerUtils
// Update the bypass
ServerUtilities.config.AdminsBypassCooldowns = bypass;
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();
}
@ -553,11 +567,43 @@ namespace AriasServerUtils
// Update the flag
ServerUtilities.config.AdminsBypassRTPMaxDistance = bypass;
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();
}
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";
@ -577,5 +623,63 @@ namespace AriasServerUtils
}
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

@ -22,7 +22,7 @@ namespace AriasServerUtils
{ CooldownType.RTP, "30s" },
{ CooldownType.Back, "5s" }
};
private static readonly int CURRENT_VERSION = 5;
private static readonly int CURRENT_VERSION = 6;
public int Version { get; set; } = 0;
@ -36,11 +36,25 @@ namespace AriasServerUtils
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())

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.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Datastructures;
using Vintagestory.API.MathTools;
using Vintagestory.API.Server;
using Vintagestory.API.Util;
@ -28,6 +29,7 @@ namespace AriasServerUtils
internal static Warps serverWarps = new Warps();
internal static Random rng = new Random((int)TimeUtil.GetUnixEpochTimestamp());
internal bool isFirstStart = true;
internal static string[] saveInvTypes = new string[] {
@ -38,6 +40,12 @@ namespace AriasServerUtils
GlobalConstants.characterInvClassName
};
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>
@ -56,6 +64,14 @@ namespace AriasServerUtils
{
}
public override bool ShouldLoad(EnumAppSide side)
{
return side == EnumAppSide.Server;
}
// Called on server and client
public override void Start(ICoreAPI api)
{
@ -74,13 +90,11 @@ namespace AriasServerUtils
api.Event.ServerRunPhase(EnumServerRunPhase.GameReady, OnGameReady);
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.PlayerJoin += OnPlayerJoin;
api.Event.PlayerDisconnect += OnPlayerDC;
api.Event.ChunkColumnLoaded += RTPFactory.ChunkLoaded;
api.Event.BreakBlock += Events.CheckBreakFarmland;
//api.Event.PlayerLeave += OnPlayerDC;
@ -168,6 +182,18 @@ namespace AriasServerUtils
.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")
@ -221,6 +247,20 @@ namespace AriasServerUtils
.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);
@ -235,6 +275,162 @@ namespace AriasServerUtils
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);
}
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)
@ -395,6 +591,13 @@ namespace AriasServerUtils
// -> 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)

View file

@ -59,42 +59,6 @@ public class RTPFactory
check.Y = height + 1;
PPos.Y = height + 1;
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>

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}",
"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",
@ -40,5 +43,8 @@
"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}]"
"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",
"name": "Aria's Server Utilities",
"authors": ["zontreck"],
"description": "A collection of server utilities\n\nBuild Date: 03-10-2025 @ 12:04 PM MST",
"version": "1.0.6",
"description": "A collection of server utilities\n\nBuild Date: 05-7-2025 @ 12:21 PM MST",
"version": "1.0.11",
"dependencies": {
"game": ""
}