Clean up updateUser and some small changes (#246)

* Clean up updateUser and other minor changes

* Check if null or whitespace instead of checking string length

Co-authored-by: jvyden <jvyden@jvyden.xyz>
This commit is contained in:
Josh 2022-03-19 18:05:20 -05:00 committed by GitHub
commit 0d6e40a75d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 118 deletions

View file

@ -82,7 +82,8 @@ public class ResourcesController : ControllerBase
string path = FileHelper.GetResourcePath(hash);
FileHelper.EnsureDirectoryCreated(assetsDirectory);
if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded
// lbp treats code 409 as success and as an indicator that the file is already present
if (FileHelper.ResourceExists(hash)) this.Conflict();
Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance);
LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader));
@ -90,7 +91,7 @@ public class ResourcesController : ControllerBase
if (!FileHelper.IsFileSafe(file))
{
Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
return this.UnprocessableEntity();
return this.Conflict();
}
string calculatedHash = file.Hash;

View file

@ -5,7 +5,7 @@ using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles;
@ -69,130 +69,66 @@ public class UserController : ControllerBase
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
XmlReaderSettings settings = new()
{
Async = true, // this is apparently not default
};
bool locationChanged = false;
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
// xml hack so we can use one class to deserialize different root names
string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user";
XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement));
UserUpdate? update = (UserUpdate?) serializer.Deserialize(new StringReader(bodyString));
// this is an absolute mess, but necessary because LBP only sends what changed
//
// example for changing profile card location:
// <updateUser>
// <location>
// <x>1234</x>
// <y>1234</y>
// </location>
// </updateUser>
//
// example for changing biography:
// <updateUser>
// <biography>biography stuff</biography>
// </updateUser>
//
// if you find a way to make it not stupid feel free to replace this
using (XmlReader reader = XmlReader.Create(this.Request.Body, settings))
if (update == null) return this.BadRequest();
if (update.Biography != null) user.Biography = update.Biography;
if (update.IconHash != null) user.IconHash = update.IconHash;
if (update.YayHash != null) user.YayHash = update.YayHash;
if (update.MehHash != null) user.MehHash = update.MehHash;
if (update.BooHash != null) user.BooHash = update.BooHash;
if (update.PlanetHash != null)
{
List<string> path = new(); // you can think of this as a file path in the XML, like <updateUser> -> <location> -> <x>
while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (reader.NodeType)
switch (gameToken.GameVersion)
{
case GameVersion.LittleBigPlanet2: // LBP2 planets will apply to LBP3
{
case XmlNodeType.Element:
path.Add(reader.Name);
break;
case XmlNodeType.Text:
switch (path[1])
{
case "biography":
{
user.Biography = await reader.GetValueAsync();
break;
}
case "location":
{
locationChanged = true; // if we're here then we're probably about to change the location.
// ReSharper disable once ConvertIfStatementToSwitchStatement
if (path[2] == "x")
user.Location.X = Convert.ToInt32
(await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol
else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync());
break;
}
case "icon":
{
user.IconHash = await reader.GetValueAsync();
break;
}
case "planets":
{
switch (gameToken.GameVersion)
{
case GameVersion.LittleBigPlanet2: // LBP2 planets will apply to LBP3
{
user.PlanetHashLBP2 = await reader.GetValueAsync();
user.PlanetHashLBP3 = await reader.GetValueAsync();
break;
}
case GameVersion.LittleBigPlanet3: // LBP3 and vita can only apply to their own games, only set 1 here
{
user.PlanetHashLBP3 = await reader.GetValueAsync();
break;
}
case GameVersion.LittleBigPlanetVita:
{
user.PlanetHashLBPVita = await reader.GetValueAsync();
break;
}
case GameVersion.LittleBigPlanet1:
case GameVersion.LittleBigPlanetPSP:
case GameVersion.Unknown:
default: // The rest do not support custom earths.
{
throw new ArgumentException($"invalid gameVersion {gameToken.GameVersion} for setting earth");
}
}
break;
}
case "yay2":
{
user.YayHash = await reader.GetValueAsync();
break;
}
case "meh2":
{
user.MehHash = await reader.GetValueAsync();
break;
}
case "boo2":
{
user.BooHash = await reader.GetValueAsync();
break;
}
}
break;
case XmlNodeType.EndElement:
path.RemoveAt(path.Count - 1);
break;
user.PlanetHashLBP2 = update.PlanetHash;
user.PlanetHashLBP3 = update.PlanetHash;
break;
}
case GameVersion.LittleBigPlanet3: // LBP3 and vita can only apply to their own games, only set 1 here
{
user.PlanetHashLBP3 = update.PlanetHash;
break;
}
case GameVersion.LittleBigPlanetVita:
{
user.PlanetHashLBPVita = update.PlanetHash;
break;
}
case GameVersion.LittleBigPlanet1:
case GameVersion.LittleBigPlanetPSP:
case GameVersion.Unknown:
default: // The rest do not support custom earths.
{
throw new ArgumentException($"invalid gameVersion {gameToken.GameVersion} for setting earth");
}
}
}
// the way location on a user card works is stupid and will not save with the way below as-is, so we do the following:
if (locationChanged) // only modify the database if we modify here
if (update.Location != null)
{
Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again
Location? loc = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId);
if (loc == null) throw new Exception("User loc is null, this should never happen.");
if (l == null) throw new Exception("this shouldn't happen ever but we handle this");
// set the location in the database to the one we modified above
l.X = user.Location.X;
l.Y = user.Location.Y;
// now both are in sync, and will update in the database.
loc.X = update.Location.X;
loc.Y = update.Location.Y;
}
if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything
if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync();
return this.Ok();
}

View file

@ -75,7 +75,7 @@ public class Database : DbContext
Username = username,
Password = password,
LocationId = l.Id,
Biography = username + " hasn't introduced themselves yet.",
Biography = "",
EmailAddress = emailAddress,
};
this.Users.Add(user);

View file

@ -83,7 +83,14 @@
<div class="eight wide column">
<div class="ui blue segment">
<h2>Biography</h2>
<p>@Model.ProfileUser.Biography</p>
@if (string.IsNullOrWhiteSpace(Model.ProfileUser.Biography))
{
<p>@Model.ProfileUser.Username hasn't introduced themselves yet</p>
}
else
{
<p>@Model.ProfileUser.Biography</p>
}
</div>
</div>
<div class="eight wide column">

View file

@ -0,0 +1,29 @@
#nullable enable
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Types.Profiles;
namespace LBPUnion.ProjectLighthouse.Types;
public class UserUpdate
{
[XmlElement("location")]
public Location? Location;
[XmlElement("biography")]
public string? Biography;
[XmlElement("icon")]
public string? IconHash;
[XmlElement("planets")]
public string? PlanetHash;
[XmlElement("yay2")]
public string? YayHash;
[XmlElement("meh2")]
public string? MehHash;
[XmlElement("boo2")]
public string? BooHash;
}