Skip to content

Commit

Permalink
world, viewer: fix sign text colors
Browse files Browse the repository at this point in the history
- Fix text colors for signs modified using dye
- Fix text colors specified using `#rrggbb` CSS syntax in JSON text

Only named colors specified via JSON text were working as intended. Dyed signs use different
color names.

The mapping of color names to values is now handled by the generator. Both the generator and the
viewer must be updated for sign text colors to work.
  • Loading branch information
neocturne committed Jan 6, 2025
1 parent 9375af8 commit ff6e28d
Showing 7 changed files with 192 additions and 27 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,17 @@

## [Unreleased] - ReleaseDate

### Fixed

- Fix text colors for signs modified using dye
- Fix text colors specified using `#rrggbb` CSS syntax in JSON text

Only named colors specified via JSON text were working as intended. Dyed signs use different
color names.

The mapping of color names to values is now handled by the generator. Both the generator and the
viewer must be updated for sign text colors to work.

## [2.3.0] - 2025-01-02

### Added
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -52,6 +52,7 @@ minedmap-resource = { version = "0.5.0", path = "crates/resource" }
minedmap-types = { version = "0.1.2", path = "crates/types" }
num-integer = "0.1.45"
num_cpus = "1.16.0"
phf = { version = "0.11.2", features = ["macros"] }
rayon = "1.7.0"
regex = "1.10.2"
rustc-hash = "2.0.0"
2 changes: 1 addition & 1 deletion src/core/common.rs
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ pub const MIPMAP_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
/// MinedMap processed entity data version number
///
/// Increase when entity collection changes bacause of code changes.
pub const ENTITIES_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
pub const ENTITIES_FILE_META_VERSION: FileMetaVersion = FileMetaVersion(1);

/// Coordinate pair of a generated tile
///
89 changes: 85 additions & 4 deletions src/world/json_text.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Newtype and helper methods for handling Minecraft Raw JSON Text
use std::{collections::VecDeque, fmt::Display, sync::Arc};
use std::{collections::VecDeque, fmt::Display};

use minedmap_resource::Color;
use serde::{Deserialize, Serialize};

/// A span of formatted text
@@ -17,8 +18,8 @@ pub struct FormattedText {
/// Text content
pub text: String,
/// Text color
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<Arc<String>>,
#[serde(skip_serializing_if = "Option::is_none", with = "json_color")]
pub color: Option<Color>,
/// Bold formatting
#[serde(skip_serializing_if = "Option::is_none")]
pub bold: Option<bool>,
@@ -41,7 +42,7 @@ impl FormattedText {
pub fn inherit(self, parent: &Self) -> Self {
FormattedText {
text: self.text,
color: self.color.or_else(|| parent.color.clone()),
color: self.color.or(parent.color),
bold: self.bold.or(parent.bold),
italic: self.italic.or(parent.italic),
underlined: self.underlined.or(parent.underlined),
@@ -175,3 +176,83 @@ impl JSONText {
serde_json::from_str(&self.0).unwrap_or_default()
}
}

mod json_color {
//! Helpers for serializing and deserializing [FormattedText](super::FormattedText) colors
use minedmap_resource::Color;
use serde::{
de::{self, Visitor},
ser::Error as _,
Deserializer, Serializer,
};

/// Named JSON text colors
static COLORS: phf::Map<&'static str, Color> = phf::phf_map! {
"black" => Color([0x00, 0x00, 0x00]),
"dark_blue" => Color([0x00, 0x00, 0xAA]),
"dark_green" => Color([0x00, 0xAA, 0x00]),
"dark_aqua" => Color([0x00, 0xAA, 0xAA]),
"dark_red" => Color([0xAA, 0x00, 0x00]),
"dark_purple" => Color([0xAA, 0x00, 0xAA]),
"gold" => Color([0xFF, 0xAA, 0x00]),
"gray" => Color([0xAA, 0xAA, 0xAA]),
"dark_gray" => Color([0x55, 0x55, 0x55]),
"blue" => Color([0x55, 0x55, 0xFF]),
"green" => Color([0x55, 0xFF, 0x55]),
"aqua" => Color([0x55, 0xFF, 0xFF]),
"red" => Color([0xFF, 0x55, 0x55]),
"light_purple" => Color([0xFF, 0x55, 0xFF]),
"yellow" => Color([0xFF, 0xFF, 0x55]),
"white" => Color([0xFF, 0xFF, 0xFF]),
};

/// serde serialize function for [FormattedText::color](super::FormattedText::color)
pub fn serialize<S>(color: &Option<Color>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let &Some(color) = color else {
return Err(S::Error::custom("serialize called for None sign color"));
};

let text = format!("#{:02x}{:02x}{:02x}", color.0[0], color.0[1], color.0[2]);
serializer.serialize_str(&text)
}

/// serde [Visitor] for use by [deserialize]
struct ColorVisitor;

impl Visitor<'_> for ColorVisitor {
type Value = Option<Color>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string representing a color")
}

fn visit_str<E>(self, color: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
if let Some(hex) = color.strip_prefix("#") {
if let Ok(value) = u32::from_str_radix(hex, 16) {
return Ok(Some(Color([
(value >> 16) as u8,
(value >> 8) as u8,
value as u8,
])));
}
}

Ok(COLORS.get(color).copied())
}
}

/// serde deserialize function for [FormattedText::color](super::FormattedText::color)
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Color>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(ColorVisitor)
}
}
29 changes: 27 additions & 2 deletions src/world/sign.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Processing of sign text
use std::{fmt::Display, sync::Arc};
use std::fmt::Display;

