Skip to content
Merged
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
37 changes: 33 additions & 4 deletions src/main/smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8978,19 +8978,30 @@ export async function runWebSmoke(): Promise<void> {
const exact: LivePlayer[] = [
{ name: 'Alex', uuid: 'u-1', world: 'world', dim: 'overworld', x: 1234, y: 12, z: -987 }
]
const pub = redactPlayers(exact, { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's' })
// Rounding is asked for EXPLICITLY here rather than taken from the
// defaults. It used to come from them, and when the default changed to
// exact this block started asserting that the feature was broken — the
// two questions are "does redaction work when switched on" and "is it on
// by default", and only the first belongs in this block.
const pub = redactPlayers(exact, { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's', round: 64 })
const one = pub[0] as unknown as Record<string, unknown>
// The panel payload's fields must not arrive here by being spread
// through. Height is the sharp one: y=12 says "in a cave", which is
// when a player cannot defend the base you would then walk to.
// when a player cannot defend the base you would then walk to. This
// holds whatever the rounding is — the fields are the protection that
// does not have a setting.
const bare = redactPlayers(exact, { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's' })[0]
for (const leaked of ['y', 'world', 'uuid']) {
if (leaked in one) return fail('the public map payload carries "' + leaked + '"')
if (leaked in (bare as unknown as Record<string, unknown>)) {
return fail('with rounding off the public map payload carries "' + leaked + '"')
}
}
if (one.x === 1234 || one.z === -987) return fail('the public map published exact coordinates')
if (one.x === 1234 || one.z === -987) return fail('rounding did not move a player at all')
if (Math.abs((one.x as number) - 1234) > 32) return fail('rounding moved a player more than half a cell')
// Deterministic, not jittered: a watcher who samples a stationary
// player repeatedly must not be able to average the noise away.
const again = redactPlayers(exact, { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's' })
const again = redactPlayers(exact, { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's', round: 64 })
if (again[0].x !== pub[0].x || again[0].z !== pub[0].z) return fail('redaction is not deterministic')
// Opt-ins.
// Heads are drawn from the name since #116, so heads-on must publish
Expand All @@ -9009,6 +9020,24 @@ export async function runWebSmoke(): Promise<void> {
if ('name' in (noNames[0] as unknown as Record<string, unknown>)) {
return fail('names off still published a name')
}
// Exact by default. Rounding is a real protection and stays available,
// but it was ON at 64 blocks for everyone, and on a map that now draws
// terrain that puts a player visibly beside the house they are standing
// in — which reads as a placement bug, and was reported as one.
if (PUBLIC_MAP_DEFAULTS.round !== 0) {
return fail('the public map rounds positions by default: ' + PUBLIC_MAP_DEFAULTS.round)
}
if (MAP_PAGE_DEFAULTS.round !== 0) {
return fail('the map page rounds positions by default: ' + MAP_PAGE_DEFAULTS.round)
}
// Still available, and still exactly as strong when asked for.
{
const exactCfg = { ...PUBLIC_MAP_DEFAULTS, enabled: true, serverId: 's' }
const p = redactPlayers(exact, exactCfg)[0]
if (p.x !== 1234 || p.z !== -987) return fail('the default no longer publishes exact positions')
const rounded = redactPlayers(exact, { ...exactCfg, round: 64 })[0]
if (rounded.x % 64 !== 0 || rounded.z % 64 !== 0) return fail('opting into rounding stopped working')
}
if (clampRound(-5) !== 0) return fail('a negative rounding was accepted')
if (clampRound(99999) !== 512) return fail('rounding was not capped')
if (clampRound('lots') !== PUBLIC_MAP_DEFAULTS.round) return fail('a junk rounding did not fall back')
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ export default {
mapServer: 'Live map server',
showMap: 'Show live map on the site',
mapRound: 'Round to (blocks)',
mapRoundHint: '0 publishes exact positions. Any other value hides them: a player is drawn up to half of it away from where they really are, which on a terrain map looks like a mistake rather than like privacy.',
mapNames: 'Show names',
mapHeads: 'Draw skin heads',
mapWorld: 'Show the terrain',
Expand Down Expand Up @@ -914,6 +915,7 @@ export default {
mapStructures: 'Structures',
mapHeat: 'Heatmap',
mapRound: 'Round positions to',
mapRoundHint: '0 publishes exact positions. Any other value draws a player up to half of it from where they really are.',
mapPin: 'Pin one world',
mapPinAny: 'Let visitors switch',
siteSection: 'Public website',
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/locales/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ const tr: typeof en = {
mapServer: 'Canlı harita sunucusu',
showMap: 'Sitede canlı haritayı göster',
mapRound: 'Yuvarlama (blok)',
mapRoundHint: '0 tam konumu yayımlar. Başka bir değer konumu gizler: oyuncu, gerçekte durduğu yerden bu değerin yarısı kadar uzakta çizilir — arazi haritasında bu, gizlilikten çok yerleştirme hatası gibi görünür.',
mapNames: 'İsimleri göster',
mapHeads: 'Oyuncu kafalarını çiz',
mapWorld: 'Araziyi göster',
Expand Down Expand Up @@ -918,6 +919,7 @@ const tr: typeof en = {
mapStructures: 'Yapılar',
mapHeat: 'Isı haritası',
mapRound: 'Konumları şuna yuvarla',
mapRoundHint: '0 tam konumu yayımlar. Başka bir değer oyuncuyu gerçek yerinden bu değerin yarısı kadar uzakta çizer.',
mapPin: 'Tek dünyaya sabitle',
mapPinAny: 'Ziyaretçi değiştirebilsin',
siteSection: 'Herkese açık site',
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/src/views/SiteView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ export function SiteView(): JSX.Element {
<input type="checkbox" checked={cfg.map.enabled} onChange={(e) => patchMap({ enabled: e.target.checked })} />
{t('site.showMap')}
</label>
{/* The number that decides whether the map looks right. Said in
blocks and in consequences, because "Round to 64" reads as a
display nicety and is actually "every player is drawn up to 32
blocks from where they are" — which on a map that now draws real
terrain is indistinguishable from a bug. */}
<div className="field" style={{ width: 150, marginBottom: 0 }}>
<label>{t('site.mapRound')}</label>
<input
Expand All @@ -207,6 +212,9 @@ export function SiteView(): JSX.Element {
onChange={(e) => patchMap({ round: Number(e.target.value) })}
/>
</div>
<p className="hint" style={{ flexBasis: '100%', marginTop: 0 }}>
{t('site.mapRoundHint')}
</p>
<label className="switch" style={{ paddingBottom: 8 }}>
<input type="checkbox" checked={cfg.map.names} onChange={(e) => patchMap({ names: e.target.checked })} />
{t('site.mapNames')}
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/views/WebPanelView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export function WebPanelView(): JSX.Element {
value={mapPage.round}
onChange={(e) => setMapPage({ ...mapPage, round: Number(e.target.value) })}
/>
<p className="hint" style={{ marginTop: 4 }}>{t('web.mapRoundHint')}</p>
</div>
<div className="field" style={{ flex: 1, minWidth: 140 }}>
<label>{t('web.mapPin')}</label>
Expand Down
11 changes: 10 additions & 1 deletion src/shared/livemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,16 @@ export interface PublicMapConfig {
export const PUBLIC_MAP_DEFAULTS: PublicMapConfig = {
enabled: false,
serverId: '',
round: 64,
/**
* Exact, and rounding is the opt-in.
*
* This was 64 — chosen when the public map was dots on an empty grid, where
* being up to 32 blocks out was invisible. The map draws real terrain now, so
* the same rounding puts a player visibly beside the house they are standing
* in, and it reads as a placement bug rather than as privacy. An operator who
* wants the protection can still have it, and the field says what it costs.
*/
round: 0,
heads: false,
names: true,
// The terrain is the map. Publishing a grid with dots on it and calling it a
Expand Down
4 changes: 3 additions & 1 deletion src/shared/mapPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ export const MAP_PAGE_DEFAULTS: MapPageConfig = {
players: true,
names: true,
heads: true,
round: 64,
// Exact, matching the public site — see PUBLIC_MAP_DEFAULTS. Rounding is the
// opt-in now that the map draws terrain a player can be seen standing beside.
round: 0,
structures: false,
areas: true,
heatmap: false
Expand Down
Loading