Disposal mailing (#2194)

* Implement device networking

* Implement device configuration menu

* Fix device network

* Implement disposal mailing unit

* Implement base network connection
Implement wired and wireless network connection
Implement device network metadata

* Fix dereference null error

* Fix wired network null checks

* Change BaseNetworks enum to NetworkUtils class
Add PingResponse function to NetworkUtils
Change device network file structure

* Add doc comments

* Apply suggestions from code review

Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>

* Add tag validation to disposal mailing unit

* Add tag validation to the mailing unit component

* Address reviews
Change WiredNetwork can connect check
Change device networking string literals to constants

* Address reviews
Revert changes to PowerProvider and PowerReceiver
Add new NodeGroup
WELP

* Fix recursive access to Owner property

* Integrate suggested changes

* Fix TryGetWireNet acting on NullPowerProvider
Fix network connections not checking if their owner has been deleted

* Close device network connection when the owning entity got deleted
Fix mailing unit not closing the device network connection on remove

* Remove GetWireNet from NullPowerProvider

Co-authored-by: Julian Giebel <j.giebel@netrocks.info>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
Julian Giebel
2020-10-30 01:16:26 +01:00
committed by GitHub
parent 1b3cbd4d3a
commit 45b610f933
31 changed files with 2450 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using Content.Client.GameObjects.Components.Disposal;
using Content.Client.GameObjects.Components.MedicalScanner;
using Content.Shared.GameObjects.Components.Body;
@@ -14,7 +14,8 @@ namespace Content.Client.GameObjects.Components.Body
public bool CanDrop(CanDropEventArgs eventArgs)
{
if (eventArgs.Target.HasComponent<DisposalUnitComponent>() ||
eventArgs.Target.HasComponent<MedicalScannerComponent>())
eventArgs.Target.HasComponent<MedicalScannerComponent>() ||
eventArgs.Target.HasComponent<DisposalMailingUnitComponent>())
{
return true;
}

View File

@@ -0,0 +1,55 @@
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using static Content.Shared.GameObjects.Components.SharedConfigurationComponent;
namespace Content.Client.GameObjects.Components.Wires
{
public class ConfigurationBoundUserInterface : BoundUserInterface
{
public Regex Validation { get; internal set; }
public ConfigurationBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private ConfigurationMenu _menu;
protected override void Open()
{
base.Open();
_menu = new ConfigurationMenu(this);
_menu.OnClose += Close;
_menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_menu.Populate(state as ConfigurationBoundUserInterfaceState);
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
base.ReceiveMessage(message);
if (message is ValidationUpdateMessage msg)
{
Validation = new Regex(msg.ValidationString, RegexOptions.Compiled);
}
}
public void SendConfiguration(Dictionary<string, string> config)
{
SendMessage(new ConfigurationUpdatedMessage(config));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_menu.Close();
}
}
}

View File

@@ -0,0 +1,176 @@
using System.Collections.Generic;
using Namotion.Reflection;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.SharedConfigurationComponent;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.GameObjects.Components.Wires
{
public class ConfigurationMenu : SS14Window
{
public ConfigurationBoundUserInterface Owner { get; }
private readonly VBoxContainer _baseContainer;
private readonly VBoxContainer _column;
private readonly HBoxContainer _row;
private readonly List<(string name, LineEdit input)> _inputs;
protected override Vector2? CustomSize => (300, 250);
public ConfigurationMenu(ConfigurationBoundUserInterface owner)
{
Owner = owner;
_inputs = new List<(string name, LineEdit input)>();
Title = Loc.GetString("Device Configuration");
var margin = new MarginContainer
{
MarginBottomOverride = 8,
MarginLeftOverride = 8,
MarginRightOverride = 8,
MarginTopOverride = 8
};
_baseContainer = new VBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand
};
_column = new VBoxContainer
{
SeparationOverride = 16,
SizeFlagsVertical = SizeFlags.Fill
};
_row = new HBoxContainer
{
SeparationOverride = 16,
SizeFlagsHorizontal = SizeFlags.FillExpand
};
var buttonRow = new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand
};
var spacer1 = new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.Expand
};
var spacer2 = new HBoxContainer()
{
SizeFlagsHorizontal = SizeFlags.Expand
};
var confirmButton = new Button
{
Text = Loc.GetString("Confirm"),
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
confirmButton.OnButtonUp += OnConfirm;
buttonRow.AddChild(spacer1);
buttonRow.AddChild(confirmButton);
buttonRow.AddChild(spacer2);
var outerColumn = new ScrollContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
ModulateSelfOverride = Color.FromHex("#202025")
};
margin.AddChild(_column);
outerColumn.AddChild(margin);
_baseContainer.AddChild(outerColumn);
_baseContainer.AddChild(buttonRow);
Contents.AddChild(_baseContainer);
}
public void Populate(ConfigurationBoundUserInterfaceState state)
{
_column.Children.Clear();
_inputs.Clear();
foreach (var field in state.Config)
{
var margin = new MarginContainer
{
MarginRightOverride = 8
};
var label = new Label
{
Name = field.Key,
Text = field.Key + ":",
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = .2f,
CustomMinimumSize = new Vector2(60, 0)
};
var input = new LineEdit
{
Name = field.Key + "-input",
Text = field.Value,
IsValid = Validate,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = .8f
};
_inputs.Add((field.Key, input));
var row = new HBoxContainer();
CopyProperties(_row, row);
margin.AddChild(label);
row.AddChild(margin);
row.AddChild(input);
_column.AddChild(row);
}
}
private void OnConfirm(ButtonEventArgs args)
{
var config = GenerateDictionary<string, LineEdit>(_inputs, "Text");
Owner.SendConfiguration(config);
Close();
}
private bool Validate(string value)
{
return Owner.Validation == null || Owner.Validation.IsMatch(value);
}
private Dictionary<string, TConfig> GenerateDictionary<TConfig, TInput>(List<(string name, TInput input)> inputs, string propertyName) where TInput : Control
{
var dictionary = new Dictionary<string, TConfig>();
foreach (var input in inputs)
{
var value = input.input.TryGetPropertyValue<TConfig>(propertyName);
dictionary.Add(input.name, value);
}
return dictionary;
}
private static void CopyProperties<T>(T from, T to) where T : Control
{
foreach (var property in from.AllAttachedProperties)
{
to.SetValue(property.Key, property.Value);
}
}
}
}

