Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Assets/Content/Systems/UI/Lobby/Canvas/LobbyCanvas.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -7662,6 +7662,7 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 8942214116569629255}
- component: {fileID: 6248120931421175401}
m_Layer: 5
m_Name: Administrator View
m_TagString: Untagged
Expand Down Expand Up @@ -7691,6 +7692,18 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &6248120931421175401
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5397281151860095728}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6b6af73a682645fba4cf9199f63693e1, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &7135706429151112205
GameObject:
m_ObjectHideFlags: 0
Expand Down
53 changes: 52 additions & 1 deletion Assets/Scripts/SS3D/Permissions/PermissionSubSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FishNet.Object;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Object.Synchronizing;
using SS3D.Core.Behaviours;
using SS3D.Data;
Expand Down Expand Up @@ -29,6 +30,11 @@ public sealed class PermissionSubSystem : NetworkSubSystem
[SyncVar]
public bool HasLoadedPermissions;

[SyncVar(OnChange = nameof(SyncAdminFunctionsEnabledForAll))]
private bool _adminFunctionsEnabledForAll;

public bool AdminFunctionsEnabledForAll => _adminFunctionsEnabledForAll;

/// <summary>
/// File name to the permissions file.
/// TODO: Move this to a PermissionSettings and create permissions via JSON.
Expand Down Expand Up @@ -198,5 +204,50 @@ public bool IsAtLeast(string ckey, ServerRoleTypes permissionLevelCheck)
return userPermission >= permissionLevelCheck;

}

public bool CanUseAdminFunctions(string ckey)
{
if (_adminFunctionsEnabledForAll)
{
return true;
}

return IsAtLeast(ckey, ServerRoleTypes.Administrator);
}

[Server]
public void SetAdminFunctionsEnabledForAll(bool enabled)
{
if (_adminFunctionsEnabledForAll == enabled)
{
return;
}

Log.Information(this, "Setting admin functions for all users to {enabled}", Logs.ServerOnly, enabled);
_adminFunctionsEnabledForAll = enabled;
SyncUserPermissions();
}

[ServerRpc(RequireOwnership = false)]
public void CmdSetAdminFunctionsEnabledForAll(bool enabled, NetworkConnection conn = null)
{
if (conn == null || !conn.IsHost)
{
Log.Warning(this, "Rejected admin functions toggle request from non-host connection", Logs.ServerOnly);
return;
}

SetAdminFunctionsEnabledForAll(enabled);
}

