diff --git a/TSOClient/FSO.Server/Servers/Lot/Domain/LotContainer.cs b/TSOClient/FSO.Server/Servers/Lot/Domain/LotContainer.cs index b06e1b477..dec90c7dd 100644 --- a/TSOClient/FSO.Server/Servers/Lot/Domain/LotContainer.cs +++ b/TSOClient/FSO.Server/Servers/Lot/Domain/LotContainer.cs @@ -1729,6 +1729,37 @@ namespace FSO.Server.Servers.Lot.Domain if (RelationshipsToSave.Count > 0) BatchRelationshipSave(); } + private bool IsAvatarDebug(uint modLevel) + { + if (Config.Archive != null) + { + // Use the archive flags to gate debug features + var flags = Config.Archive.Flags; + + if (flags.HasFlag(FSO.Common.ArchiveConfigFlags.DebugFeatures)) + { + int requiredLevel = 2; + + if (flags.HasFlag(FSO.Common.ArchiveConfigFlags.DebugFeaturesAllUsers)) + { + requiredLevel = 0; + } + else if (flags.HasFlag(FSO.Common.ArchiveConfigFlags.DebugFeaturesMods)) + { + requiredLevel = 1; + } + + return modLevel >= requiredLevel; + } + else + { + return false; + } + } + + return modLevel > 0; + } + //Run on the background thread public void AvatarJoin(IVoltronSession session) { @@ -1803,6 +1834,8 @@ namespace FSO.Server.Servers.Lot.Domain } } + state.AvatarFlags |= IsAvatarDebug((session as VoltronSession)?.ModerationLevel ?? 0) ? VMTSOAvatarFlags.Debug : 0; + Host.RecordStartVisit(session, visitorType); var hollowLoadMask = (transitionInfo?.GetSurroundingLotMask() ?? HOLLOW_LOAD_ALL); @@ -2119,6 +2152,7 @@ namespace FSO.Server.Servers.Lot.Domain if (!signalled) return; //give up VMTSOAvatarPermissions newLevel = VMTSOAvatarPermissions.Visitor; VMChangePermissionsMode mode = VMChangePermissionsMode.NORMAL; + bool? debug = null; switch (change) { case ChangeType.ADD_ROOMMATE: @@ -2139,6 +2173,7 @@ namespace FSO.Server.Servers.Lot.Domain var ava = da.Avatars.Get(avatar_id); var roomies = da.Roommates.GetAvatarsLots(avatar_id); newLevel = GetAvatarPermissions(ava, roomies); + debug = IsAvatarDebug(ava.moderation_level); } break; } @@ -2150,6 +2185,7 @@ namespace FSO.Server.Servers.Lot.Domain TargetUID = avatar_id, Level = newLevel, Mode = mode, + Debug = debug, ReplaceUID = replace_id, Verified = true, }); diff --git a/TSOClient/tso.client/UI/Archive/UIArchiveCreateServer.cs b/TSOClient/tso.client/UI/Archive/UIArchiveCreateServer.cs index bc49e1410..038e1c37c 100644 --- a/TSOClient/tso.client/UI/Archive/UIArchiveCreateServer.cs +++ b/TSOClient/tso.client/UI/Archive/UIArchiveCreateServer.cs @@ -597,7 +597,7 @@ namespace FSO.Client.UI.Archive public static void DebugModeHelp() { - UIAlert.Alert("Debug interactions", "When enabled, the archive server gives admins the ability to spawn debug objects and use debug interactions. These interactions can optionally be enabled for moderators and all other users.", true); + UIAlert.Alert("Debug interactions", "When enabled, the archive server gives admins the ability to spawn all debug objects from build mode and use debug interactions. These objects/interactions allow users to cheat money/skill, but can potentially cause object errors crash the game. If you want a 'safe' selection of debug objects for all players, you can enable the 'debug' catalog in the events configuration. \nThese interactions can optionally be enabled for moderators and all other users.", true); } public static void ArchivedCharacterHelp() diff --git a/TSOClient/tso.client/UI/Panels/UIBuildMode.cs b/TSOClient/tso.client/UI/Panels/UIBuildMode.cs index b70d91572..1f70e81b4 100644 --- a/TSOClient/tso.client/UI/Panels/UIBuildMode.cs +++ b/TSOClient/tso.client/UI/Panels/UIBuildMode.cs @@ -196,7 +196,7 @@ namespace FSO.Client.UI.Panels public override void Update(UpdateState state) { - bool allowDebug = (LotController?.ActiveEntity?.TSOState as VMTSOAvatarState)?.Permissions >= VMTSOAvatarPermissions.Admin; + bool allowDebug = (LotController?.ActiveEntity?.TSOState as VMTSOAvatarState)?.Flags.HasFlag(VMTSOAvatarFlags.Debug) ?? false; if (DebugButton.Visible != allowDebug) { diff --git a/TSOClient/tso.client/UI/Screens/SandboxGameScreen.cs b/TSOClient/tso.client/UI/Screens/SandboxGameScreen.cs index 55c41d7cd..c0d28c35b 100644 --- a/TSOClient/tso.client/UI/Screens/SandboxGameScreen.cs +++ b/TSOClient/tso.client/UI/Screens/SandboxGameScreen.cs @@ -450,6 +450,7 @@ namespace FSO.Client.UI.Screens SkinTone = (byte)settings.DebugSkin, Gender = (short)(settings.DebugGender ? 0 : 1), Permissions = SimAntics.Model.TSOPlatform.VMTSOAvatarPermissions.Admin, + AvatarFlags = SimAntics.Model.TSOPlatform.VMTSOAvatarFlags.Debug, //CustomGUID = 0x396CD3D1, Budget = 1000000, }; diff --git a/TSOClient/tso.simantics/Engine/VMThread.cs b/TSOClient/tso.simantics/Engine/VMThread.cs index e9b98f80e..8e60ed729 100644 --- a/TSOClient/tso.simantics/Engine/VMThread.cs +++ b/TSOClient/tso.simantics/Engine/VMThread.cs @@ -1018,10 +1018,9 @@ namespace FSO.SimAntics.Engine //if flags are empty apart from "Non-Empty", force everything but visitor. (a kind of default state) if (tsoCompare == TSOFlags.NonEmpty) tsoCompare |= TSOFlags.AllowFriends | TSOFlags.AllowRoommates | TSOFlags.AllowObjectOwner; - //DEBUG: enable debug interction for all CSRs. if ((action.Flags & TTABFlags.Debug) > 0) { - if ((tsoState & TSOFlags.AllowCSRs) > 0) + if (avatar.AvatarState.Flags.HasFlag(VMTSOAvatarFlags.Debug)) return result; //do not bother running check else return null; //disable debug for everyone else. diff --git a/TSOClient/tso.simantics/Model/Platform/VMDefaultValidator.cs b/TSOClient/tso.simantics/Model/Platform/VMDefaultValidator.cs index 0feed733c..d234049da 100644 --- a/TSOClient/tso.simantics/Model/Platform/VMDefaultValidator.cs +++ b/TSOClient/tso.simantics/Model/Platform/VMDefaultValidator.cs @@ -94,7 +94,7 @@ namespace FSO.SimAntics.Model.Platform var whitelist = (ava.AvatarState.Permissions == VMTSOAvatarPermissions.Roommate) ? RoomieWhiteList : BuilderWhiteList; if (item == null || !whitelist.Contains(item.Value.Category)) { - if (ava.AvatarState.Permissions != VMTSOAvatarPermissions.Admin) return PurchaseMode.Disallowed; + if (!ava.AvatarState.Flags.HasFlag(VMTSOAvatarFlags.Debug)) return PurchaseMode.Disallowed; } } diff --git a/TSOClient/tso.simantics/Model/Platform/VMIAvatarState.cs b/TSOClient/tso.simantics/Model/Platform/VMIAvatarState.cs index c0ca4aa5e..772837248 100644 --- a/TSOClient/tso.simantics/Model/Platform/VMIAvatarState.cs +++ b/TSOClient/tso.simantics/Model/Platform/VMIAvatarState.cs @@ -5,5 +5,6 @@ namespace FSO.SimAntics.Model.Platform public interface VMIAvatarState { VMTSOAvatarPermissions Permissions { get; set; } + VMTSOAvatarFlags Flags { get; set; } } } diff --git a/TSOClient/tso.simantics/Model/TS1Platform/VMTS1AvatarState.cs b/TSOClient/tso.simantics/Model/TS1Platform/VMTS1AvatarState.cs index 48d2d8e11..d0f6500a2 100644 --- a/TSOClient/tso.simantics/Model/TS1Platform/VMTS1AvatarState.cs +++ b/TSOClient/tso.simantics/Model/TS1Platform/VMTS1AvatarState.cs @@ -13,6 +13,10 @@ namespace FSO.SimAntics.Model.TS1Platform { get; set; } + public VMTSOAvatarFlags Flags + { + get; set; + } public override void Deserialize(BinaryReader reader) { diff --git a/TSOClient/tso.simantics/Model/TSOPlatform/VMTSOAvatarState.cs b/TSOClient/tso.simantics/Model/TSOPlatform/VMTSOAvatarState.cs index e7667a765..a515d457f 100644 --- a/TSOClient/tso.simantics/Model/TSOPlatform/VMTSOAvatarState.cs +++ b/TSOClient/tso.simantics/Model/TSOPlatform/VMTSOAvatarState.cs @@ -11,7 +11,7 @@ namespace FSO.SimAntics.Model.TSOPlatform public VMTSOAvatarPermissions Permissions { get; set; } = VMTSOAvatarPermissions.Visitor; public HashSet IgnoredAvatars = new HashSet(); public Dictionary JobInfo = new Dictionary(); - public VMTSOAvatarFlags Flags; + public VMTSOAvatarFlags Flags { get; set; } public bool IsSpectator => Flags.HasFlag(VMTSOAvatarFlags.Spectator); public Color ChatColor = Color.White; public sbyte ChatTTSPitch; //-100 to 100. @@ -106,9 +106,29 @@ namespace FSO.SimAntics.Model.TSOPlatform [Flags] public enum VMTSOAvatarFlags : uint { + /// + /// Isn't a roomie or owner of another lot. + /// CanBeRoommate = 1, //TODO: update on becoming roomie of another lot, while on this lot. - NewPlayer = 2, //under a week old - Mayor = 4, //is mayor of this neighborhood - Spectator = 8, //read-only visitor in spectator mode + + /// + /// Under a week old. + /// + NewPlayer = 2, + + /// + /// Is mayor of this neighborhood. + /// + Mayor = 4, + + /// + /// Read-only visitor in spectator mode. + /// + Spectator = 8, + + /// + /// Can use debug interactions. Tends to be set on moderators/administrators, but archive mode can enforce extra rules. + /// + Debug = 16, } } diff --git a/TSOClient/tso.simantics/NetPlay/Model/Commands/VMChangePermissionsCmd.cs b/TSOClient/tso.simantics/NetPlay/Model/Commands/VMChangePermissionsCmd.cs index fabab577d..7c3ad0fd0 100644 --- a/TSOClient/tso.simantics/NetPlay/Model/Commands/VMChangePermissionsCmd.cs +++ b/TSOClient/tso.simantics/NetPlay/Model/Commands/VMChangePermissionsCmd.cs @@ -1,7 +1,5 @@ using FSO.SimAntics.Model; using FSO.SimAntics.Model.TSOPlatform; -using System.Collections.Generic; -using System.IO; namespace FSO.SimAntics.NetPlay.Model.Commands { @@ -10,6 +8,7 @@ namespace FSO.SimAntics.NetPlay.Model.Commands public uint TargetUID; public VMTSOAvatarPermissions Level; public VMChangePermissionsMode Mode; + public bool? Debug; public uint ReplaceUID; //for object inherit modes. Set implicitly for owner replacement. public bool Verified; public override bool Execute(VM vm) @@ -113,6 +112,22 @@ namespace FSO.SimAntics.NetPlay.Model.Commands if (level >= VMTSOAvatarPermissions.BuildBuyRoommate) vm.TSOState.BuildRoommates.Add(obj.PersistID); if (level == VMTSOAvatarPermissions.Owner) vm.TSOState.OwnerID = pid; else if (vm.TSOState.OwnerID == pid) vm.TSOState.OwnerID = 0; + + if (Debug != null) + { + var flags = obj.AvatarState.Flags; + + if (Debug.Value) + { + flags |= VMTSOAvatarFlags.Debug; + } + else + { + flags &= ~VMTSOAvatarFlags.Debug; + } + + obj.AvatarState.Flags = flags; + } } return roomieChange && playerOwned; } @@ -167,6 +182,12 @@ namespace FSO.SimAntics.NetPlay.Model.Commands writer.Write(ReplaceUID); writer.Write((byte)Level); writer.Write((byte)Mode); + writer.Write(Debug.HasValue); + + if (Debug.HasValue) + { + writer.Write(Debug.Value); + } } public override void Deserialize(BinaryReader reader) @@ -176,6 +197,9 @@ namespace FSO.SimAntics.NetPlay.Model.Commands ReplaceUID = reader.ReadUInt32(); Level = (VMTSOAvatarPermissions)reader.ReadByte(); Mode = (VMChangePermissionsMode)reader.ReadByte(); + + var hasDebug = reader.ReadBoolean(); + Debug = hasDebug ? reader.ReadBoolean() : null; } #endregion