View File

@@ -0,0 +1,73 @@
#nullable enable
using JetBrains.Annotations;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects.Components.UserInterface;
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalMailingUnitComponent;
namespace Content.Client.GameObjects.Components.Disposal
{
/// <summary>
/// Initializes a <see cref="DisposalMailingUnitWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public class DisposalMailingUnitBoundUserInterface : BoundUserInterface
{
private DisposalMailingUnitWindow? _window;
public DisposalMailingUnitBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private void ButtonPressed(UiButton button)
{
SendMessage(new UiButtonPressedMessage(button));
}
protected override void Open()
{
base.Open();
_window = new DisposalMailingUnitWindow();
_window.OpenCentered();
_window.OnClose += Close;
_window.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
_window.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
_window.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
_window.TargetListContainer.OnItemSelected += TargetSelected;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (!(state is DisposalMailingUnitBoundUserInterfaceState cast))
{
return;
}
_window?.UpdateState(cast);
}
private void TargetSelected(ItemList.ItemListSelectedEventArgs item)
{
SendMessage(new UiTargetUpdateMessage(_window?.TargetList[item.ItemIndex]));
//(ノ°Д°)ノ︵ ┻━┻
if (_window != null) _window.Engage.Disabled = false;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window?.Dispose();
}
}
}
}

View File

@@ -0,0 +1,11 @@
using Content.Shared.GameObjects.Components.Disposal;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Disposal
{
[RegisterComponent]
[ComponentReference(typeof(SharedDisposalMailingUnitComponent))]
public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent
{
}
}

View File

