Skip to content

Commit

Permalink
Merge pull request #267 from formlessnameless/dev
Browse files Browse the repository at this point in the history
Upstream Merge
  • Loading branch information
formlessnameless authored Sep 16, 2024
2 parents 78f0ace + 2871720 commit ffd2bcc
Show file tree
Hide file tree
Showing 216 changed files with 4,665 additions and 3,301 deletions.
45 changes: 45 additions & 0 deletions Content.Client/Guidebook/GuidebookDataSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Content.Shared.Guidebook;

namespace Content.Client.Guidebook;

/// <summary>
/// Client system for storing and retrieving values extracted from entity prototypes
/// for display in the guidebook (<see cref="RichText.ProtodataTag"/>).
/// Requests data from the server on <see cref="Initialize"/>.
/// Can also be pushed new data when the server reloads prototypes.
/// </summary>
public sealed class GuidebookDataSystem : EntitySystem
{
private GuidebookData? _data;

public override void Initialize()
{
base.Initialize();

SubscribeNetworkEvent<UpdateGuidebookDataEvent>(OnServerUpdated);

// Request data from the server
RaiseNetworkEvent(new RequestGuidebookDataEvent());
}

private void OnServerUpdated(UpdateGuidebookDataEvent args)
{
// Got new data from the server, either in response to our request, or because prototypes reloaded on the server
_data = args.Data;
_data.Freeze();
}

/// <summary>
/// Attempts to retrieve a value using the given identifiers.
/// See <see cref="GuidebookData.TryGetValue"/> for more information.
/// </summary>
public bool TryGetValue(string prototype, string component, string field, out object? value)
{
if (_data == null)
{
value = null;
return false;
}
return _data.TryGetValue(prototype, component, field, out value);
}
}
49 changes: 49 additions & 0 deletions Content.Client/Guidebook/Richtext/ProtodataTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Globalization;
using Robust.Client.UserInterface.RichText;
using Robust.Shared.Utility;

namespace Content.Client.Guidebook.RichText;

/// <summary>
/// RichText tag that can display values extracted from entity prototypes.
/// In order to be accessed by this tag, the desired field/property must
/// be tagged with <see cref="Shared.Guidebook.GuidebookDataAttribute"/>.
/// </summary>
public sealed class ProtodataTag : IMarkupTag
{
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IEntityManager _entMan = default!;

public string Name => "protodata";
private ISawmill Log => _log ??= _logMan.GetSawmill("protodata_tag");
private ISawmill? _log;

public string TextBefore(MarkupNode node)
{
// Do nothing with an empty tag
if (!node.Value.TryGetString(out var prototype))
return string.Empty;

if (!node.Attributes.TryGetValue("comp", out var component))
return string.Empty;
if (!node.Attributes.TryGetValue("member", out var member))
return string.Empty;
node.Attributes.TryGetValue("format", out var format);

var guidebookData = _entMan.System<GuidebookDataSystem>();

// Try to get the value
if (!guidebookData.TryGetValue(prototype, component.StringValue!, member.StringValue!, out var value))
{
Log.Error($"Failed to find protodata for {component}.{member} in {prototype}");
return "???";
}

// If we have a format string and a formattable value, format it as requested
if (!string.IsNullOrEmpty(format.StringValue) && value is IFormattable formattable)
return formattable.ToString(format.StringValue, CultureInfo.CurrentCulture);

// No format string given, so just use default ToString
return value?.ToString() ?? "NULL";
}
}
12 changes: 10 additions & 2 deletions Content.Client/PDA/PdaBoundUserInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
using Content.Shared.PDA;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;

namespace Content.Client.PDA
{
[UsedImplicitly]
public sealed class PdaBoundUserInterface : CartridgeLoaderBoundUserInterface
{
private readonly PdaSystem _pdaSystem;

[ViewVariables]
private PdaMenu? _menu;

public PdaBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_pdaSystem = EntMan.System<PdaSystem>();
}

protected override void Open()
Expand Down Expand Up @@ -92,7 +94,13 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (state is not PdaUpdateState updateState)
return;

_menu?.UpdateState(updateState);
if (_menu == null)
{
_pdaSystem.Log.Error("PDA state received before menu was created.");
return;
}

_menu.UpdateState(updateState);
}

protected override void AttachCartridgeUI(Control cartridgeUIFragment, string? title)
Expand Down
3 changes: 3 additions & 0 deletions Content.Client/PDA/PdaMenu.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,17 @@
Description="{Loc 'comp-pda-ui-ringtone-button-description'}"/>
<pda:PdaSettingsButton Name="ActivateMusicButton"
Access="Public"
Visible="False"
Text="{Loc 'pda-bound-user-interface-music-button'}"
Description="{Loc 'pda-bound-user-interface-music-button-description'}"/>
<pda:PdaSettingsButton Name="ShowUplinkButton"
Access="Public"
Visible="False"
Text="{Loc 'pda-bound-user-interface-show-uplink-title'}"
Description="{Loc 'pda-bound-user-interface-show-uplink-description'}"/>
<pda:PdaSettingsButton Name="LockUplinkButton"
Access="Public"
Visible="False"
Text="{Loc 'pda-bound-user-interface-lock-uplink-title'}"
Description="{Loc 'pda-bound-user-interface-lock-uplink-description'}"/>
</BoxContainer>
Expand Down
201 changes: 100 additions & 101 deletions Content.Client/Physics/Controllers/MoverController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,132 +8,131 @@
using Robust.Shared.Player;
using Robust.Shared.Timing;

namespace Content.Client.Physics.Controllers
namespace Content.Client.Physics.Controllers;

