Drop ICharacterProfile/ICharacterAppearance interfaces (#42661)

This commit is contained in:
pathetic meowmeow
2026-01-26 15:15:44 -05:00
committed by GitHub
parent 4ddac1463c
commit 4e9a456591
15 changed files with 56 additions and 135 deletions

View File

@@ -44,7 +44,7 @@ namespace Content.Client.Lobby
}
}
public void SelectCharacter(ICharacterProfile profile)
public void SelectCharacter(HumanoidCharacterProfile profile)
{
SelectCharacter(Preferences.IndexOfCharacter(profile));
}
@@ -59,11 +59,11 @@ namespace Content.Client.Lobby
_netManager.ClientSendMessage(msg);
}
public void UpdateCharacter(ICharacterProfile profile, int slot)
public void UpdateCharacter(HumanoidCharacterProfile profile, int slot)
{
var collection = IoCManager.Instance!;
profile.EnsureValid(_playerManager.LocalSession!, collection);
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
var characters = new Dictionary<int, HumanoidCharacterProfile>(Preferences.Characters) {[slot] = profile};
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor, Preferences.ConstructionFavorites);
var msg = new MsgUpdateCharacter
{
@@ -73,9 +73,9 @@ namespace Content.Client.Lobby
_netManager.ClientSendMessage(msg);
}
public void CreateCharacter(ICharacterProfile profile)
public void CreateCharacter(HumanoidCharacterProfile profile)
{
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters);
var characters = new Dictionary<int, HumanoidCharacterProfile>(Preferences.Characters);
var lowest = Enumerable.Range(0, Settings.MaxCharacterSlots)
.Except(characters.Keys)
.FirstOrNull();
@@ -92,7 +92,7 @@ namespace Content.Client.Lobby
UpdateCharacter(profile, l);
}
public void DeleteCharacter(ICharacterProfile profile)
public void DeleteCharacter(HumanoidCharacterProfile profile)
{
DeleteCharacter(Preferences.IndexOfCharacter(profile));
}

View File

@@ -13,11 +13,11 @@ namespace Content.Client.Lobby
GameSettings? Settings { get; }
PlayerPreferences? Preferences { get; }
void Initialize();
void SelectCharacter(ICharacterProfile profile);
void SelectCharacter(HumanoidCharacterProfile profile);
void SelectCharacter(int slot);
void UpdateCharacter(ICharacterProfile profile, int slot);
void CreateCharacter(ICharacterProfile profile);
void DeleteCharacter(ICharacterProfile profile);
void UpdateCharacter(HumanoidCharacterProfile profile, int slot);
void CreateCharacter(HumanoidCharacterProfile profile);
void DeleteCharacter(HumanoidCharacterProfile profile);
void DeleteCharacter(int slot);
void UpdateConstructionFavorites(List<ProtoId<ConstructionPrototype>> favorites);
}

View File