@@ -0,0 +1,285 @@
using Content.Shared.GameObjects.Components.Disposal;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using System.Collections.Generic;
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalMailingUnitComponent;
namespace Content.Client.GameObjects.Components.Disposal
{
/// <summary>
/// Client-side UI used to control a <see cref="SharedDisposalMailingUnitComponent"/>
/// </summary>
public class DisposalMailingUnitWindow : SS14Window
{
private readonly Label _unitState;
private readonly ProgressBar _pressureBar;
private readonly Label _pressurePercentage;
public readonly Button Engage;
public readonly Button Eject;
public readonly Button Power;
public readonly ItemList TargetListContainer;
public List<string> TargetList;
private readonly Label _tagLabel;
protected override Vector2? CustomSize => (460, 220);
public DisposalMailingUnitWindow()
{
TargetList = new List<string>();
Contents.AddChild(new HBoxContainer
{
Children =
{
new MarginContainer
{
MarginLeftOverride = 8,
MarginRightOverride = 8,
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new HBoxContainer
{
Children =
{
new Label {Text = Loc.GetString("State: ")},
new Control {CustomMinimumSize = (4, 0)},
(_unitState = new Label {Text = Loc.GetString("Ready")})
}
},
new Control {CustomMinimumSize = (0, 10)},
new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Label {Text = Loc.GetString("Pressure:")},
new Control {CustomMinimumSize = (4, 0)},
(_pressureBar = new ProgressBar
{
CustomMinimumSize = (100, 20),
SizeFlagsHorizontal = SizeFlags.FillExpand,
MinValue = 0,
MaxValue = 1,
Page = 0,
Value = 0.5f,
Children =
{
(_pressurePercentage = new Label())
}
})
}
},
new Control {CustomMinimumSize = (0, 10)},
new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Label {Text = Loc.GetString("Handle:")},
new Control {
CustomMinimumSize = (4, 0),
SizeFlagsHorizontal = SizeFlags.FillExpand
},
(Engage = new Button
{
CustomMinimumSize = (16, 0),
Text = Loc.GetString("Engage"),
ToggleMode = true,
Disabled = true
})
}
},
new Control {CustomMinimumSize = (0, 10)},
new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Label {Text = Loc.GetString("Eject:")},
new Control {
CustomMinimumSize = (4, 0),
SizeFlagsHorizontal = SizeFlags.FillExpand
},
(Eject = new Button {
CustomMinimumSize = (16, 0),
Text = Loc.GetString("Eject Contents"),
//SizeFlagsHorizontal = SizeFlags.ShrinkEnd
})
}
},
new Control {CustomMinimumSize = (0, 10)},
new HBoxContainer
{
Children =
{
(Power = new CheckButton {Text = Loc.GetString("Power")}),
}
}
}
},
}
},
new MarginContainer
{
MarginLeftOverride = 12,
MarginRightOverride = 8,
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.Fill,
Children =
{
new HBoxContainer
{
Children =
{
new Label
{
Text = Loc.GetString("Select a destination:")
}
}
},
new Control { CustomMinimumSize = new Vector2(0, 8) },
new HBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
Children =
{
(TargetListContainer = new ItemList
{
SelectMode = ItemList.ItemListSelectMode.Single,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand
})
}
},
new PanelContainer
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = Color.FromHex("#ACBDBA")
},
SizeFlagsHorizontal = SizeFlags.FillExpand,
CustomMinimumSize = new Vector2(0, 1),
},
new HBoxContainer
{
Children =
{
new VBoxContainer
{
Children =
{
new MarginContainer
{
MarginLeftOverride = 4,
Children =
{
new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Label
{
Text = Loc.GetString("This unit:")
},
new Control
{
CustomMinimumSize = new Vector2(4, 0)
},
(_tagLabel = new Label
{
Text = "-",
SizeFlagsVertical = SizeFlags.ShrinkEnd
})
}
}
}
},
}
}
}
}
}
}
}
}
}
});
}
private void UpdatePressureBar(float pressure)
{
_pressureBar.Value = pressure;
var normalized = pressure / _pressureBar.MaxValue;
const float leftHue = 0.0f; // Red
const float middleHue = 0.066f; // Orange
const float rightHue = 0.33f; // Green
const float saturation = 1.0f; // Uniform saturation
const float value = 0.8f; // Uniform value / brightness
const float alpha = 1.0f; // Uniform alpha
// These should add up to 1.0 or your transition won't be smooth
const float leftSideSize = 0.5f; // Fraction of _chargeBar lerped from leftHue to middleHue
const float rightSideSize = 0.5f; // Fraction of _chargeBar lerped from middleHue to rightHue
float finalHue;
if (normalized <= leftSideSize)
{
normalized /= leftSideSize; // Adjust range to 0.0 to 1.0
finalHue = MathHelper.Lerp(leftHue, middleHue, normalized);
}
else
{
normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
finalHue = MathHelper.Lerp(middleHue, rightHue, normalized);
}
// Check if null first to avoid repeatedly creating this.
_pressureBar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
var foregroundStyleBoxOverride = (StyleBoxFlat) _pressureBar.ForegroundStyleBoxOverride;
foregroundStyleBoxOverride.BackgroundColor =
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
var percentage = pressure / _pressureBar.MaxValue * 100;
_pressurePercentage.Text = $" {percentage:0}%";
}
public void UpdateState(DisposalMailingUnitBoundUserInterfaceState state)
{
Title = state.UnitName;
_unitState.Text = state.UnitState;
UpdatePressureBar(state.Pressure);
Power.Pressed = state.Powered;
Engage.Pressed = state.Engaged;
PopulateTargetList(state.Tags);
_tagLabel.Text = state.Tag;
TargetList = state.Tags;
}
private void PopulateTargetList(List<string> tags)
{
TargetListContainer.Clear();
foreach (var target in tags)
{
TargetListContainer.AddItem(target);
}
}
}
}