Use EntityUID for all ghost warps (#11113)

This commit is contained in:
Illiux
2022-09-10 14:47:17 -07:00
committed by GitHub
parent 4cc5fa239e
commit acd24bed4d
4 changed files with 77 additions and 138 deletions

View File

@@ -103,8 +103,7 @@ namespace Content.Client.Ghost
if (window != null) if (window != null)
{ {
window.Locations = msg.Locations; window.UpdateWarps(msg.Warps);
window.Players = msg.Players;
window.Populate(); window.Populate();
} }
} }

View File

@@ -1,14 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
namespace Content.Client.Ghost.UI namespace Content.Client.Ghost.UI
{ {
@@ -17,9 +12,7 @@ namespace Content.Client.Ghost.UI
{ {
private readonly IEntityNetworkManager _netManager; private readonly IEntityNetworkManager _netManager;
public List<string> Locations { get; set; } = new(); private List<(string, EntityUid)> _warps = new();
public Dictionary<EntityUid, string> Players { get; set; } = new();
public GhostTargetWindow(IEntityNetworkManager netManager) public GhostTargetWindow(IEntityNetworkManager netManager)
{ {
@@ -28,58 +21,37 @@ namespace Content.Client.Ghost.UI
_netManager = netManager; _netManager = netManager;
} }
public void UpdateWarps(IEnumerable<GhostWarp> warps)
{
// Server COULD send these sorted but how about we just use the client to do it instead
_warps = warps
.OrderBy(w => w.IsWarpPoint)
.ThenBy(w => w.DisplayName, Comparer<string>.Create(
(x, y) => string.Compare(x, y, StringComparison.Ordinal)))
.Select(w =>
{
var name = w.IsWarpPoint
? Loc.GetString("ghost-target-window-current-button", ("name", w.DisplayName))
: w.DisplayName;
return (name, w.Entity);
})
.ToList();
}
public void Populate() public void Populate()
{ {
ButtonContainer.DisposeAllChildren(); ButtonContainer.DisposeAllChildren();
AddButtonPlayers(); AddButtons();
AddButtonLocations();
} }
private void AddButtonPlayers() private void AddButtons()
{ {
var sortedPlayers = new List<(string, EntityUid)>(Players.Count); foreach (var (name, warp) in _warps)
foreach (var (key, player) in Players)
{
sortedPlayers.Add((player, key));
}
sortedPlayers.Sort((x, y) => string.Compare(x.Item1, y.Item1, StringComparison.Ordinal));
foreach (var (key, player) in sortedPlayers)
{ {
var currentButtonRef = new Button var currentButtonRef = new Button
{ {
Text = key, Text = name,
TextAlign = Label.AlignMode.Right,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
SizeFlagsStretchRatio = 1,
MinSize = (340, 20),
ClipText = true,
};
currentButtonRef.OnPressed += (_) =>
{
var msg = new GhostWarpToTargetRequestEvent(player);
_netManager.SendSystemNetworkMessage(msg);
};
ButtonContainer.AddChild(currentButtonRef);
}
}
private void AddButtonLocations()
{
// Server COULD send these sorted but how about we just use the client to do it instead.
var sortedLocations = new List<string>(Locations);
sortedLocations.Sort((x, y) => string.Compare(x, y, StringComparison.Ordinal));
foreach (var name in sortedLocations)
{
var currentButtonRef = new Button
{
Text = Loc.GetString("ghost-target-window-current-button", ("name", name)),
TextAlign = Label.AlignMode.Right, TextAlign = Label.AlignMode.Right,
HorizontalAlignment = HAlignment.Center, HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center, VerticalAlignment = VAlignment.Center,
@@ -90,7 +62,7 @@ namespace Content.Client.Ghost.UI
currentButtonRef.OnPressed += _ => currentButtonRef.OnPressed += _ =>
{ {
var msg = new GhostWarpToLocationRequestEvent(name); var msg = new GhostWarpToTargetRequestEvent(warp);
_netManager.SendSystemNetworkMessage(msg); _netManager.SendSystemNetworkMessage(msg);
}; };

View File

@@ -48,7 +48,6 @@ namespace Content.Server.Ghost
SubscribeNetworkEvent<GhostWarpsRequestEvent>(OnGhostWarpsRequest); SubscribeNetworkEvent<GhostWarpsRequestEvent>(OnGhostWarpsRequest);
SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest); SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest);
SubscribeNetworkEvent<GhostWarpToLocationRequestEvent>(OnGhostWarpToLocationRequest);
SubscribeNetworkEvent<GhostWarpToTargetRequestEvent>(OnGhostWarpToTargetRequest); SubscribeNetworkEvent<GhostWarpToTargetRequestEvent>(OnGhostWarpToTargetRequest);
SubscribeLocalEvent<GhostComponent, BooActionEvent>(OnActionPerform); SubscribeLocalEvent<GhostComponent, BooActionEvent>(OnActionPerform);
@@ -157,7 +156,7 @@ namespace Content.Server.Ghost
return; return;
} }
var response = new GhostWarpsResponseEvent(GetLocationNames().ToList(), GetPlayerWarps(entity)); var response = new GhostWarpsResponseEvent(GetPlayerWarps(entity).Concat(GetLocationWarps()).ToList());
RaiseNetworkEvent(response, args.SenderSession.ConnectedClient); RaiseNetworkEvent(response, args.SenderSession.ConnectedClient);
} }
@@ -175,37 +174,6 @@ namespace Content.Server.Ghost
actor.PlayerSession.ContentData()!.Mind?.UnVisit(); actor.PlayerSession.ContentData()!.Mind?.UnVisit();
} }
private void OnGhostWarpToLocationRequest(GhostWarpToLocationRequestEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not {Valid: true} attached ||
!EntityManager.TryGetComponent(attached, out GhostComponent? ghost))
{
Logger.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Name} without being a ghost.");
return;
}
// TODO: why the fuck is this using the name instead of an entity id or something?
// at least it makes sense for the warp command to need to use names, but not this.
if (FindLocation(msg.Name) is not { } warp)
{
Logger.Warning($"User {args.SenderSession.Name} tried to warp to an invalid warp: {msg.Name}");
return;
}
if (warp.Follow)
{
_followerSystem.StartFollowingEntity(attached, warp.Owner);
return;
}
var xform = Transform(attached);
xform.Coordinates = Transform(warp.Owner).Coordinates;
xform.AttachToGridOrMap();
if (TryComp(attached, out PhysicsComponent? physics))
physics.LinearVelocity = Vector2.Zero;
}
private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args) private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
{ {
if (args.SenderSession.AttachedEntity is not {Valid: true} attached || if (args.SenderSession.AttachedEntity is not {Valid: true} attached ||
@@ -221,7 +189,18 @@ namespace Content.Server.Ghost
return; return;
} }
_followerSystem.StartFollowingEntity(ghost.Owner, msg.Target); if (TryComp(msg.Target, out WarpPointComponent? warp) && warp.Follow
|| HasComp<MobStateComponent>(msg.Target))
{
_followerSystem.StartFollowingEntity(ghost.Owner, msg.Target);
return;
}
var xform = Transform(ghost.Owner);
xform.Coordinates = Transform(msg.Target).Coordinates;
xform.AttachToGridOrMap();
if (TryComp(attached, out PhysicsComponent? physics))
physics.LinearVelocity = Vector2.Zero;
} }
private void DeleteEntity(EntityUid uid) private void DeleteEntity(EntityUid uid)
@@ -234,50 +213,33 @@ namespace Content.Server.Ghost
EntityManager.DeleteEntity(uid); EntityManager.DeleteEntity(uid);
} }
private IEnumerable<string> GetLocationNames() private IEnumerable<GhostWarp> GetLocationWarps()
{ {
foreach (var warp in EntityManager.EntityQuery<WarpPointComponent>(true)) foreach (var warp in EntityManager.EntityQuery<WarpPointComponent>(true))
{ {
if (warp.Location != null) if (warp.Location != null)
{ {
yield return warp.Location; yield return new GhostWarp(warp.Owner, warp.Location, true);
} }
} }
} }
private WarpPointComponent? FindLocation(string name) private IEnumerable<GhostWarp> GetPlayerWarps(EntityUid except)
{ {
foreach (var warp in EntityManager.EntityQuery<WarpPointComponent>(true))
{
if (warp.Location == name)
{
return warp;
}
}
return null;
}
private Dictionary<EntityUid, string> GetPlayerWarps(EntityUid except)
{
var players = new Dictionary<EntityUid, string>();
foreach (var player in _playerManager.Sessions) foreach (var player in _playerManager.Sessions)
{ {
if (player.AttachedEntity is {Valid: true} attached) if (player.AttachedEntity is {Valid: true} attached)
{ {
if (attached == except) continue;
TryComp<MindComponent>(attached, out var mind); TryComp<MindComponent>(attached, out var mind);
string playerInfo = $"{EntityManager.GetComponent<MetaDataComponent>(attached).EntityName} ({mind?.Mind?.CurrentJob?.Name ?? "Unknown"})"; string playerInfo = $"{EntityManager.GetComponent<MetaDataComponent>(attached).EntityName} ({mind?.Mind?.CurrentJob?.Name ?? "Unknown"})";
if (TryComp<MobStateComponent>(attached, out var state) && !state.IsDead()) if (TryComp<MobStateComponent>(attached, out var state) && !state.IsDead())
players.Add(attached, playerInfo); yield return new GhostWarp(attached, playerInfo, false);
} }
} }
players.Remove(except);
return players;
} }
public void OnEntityStorageInsertAttempt(EntityUid uid, GhostComponent comp, InsertIntoEntityStorageAttemptEvent args) public void OnEntityStorageInsertAttempt(EntityUid uid, GhostComponent comp, InsertIntoEntityStorageAttemptEvent args)

