Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/client/java/dev/creesch/model/PlayerListInfoEntry.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.creesch.model;

import com.google.gson.JsonObject;
import lombok.Builder;
import lombok.Data;

Expand All @@ -10,6 +9,6 @@ public class PlayerListInfoEntry {

private String playerId;
private String playerName;
private JsonObject playerDisplayName;
private String playerDisplayName;
private String playerTextureUrl;
}
28 changes: 4 additions & 24 deletions src/client/java/dev/creesch/model/WebsocketMessageBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,27 +359,9 @@ public static WebsocketJsonMessage createPlayerList(
GameProfile profile = player.getProfile(); // Contains UUID and name
String playerId = profile.getId().toString();
String playerName = profile.getName();

Text playerDisplayName = player.getDisplayName() != null
? player.getDisplayName()
: Text.literal(playerName);
String minecraftChatJson;
try {
minecraftChatJson = toJsonString(
playerDisplayName,
client.world.getRegistryManager()
);
} catch (JsonParseException exception) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we already catch JsonParseException here, it seems to me that also catching JsonSyntaxException would likely complete the workaround.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be redundant:

public final class JsonSyntaxException extends JsonParseException

LOGGER.warn(
"Failed to serialize chat message: " +
playerDisplayName.getString()
);
LOGGER.warn("Exception info: ", exception);

minecraftChatJson = "{\"text\":\"%s\"}".formatted(
playerDisplayName.getString()
);
}
String playerDisplayName = player.getDisplayName() != null
? player.getDisplayName().getString()
: playerName;

