Skip to content

Commit b4ffea9

Browse files
authored
Merge pull request #627 from Rian8337/avatar-multi-card
Add avatar to player card in multiplayer
2 parents a710b90 + a9375d1 commit b4ffea9

3 files changed

Lines changed: 130 additions & 50 deletions

File tree

src/com/osudroid/ui/v2/multi/RoomPlayerCard.kt

Lines changed: 124 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import kotlinx.coroutines.Job
3030
import kotlinx.coroutines.ensureActive
3131
import ru.nsu.ccfit.zuev.osu.Config
3232
import ru.nsu.ccfit.zuev.osu.ResourceManager
33+
import ru.nsu.ccfit.zuev.osu.helper.MD5Calculator
3334
import ru.nsu.ccfit.zuev.osu.helper.StringTable
3435
import ru.nsu.ccfit.zuev.osu.online.OnlineManager
3536
import ru.nsu.ccfit.zuev.osuplus.R
@@ -73,7 +74,8 @@ class RoomPlayerCard : UILinearContainer() {
7374
}
7475
}
7576

76-
fun cancelBannerJob() {
77+
fun cancelJobs() {
78+
playerButton.avatarJob?.cancel()
7779
playerButton.bannerJob?.cancel()
7880
}
7981

@@ -86,17 +88,22 @@ class RoomPlayerCard : UILinearContainer() {
8688
private var modDisplay: UIComponent? = null
8789

8890
private val bannerSprite: UIShapedSprite
91+
private val avatarSprite: UIShapedSprite
8992

9093
var bannerJob: Job? = null
9194
private set
9295

96+
var avatarJob: Job? = null
97+
private set
98+
9399
private val defaultBackground = UIBox().apply {
94100
cornerRadius = 12f
95101
color = Theme.current.accentColor * 0.15f
96102
alpha = 0.5f
97103
}
98104

99105
private var lastPlayerId = -1L
106+
private val defaultAvatar = ResourceManager.getInstance().getTexture("emptyavatar")
100107

101108
private val hostIcon = FontAwesomeIcon(Icon.Crown).apply {
102109
applyTheme = { color = it.accentColor }
@@ -115,9 +122,11 @@ class RoomPlayerCard : UILinearContainer() {
115122
init {
116123
width = FillParent
117124
orientation = Orientation.Horizontal
118-
spacing = 6f
125+
spacing = 12f
119126

120127
bannerSprite = UIShapedSprite().apply {
128+
inheritAncestorsColor = false
129+
121130
shape = object : UIBox() {
122131
init {
123132
cornerRadius = 12f
@@ -134,13 +143,35 @@ class RoomPlayerCard : UILinearContainer() {
134143
setColor(0.25f, 0.25f, 0.25f)
135144
}
136145

146+
avatarSprite = UIShapedSprite().apply {
147+
inheritAncestorsColor = false
148+
size = Vec2(50f)
149+
150+
shape = object : UIBox() {
151+
init {
152+
cornerRadius = 8f
153+
color = Color4.Transparent
154+
}
155+
156+
override fun beginDraw(gl: GL10) {
157+
gl.glDepthMask(true)
158+
super.beginDraw(gl)
159+
}
160+
}
161+
162+
scaleType = ScaleType.Crop
163+
textureRegion = defaultAvatar
164+
}
165+
137166
background = defaultBackground
138167

139168
foreground = UIBox().apply {
140169
cornerRadius = 12f
141170
paintStyle = PaintStyle.Outline
142171
}
143172

173+
+avatarSprite
174+
144175
innerContainer = linearContainer {
145176
orientation = Orientation.Vertical
146177
inheritAncestorsColor = false
@@ -157,7 +188,7 @@ class RoomPlayerCard : UILinearContainer() {
157188
textureRegion = ResourceManager.getInstance().getTexture("missing")
158189
anchor = Anchor.CenterLeft
159190
origin = Anchor.CenterLeft
160-
size = Vec2(18f)
191+
size = Vec2(24f)
161192
}
162193
}
163194
}
@@ -167,6 +198,8 @@ class RoomPlayerCard : UILinearContainer() {
167198

168199
override fun onDetached() {
169200
super.onDetached()
201+
202+
avatarJob?.cancel()
170203
bannerJob?.cancel()
171204
}
172205

@@ -178,50 +211,11 @@ class RoomPlayerCard : UILinearContainer() {
178211
}
179212

180213
if (lastPlayerId != player.id) {
181-
lastPlayerId = player.id
182-
val bannerUrl = OnlineManager.getProfileBannerURL(player.id)
183-
184-
bannerJob?.cancel()
185-
bannerJob = null
186-
187-
val resourceManager = ResourceManager.getInstance()
188-
val loadedTexture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
189-
190-
if (loadedTexture != null) {
191-
bannerSprite.textureRegion = loadedTexture
192-
background = bannerSprite
193-
} else {
194-
bannerSprite.textureRegion = null
195-
background = defaultBackground
196-
197-
bannerJob = async {
198-
ensureActive()
199-
200-
if (OnlineManager.getInstance().loadProfileBannerToTextureManager(bannerUrl)) {
201-
ensureActive()
202-
203-
val texture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
204-
205-
updateThread {
206-
if (lastPlayerId == player.id) {
207-
bannerSprite.textureRegion = texture
208-
209-
if (texture != null) {
210-
background = bannerSprite
211-
}
212-
}
213-
}
214-
} else {
215-
updateThread {
216-
if (lastPlayerId == player.id) {
217-
background = defaultBackground
218-
}
219-
}
220-
}
221-
}
222-
}
214+
loadAvatar(player.id)
215+
loadBanner(player.id)
223216
}
224217

218+
lastPlayerId = player.id
225219
nameText.text = player.name
226220
nameText.spacing = 6f
227221

@@ -250,7 +244,7 @@ class RoomPlayerCard : UILinearContainer() {
250244
modDisplay?.detachSelf()
251245

252246
modDisplay = UIText().apply {
253-
minHeight = 18f // Force to take space even if no mods are enabled
247+
minHeight = 24f // Force to take space even if no mods are enabled
254248
font = ResourceManager.getInstance().getFont("xs")
255249
applyTheme = { color = it.accentColor * 0.8f }
256250
}
@@ -264,8 +258,8 @@ class RoomPlayerCard : UILinearContainer() {
264258
modDisplay?.detachSelf()
265259

266260
modDisplay = ModsIndicator().apply {
267-
minHeight = 18f // Force to take space even if no mods are enabled
268-
iconSize = 18f
261+
minHeight = 24f // Force to take space even if no mods are enabled
262+
iconSize = 24f
269263
}
270264

271265
innerContainer += modDisplay!!
@@ -338,5 +332,87 @@ class RoomPlayerCard : UILinearContainer() {
338332
}.show()
339333
}
340334
}
335+
336+
private fun loadAvatar(userId: Long) {
337+
val resourceManager = ResourceManager.getInstance()
338+
val avatarUrl = OnlineManager.getAvatarURL(userId)
339+
340+
avatarJob?.cancel()
341+
avatarJob = null
342+
343+
val avatarKey = MD5Calculator.getStringMD5(avatarUrl)
344+
val loadedTexture = resourceManager.getTextureIfLoaded(avatarKey)
345+
346+
if (loadedTexture != null) {
347+
avatarSprite.textureRegion = loadedTexture
348+
} else {
349+
avatarSprite.textureRegion = defaultAvatar
350+
351+
avatarJob = async {
352+
ensureActive()
353+
354+
if (OnlineManager.getInstance().loadAvatarToTextureManager(avatarUrl)) {
355+
ensureActive()
356+
357+
val texture = resourceManager.getTextureIfLoaded(avatarKey)
358+
359+
updateThread {
360+
if (lastPlayerId == userId) {
361+
avatarSprite.textureRegion = texture ?: defaultAvatar
362+
}
363+
}
364+
} else {
365+
updateThread {
366+
if (lastPlayerId == userId) {
367+
avatarSprite.textureRegion = defaultAvatar
368+
}
369+
}
370+
}
371+
}
372+
}
373+
}
374+
375+
private fun loadBanner(userId: Long) {
376+
val resourceManager = ResourceManager.getInstance()
377+
val bannerUrl = OnlineManager.getProfileBannerURL(userId)
378+
379+
bannerJob?.cancel()
380+
bannerJob = null
381+
382+
val loadedTexture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
383+
384+
if (loadedTexture != null) {
385+
bannerSprite.textureRegion = loadedTexture
386+
background = bannerSprite
387+
} else {
388+
bannerSprite.textureRegion = null
389+
background = defaultBackground
390+
391+
bannerJob = async {
392+
ensureActive()
393+
394+
if (OnlineManager.getInstance().loadProfileBannerToTextureManager(bannerUrl)) {
395+
ensureActive()
396+
397+
val texture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
398+
399+
updateThread {
400+
if (lastPlayerId == userId) {
401+
bannerSprite.textureRegion = texture
402+
if (texture != null) {
403+
background = bannerSprite
404+
}
405+
}
406+
}
407+
} else {
408+
updateThread {
409+
if (lastPlayerId == userId) {
410+
background = defaultBackground
411+
}
412+
}
413+
}
414+
}
415+
}
416+
}
341417
}
342418
}

src/com/osudroid/ui/v2/multi/RoomScene.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ class RoomScene(
708708
private fun teardownSession() {
709709
Multiplayer.cancelReconnection()
710710
beatmapInfoLayout.cancelCalculation()
711-
playersContainer.forEach { (it as RoomPlayerCard).cancelBannerJob() }
711+
playersContainer.forEach { (it as RoomPlayerCard).cancelJobs() }
712712

713713
// Null out event listeners before disconnect so any queued socket events that
714714
// arrive after teardown find no listener to call.

src/ru/nsu/ccfit/zuev/osu/online/OnlineManager.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class OnlineManager {
3333
public static final String hostname = "osudroid.moe";
3434
public static final String endpoint = "https://" + hostname + "/api/";
3535
public static final String updateEndpoint = endpoint + "update.php?lang=";
36-
public static final String defaultAvatarURL = "https://" + hostname + "/user/avatar/0.png";
36+
public static final String defaultAvatarURL = getAvatarURL(0);
3737
public static final String profileBannerEndpoint = "https://" + hostname + "/user/banner/";
3838
private static final String onlineVersion = "60";
3939

@@ -71,6 +71,10 @@ public static String getReplayURL(int playID) {
7171
};
7272
}
7373

74+
public static String getAvatarURL(long userId) {
75+
return "https://" + hostname + "/user/avatar/" + userId + ".png";
76+
}
77+
7478
public static String getProfileBannerURL(long userId) {
7579
return profileBannerEndpoint + userId + ".png";
7680
}

0 commit comments

Comments
 (0)