介紹
Dynmap 是 Minecraft 中以網頁 Web 形式呈現地圖的模組,和 BlueMap 等類似。我自己倒是 Dynmap 用多了感覺更習慣一些就一直用下去了,雖然如今 BlueMap 之類的確實更先進。
LiveAtlas
LiveAtlas 是 Dynmap 的第三方皮膚擴充套件,下載好後直接匯入 dynmap/web 資料夾覆蓋即可。
配置
這是下載後的檔案結構,從 repo 來看這個專案用的 Yarn + Vue,開啟 index.html 看看。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="preload" href="https://fonts.gstatic.com/s/raleway/v22/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaorCIPrE.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="https://fonts.gstatic.com/s/raleway/v22/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVsEpbCIPrE.woff2" as="font" crossorigin="anonymous">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="theme-color" content="#222222">
<link rel="manifest" href="./live-atlas/favicons/site.webmanifest">
<link rel="icon" href="./live-atlas/favicons/favicon.svg">
<link rel=”mask-icon” href="./live-atlas/favicons/mask.svg" color="#cccccc">
<link rel="apple-touch-icon" sizes="180x180" href="./live-atlas/favicons/apple-touch-icon.png">
<meta name="keywords" content="minecraft, map, dynamic, liveatlas" />
<meta name="description" content="Minecraft Dynamic Map" />
<title>Minecraft Dynamic Map - LiveAtlas</title>
<!-- Remove this if you are using the servers config below -->
<script src="./standalone/config.js"></script>
<script>
window.liveAtlasConfig = {
// By default LiveAtlas looks for a dynmap standalone/config.js file
// This configuration can be used instead to support Pl3xmap and Squaremap installations as well as multiple servers (external webserver required)
// To configure multiple servers, see https://github.com/JLyne/LiveAtlas/wiki/Configuring-Multiple-Servers.
// Example Squaremap internal webserver configuration
// servers: {
// squaremap: {
// squaremap: window.location.pathname
// },
// },
// Example Pl3xmap internal webserver configuration
// servers: {
// pl3xmap: {
// pl3xmap: window.location.pathname
// },
// },
// Example Overviewer configuration
// servers: {
// overviewer: {
// overviewer: window.location.pathname
// },
// },
// Example Dynmap internal webserver configuration without using standalone/config.js
// servers: {
// dynmap: {
// dynmap: {
// configuration: 'standalone/dynmap_config.json?_={timestamp}',
// update: 'standalone/dynmap_{world}.json?_={timestamp}',
// sendmessage: 'standalone/sendmessage.php',
// login: 'standalone/login.php',
// register: 'standalone/register.php',
// tiles: 'tiles/',
// markers: 'tiles/'
// }
// },
// },
// Example multiple Dynmap servers on external webserver configuration
// servers: {
// creative: {
// label: 'Creative',
// dynmap: {
// configuration: 'http://dynmap.local/standalone/creative/MySQL_configuration.php',
// update: 'http://dynmap.local/standalone/creative/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/standalone/creative/MySQL_sendmessage.php',
// login: 'http://dynmap.local/standalone/creative/MySQL_login.php',
// register: 'http://dynmap.local/standalone/creative/MySQL_register.php',
// tiles: 'http://dynmap.local/standalone/creative/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/standalone/creative/MySQL_markers.php?marker='
// }
// },
// survival: {
// label: 'Survival',
// dynmap: {
// configuration: 'http://dynmap.local/standalone/survival/MySQL_configuration.php',
// update: 'http://dynmap.local/standalone/survival/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/standalone/survival/MySQL_sendmessage.php',
// login: 'http://dynmap.local/standalone/survival/MySQL_login.php',
// register: 'http://dynmap.local/standalone/survival/MySQL_register.php',
// tiles: 'http://dynmap.local/standalone/survival/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/standalone/survival/MySQL_markers.php?marker='
// }
// },
// },
// These messages are used throughout LiveAtlas and can be translated here
// If a message you want to translate isn't here, it is likely controlled by dynmap itself
// see https://github.com/webbukkit/dynmap/wiki/Configuration.txt
messages: {
chatNoMessages: 'No chat messages yet...',
chatTitle: 'Chat',
chatLogin: 'Please login to send chat messages',
chatSend: 'Send',
chatPlaceholder: 'Type your chat message here...',
chatErrorUnknown: 'Unexpected error while sending chat message',
chatErrorDisabled: 'Chat is not enabled',
serversHeading: 'Servers',
markersHeading: 'Markers',
markersSearchPlaceholder: 'Search markers...',
markersSkeleton: 'No markers exist for the current world',
markersSetSkeleton: 'This marker set is empty',
markersSearchSkeleton: 'No matching markers found',
markersUnnamed: '(Unnamed marker)',
worldsSkeleton: 'No maps have been configured',
playersSkeleton: 'No players are currently online',
playersTitle: 'Click to center on player\nDouble-click to follow player',
playersTitleHidden: 'This player is currently hidden from the map\nDouble-click to follow player when they become visible',
playersTitleOtherWorld: 'This player is in another world.\nClick to center on player\nDouble-click to follow player',
playersSearchPlaceholder: 'Search players...',
playersSearchSkeleton: 'No matching players found',
followingHeading: 'Following',
followingUnfollow: 'Unfollow',
followingTitleUnfollow: 'Stop following this player',
followingHidden: 'Currently hidden',
linkTitle: 'Copy link to current location',
loadingTitle: 'Loading...',
locationRegion: 'Region',
locationChunk: 'Chunk',
contextMenuCopyLink: 'Copy link to here',
contextMenuCenterHere: 'Center here',
toggleTitle: 'Click to toggle this section',
mapTitle: 'Map - Use the arrow keys to pan the map',
layersTitle: 'Layers',
copyToClipboardSuccess: 'Copied to clipboard',
copyToClipboardError: 'Unable to copy to clipboard',
loginTitle: 'Login/Register',
loginHeading: 'Existing User',
loginUsernameLabel: 'Username',
loginPasswordLabel: 'Password',
loginSubmit: 'Login',
loginErrorUnknown: 'Unexpected error while logging in',
loginErrorDisabled: 'Logging in is disabled on this server',
loginErrorIncorrect: 'Incorrect username or password',
loginSuccess: 'Logged in successfully',
registerHeading: 'New User',
registerDescription: `Enter your username and password, along with your registration code.
You can get a registration code by running /dynmap webregister in-game.`,
registerConfirmPasswordLabel: 'Confirm Password',
registerCodeLabel: 'Registration Code',
registerSubmit: 'Register',
registerErrorUnknown: 'Unexpected error during registration',
registerErrorDisabled: 'Registration is disabled on this server',
registerErrorVerifyFailed: 'The entered passwords do not match',
registerErrorIncorrect: 'Registration failed, please check the entered details are correct',
logoutTitle: 'Logout',
logoutErrorUnknown: 'Unexpected error while logging out',
logoutSuccess: 'Logged out successfully',
closeTitle: 'Close',
showMore: 'Show more'
},
ui: {
// If true, player markers will always be displayed in front of other marker types
playersAboveMarkers: true,
// Whether to enable the player list search box
playersSearch: true,
// Use more compact pre-2.0 player marker style
compactPlayerMarkers: false,
// Disable the map right click menu
disableContextMenu: false,
// Disable the markers button and list
disableMarkerUI: false,
// Custom URL to redirect to when logging in is required
// This URL will need to handle the login process and redirect users back to LiveAtlas
customLoginUrl: null
},
// Config version. Do not modify.
version: 1
};
</script>
<style>
/* Theme colours */
:root {
--background-base: #222222; /* Foreground UI elements */
--background-dark: #121212; /* Body/Splash screen/Shadows */
--background-light: #363636; /* Scrollbars/inputs */
--background-error: #771616; /* Errors */
--background-marker: var(--background-dark); /* Map markers */
--background-disabled: #555555; /* Disabled controls */
--background-hover: #363636; /* :hovered buttons/menu items */
--background-active: #6d6d6d; /* Button :active */
--background-selected: #BDBDBD; /* Selected buttons/menu items */
--outline-focus: #eeeeee; /* :focus outline */
--border-radius: 0.5rem;
--border-color: #333333; /* Control borders */
--border-error: #cc0e0e;
--box-shadow: 2px 2px 0px #111111;
--text-base: rgba(255, 255, 255, 0.7); /* Normal text */
--text-emphasis: rgba(255, 255, 255, 0.87); /* Chat messages/:focus inputs */
--text-subtle: rgba(255, 255, 255, 0.5); /* Skeletons/secondary text */
--text-disabled: var(--text-subtle); /* Disabled controls */
--text-marker: var(--text-base); /* Map markers */
--text-hover: var(--text-base); /* Text in :hover buttons */
--text-active: var(--text-base); /* Text in :active buttons */
--text-selected: var(--background-base); /* Text in selected buttons */
--text-shadow: 0.1rem 0.1rem #000000; /* Text in selected buttons */
--text-night: #ddffff; /* Clock time at night */
--text-day: #ffdd33; /* Clock time in day */
}
@keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
* {
scrollbar-width: thin;
scrollbar-color: var(--background-light) transparent;
}
*:hover, *:focus-within {
scrollbar-color: var(--background-hover) transparent;
}
*::-webkit-scrollbar {
width: 0.7rem;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: var(--background-light);
border: 2px solid #000000;
border-radius: 2rem;
transition: background 1s ease-in;
padding-right: 0.2rem;
}
*:hover::-webkit-scrollbar-thumb, *:focus-within::-webkit-scrollbar-thumb {
background-color: var(--background-hover);
}
*::-webkit-scrollbar-button {
display: none;
}
html, body {
background-color: var(--background-dark);
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overscroll-behavior: none;
}
html {
font-size: 62.5%;
}
body {
font-family: Raleway,
system-ui,
-apple-system,
'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji';
color: var(--text-base);
text-shadow: var(--text-shadow);
letter-spacing: 0.02rem;
}
noscript {
color: var(--text-base);
font-size: 1rem;
font-family: sans-serif;
text-align: center;
line-height: 1.25;
}
#splash, noscript {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
transition: 0.3s opacity linear;
z-index: 100;
background-color: var(--background-dark);
cursor: wait;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 1.6rem;
padding: 4rem;
}
#splash[hidden] {
display: none;
}
#splash__spinner {
margin-top: 4rem;
animation: fade 0.5s linear 1s;
animation-fill-mode: both;
}
#splash__error {
margin-top: 2rem;
transition: opacity 0.5s ease-in;
display: flex;
flex-direction: column;
text-align: center;
max-width: 60rem;
}
#splash__error-message {
font-family: monospace;
background-color: var(--background-error);
padding: 1rem 1.5rem;
border-radius: var(--border-radius);
margin-bottom: 1rem;
}
#splash__error[aria-hidden=true] {
opacity: 0;
}
#app {
font-size: 1.6rem;
height: 100%;
}
</style>
<script type="module" crossorigin src="./live-atlas/assets/index.a82d2095.js"></script>
<link rel="modulepreload" href="./live-atlas/assets/vendor.d0ab50b1.js">
<link rel="stylesheet" href="./live-atlas/assets/index.1de34b3a.css">
</head>
<body>
<div id="splash">
<svg id="splash__logo" width="200" height="200" viewBox="0 0 268.83 266.53" aria-hidden="true" fill="rgba(255, 255, 255, 0.7)">
<path d="M5.235.662C2.153.606 0 2.945 0 6.912v216.577c0 5.288 3.828 11.41 8.582 13.725l57.835 28.165c4.755 2.316 8.582-.078 8.582-5.367V43.434c0-5.288-3.827-11.41-8.582-13.727L8.582 1.544C7.394.965 6.262.681 5.235.662zm32.283 135.96c14.394 0 26.062 11.669 26.062 26.063 0 14.394-24.167 59.55-26.062 57.654-1.854 1.854-26.063-43.26-26.063-57.654 0-14.394 11.669-26.063 26.063-26.063zM202.388 1.013l57.833 28.165c4.755 2.315 8.583 8.437 8.583 13.726v216.58c0 5.29-3.828 7.683-8.583 5.367l-57.833-28.164c-4.755-2.316-8.583-8.438-8.583-13.727V6.38c0-5.289 3.828-7.682 8.583-5.367zM172.012.39c-1.051-.035-2.209.191-3.426.709l-68.342 29.053c-4.867 2.07-8.786 7.993-8.786 13.282V260.01c0 5.29 3.919 7.88 8.786 5.811l68.342-29.053c4.867-2.07 8.786-7.991 8.786-13.28V6.91c0-3.967-2.204-6.417-5.36-6.521zm-36.949 41.216c14.394 0 26.063 11.668 26.063 26.062 0 14.394-24.168 59.55-26.063 57.655C133.209 127.177 109 82.063 109 67.668c0-14.394 11.669-26.062 26.063-26.062z"/>
<path d="M48.573 162.689a11.056 11.056 0 0 1-11.056 11.056 11.056 11.056 0 0 1-11.056-11.056 11.056 11.056 0 0 1 11.056-11.056 11.056 11.056 0 0 1 11.056 11.056zM146.12 67.669a11.056 11.056 0 0 1-11.057 11.056 11.056 11.056 0 0 1-11.056-11.056 11.056 11.056 0 0 1 11.056-11.056 11.056 11.056 0 0 1 11.056 11.056z"/>
</svg>
<svg id="splash__spinner" width="38" height="38" viewBox="0 0 38 38" stroke="#fff" aria-label="LiveAtlas is loading">
<g transform="translate(1 1)" stroke-width="2" fill="none">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"/>
</path>
</g>
</svg>
<div id="splash__error" aria-hidden="true">
<span id="splash__error-message" role="alert"></span>
<span id="splash__error-retry" aria-live="polite"></span>
</div>
</div>
<noscript>
<strong>LiveAtlas requires JavaScript to work.<br />Please enable it to continue.</strong>
</noscript>
<main id="app" aria-hidden="true"></main>
<script>
window.addEventListener('load', () => {
if(!window.liveAtlasLoaded) {
document.getElementById('splash__error').setAttribute('aria-hidden', 'false');
document.getElementById('splash__error-message').innerText = 'Required LiveAtlas files are missing or failed to load.\nPlease reinstall LiveAtlas.';
document.getElementById('splash__spinner').style.visibility = 'hidden';
}
});
</script>
</body>
</html>
index.html 裡面的內容都預留好了配置口,比如翻譯資訊、CSS,需要的話直接覆蓋修改即可。以下是筆者自己的翻譯資訊:
messages: {
chatNoMessages: '沒人發資訊喵。',
chatTitle: '聊天',
chatLogin: '只有登入了才能發訊息喵~',
chatSend: '傳送',
chatPlaceholder: '聊天資訊輸在這',
chatErrorUnknown: '不知道為什麼訊息發不出去',
chatErrorDisabled: '聊天未啟用',
serversHeading: '伺服器',
markersHeading: '標記',
markersSearchPlaceholder: '搜尋標記',
markersSkeleton: '當前世界沒有標記點存在',
markersSetSkeleton: '這個標記點是空的',
markersSearchSkeleton: '無匹配結果',
markersUnnamed: '(未命名標記)',
worldsSkeleton: '沒有地圖被配置',
playersSkeleton: '現在沒人線上喵!',
playersTitle: '單擊以定位玩家\n雙擊以跟隨玩家',
playersTitleHidden: '該玩家在當前地圖上隱藏\n雙擊可在玩家可見時跟隨',
playersTitleOtherWorld: '該玩家在另一個世界\n點選以定位到玩家\n雙擊以跟隨玩家',
playersSearchPlaceholder: '搜尋玩家…',
playersSearchSkeleton: '找不到匹配的玩家',
followingHeading: '跟隨中',
followingUnfollow: '取消跟隨',
followingTitleUnfollow: '停止跟隨該玩家',
followingHidden: '當前隱藏',
linkTitle: '獲取當前座標的網址',
loadingTitle: '載入中…',
locationRegion: '區域',
locationChunk: '區塊',
contextMenuCopyLink: '複製連結',
contextMenuCenterHere: '定位到此處',
toggleTitle: '點選以切換此部分',
mapTitle: '地圖 - 使用方向鍵移動地圖',
layersTitle: '圖層',
copyToClipboardSuccess: '已複製到剪貼簿',
copyToClipboardError: '無法複製到剪貼簿',
loginTitle: '登入',
loginHeading: '已有賬戶登入',
loginUsernameLabel: '使用者名稱',
loginPasswordLabel: '密碼',
loginSubmit: '登入',
loginErrorUnknown: '登入時出現未知錯誤',
loginErrorDisabled: '登入已禁用',
loginErrorIncorrect: '使用者名稱或密碼錯誤',
loginSuccess: '登入成功喵!!',
registerHeading: '新使用者註冊',
registerDescription: `分別輸入你的使用者名稱、密碼和註冊碼喵!
註冊碼需要在伺服器中獲取。在遊戲中輸入/dynmap webregister可以獲得註冊碼。`,
registerConfirmPasswordLabel: '確認密碼',
registerCodeLabel: '註冊碼',
registerSubmit: '註冊',
registerErrorUnknown: '註冊時出現未知錯誤',
registerErrorDisabled: '註冊已禁用',
registerErrorVerifyFailed: '前後輸入的密碼不一致,笨蛋。',
registerErrorIncorrect: '註冊失敗了,檢查下輸入內容的是否有誤喵',
logoutTitle: '登出',
logoutErrorUnknown: '登出時出現未知錯誤',
logoutSuccess: '已登出',
closeTitle: '關閉',
showMore: '顯示更多'
}
CSS 也是按需修改,筆者還是比較喜歡淺色的()所以自己的幾個站點風格都比較接近,感興趣的可以去 repo 看程式碼。
CDN
筆者給衛星地圖配置的域名是透過 CDN 的,服務商是騰訊雲,反代總擔心效能和頻寬都不夠,而且每次這麼多圖片請求壓力也比較大,考慮到衛星地圖看的人不多就上了 CDN。騰訊雲國內流量是 19 元 100 G,用個半年沒啥問題,每天頂多 100 M()