public sealed class MoverController : SharedMoverController
{
public sealed class MoverController : SharedMoverController
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerAttachedEvent>(OnRelayPlayerAttached);
SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerDetachedEvent>(OnRelayPlayerDetached);
SubscribeLocalEvent<InputMoverComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<InputMoverComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);

SubscribeLocalEvent<InputMoverComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
SubscribeLocalEvent<MovementRelayTargetComponent, UpdateIsPredictedEvent>(OnUpdateRelayTargetPredicted);
SubscribeLocalEvent<PullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
}
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerAttachedEvent>(OnRelayPlayerAttached);
SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerDetachedEvent>(OnRelayPlayerDetached);
SubscribeLocalEvent<InputMoverComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<InputMoverComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);

SubscribeLocalEvent<InputMoverComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
SubscribeLocalEvent<MovementRelayTargetComponent, UpdateIsPredictedEvent>(OnUpdateRelayTargetPredicted);
SubscribeLocalEvent<PullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
}

private void OnUpdatePredicted(Entity<InputMoverComponent> entity, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is controlled by the player
if (entity.Owner == _playerManager.LocalEntity)
args.IsPredicted = true;
}
private void OnUpdatePredicted(Entity<InputMoverComponent> entity, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is controlled by the player
if (entity.Owner == _playerManager.LocalEntity)
args.IsPredicted = true;
}

private void OnUpdateRelayTargetPredicted(Entity<MovementRelayTargetComponent> entity, ref UpdateIsPredictedEvent args)
{
if (entity.Comp.Source == _playerManager.LocalEntity)
args.IsPredicted = true;
}
private void OnUpdateRelayTargetPredicted(Entity<MovementRelayTargetComponent> entity, ref UpdateIsPredictedEvent args)
{
if (entity.Comp.Source == _playerManager.LocalEntity)
args.IsPredicted = true;
}

private void OnUpdatePullablePredicted(Entity<PullableComponent> entity, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is being pulled by the player.
// Disable prediction if an entity is being pulled by some non-player entity.
private void OnUpdatePullablePredicted(Entity<PullableComponent> entity, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is being pulled by the player.
// Disable prediction if an entity is being pulled by some non-player entity.

if (entity.Comp.Puller == _playerManager.LocalEntity)
args.IsPredicted = true;
else if (entity.Comp.Puller != null)
args.BlockPrediction = true;
if (entity.Comp.Puller == _playerManager.LocalEntity)
args.IsPredicted = true;
else if (entity.Comp.Puller != null)
args.BlockPrediction = true;

// TODO recursive pulling checks?
// What if the entity is being pulled by a vehicle controlled by the player?
}
// TODO recursive pulling checks?
// What if the entity is being pulled by a vehicle controlled by the player?
}

private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}
private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}

private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}
private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}

private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
SetMoveInput(entity, MoveButtons.None);
}
private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
SetMoveInput(entity, MoveButtons.None);
}

private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
SetMoveInput(entity, MoveButtons.None);
}
private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
SetMoveInput(entity, MoveButtons.None);
}

public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);

if (_playerManager.LocalEntity is not {Valid: true} player)
return;
if (_playerManager.LocalEntity is not {Valid: true} player)
return;

if (RelayQuery.TryGetComponent(player, out var relayMover))
HandleClientsideMovement(relayMover.RelayEntity, frameTime);
if (RelayQuery.TryGetComponent(player, out var relayMover))
HandleClientsideMovement(relayMover.RelayEntity, frameTime);

HandleClientsideMovement(player, frameTime);
}
HandleClientsideMovement(player, frameTime);
}

private void HandleClientsideMovement(EntityUid player, float frameTime)
private void HandleClientsideMovement(EntityUid player, float frameTime)
{
if (!MoverQuery.TryGetComponent(player, out var mover) ||
!XformQuery.TryGetComponent(player, out var xform))
{
if (!MoverQuery.TryGetComponent(player, out var mover) ||
!XformQuery.TryGetComponent(player, out var xform))
{
return;
}

var physicsUid = player;
PhysicsComponent? body;
var xformMover = xform;
return;
}

if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
{
if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
!XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
{
return;
}
var physicsUid = player;
PhysicsComponent? body;
var xformMover = xform;

physicsUid = xform.ParentUid;
}
else if (!PhysicsQuery.TryGetComponent(player, out body))
if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
{
if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
!XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
{
return;
}

// Server-side should just be handled on its own so we'll just do this shizznit
HandleMobMovement(
player,
mover,
physicsUid,
body,
xformMover,
frameTime);
physicsUid = xform.ParentUid;
}

protected override bool CanSound()
else if (!PhysicsQuery.TryGetComponent(player, out body))
{
return _timing is { IsFirstTimePredicted: true, InSimulation: true };
return;
}

// Server-side should just be handled on its own so we'll just do this shizznit
HandleMobMovement(
player,
mover,
physicsUid,
body,
xformMover,
frameTime);
}

protected override bool CanSound()
{
return _timing is { IsFirstTimePredicted: true, InSimulation: true };
}
}
3 changes: 0 additions & 3 deletions Content.Client/Power/ActivatableUIRequiresPowerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ protected override void OnActivate(Entity<ActivatableUIRequiresPowerComponent> e
return;
}

if (TryComp<WiresPanelComponent>(ent.Owner, out var panel) && panel.Open)
return;

_popup.PopupClient(Loc.GetString("base-computer-ui-component-not-powered", ("machine", ent.Owner)), args.User, args.User);
args.Cancel();
}
Expand Down
Loading

0 comments on commit ffd2bcc

Please sign in to comment.