View File

@@ -43,6 +43,35 @@ namespace Content.Shared.Ghost
{ {
} }
/// <summary>
/// An individual place a ghost can warp to.
/// This is used as part of <see cref="GhostWarpsResponseEvent"/>
/// </summary>
[Serializable, NetSerializable]
public struct GhostWarp
{
public GhostWarp(EntityUid entity, string displayName, bool isWarpPoint)
{
Entity = entity;
DisplayName = displayName;
IsWarpPoint = isWarpPoint;
}
/// <summary>
/// The entity representing the warp point.
/// This is passed back to the server in <see cref="GhostWarpToTargetRequestEvent"/>
/// </summary>
public EntityUid Entity { get; }
/// <summary>
/// The display name to be surfaced in the ghost warps menu
/// </summary>
public string DisplayName { get; }
/// <summary>
/// Whether this warp represents a warp point or a player
/// </summary>
public bool IsWarpPoint { get; }
}
/// <summary> /// <summary>
/// A server to client response for a <see cref="GhostWarpsRequestEvent"/>. /// A server to client response for a <see cref="GhostWarpsRequestEvent"/>.
/// Contains players, and locations a ghost can warp to /// Contains players, and locations a ghost can warp to
@@ -50,38 +79,15 @@ namespace Content.Shared.Ghost
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class GhostWarpsResponseEvent : EntityEventArgs public sealed class GhostWarpsResponseEvent : EntityEventArgs
{ {
public GhostWarpsResponseEvent(List<string> locations, Dictionary<EntityUid, string> players) public GhostWarpsResponseEvent(List<GhostWarp> warps)
{ {
Locations = locations; Warps = warps;
Players = players;
} }
/// <summary> /// <summary>
/// A list of location names that can be warped to. /// A list of warp points.
/// </summary> /// </summary>
public List<string> Locations { get; } public List<GhostWarp> Warps { get; }
/// <summary>
/// A dictionary containing the entity id, and name of players that can be warped to.
/// </summary>
public Dictionary<EntityUid, string> Players { get; }
}
/// <summary>
/// A client to server request for their ghost to be warped to a location
/// </summary>
[Serializable, NetSerializable]
public sealed class GhostWarpToLocationRequestEvent : EntityEventArgs
{
/// <summary>
/// The location name to warp to.
/// </summary>
public string Name { get; }
public GhostWarpToLocationRequestEvent(string locationName)
{
Name = locationName;
}
} }
/// <summary> /// <summary>