Make the client validate character information (#3563)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
@@ -47,6 +47,7 @@ namespace Content.Client
|
|||||||
|
|
||||||
public void UpdateCharacter(ICharacterProfile profile, int slot)
|
public void UpdateCharacter(ICharacterProfile profile, int slot)
|
||||||
{
|
{
|
||||||
|
profile.EnsureValid();
|
||||||
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
|
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
|
||||||
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
|
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
|
||||||
var msg = _netManager.CreateNetMessage<MsgUpdateCharacter>();
|
var msg = _netManager.CreateNetMessage<MsgUpdateCharacter>();
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ namespace Content.Client.UserInterface
|
|||||||
MinSize = (2, 0)
|
MinSize = (2, 0)
|
||||||
});
|
});
|
||||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, entityManager);
|
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, entityManager);
|
||||||
_humanoidProfileEditor.OnProfileChanged += newProfile => { UpdateUI(); };
|
_humanoidProfileEditor.OnProfileChanged += ProfileChanged;
|
||||||
hBox.AddChild(_humanoidProfileEditor);
|
hBox.AddChild(_humanoidProfileEditor);
|
||||||
|
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
@@ -155,6 +155,12 @@ namespace Content.Client.UserInterface
|
|||||||
|
|
||||||
public void Save() => _humanoidProfileEditor.Save();
|
public void Save() => _humanoidProfileEditor.Save();
|
||||||
|
|
||||||
|
private void ProfileChanged(ICharacterProfile profile, int profileSlot)
|
||||||
|
{
|
||||||
|
_humanoidProfileEditor.UpdateControls();
|
||||||
|
UpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateUI()
|
private void UpdateUI()
|
||||||
{
|
{
|
||||||
var numberOfFullSlots = 0;
|
var numberOfFullSlots = 0;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace Content.Client.UserInterface
|
|||||||
private bool _isDirty;
|
private bool _isDirty;
|
||||||
public int CharacterSlot;
|
public int CharacterSlot;
|
||||||
public HumanoidCharacterProfile Profile;
|
public HumanoidCharacterProfile Profile;
|
||||||
public event Action<HumanoidCharacterProfile> OnProfileChanged;
|
public event Action<HumanoidCharacterProfile, int> OnProfileChanged;
|
||||||
|
|
||||||
public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager,
|
public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager,
|
||||||
IEntityManager entityManager)
|
IEntityManager entityManager)
|
||||||
@@ -709,7 +709,7 @@ namespace Content.Client.UserInterface
|
|||||||
{
|
{
|
||||||
IsDirty = false;
|
IsDirty = false;
|
||||||
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
|
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
|
||||||
OnProfileChanged?.Invoke(Profile);
|
OnProfileChanged?.Invoke(Profile, CharacterSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsDirty
|
private bool IsDirty
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ namespace Content.Server.Preferences
|
|||||||
HandleDeleteCharacterMessage);
|
HandleDeleteCharacterMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void HandleSelectCharacterMessage(MsgSelectCharacter message)
|
private async void HandleSelectCharacterMessage(MsgSelectCharacter message)
|
||||||
{
|
{
|
||||||
var index = message.SelectedCharacterIndex;
|
var index = message.SelectedCharacterIndex;
|
||||||
@@ -107,9 +106,11 @@ namespace Content.Server.Preferences
|
|||||||
|
|
||||||
var curPrefs = prefsData.Prefs!;
|
var curPrefs = prefsData.Prefs!;
|
||||||
|
|
||||||
|
profile.EnsureValid();
|
||||||
|
|
||||||
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
||||||
{
|
{
|
||||||
[slot] = HumanoidCharacterProfile.EnsureValid((HumanoidCharacterProfile) profile, _protos)
|
[slot] = profile
|
||||||
};
|
};
|
||||||
|
|
||||||
prefsData.Prefs = new PlayerPreferences(profiles, slot, curPrefs.AdminOOCColor);
|
prefsData.Prefs = new PlayerPreferences(profiles, slot, curPrefs.AdminOOCColor);
|
||||||
|
|||||||
@@ -201,90 +201,6 @@ namespace Content.Shared.Preferences
|
|||||||
return new(this, _jobPriorities, list);
|
return new(this, _jobPriorities, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Makes this profile valid so there's no bad data like negative ages.
|
|
||||||
/// </summary>
|
|
||||||
public static HumanoidCharacterProfile EnsureValid(
|
|
||||||
HumanoidCharacterProfile profile,
|
|
||||||
IPrototypeManager prototypeManager)
|
|
||||||
{
|
|
||||||
var age = Math.Clamp(profile.Age, MinimumAge, MaximumAge);
|
|
||||||
var sex = profile.Sex switch
|
|
||||||
{
|
|
||||||
Sex.Male => Sex.Male,
|
|
||||||
Sex.Female => Sex.Female,
|
|
||||||
_ => Sex.Male // Invalid enum values.
|
|
||||||
};
|
|
||||||
var gender = profile.Gender switch
|
|
||||||
{
|
|
||||||
Gender.Epicene => Gender.Epicene,
|
|
||||||
Gender.Female => Gender.Female,
|
|
||||||
Gender.Male => Gender.Male,
|
|
||||||
Gender.Neuter => Gender.Neuter,
|
|
||||||
_ => Gender.Epicene // Invalid enum values.
|
|
||||||
};
|
|
||||||
|
|
||||||
string name;
|
|
||||||
if (string.IsNullOrEmpty(profile.Name))
|
|
||||||
{
|
|
||||||
name = "Urist McHands";
|
|
||||||
}
|
|
||||||
else if (profile.Name.Length > MaxNameLength)
|
|
||||||
{
|
|
||||||
name = profile.Name[..MaxNameLength];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
name = profile.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Avoid Z̨͇̙͉͎̭͔̼̿͋A͚̖̞̗̞͈̓̾̀ͩͩ̔L̟ͮ̈͝G̙O͍͎̗̺̺ͫ̀̽͊̓͝ͅ tier shenanigans.
|
|
||||||
// And other stuff like RTL overrides and such.
|
|
||||||
// Probably also emojis...
|
|
||||||
|
|
||||||
name = name.Trim();
|
|
||||||
|
|
||||||
var appearance = HumanoidCharacterAppearance.EnsureValid(profile.Appearance);
|
|
||||||
|
|
||||||
var prefsUnavailableMode = profile.PreferenceUnavailable switch
|
|
||||||
{
|
|
||||||
PreferenceUnavailableMode.StayInLobby => PreferenceUnavailableMode.StayInLobby,
|
|
||||||
PreferenceUnavailableMode.SpawnAsOverflow => PreferenceUnavailableMode.SpawnAsOverflow,
|
|
||||||
_ => PreferenceUnavailableMode.StayInLobby // Invalid enum values.
|
|
||||||
};
|
|
||||||
|
|
||||||
var clothing = profile.Clothing switch
|
|
||||||
{
|
|
||||||
ClothingPreference.Jumpsuit => ClothingPreference.Jumpsuit,
|
|
||||||
ClothingPreference.Jumpskirt => ClothingPreference.Jumpskirt,
|
|
||||||
_ => ClothingPreference.Jumpsuit // Invalid enum values.
|
|
||||||
};
|
|
||||||
|
|
||||||
var backpack = profile.Backpack switch
|
|
||||||
{
|
|
||||||
BackpackPreference.Backpack => BackpackPreference.Backpack,
|
|
||||||
BackpackPreference.Satchel => BackpackPreference.Satchel,
|
|
||||||
BackpackPreference.Duffelbag => BackpackPreference.Duffelbag,
|
|
||||||
_ => BackpackPreference.Backpack // Invalid enum values.
|
|
||||||
};
|
|
||||||
|
|
||||||
var priorities = new Dictionary<string, JobPriority>(profile.JobPriorities
|
|
||||||
.Where(p => prototypeManager.HasIndex<JobPrototype>(p.Key) && p.Value switch
|
|
||||||
{
|
|
||||||
JobPriority.Never => false, // Drop never since that's assumed default.
|
|
||||||
JobPriority.Low => true,
|
|
||||||
JobPriority.Medium => true,
|
|
||||||
JobPriority.High => true,
|
|
||||||
_ => false
|
|
||||||
}));
|
|
||||||
|
|
||||||
var antags = profile.AntagPreferences
|
|
||||||
.Where(prototypeManager.HasIndex<AntagPrototype>)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return new HumanoidCharacterProfile(name, age, sex, gender, appearance, clothing, backpack, priorities, prefsUnavailableMode, antags);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Summary =>
|
public string Summary =>
|
||||||
Loc.GetString(
|
Loc.GetString(
|
||||||
"humanoid-character-profile-summary",
|
"humanoid-character-profile-summary",
|
||||||
@@ -308,6 +224,107 @@ namespace Content.Shared.Preferences
|
|||||||
return Appearance.MemberwiseEquals(other.Appearance);
|
return Appearance.MemberwiseEquals(other.Appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnsureValid()
|
||||||
|
{
|
||||||
|
var age = Math.Clamp(Age, MinimumAge, MaximumAge);
|
||||||
|
|
||||||
|
var sex = Sex switch
|
||||||
|
{
|
||||||
|
Sex.Male => Sex.Male,
|
||||||
|
Sex.Female => Sex.Female,
|
||||||
|
_ => Sex.Male // Invalid enum values.
|
||||||
|
};
|
||||||
|
|
||||||
|
var gender = Gender switch
|
||||||
|
{
|
||||||
|
Gender.Epicene => Gender.Epicene,
|
||||||
|
Gender.Female => Gender.Female,
|
||||||
|
Gender.Male => Gender.Male,
|
||||||
|
Gender.Neuter => Gender.Neuter,
|
||||||
|
_ => Gender.Epicene // Invalid enum values.
|
||||||
|
};
|
||||||
|
|
||||||
|
string name;
|
||||||
|
if (string.IsNullOrEmpty(Name))
|
||||||
|
{
|
||||||
|
name = "Urist McHands";
|
||||||
|
}
|
||||||
|
else if (Name.Length > MaxNameLength)
|
||||||
|
{
|
||||||
|
name = Name[..MaxNameLength];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Avoid Z̨͇̙͉͎̭͔̼̿͋A͚̖̞̗̞͈̓̾̀ͩͩ̔L̟ͮ̈͝G̙O͍͎̗̺̺ͫ̀̽͊̓͝ͅ tier shenanigans.
|
||||||
|
// And other stuff like RTL overrides and such.
|
||||||
|
// Probably also emojis...
|
||||||
|
|
||||||
|
name = name.Trim();
|
||||||
|
|
||||||
|
var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance);
|
||||||
|
|
||||||
|
var prefsUnavailableMode = PreferenceUnavailable switch
|
||||||
|
{
|
||||||
|
PreferenceUnavailableMode.StayInLobby => PreferenceUnavailableMode.StayInLobby,
|
||||||
|
PreferenceUnavailableMode.SpawnAsOverflow => PreferenceUnavailableMode.SpawnAsOverflow,
|
||||||
|
_ => PreferenceUnavailableMode.StayInLobby // Invalid enum values.
|
||||||
|
};
|
||||||
|
|
||||||
|
var clothing = Clothing switch
|
||||||
|
{
|
||||||
|
ClothingPreference.Jumpsuit => ClothingPreference.Jumpsuit,
|
||||||
|
ClothingPreference.Jumpskirt => ClothingPreference.Jumpskirt,
|
||||||
|
_ => ClothingPreference.Jumpsuit // Invalid enum values.
|
||||||
|
};
|
||||||
|
|
||||||
|
var backpack = Backpack switch
|
||||||
|
{
|
||||||
|
BackpackPreference.Backpack => BackpackPreference.Backpack,
|
||||||
|
BackpackPreference.Satchel => BackpackPreference.Satchel,
|
||||||
|
BackpackPreference.Duffelbag => BackpackPreference.Duffelbag,
|
||||||
|
_ => BackpackPreference.Backpack // Invalid enum values.
|
||||||
|
};
|
||||||
|
|
||||||
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
|
var priorities = new Dictionary<string, JobPriority>(JobPriorities
|
||||||
|
.Where(p => prototypeManager.HasIndex<JobPrototype>(p.Key) && p.Value switch
|
||||||
|
{
|
||||||
|
JobPriority.Never => false, // Drop never since that's assumed default.
|
||||||
|
JobPriority.Low => true,
|
||||||
|
JobPriority.Medium => true,
|
||||||
|
JobPriority.High => true,
|
||||||
|
_ => false
|
||||||
|
}));
|
||||||
|
|
||||||
|
var antags = AntagPreferences
|
||||||
|
.Where(prototypeManager.HasIndex<AntagPrototype>)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
Age = age;
|
||||||
|
Sex = sex;
|
||||||
|
Gender = gender;
|
||||||
|
Appearance = appearance;
|
||||||
|
Clothing = clothing;
|
||||||
|
Backpack = backpack;
|
||||||
|
|
||||||
|
_jobPriorities.Clear();
|
||||||
|
|
||||||
|
foreach (var (job, priority) in priorities)
|
||||||
|
{
|
||||||
|
_jobPriorities.Add(job, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
PreferenceUnavailable = prefsUnavailableMode;
|
||||||
|
|
||||||
|
_antagPreferences.Clear();
|
||||||
|
_antagPreferences.AddRange(antags);
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
return obj is HumanoidCharacterProfile other && MemberwiseEquals(other);
|
return obj is HumanoidCharacterProfile other && MemberwiseEquals(other);
|
||||||
|
|||||||
@@ -5,7 +5,14 @@ namespace Content.Shared.Preferences
|
|||||||
public interface ICharacterProfile
|
public interface ICharacterProfile
|
||||||
{
|
{
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
ICharacterAppearance CharacterAppearance { get; }
|
ICharacterAppearance CharacterAppearance { get; }
|
||||||
|
|
||||||
bool MemberwiseEquals(ICharacterProfile other);
|
bool MemberwiseEquals(ICharacterProfile other);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes this profile valid so there's no bad data like negative ages.
|
||||||
|
/// </summary>
|
||||||
|
void EnsureValid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user