@@ -30,7 +30,7 @@ public sealed partial class CharacterPickerButton : ContainerButton
IPrototypeManager prototypeManager,
ISharedPlayerManager playerMan,
ButtonGroup group,
ICharacterProfile profile,
HumanoidCharacterProfile profile,
bool isSelected)
{
RobustXamlLoader.Load(this);
@@ -41,15 +41,11 @@ public sealed partial class CharacterPickerButton : ContainerButton
View.LoadPreview(profile);
if (profile is HumanoidCharacterProfile humanoid)
var highPriorityJob = profile.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
if (highPriorityJob != default)
{
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
if (highPriorityJob != default)
{
var jobName = prototypeManager.Index(highPriorityJob).LocalizedName;
description = $"{description}\n{jobName}";
}
var jobName = prototypeManager.Index(highPriorityJob).LocalizedName;
description = $"{description}\n{jobName}";
}
Pressed = isSelected;

View File

@@ -27,19 +27,12 @@ public sealed partial class ProfilePreviewSpriteView : SpriteView
/// <remarks>
/// This is expensive so not recommended to run if you have a slider.
/// </remarks>
public void LoadPreview(ICharacterProfile profile, JobPrototype? jobOverride = null, bool showClothes = true)
public void LoadPreview(HumanoidCharacterProfile profile, JobPrototype? jobOverride = null, bool showClothes = true)
{
EntMan.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
switch (profile)
{
case HumanoidCharacterProfile humanoid:
LoadHumanoidEntity(humanoid, jobOverride, showClothes);
break;
default:
throw new ArgumentException("Only humanoid profiles are implemented in ProfilePreviewSpriteView");
}
LoadHumanoidEntity(profile, jobOverride, showClothes);
SetEntity(PreviewDummy);
SetName(profile.Name);
@@ -56,16 +49,9 @@ public sealed partial class ProfilePreviewSpriteView : SpriteView
/// <summary>
/// A slim reload that only updates the entity itself and not any of the job entities, etc.
/// </summary>
public void ReloadProfilePreview(ICharacterProfile profile)
public void ReloadProfilePreview(HumanoidCharacterProfile profile)
{
switch (profile)
{
case HumanoidCharacterProfile humanoid:
ReloadHumanoidEntity(humanoid);
break;
default:
throw new ArgumentException("Only humanoid profiles are implemented in ProfilePreviewSpriteView");
}
ReloadHumanoidEntity(profile);
}
public void ClearPreview()

View File

@@ -75,17 +75,11 @@ public sealed class CharacterCreationTest
await pair.CleanReturnAsync();
}
private void AssertEqual(ICharacterProfile clientCharacter, HumanoidCharacterProfile b)
private void AssertEqual(HumanoidCharacterProfile a, HumanoidCharacterProfile b)
{
if (clientCharacter.MemberwiseEquals(b))
if (a.MemberwiseEquals(b))
return;
if (clientCharacter is not HumanoidCharacterProfile a)
{
Assert.Fail($"Not a {nameof(HumanoidCharacterProfile)}");
return;
}
Assert.Multiple(() =>
{
Assert.That(a.Name, Is.EqualTo(b.Name));
@@ -107,7 +101,7 @@ public sealed class CharacterCreationTest
private void AssertEqual(HumanoidCharacterAppearance a, HumanoidCharacterAppearance b)
{
if (a.MemberwiseEquals(b))
if (a.Equals(b))
return;
Assert.That(a.EyeColor, Is.EqualTo(b.EyeColor));

View File

@@ -66,7 +66,7 @@ namespace Content.Server.Database
return null;
var maxSlot = prefs.Profiles.Max(p => p.Slot) + 1;
var profiles = new Dictionary<int, ICharacterProfile>(maxSlot);
var profiles = new Dictionary<int, HumanoidCharacterProfile>(maxSlot);
foreach (var profile in prefs.Profiles)
{
profiles[profile.Slot] = await ConvertProfiles(profile);
@@ -88,23 +88,17 @@ namespace Content.Server.Database
await db.DbContext.SaveChangesAsync();
}
public async Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? profile, int slot)
public async Task SaveCharacterSlotAsync(NetUserId userId, HumanoidCharacterProfile? humanoid, int slot)
{
await using var db = await GetDb();
if (profile is null)
if (humanoid is null)
{
await DeleteCharacterSlot(db.DbContext, userId, slot);
await db.DbContext.SaveChangesAsync();
return;
}
if (profile is not HumanoidCharacterProfile humanoid)
{
// TODO: Handle other ICharacterProfile implementations properly
throw new NotImplementedException();
}
var oldProfile = db.DbContext.Profile
.Include(p => p.Preference)
.Where(p => p.Preference.UserId == userId.UserId)
@@ -145,7 +139,7 @@ namespace Content.Server.Database
db.Profile.Remove(profile);
}
public async Task<PlayerPreferences> InitPrefsAsync(NetUserId userId, ICharacterProfile defaultProfile)
public async Task<PlayerPreferences> InitPrefsAsync(NetUserId userId, HumanoidCharacterProfile defaultProfile)
{
await using var db = await GetDb();
@@ -164,7 +158,7 @@ namespace Content.Server.Database
await db.DbContext.SaveChangesAsync();
return new PlayerPreferences(new[] { new KeyValuePair<int, ICharacterProfile>(0, defaultProfile) }, 0, Color.FromHex(prefs.AdminOOCColor), []);
return new PlayerPreferences(new[] { new KeyValuePair<int, HumanoidCharacterProfile>(0, defaultProfile) }, 0, Color.FromHex(prefs.AdminOOCColor), []);
}
public async Task DeleteSlotAndSetSelectedIndex(NetUserId userId, int deleteSlot, int newSlot)
@@ -336,7 +330,7 @@ namespace Content.Server.Database
private Profile ConvertProfiles(HumanoidCharacterProfile humanoid, int slot, Profile? profile = null)
{
profile ??= new Profile();
var appearance = (HumanoidCharacterAppearance) humanoid.CharacterAppearance;
var appearance = humanoid.Appearance;
var dataNode = _serialization.WriteValue(appearance.Markings, alwaysWrite: true, notNullableOverride: true);
profile.CharacterName = humanoid.Name;

View File

@@ -38,12 +38,12 @@ namespace Content.Server.Database
#region Preferences
Task<PlayerPreferences> InitPrefsAsync(
NetUserId userId,
ICharacterProfile defaultProfile,
HumanoidCharacterProfile defaultProfile,
CancellationToken cancel);
Task SaveSelectedCharacterIndexAsync(NetUserId userId, int index);
Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? profile, int slot);
Task SaveCharacterSlotAsync(NetUserId userId, HumanoidCharacterProfile? profile, int slot);
Task SaveAdminOOCColorAsync(NetUserId userId, Color color);
@@ -428,7 +428,7 @@ namespace Content.Server.Database
public Task<PlayerPreferences> InitPrefsAsync(
NetUserId userId,
ICharacterProfile defaultProfile,
HumanoidCharacterProfile defaultProfile,
CancellationToken cancel)
{
DbWriteOpsMetric.Inc();
@@ -441,7 +441,7 @@ namespace Content.Server.Database
return RunDbCommand(() => _db.SaveSelectedCharacterIndexAsync(userId, index));
}
public Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? profile, int slot)
public Task SaveCharacterSlotAsync(NetUserId userId, HumanoidCharacterProfile? profile, int slot)
{
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.SaveCharacterSlotAsync(userId, profile, slot));

View File

@@ -20,10 +20,10 @@ namespace Content.Server.Preferences.Managers
bool TryGetCachedPreferences(NetUserId userId, [NotNullWhen(true)] out PlayerPreferences? playerPreferences);
PlayerPreferences GetPreferences(NetUserId userId);
PlayerPreferences? GetPreferencesOrNull(NetUserId? userId);
IEnumerable<KeyValuePair<NetUserId, ICharacterProfile>> GetSelectedProfilesForPlayers(List<NetUserId> userIds);
IEnumerable<KeyValuePair<NetUserId, HumanoidCharacterProfile>> GetSelectedProfilesForPlayers(List<NetUserId> userIds);
bool HavePreferencesLoaded(ICommonSession session);
Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile);
Task SetProfile(NetUserId userId, int slot, HumanoidCharacterProfile profile);
Task SetConstructionFavorites(NetUserId userId, List<ProtoId<ConstructionPrototype>> favorites);
}
}

