Client LuaCsForBarotrauma
EpicFriendProvider.cs
1 using System;
2 using System.Collections.Immutable;
3 using System.Globalization;
4 using System.Linq;
5 using System.Numerics;
6 using System.Threading.Tasks;
9 using Microsoft.Xna.Framework;
10 using Microsoft.Xna.Framework.Graphics;
11 using Vector2 = Microsoft.Xna.Framework.Vector2;
12 
13 namespace Barotrauma;
14 
15 sealed class EpicFriendProvider : FriendProvider
16 {
17  private FriendInfo EgsFriendToFriendInfo(EosInterface.EgsFriend egsFriend)
18  {
19  return new FriendInfo(
20  name: egsFriend.DisplayName,
21  id: egsFriend.EpicAccountId,
22  status: egsFriend.Status,
23  serverName: egsFriend.ServerName,
24  connectCommand: ConnectCommand.Parse(egsFriend.ConnectCommand),
25  provider: this);
26  }
27 
28  public override async Task<Option<FriendInfo>> RetrieveFriend(AccountId id)
29  {
30  if (id is not EpicAccountId friendEaid) { return Option.None; }
31  var selfEaidOption = Eos.EosAccount.SelfAccountIds.OfType<EpicAccountId>().FirstOrNone();
32  if (!selfEaidOption.TryUnwrap(out var selfEaid)) { return Option.None; }
33 
34  var friendResult = await EosInterface.Friends.GetFriend(selfEaid, friendEaid);
35  if (!friendResult.TryUnwrapSuccess(out var f)) { return Option.None; }
36 
37  return Option.Some(EgsFriendToFriendInfo(f));
38  }
39 
40  public override async Task<ImmutableArray<FriendInfo>> RetrieveFriends()
41  {
42  var epicAccountIdOption = Eos.EosAccount.SelfAccountIds.OfType<EpicAccountId>().FirstOrNone();
43  if (!epicAccountIdOption.TryUnwrap(out var epicAccountId)) { return ImmutableArray<FriendInfo>.Empty; }
44 
45  var friendsResult = await EosInterface.Friends.GetFriends(epicAccountId);
46  if (!friendsResult.TryUnwrapSuccess(out var friends)) { return ImmutableArray<FriendInfo>.Empty; }
47 
48  return friends.Select(EgsFriendToFriendInfo).ToImmutableArray();
49  }
50 
51  private static readonly ImmutableArray<Color> egsProfileColors = new[]
52  {
53  // Cyan
54  new Color(0xfff0a950),
55 
56  // Dark green
57  new Color(0xff2b9850),
58 
59  // Yellow-green
60  new Color(0xff2ba08e),
61 
62  // Purple
63  new Color(0xff951249),
64 
65  // Purple-red
66  new Color(0xff9a0c71),
67 
68  // Red
69  new Color(0xff3e29c6),
70 
71  // Orange
72  new Color(0xff3875ed),
73 
74  // Yellow-orange
75  new Color(0xff1ea5ed)
76  }.ToImmutableArray();
77 
78  public override Task<Option<Sprite>> RetrieveAvatar(FriendInfo friend, int avatarSize)
79  {
80  if (friend.Id is not EpicAccountId epicAccount) { return Task.FromResult<Option<Sprite>>(Option.None); }
81 
82  // EGS doesn't have profile pictures yet.
83  // Instead, each player gets a color based on their account ID.
84  // This is an educated guess of how Epic picks that color, and is likely incorrect for IDs nearing the boundaries of ranges:
85  Color color = Color.Black;
86  if (ulong.TryParse(epicAccount.EosStringRepresentation[..16], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var mostSignificant64Bits)
87  && ulong.TryParse(epicAccount.EosStringRepresentation[16..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var leastSignificant64Bits))
88  {
89  BigInteger fullId = mostSignificant64Bits;
90  fullId <<= 64;
91  fullId |= leastSignificant64Bits;
92 
93  BigInteger idMaxValue = ulong.MaxValue;
94  idMaxValue <<= 64;
95  idMaxValue |= ulong.MaxValue;
96 
97  BigInteger middleRangeSize = idMaxValue / 7;
98  BigInteger firstRangeSize = middleRangeSize / 2;
99  if (fullId <= firstRangeSize)
100  {
101  color = egsProfileColors[0];
102  }
103  else
104  {
105  color = egsProfileColors[(int)((fullId - firstRangeSize) / middleRangeSize) + 1];
106  }
107  }
108 
109  char glyphChar = friend.Name.FallbackNullOrEmpty("?")[0];
110  var font = GUIStyle.UnscaledSmallFont.GetFontForStr(glyphChar.ToString());
111 
112  Texture2D tex = null;
113  if (font != null)
114  {
115  var (glyphData, glyphTexture) = font.GetGlyphDataAndTextureForChar(glyphChar);
116  var glyphSize = new Vector2(glyphData.TexCoords.Width, glyphData.TexCoords.Height);
117  int texSize = (int)Math.Max(
118  MathUtils.RoundUpToPowerOfTwo((uint)(font.LineHeight * 1.5f)),
119  MathUtils.RoundUpToPowerOfTwo((uint)(font.LineHeight * 1.5f)));
120 
121  if (glyphTexture is not null)
122  {
123  var glyphTextureData = new Color[(int)glyphSize.X * (int)glyphSize.Y];
124  glyphTexture.GetData(
125  level: 0,
126  rect: glyphData.TexCoords,
127  data: glyphTextureData,
128  startIndex: 0,
129  elementCount: glyphTextureData.Length);
130 
131  var texData = Enumerable.Range(0, texSize * texSize).Select(_ => color).ToArray();
132  var start = (new Vector2(texSize, texSize) / 2 - glyphSize / 2).ToPoint();
133  var end = start + glyphSize.ToPoint();
134 
135  for (int x = start.X; x < end.X; x++)
136  {
137  for (int y = start.Y; y < end.Y; y++)
138  {
139  texData[x + y * texSize] =
140  Color.Lerp(
141  color,
142  Color.White,
143  glyphTextureData[(x - start.X) + (y - start.Y) * (int)glyphSize.X].A / 255f);
144  }
145  }
146 
147  tex = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, texSize, texSize);
148  tex.SetData(texData);
149  }
150  }
151 
152  if (tex is null)
153  {
154  tex = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, 2, 2);
155  tex.SetData(new[] { color, color, color, color });
156  }
157 
158 
159  var sprite = new Sprite(tex, null, null);
160  return Task.FromResult(Option.Some(sprite));
161  }
162 
163  public override async Task<string> GetSelfUserName()
164  {
165  var epicAccountIdOption = Eos.EosAccount.SelfAccountIds.OfType<EpicAccountId>().FirstOrNone();
166  if (!epicAccountIdOption.TryUnwrap(out var epicAccountId)) { return ""; }
167 
168  var selfInfoResult = await EosInterface.Friends.GetSelfUserInfo(epicAccountId);
169  if (!selfInfoResult.TryUnwrapSuccess(out var selfInfo)) { return ""; }
170 
171  return selfInfo.DisplayName;
172  }
173 }
override Task< Option< Sprite > > RetrieveAvatar(FriendInfo friend, int avatarSize)
override async Task< string > GetSelfUserName()
override async Task< Option< FriendInfo > > RetrieveFriend(AccountId id)
override async Task< ImmutableArray< FriendInfo > > RetrieveFriends()