use minedmap_resource::Color;
use serde::{Deserialize, Serialize};

use super::{
@@ -23,10 +24,34 @@ pub struct RawSignText<'a> {
pub color: Option<&'a str>,
}

/// The color to use for signs without a color attribute ("black")
const DEFAULT_COLOR: Color = Color([0, 0, 0]);

/// Map of text colors associated with dyes (except for black)
static DYE_COLORS: phf::Map<&'static str, Color> = phf::phf_map! {
"white" => Color([255, 255, 255]),
"orange" => Color([255, 104, 31]),
"magenta" => Color([255, 0, 255]),
"light_blue" => Color([154, 192, 205]),
"yellow" => Color([255, 255, 0]),
"lime" => Color([191, 255, 0]),
"pink" => Color([255, 105, 180]),
"gray" => Color([128, 128, 128]),
"light_gray" => Color([211, 211, 211]),
"cyan" => Color([0, 255, 255]),
"purple" => Color([160, 32, 240]),
"blue" => Color([0, 0, 255]),
"brown" => Color([139, 69, 19]),
"green" => Color([0, 255, 0]),
"red" => Color([255, 0, 0]),
};

impl RawSignText<'_> {
/// Decodes the [RawSignText] into a [SignText]
pub fn decode(&self) -> SignText {
let color = self.color.map(|c| Arc::new(c.to_owned()));
let color = self
.color
.map(|c| DYE_COLORS.get(c).copied().unwrap_or(DEFAULT_COLOR));
let parent = FormattedText {
color,
..Default::default()
23 changes: 3 additions & 20 deletions viewer/MinedMap.js
Original file line number Diff line number Diff line change
@@ -153,25 +153,6 @@ const parseHash = function () {
return args;
}

const colors = {
black: '#000000',
dark_blue: '#0000AA',
dark_green: '#00AA00',
dark_aqua: '#00AAAA',
dark_red: '#AA0000',
dark_purple: '#AA00AA',
gold: '#FFAA00',
gray: '#AAAAAA',
dark_gray: '#555555',
blue: '#5555FF',
green: '#55FF55',
aqua: '#55FFFF',
red: '#FF5555',
light_purple: '#FF55FF',
yellow: '#FFFF55',
white: '#FFFFFF',
};

function formatSignLine(line) {
const el = document.createElement('span');
el.style.whiteSpace = 'pre';
@@ -180,7 +161,9 @@ function formatSignLine(line) {
const child = document.createElement('span');
child.textContent = span.text;

const color = colors[span.color ?? 'black'] || colors['black'];
let color = span.color ?? '';
if (color[0] !== '#')
color = '#000000';

if (span.bold)
child.style.fontWeight = 'bold';

0 comments on commit ff6e28d

Please sign in to comment.