View File

@@ -91,7 +91,7 @@ namespace Content.Server.Preferences.Managers
await SetProfile(userId, message.Slot, message.Profile);
}
public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile)
public async Task SetProfile(NetUserId userId, int slot, HumanoidCharacterProfile profile)
{
if (!_cachedPlayerPrefs.TryGetValue(userId, out var prefsData) || !prefsData.PrefsLoaded)
{
@@ -107,7 +107,7 @@ namespace Content.Server.Preferences.Managers
profile.EnsureValid(session, _dependencies);
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
var profiles = new Dictionary<int, HumanoidCharacterProfile>(curPrefs.Characters)
{
[slot] = profile
};
@@ -168,7 +168,7 @@ namespace Content.Server.Preferences.Managers
nextSlot = ns;
}
var arr = new Dictionary<int, ICharacterProfile>(curPrefs.Characters);
var arr = new Dictionary<int, HumanoidCharacterProfile>(curPrefs.Characters);
arr.Remove(slot);
prefsData.Prefs = new PlayerPreferences(arr, nextSlot ?? curPrefs.SelectedCharacterIndex, curPrefs.AdminOOCColor, curPrefs.ConstructionFavorites);
@@ -230,7 +230,7 @@ namespace Content.Server.Preferences.Managers
{
PrefsLoaded = true,
Prefs = new PlayerPreferences(
new[] { new KeyValuePair<int, ICharacterProfile>(0, HumanoidCharacterProfile.Random()) },
new[] { new KeyValuePair<int, HumanoidCharacterProfile>(0, HumanoidCharacterProfile.Random()) },
0, Color.Transparent, [])
};
@@ -349,17 +349,17 @@ namespace Content.Server.Preferences.Managers
return new PlayerPreferences(prefs.Characters.Select(p =>
{
return new KeyValuePair<int, ICharacterProfile>(p.Key, p.Value.Validated(session, collection));
return new KeyValuePair<int, HumanoidCharacterProfile>(p.Key, p.Value.Validated(session, collection));
}), prefs.SelectedCharacterIndex, prefs.AdminOOCColor, prefs.ConstructionFavorites);
}
public IEnumerable<KeyValuePair<NetUserId, ICharacterProfile>> GetSelectedProfilesForPlayers(
public IEnumerable<KeyValuePair<NetUserId, HumanoidCharacterProfile>> GetSelectedProfilesForPlayers(
List<NetUserId> usernames)
{
return usernames
.Select(p => (_cachedPlayerPrefs[p].Prefs, p))
.Where(p => p.Prefs != null)
.Select(p => new KeyValuePair<NetUserId, ICharacterProfile>(p.p, p.Prefs!.SelectedCharacter));
.Select(p => new KeyValuePair<NetUserId, HumanoidCharacterProfile>(p.p, p.Prefs!.SelectedCharacter));
}
internal static bool ShouldStorePrefs(LoginType loginType)

View File

@@ -12,7 +12,7 @@ namespace Content.Shared.Humanoid;
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance, IEquatable<HumanoidCharacterAppearance>
public sealed partial class HumanoidCharacterAppearance : IEquatable<HumanoidCharacterAppearance>
{
[DataField]
public Color EyeColor { get; set; } = Color.Black;
@@ -158,15 +158,6 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
validatedMarkings);
}
public bool MemberwiseEquals(ICharacterAppearance maybeOther)
{
if (maybeOther is not HumanoidCharacterAppearance other) return false;
if (!EyeColor.Equals(other.EyeColor)) return false;
if (!SkinColor.Equals(other.SkinColor)) return false;
if (!MarkingManager.MarkingsAreEqual(Markings, other.Markings)) return false;
return true;
}
public bool Equals(HumanoidCharacterAppearance? other)
{
if (ReferenceEquals(null, other)) return false;

View File

@@ -1,8 +0,0 @@
namespace Content.Shared.Humanoid
{
public interface ICharacterAppearance
{
bool MemberwiseEquals(ICharacterAppearance other);
}
}

View File

@@ -28,7 +28,7 @@ namespace Content.Shared.Preferences
/// </summary>
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class HumanoidCharacterProfile : ICharacterProfile
public sealed partial class HumanoidCharacterProfile
{
public static readonly ProtoId<SpeciesPrototype> DefaultSpecies = "Human";
private static readonly Regex RestrictedNameRegex = new(@"[^A-Za-z0-9 '\-]");
@@ -89,11 +89,6 @@ namespace Content.Shared.Preferences
[DataField]
public Gender Gender { get; private set; } = Gender.Male;
/// <summary>
/// <see cref="Appearance"/>
/// </summary>
public ICharacterAppearance CharacterAppearance => Appearance;
/// <summary>
/// Stores markings, eye colors, etc for the profile.
/// </summary>
@@ -465,9 +460,8 @@ namespace Content.Shared.Preferences
("age", Age)
);
public bool MemberwiseEquals(ICharacterProfile maybeOther)
public bool MemberwiseEquals(HumanoidCharacterProfile other)
{
if (maybeOther is not HumanoidCharacterProfile other) return false;
if (Name != other.Name) return false;
if (Age != other.Age) return false;
if (Sex != other.Sex) return false;
@@ -480,7 +474,7 @@ namespace Content.Shared.Preferences
if (!_traitPreferences.SequenceEqual(other._traitPreferences)) return false;
if (!Loadouts.SequenceEqual(other.Loadouts)) return false;
if (FlavorText != other.FlavorText) return false;
return Appearance.MemberwiseEquals(other.Appearance);
return Appearance.Equals(other.Appearance);
}
public void EnsureValid(ICommonSession session, IDependencyCollection collection)
@@ -692,7 +686,7 @@ namespace Content.Shared.Preferences
return result;
}
public ICharacterProfile Validated(ICommonSession session, IDependencyCollection collection)
public HumanoidCharacterProfile Validated(ICommonSession session, IDependencyCollection collection)
{
var profile = new HumanoidCharacterProfile(this);
profile.EnsureValid(session, collection);

View File

@@ -1,26 +0,0 @@
using Content.Shared.Humanoid;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Shared.Preferences
{
public interface ICharacterProfile
{
string Name { get; }
ICharacterAppearance CharacterAppearance { get; }
bool MemberwiseEquals(ICharacterProfile other);
/// <summary>
/// Makes this profile valid so there's no bad data like negative ages.
/// </summary>
void EnsureValid(ICommonSession session, IDependencyCollection collection);
/// <summary>
/// Gets a copy of this profile that has <see cref="EnsureValid"/> applied, i.e. no invalid data.
/// </summary>
ICharacterProfile Validated(ICommonSession session, IDependencyCollection collection);
}
}

View File

@@ -13,7 +13,7 @@ namespace Content.Shared.Preferences
public override MsgGroups MsgGroup => MsgGroups.Command;
public int Slot;
public ICharacterProfile Profile = default!;
public HumanoidCharacterProfile Profile = default!;
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
@@ -21,7 +21,7 @@ namespace Content.Shared.Preferences
var length = buffer.ReadVariableInt32();
using var stream = new MemoryStream(length);
buffer.ReadAlignedMemory(stream, length);
Profile = serializer.Deserialize<ICharacterProfile>(stream);
Profile = serializer.Deserialize<HumanoidCharacterProfile>(stream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)

View File

@@ -13,11 +13,11 @@ namespace Content.Shared.Preferences
[NetSerializable]
public sealed class PlayerPreferences
{
private Dictionary<int, ICharacterProfile> _characters;
private Dictionary<int, HumanoidCharacterProfile> _characters;
public PlayerPreferences(IEnumerable<KeyValuePair<int, ICharacterProfile>> characters, int selectedCharacterIndex, Color adminOOCColor, List<ProtoId<ConstructionPrototype>> constructionFavorites)
public PlayerPreferences(IEnumerable<KeyValuePair<int, HumanoidCharacterProfile>> characters, int selectedCharacterIndex, Color adminOOCColor, List<ProtoId<ConstructionPrototype>> constructionFavorites)
{
_characters = new Dictionary<int, ICharacterProfile>(characters);
_characters = new Dictionary<int, HumanoidCharacterProfile>(characters);
SelectedCharacterIndex = selectedCharacterIndex;
AdminOOCColor = adminOOCColor;
ConstructionFavorites = constructionFavorites;
@@ -26,9 +26,9 @@ namespace Content.Shared.Preferences
/// <summary>
/// All player characters.
/// </summary>
public IReadOnlyDictionary<int, ICharacterProfile> Characters => _characters;
public IReadOnlyDictionary<int, HumanoidCharacterProfile> Characters => _characters;
public ICharacterProfile GetProfile(int index)
public HumanoidCharacterProfile GetProfile(int index)
{
return _characters[index];
}
@@ -41,7 +41,7 @@ namespace Content.Shared.Preferences
/// <summary>
/// The currently selected character.
/// </summary>
public ICharacterProfile SelectedCharacter => Characters[SelectedCharacterIndex];
public HumanoidCharacterProfile SelectedCharacter => Characters[SelectedCharacterIndex];
public Color AdminOOCColor { get; set; }
@@ -50,12 +50,12 @@ namespace Content.Shared.Preferences
/// </summary>
public List<ProtoId<ConstructionPrototype>> ConstructionFavorites { get; set; } = [];
public int IndexOfCharacter(ICharacterProfile profile)
public int IndexOfCharacter(HumanoidCharacterProfile profile)
{
return _characters.FirstOrNull(p => p.Value == profile)?.Key ?? -1;
}
public bool TryIndexOfCharacter(ICharacterProfile profile, out int index)
public bool TryIndexOfCharacter(HumanoidCharacterProfile profile, out int index)
{
return (index = IndexOfCharacter(profile)) != -1;
}