// To get the texture we need to digg a little bit deeper.
// Note: This retrieves the texture URL. In theory, it is possible to fetch player textures from minecraft.
Expand All @@ -390,9 +372,7 @@ public static WebsocketJsonMessage createPlayerList(
PlayerListInfoEntry playerInfo = PlayerListInfoEntry.builder()
.playerId(playerId)
.playerName(playerName)
.playerDisplayName(
gson.fromJson(minecraftChatJson, JsonObject.class)
)
.playerDisplayName(playerDisplayName)
.playerTextureUrl(playerTextureUrl)
.build();

Expand Down
8 changes: 4 additions & 4 deletions src/client/resources/web/js/chat.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { querySelectorWithAssertion, formatTimestamp } from './utils.mjs';
import {
assertIsComponent,
ComponentError,
formatMessage,
formatChatMessage,
initializeObfuscation,
} from './messages/message_parsing.mjs';
import { serverInfo } from './managers/server_info.mjs';
Expand Down Expand Up @@ -230,7 +230,7 @@ function handleChatMessage(message) {
try {
// Format the chat message - this uses the Component format from message_parsing
assertIsComponent(message.payload.component);
const chatContent = formatMessage(
const chatContent = formatChatMessage(
message.payload.component,
message.payload.translations,
);
Expand All @@ -245,7 +245,7 @@ function handleChatMessage(message) {
if (e instanceof ComponentError) {
console.error('Invalid component:', e.toString());
messageElement.appendChild(
formatMessage(
formatChatMessage(
{
text: 'Invalid message received from server',
color: 'red',
Expand All @@ -256,7 +256,7 @@ function handleChatMessage(message) {
} else {
console.error('Error parsing message:', e);
messageElement.appendChild(
formatMessage(
formatChatMessage(
{
text: 'Error parsing message',
color: 'red',
Expand Down
6 changes: 2 additions & 4 deletions src/client/resources/web/js/managers/direct_message.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @ts-check
'use strict';

import { formatComponentToString } from '../messages/message_parsing.mjs';
import { querySelectorWithAssertion } from '../utils.mjs';

/**
Expand Down Expand Up @@ -48,12 +47,11 @@ class DirectMessageManager {
this.#resetCallback = null;
}

const displayName = formatComponentToString(player.playerDisplayName);
this.#chatInputElement.placeholder = `Message ${displayName}...`;
this.#chatInputElement.placeholder = `Message ${player.playerDisplayName}...`;
this.#chatInputElement.focus();
this.#currentPlayer = player;
this.#clearButton.style.display = 'block';
this.#clearButton.title = `Stop chat with ${displayName}`;
this.#clearButton.title = `Stop chat with ${player.playerDisplayName}`;

if (onReset) {
this.#resetCallback = onReset;
Expand Down
29 changes: 11 additions & 18 deletions src/client/resources/web/js/managers/player_list.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
'use strict';

import { directMessageManager } from './direct_message.mjs';
import {
formatMessage,
formatComponentToString,
} from '../messages/message_parsing.mjs';
import { querySelectorWithAssertion } from '../utils.mjs';

/**
Expand Down Expand Up @@ -153,9 +149,7 @@ class PlayerList {
player.element.querySelector('.player-head-container')
);
if (headContainer) {
headContainer.title = `${formatComponentToString(
player.playerDisplayName,
)}'s head`;
headContainer.title = `${player.playerDisplayName}'s head`;
}

const headImg = /** @type {HTMLImageElement | null} */ (
Expand All @@ -176,9 +170,7 @@ class PlayerList {
player.element.querySelector('.player-name')
);
if (nameSpan) {
nameSpan.replaceChildren(
formatMessage(player.playerDisplayName, {}),
);
nameSpan.textContent = player.playerDisplayName;
nameSpan.title = player.playerName;
}

Expand Down Expand Up @@ -223,18 +215,19 @@ class PlayerList {
// Create and configure the player's display name span.
const nameSpan = document.createElement('span');
nameSpan.className = 'player-name';
nameSpan.replaceChildren(formatMessage(player.playerDisplayName, {}));

const displayName = formatComponentToString(player.playerDisplayName);
nameSpan.textContent = player.playerDisplayName;

// Specifically for aria labels show both playerDisplayName and playerName if they are different.
if (displayName !== player.playerName) {
if (player.playerDisplayName !== player.playerName) {
nameSpan.setAttribute(
'aria-label',
`Display name: ${displayName}, Username: ${player.playerName}`,
`Display name: ${player.playerDisplayName}, Username: ${player.playerName}`,
);
} else {
nameSpan.setAttribute('aria-label', `Player name: ${displayName}`);
nameSpan.setAttribute(
'aria-label',
`Player name: ${player.playerDisplayName}`,
);
}

const chatIcon = document.createElement('img');
Expand All @@ -253,13 +246,13 @@ class PlayerList {
'aria-label',
'Stop chat with player',
);
playerElement.title = `Stop chat with ${displayName}`;
playerElement.title = `Stop chat with ${player.playerDisplayName}`;
chatIcon.style.display = 'block';
}

function deselectPlayer() {
playerElement.setAttribute('aria-label', 'Chat with player');
playerElement.title = `Chat with ${displayName}`;
playerElement.title = `Chat with ${player.playerDisplayName}`;
chatIcon.style.display = 'none';
}

Expand Down
18 changes: 6 additions & 12 deletions src/client/resources/web/js/managers/tab_list_manager.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @ts-check
'use strict';

import { formatComponentToString } from '../messages/message_parsing.mjs';
import { querySelectorWithAssertion } from '../utils.mjs';

/**
Expand Down Expand Up @@ -49,7 +48,7 @@ class TabListManager {
// Find players that start with the word before the cursor
const matches = players.filter(
(player) =>
formatComponentToString(player.playerDisplayName)
player.playerDisplayName
.toLocaleLowerCase()
.startsWith(wordBefore) ||
player.playerName.toLocaleLowerCase().startsWith(wordBefore),
Expand Down Expand Up @@ -165,9 +164,7 @@ class TabListManager {
...matches
// Show names in alphabetical order.
.sort((a, b) =>
formatComponentToString(a.playerDisplayName).localeCompare(
formatComponentToString(b.playerDisplayName),
),
a.playerDisplayName.localeCompare(b.playerDisplayName),
)
// Show only first 5 matches.
.slice(0, 5)
Expand All @@ -181,15 +178,12 @@ class TabListManager {
this.#updateSelection(index);
});

const displayName = formatComponentToString(
match.playerDisplayName,
);
const displayNameUnchanged =
displayName.toLocaleLowerCase() ===
match.playerDisplayName.toLocaleLowerCase() ===
match.playerName.toLocaleLowerCase();
li.textContent = displayNameUnchanged
? displayName
: `${displayName} (${match.playerName})`;
? match.playerDisplayName
: `${match.playerDisplayName} (${match.playerName})`;

return li;
}),
Expand Down Expand Up @@ -255,7 +249,7 @@ class TabListManager {
// Use playerName instead of playerDisplayName for direct-message commands
const playerNameToInsert = beforeCursor.match(/^\/(tell|msg|w)[ ]+\w+$/)
? selectedPlayer.playerName
: formatComponentToString(selectedPlayer.playerDisplayName);
: selectedPlayer.playerDisplayName;

// There is a partial player name before the cursor. Get everything
// before it and add the full player name after.
Expand Down
13 changes: 1 addition & 12 deletions src/client/resources/web/js/messages/message_parsing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1417,24 +1417,13 @@ export function formatPlainText(element) {
}
}

/**
* Transforms a Minecraft component into plain text.
* @param {Component} component
* @returns {string}
*/
export function formatComponentToString(component) {
const element = formatComponent(component, {});
formatPlainText(element);
return element.textContent ?? '';
}

/**
* Transforms a Minecraft component into HTML.
* @param {Component} component
* @param {Record<string, string>} translations Translation key-value pairs
* @returns {Element | Text}
*/
export function formatMessage(component, translations) {
export function formatChatMessage(component, translations) {
/** @type {Element} */
let element;
try {
Expand Down
2 changes: 1 addition & 1 deletion src/client/resources/web/js/messages/message_types.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* @typedef {Object} PlayerInfo
* @property {string} playerId
* @property {string} playerName
* @property {Component} playerDisplayName
* @property {string} playerDisplayName
* @property {string} playerTextureUrl
*/

Expand Down
4 changes: 2 additions & 2 deletions src/test/resources/web/js/message_parsing.test.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, test, beforeAll, beforeEach } from 'vitest';
import {
assertIsComponent,
formatMessage,
formatChatMessage,
} from '~/messages/message_parsing.mjs';
/**
* @typedef {import('~/messages/message_parsing.mjs').Component} Component
Expand Down Expand Up @@ -708,7 +708,7 @@ for (const [
test(name, () => {
expect(() => assertIsComponent(component)).not.toThrow();

const element = formatMessage(component, translations);
const element = formatChatMessage(component, translations);
if (element instanceof Text) {
expect(element.textContent).toBe(expected);
} else {
Expand Down