private void SyncAdminFunctionsEnabledForAll(bool oldValue, bool newValue, bool asServer)
{
if (!asServer && IsHost)
{
return;
}

SyncUserPermissions();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using SS3D.Logging;
using SS3D.Systems.Furniture;
using SS3D.Systems.Inventory.Containers;
using System;
using UnityEngine;

namespace SS3D.Systems.Inventory.Interactions
Expand Down Expand Up @@ -81,4 +82,4 @@ public bool Start(InteractionEvent interactionEvent, InteractionReference refere
return true;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using FishNet.Connection;
using SS3D.Core;
using SS3D.Permissions;

namespace SS3D.Systems.IngameConsoleSystem.Commands
{
public class AdminFunctionsCommand : Command
{
public override string ShortDescription => "View or toggle admin functions for all users";
public override string Usage => "[on/off]";
public override ServerRoleTypes AccessLevel => ServerRoleTypes.ServerOwner;
public override CommandType Type => CommandType.Server;

private record CalculatedValues(bool ShouldSet, bool Enabled) : ICalculatedValues;

public override string Perform(string[] args, NetworkConnection conn = null)
{
if (!ReceiveCheckResponse(args, out CheckArgsResponse response, out CalculatedValues values))
{
return response.InvalidArgs;
}

PermissionSubSystem permissionSystem = SubSystems.Get<PermissionSubSystem>();

if (!values.ShouldSet)
{
return $"Admin functions for all users: {FormatState(permissionSystem.AdminFunctionsEnabledForAll)}";
}

if (conn != null && !conn.IsHost)
{
return "Only the host can toggle admin functions for all users";
}

permissionSystem.SetAdminFunctionsEnabledForAll(values.Enabled);
return $"Admin functions for all users: {FormatState(values.Enabled)}";
}

protected override CheckArgsResponse CheckArgs(string[] args)
{
CheckArgsResponse response = new();

if (args.Length == 0)
{
return response.MakeValid(new CalculatedValues(false, false));
}

if (args.Length != 1)
{
return response.MakeInvalid("Invalid number of arguments");
}

if (!TryParseState(args[0], out bool enabled))
{
return response.MakeInvalid("Use on/off, true/false, or 1/0");
}

return response.MakeValid(new CalculatedValues(true, enabled));
}

private static bool TryParseState(string value, out bool enabled)
{
switch (value.ToLowerInvariant())
{
case "on":
case "true":
case "1":
case "enable":
case "enabled":
enabled = true;
return true;
case "off":
case "false":
case "0":
case "disable":
case "disabled":
enabled = false;
return true;
default:
enabled = false;
return false;
}
}

private static string FormatState(bool enabled)
{
return enabled ? "enabled" : "disabled";
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

129 changes: 129 additions & 0 deletions Assets/Scripts/SS3D/Systems/Lobby/UI/AdminFunctionsToggleView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using Coimbra.Services.Events;
using FishNet;
using SS3D.Core;
using SS3D.Core.Behaviours;
using SS3D.Core.Settings;
using SS3D.Logging;
using SS3D.Permissions;
using SS3D.Permissions.Events;
using SS3D.UI.Buttons;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

namespace SS3D.Systems.Lobby.UI
{
/// <summary>
/// Adds the host-only server setting that lets every player use admin functions.
/// </summary>
public class AdminFunctionsToggleView : Actor
{
private const string NormalText = "<sprite name=\"deny\"> admin funcs: admins";
private const string PressedText = "<sprite name=\"approve\"> admin funcs: all";

private ToggleLabelButton _toggleButton;

protected override void OnStart()
{
base.OnStart();

CreateToggleButton();
AddHandle(UserPermissionsChangedEvent.AddListener(HandleUserPermissionsUpdated));
RefreshToggleButton();
}

protected override void OnDestroyed()
{
base.OnDestroyed();

if (_toggleButton != null)
{
_toggleButton.OnPressedDown -= HandleToggleButtonPressed;
}
}

private void CreateToggleButton()
{
ToggleLabelButton templateButton = GetComponentsInChildren<ToggleLabelButton>(true)
.FirstOrDefault(button => button.gameObject.name == "Start Round");

if (templateButton == null)
{
Log.Warning(this, "Could not find a button template for the admin functions toggle", Logs.UI);
return;
}

_toggleButton = Instantiate(templateButton, templateButton.transform.parent);
_toggleButton.gameObject.name = "Admin Functions For All";
_toggleButton.NormalText(NormalText);
_toggleButton.PressedText(PressedText);
_toggleButton.OnPressedDown += HandleToggleButtonPressed;

if (_toggleButton.transform is RectTransform rectTransform)
{
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 190f);
}

if (_toggleButton.transform.parent.TryGetComponent(out HorizontalOrVerticalLayoutGroup layoutGroup))
{
layoutGroup.padding.right = 0;
layoutGroup.spacing = Mathf.Max(layoutGroup.spacing, 5f);
}

DisableCopiedLocalizers(_toggleButton.gameObject);
}

private void HandleUserPermissionsUpdated(ref EventContext context, in UserPermissionsChangedEvent e)
{
RefreshToggleButton();
}

private void HandleToggleButtonPressed(bool enabled)
{
PermissionSubSystem permissionSystem = SubSystems.Get<PermissionSubSystem>();

if (!CanToggleAdminFunctions(permissionSystem))
{
RefreshToggleButton();
return;
}

permissionSystem.CmdSetAdminFunctionsEnabledForAll(enabled);
}

private void RefreshToggleButton()
{
if (_toggleButton == null)
{
return;
}

PermissionSubSystem permissionSystem = SubSystems.Get<PermissionSubSystem>();

_toggleButton.Pressed = permissionSystem != null && permissionSystem.AdminFunctionsEnabledForAll;
_toggleButton.Disabled = !CanToggleAdminFunctions(permissionSystem);
_toggleButton.RefreshVisuals();
}

private static bool CanToggleAdminFunctions(PermissionSubSystem permissionSystem)
{
if (permissionSystem == null || !permissionSystem.HasLoadedPermissions)
{
return false;
}

return InstanceFinder.IsHost && permissionSystem.IsAtLeast(LocalPlayer.Ckey, ServerRoleTypes.ServerOwner);
}

private static void DisableCopiedLocalizers(GameObject root)
{
foreach (MonoBehaviour behaviour in root.GetComponentsInChildren<MonoBehaviour>(true))
{
if (behaviour != null && behaviour.GetType().Name == "LocalizeStringEvent")
{
behaviour.enabled = false;
}
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading