mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-09-27 14:52:57 +02:00
Merge branch 'master' into fix/heightmaps
This commit is contained in:
commit
c55d5849ca
19
.github/CONTRIBUTING.md
vendored
19
.github/CONTRIBUTING.md
vendored
@ -19,7 +19,7 @@ So, if something doesn't work because it is not implemented yet, its not a bug.
|
||||
If you are not sure, you can briefly ask about it in our [Discord](https://discord.gg/zmkyJa3) before creating an Issue. :)
|
||||
- Make sure you tested it well enough to be sure it's not an issue on your end. If something doesn't work for you but for everyone else, its probably **not** a bug!
|
||||
|
||||
Also, please make sure noone else has already reported the same or a very similar bug!
|
||||
Also, please make sure no one else has already reported the same or a very similar bug!
|
||||
If you have additional information for an existing bug-report, you can add a comment to the already existing Issue :)
|
||||
|
||||
To report your bug, please open a [new Issue](https://github.com/BlueMap-Minecraft/BlueMap/issues/new?template=bug_report.md) with the `Bug report`-template and follow these guidlines:
|
||||
@ -53,7 +53,20 @@ Make sure your Issue is easy to read and not a mess:
|
||||
Create a separate Issue for each bug you find! Issues that contain more than one bug will be closed!
|
||||
|
||||
## Suggesting a new feature or change
|
||||
**(Todo)**
|
||||
Please use our [discord](https://discord.gg/zmkyJa3)s #suggestions channel to pitch new ideas.
|
||||
We will discuss them there and if they are considered, I'll add an issue/note to out [TODO](https://github.com/orgs/BlueMap-Minecraft/projects/2/views/1)-Board!
|
||||
|
||||
## Creating a Pull-Request
|
||||
**(Todo)**
|
||||
If you want to develop a new PR, please run your Idea by me first in our [discord](https://discord.gg/zmkyJa3)!
|
||||
We can discuss details there, since I have a lot of future plans in my head that are not written anywhere, and they might need to be considered
|
||||
when implementing your feature!
|
||||
*(Also, I tend to be quite picky about certain implementation styles and details ^^')*
|
||||
|
||||
**Please keep in mind that any feature you implement will need to be maintained in the future by me.
|
||||
For this reason I will only accept PR's for features that I deem to be useful, maintainable, in-scope of the project and
|
||||
worth it's maintenance-workload!**
|
||||
|
||||
Ofc the usual "good code quality..." stuff, i think that's common sense.
|
||||
Try to match the existing code-style.
|
||||
Don't add new libraries/dependencies without my ok.
|
||||
Hacky stuff is not allowed =)
|
||||
|
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,14 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Why do you want this feature
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
@ -34,10 +34,7 @@
|
||||
import de.bluecolored.bluemap.core.util.Tristate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -396,12 +393,28 @@ private ConfigTemplate createEndMapTemplate(String name, Path worldFolder, int i
|
||||
}
|
||||
|
||||
private String formatPath(Path path) {
|
||||
return Path.of("")
|
||||
// normalize path
|
||||
path = Path.of("")
|
||||
.toAbsolutePath()
|
||||
.relativize(path.toAbsolutePath())
|
||||
.normalize()
|
||||
.toString()
|
||||
.replace("\\", "\\\\");
|
||||
.normalize();
|
||||
String pathString = path.toString();
|
||||
|
||||
String formatted = pathString;
|
||||
String separator = FileSystems.getDefault().getSeparator();
|
||||
|
||||
// try to replace separator with standardized forward slash
|
||||
if (!separator.equals("/"))
|
||||
formatted = pathString.replace(separator, "/");
|
||||
|
||||
// sanity check forward slash compatibility
|
||||
if (!Path.of(formatted).equals(path))
|
||||
formatted = pathString;
|
||||
|
||||
// escape all backslashes
|
||||
formatted = formatted.replace("\\", "\\\\");
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
public enum StorageType {
|
||||
|
||||
FILE (FileConfig.class, FileStorage::new),
|
||||
SQL (SQLConfig.class, SQLStorage::new);
|
||||
SQL (SQLConfig.class, SQLStorage::create);
|
||||
|
||||
private final Class<? extends StorageConfig> configType;
|
||||
private final StorageFactory<? extends StorageConfig> storageFactory;
|
||||
|
@ -55,6 +55,7 @@
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.BindException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
@ -194,6 +195,9 @@ private void load(@Nullable ResourcePack preloadedResourcePack) throws IOExcepti
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to resolve the ip in your webserver-config.\n" +
|
||||
"Check if that is correctly configured.", ex);
|
||||
} catch (BindException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to bind to the configured address.\n" +
|
||||
"This usually happens when the configured port (" + webserverConfig.getPort() + ") is already in use by some other program.", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to initialize the webserver.\n" +
|
||||
"Check your webserver-config if everything is configured correctly.\n" +
|
||||
|
@ -34,10 +34,18 @@
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
public class CommandHelper {
|
||||
|
||||
private static final DateTimeFormatter TIME_FORMAT =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
.withLocale(Locale.ROOT)
|
||||
.withZone(ZoneId.systemDefault());
|
||||
|
||||
private final Plugin plugin;
|
||||
private final Map<String, WeakReference<RenderTask>> taskRefMap;
|
||||
|
||||
@ -67,7 +75,9 @@ public List<Text> createStatusMessage(){
|
||||
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", status, TextColor.WHITE, "!"));
|
||||
|
||||
if (!tasks.isEmpty()) {
|
||||
if (tasks.isEmpty()) {
|
||||
lines.add(Text.of(TextColor.GRAY, " Last time running: ", TextColor.DARK_GRAY, formatTime(renderer.getLastTimeBusy())));
|
||||
} else {
|
||||
lines.add(Text.of(TextColor.WHITE, " Queued Tasks (" + tasks.size() + "):"));
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
if (i >= 10){
|
||||
@ -76,20 +86,18 @@ public List<Text> createStatusMessage(){
|
||||
}
|
||||
|
||||
RenderTask task = tasks.get(i);
|
||||
lines.add(Text.of(TextColor.GRAY, " [" + getRefForTask(task) + "] ", TextColor.GOLD, task.getDescription()));
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0[" + getRefForTask(task) + "] ", TextColor.GOLD, task.getDescription()));
|
||||
|
||||
if (i == 0) {
|
||||
String detail = task.getDetail().orElse(null);
|
||||
if (detail != null) {
|
||||
lines.add(Text.of(TextColor.GRAY, " Detail: ", TextColor.WHITE, detail));
|
||||
}
|
||||
task.getDetail().ifPresent(detail ->
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0Detail: ", TextColor.WHITE, detail)));
|
||||
|
||||
lines.add(Text.of(TextColor.GRAY, " Progress: ", TextColor.WHITE,
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0Progress: ", TextColor.WHITE,
|
||||
(Math.round(task.estimateProgress() * 10000) / 100.0) + "%"));
|
||||
|
||||
long etaMs = renderer.estimateCurrentRenderTaskTimeRemaining();
|
||||
if (etaMs > 0) {
|
||||
lines.add(Text.of(TextColor.GRAY, " ETA: ", TextColor.WHITE, DurationFormatUtils.formatDuration(etaMs, "HH:mm:ss")));
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0ETA: ", TextColor.WHITE, DurationFormatUtils.formatDuration(etaMs, "HH:mm:ss")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,7 +106,7 @@ public List<Text> createStatusMessage(){
|
||||
if (plugin.checkPausedByPlayerCount()) {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.GOLD, "paused")));
|
||||
lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, " (there are " + plugin.getConfigs().getPluginConfig().getPlayerRenderLimit() + " or more players online)"));
|
||||
lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, "\u00A0\u00A0\u00A0(there are " + plugin.getConfigs().getPluginConfig().getPlayerRenderLimit() + " or more players online)"));
|
||||
} else {
|
||||
lines.add(Text.of(TextColor.WHITE, " Render-Threads are ",
|
||||
Text.of(TextColor.RED, "stopped")
|
||||
@ -176,4 +184,9 @@ private String randomRef() {
|
||||
return ref.subSequence(0, 4).toString();
|
||||
}
|
||||
|
||||
public String formatTime(long timestamp) {
|
||||
if (timestamp < 0) return "-";
|
||||
return TIME_FORMAT.format(Instant.ofEpochMilli(timestamp));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -838,27 +838,28 @@ public int worldsCommand(CommandContext<S> context) {
|
||||
}
|
||||
|
||||
public int mapsCommand(CommandContext<S> context) {
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
List<Text> lines = new ArrayList<>();
|
||||
lines.add(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:"));
|
||||
|
||||
source.sendMessage(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:"));
|
||||
for (BmMap map : plugin.getMaps().values()) {
|
||||
boolean unfrozen = plugin.getPluginState().getMapState(map).isUpdateEnabled();
|
||||
if (unfrozen) {
|
||||
source.sendMessage(Text.of(
|
||||
TextColor.GRAY, " - ",
|
||||
TextColor.WHITE, map.getId(),
|
||||
TextColor.GRAY, " (" + map.getName() + ")"
|
||||
).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName())));
|
||||
} else {
|
||||
source.sendMessage(Text.of(
|
||||
TextColor.GRAY, " - ",
|
||||
TextColor.WHITE, map.getId(),
|
||||
TextColor.GRAY, " (" + map.getName() + ") - ",
|
||||
TextColor.AQUA, TextFormat.ITALIC, "frozen!"
|
||||
).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName())));
|
||||
}
|
||||
boolean frozen = !plugin.getPluginState().getMapState(map).isUpdateEnabled();
|
||||
|
||||
lines.add(Text.of(TextColor.GRAY, " - ",
|
||||
TextColor.WHITE, map.getId(),
|
||||
TextColor.GRAY, " (" + map.getName() + ")"));
|
||||
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0World: ",
|
||||
TextColor.DARK_GRAY, map.getWorld().getName()));
|
||||
lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0Last Update: ",
|
||||
TextColor.DARK_GRAY, helper.formatTime(map.getRenderState().getLatestRenderTime())));
|
||||
|
||||
if (frozen)
|
||||
lines.add(Text.of(TextColor.AQUA, TextFormat.ITALIC, "This map is frozen!"));
|
||||
}
|
||||
|
||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||
source.sendMessages(lines);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,8 @@ public class RenderManager {
|
||||
@DebugDump private final int id;
|
||||
@DebugDump private volatile boolean running;
|
||||
|
||||
@DebugDump private long lastTimeBusy;
|
||||
|
||||
private final AtomicInteger nextWorkerThreadIndex;
|
||||
@DebugDump private final Collection<WorkerThread> workerThreads;
|
||||
private final AtomicInteger busyCount;
|
||||
@ -55,6 +57,8 @@ public RenderManager() {
|
||||
this.workerThreads = new ConcurrentLinkedDeque<>();
|
||||
this.busyCount = new AtomicInteger(0);
|
||||
|
||||
this.lastTimeBusy = -1;
|
||||
|
||||
this.progressTracker = null;
|
||||
this.newTask = true;
|
||||
|
||||
@ -249,6 +253,10 @@ public int getWorkerThreadCount() {
|
||||
return workerThreads.size();
|
||||
}
|
||||
|
||||
public long getLastTimeBusy() {
|
||||
return lastTimeBusy;
|
||||
}
|
||||
|
||||
private void removeTasksThatAreContainedIn(RenderTask containingTask) {
|
||||
synchronized (this.renderTasks) {
|
||||
if (renderTasks.size() < 2) return;
|
||||
@ -290,13 +298,15 @@ private void doWork() throws Exception {
|
||||
}
|
||||
|
||||
this.busyCount.incrementAndGet();
|
||||
this.lastTimeBusy = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
try {
|
||||
task.doWork();
|
||||
} finally {
|
||||
synchronized (renderTasks) {
|
||||
this.busyCount.decrementAndGet();
|
||||
int busyCount = this.busyCount.decrementAndGet();
|
||||
if (busyCount > 0) this.lastTimeBusy = System.currentTimeMillis();
|
||||
this.renderTasks.notifyAll();
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,6 @@
|
||||
{ locale: "zh_TW", name: "中文(台灣)" }
|
||||
{ locale: "zh_HK", name: "中文(香港)" }
|
||||
{ locale: "ko", name: "한국어" }
|
||||
{ locale: "vi", name: "Tiếng Việt"}
|
||||
]
|
||||
}
|
||||
|
171
BlueMapCommon/webapp/public/lang/vi.conf
Normal file
171
BlueMapCommon/webapp/public/lang/vi.conf
Normal file
@ -0,0 +1,171 @@
|
||||
{
|
||||
pageTitle: "BlueMap - {map}"
|
||||
menu: {
|
||||
title: "Menu"
|
||||
tooltip: "Menu"
|
||||
}
|
||||
map: {
|
||||
unloaded: "Không có bản đồ."
|
||||
loading: "Đang tải bản đồ..."
|
||||
errored: "Có lồi khi tải bản đồ!"
|
||||
}
|
||||
maps: {
|
||||
title: "Bản đồ"
|
||||
button: "Bản đồ"
|
||||
tooltip: "Mọi bản đồ"
|
||||
}
|
||||
markers: {
|
||||
title: "Đánh dấu"
|
||||
button: "Đánh dấu"
|
||||
tooltip: "Mọi đánh dấu"
|
||||
marker: "đánh dấu | các đánh dấu"
|
||||
markerSet: "cụm đánh dấu | các cụm đánh dấu"
|
||||
searchPlaceholder: "Tìm..."
|
||||
followPlayerTitle: "Bám theo"
|
||||
sort {
|
||||
title: "Sắp xếp"
|
||||
by {
|
||||
default: "mặc định"
|
||||
label: "tên"
|
||||
distance: "khoảng cách"
|
||||
}
|
||||
}
|
||||
}
|
||||
settings: {
|
||||
title: "Cài đặt"
|
||||
button: "Cài đặt"
|
||||
}
|
||||
goFullscreen: {
|
||||
button: "Toản màn hình"
|
||||
}
|
||||
resetCamera: {
|
||||
button: "Đặt lại camera"
|
||||
tooltip: "Đặt lại camera và vị trí"
|
||||
}
|
||||
updateMap: {
|
||||
button: "Cập nhật bản đồ"
|
||||
tooltip: "Xóa bộ nhớ đệm"
|
||||
}
|
||||
lighting: {
|
||||
title: "Ánh sáng"
|
||||
dayNightSwitch: {
|
||||
tooltip: "Ngày/Đêm"
|
||||
}
|
||||
sunlight: "Nhật quang"
|
||||
ambientLight: "Phát quang"
|
||||
}
|
||||
resolution: {
|
||||
title: "Độ phân giải"
|
||||
high: "Cao (SSAA x2)"
|
||||
normal: "Thường (Native x1)"
|
||||
low: "Thấp (Upscaling x0.5)"
|
||||
}
|
||||
mapControls: {
|
||||
title: "Điều khiển"
|
||||
showZoomButtons: "Hiện nút thu phóng"
|
||||
}
|
||||
freeFlightControls: {
|
||||
title: "Chế độ bay"
|
||||
mouseSensitivity: "Độ nhạy chuột"
|
||||
invertMouseY: "Đảo trục dọc"
|
||||
}
|
||||
renderDistance: {
|
||||
title: "Khoảng cách kết xuất"
|
||||
hiresLayer: "Vùng chất lượng cao"
|
||||
lowersLayer: "Vùng chất lượng thấp"
|
||||
loadHiresWhileMoving: "Tải vùng chất lượng cao khi di chuyển"
|
||||
off: "Tắt"
|
||||
}
|
||||
theme: {
|
||||
title: "Giao diện"
|
||||
default: "Mặc định (hệ thống)"
|
||||
dark: "Tối"
|
||||
light: "Sáng"
|
||||
contrast: "Tương phản"
|
||||
}
|
||||
debug: {
|
||||
button: "Gỡ lỗi"
|
||||
}
|
||||
resetAllSettings: {
|
||||
button: "Thiết đặt lại"
|
||||
}
|
||||
players: {
|
||||
title: "Người chơi"
|
||||
tooltip: "Danh sách người chơi"
|
||||
}
|
||||
compass: {
|
||||
tooltip: "Hướng / chỉ bắc"
|
||||
}
|
||||
screenshot: {
|
||||
title: "Chụp màn hình"
|
||||
button: "Chụp màn hình"
|
||||
clipboard: "Sao chép"
|
||||
}
|
||||
controls: {
|
||||
title: "Chế độ"
|
||||
perspective: {
|
||||
button: "Xung quanh"
|
||||
tooltip: "Góc nhìn xung quanh"
|
||||
}
|
||||
flatView: {
|
||||
button: "Phẳng"
|
||||
tooltip: "Góc nhìn từ trên xuống"
|
||||
}
|
||||
freeFlight: {
|
||||
button: "Bay"
|
||||
tooltip: "Góc nhìn chim bay"
|
||||
}
|
||||
}
|
||||
language: {
|
||||
title: "Ngôn ngữ"
|
||||
}
|
||||
blockTooltip: {
|
||||
block: "Khối"
|
||||
position: "Vị chí"
|
||||
chunk: "Vùng"
|
||||
region: {
|
||||
region: "Khu vực"
|
||||
file: "File"
|
||||
}
|
||||
light: {
|
||||
light: "Ánh sáng"
|
||||
sun: "Nhật quang"
|
||||
block: "Phát quang"
|
||||
}
|
||||
}
|
||||
info: {
|
||||
title: "Thông tin"
|
||||
button: "Thông tin"
|
||||
content: """
|
||||
<img src="assets/logo.png" style="display: block; width: 40%; margin: 3em auto; border-radius: 50%">
|
||||
<p>
|
||||
<h2>Điều khiển chuột:</h2>
|
||||
<table>
|
||||
<tr><th>di chuyển</th><td><kbd>chuột trái</kbd> + kéo</td></tr>
|
||||
<tr><th>thu phóng</th><td><kbd>lăn chuột</kbd></td></tr>
|
||||
<tr><th>xoay/nghiêng</th><td><kbd>chuột phải</kbd> + kéo</td></tr>
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
<h2>Điều khiển bàn phím:</h2>
|
||||
<table>
|
||||
<tr><th>di chuyển</th><td><kbd>wasd</kbd> / <kbd>phím mũi tên</kbd></td></tr>
|
||||
<tr><th>thu phóng</th><td>Bàn phím số: <kbd>+</kbd>/<kbd>-</kbd> or <kbd>Ins</kbd>/<kbd>Home</kbd></td></tr>
|
||||
<tr><th>xoay/nghiêng</th><td><kbd>Alt trái</kbd> + <kbd>wasd</kbd> / <kbd>phím mũi tên</kbd> hoặc <kbd>Delete</kbd>/<kbd>End</kbd>/<kbd>Page Up</kbd>/<kbd>Page Down</kbd></td></tr>
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
<h2>Điều khiển cảm ứng:</h2>
|
||||
<table>
|
||||
<tr><th>di chuyển</th><td>chạm + kéo</td></tr>
|
||||
<tr><th>thu phóng</th><td>chạm 2 ngón + nhón</td></tr>
|
||||
<tr><th>xoay/nghiêng</th><td>chạm 2 ngón + di chuyển / xoay</td></tr>
|
||||
</table>
|
||||
</p>
|
||||
<br><hr>
|
||||
<p class="info-footer">
|
||||
Trang được tạo ♥ bởi <a href="https://bluecolo.red/bluemap">BlueMap</a> {version}
|
||||
</p>
|
||||
"""
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="app" :class="{'theme-light': appState.theme === 'light', 'theme-dark': appState.theme === 'dark', 'theme-contrast': appState.theme === 'contrast'}">
|
||||
<FreeFlightMobileControls v-if="mapViewer.mapLoaded && appState.controls.state === 'free'" />
|
||||
<FreeFlightMobileControls v-if="mapViewer.mapState === 'loaded' && appState.controls.state === 'free'" />
|
||||
<ZoomButtons v-if="showMapMenu && appState.controls.showZoomButtons && appState.controls.state !== 'free'" />
|
||||
<ControlBar />
|
||||
<div v-if="mapViewer.mapState !== 'loaded'" class="map-state-message">{{ $t("map." + mapViewer.mapState) }}</div>
|
||||
@ -50,7 +50,7 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
z-index: 100; // put over bluemap markers
|
||||
z-index: 10000; // put over bluemap markers
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
|
@ -65,6 +65,7 @@ export default {
|
||||
if (this.markerSet.toggleable) {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.markerSet.visible = !this.markerSet.visible
|
||||
this.markerSet.saveState();
|
||||
}
|
||||
},
|
||||
more(event) {
|
||||
|
@ -22,41 +22,85 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
import {Object3D} from "three";
|
||||
import { Object3D } from "three";
|
||||
|
||||
export * from "./MapViewer";
|
||||
export * as Three from "three";
|
||||
|
||||
export * from "./controls/freeflight/FreeFlightControls";
|
||||
export * from "./controls/freeflight/keyboard/KeyHeightControls";
|
||||
// class name conflicts with map controls
|
||||
export { KeyMoveControls as FreeFlightKeyMoveControls } from "./controls/freeflight/keyboard/KeyMoveControls";
|
||||
export { MouseAngleControls as FreeFlightMouseAngleControls } from "./controls/freeflight/mouse/MouseAngleControls";
|
||||
export { MouseRotateControls as FreeFlightMouseRotateControls } from "./controls/freeflight/mouse/MouseRotateControls";
|
||||
export * from "./controls/freeflight/touch/TouchPanControls";
|
||||
|
||||
export * from "./controls/map/MapControls";
|
||||
export * from "./controls/map/MapHeightControls";
|
||||
export * from "./controls/map/keyboard/KeyAngleControls";
|
||||
export { KeyMoveControls as MapKeyMoveControls } from "./controls/map/keyboard/KeyMoveControls";
|
||||
export * from "./controls/map/keyboard/KeyRotateControls";
|
||||
export * from "./controls/map/keyboard/KeyZoomControls";
|
||||
export { MouseAngleControls as MapMouseAngleControls } from "./controls/map/mouse/MouseAngleControls";
|
||||
export * from "./controls/map/mouse/MouseMoveControls";
|
||||
export { MouseRotateControls as MapMouseRotateControls } from "./controls/map/mouse/MouseRotateControls";
|
||||
export * from "./controls/map/mouse/MouseZoomControls";
|
||||
export * from "./controls/map/touch/TouchAngleControls";
|
||||
export * from "./controls/map/touch/TouchMoveControls";
|
||||
export * from "./controls/map/touch/TouchRotateControls";
|
||||
export * from "./controls/map/touch/TouchZoomControls";
|
||||
|
||||
export * from "./controls/ControlsManager";
|
||||
export * from "./controls/KeyCombination";
|
||||
|
||||
export * from "./map/LowresTileLoader";
|
||||
export * from "./map/Map";
|
||||
export * from "./map/Tile";
|
||||
export * from "./map/TileLoader";
|
||||
export * from "./map/TileManager";
|
||||
export * from "./map/TileMap";
|
||||
export * from "./map/hires/HiresFragmentShader";
|
||||
export * from "./map/hires/HiresVertexShader";
|
||||
export * from "./map/lowres/LowresFragmentShader";
|
||||
export * from "./map/lowres/LowresVertexShader";
|
||||
|
||||
export * from "./markers/ExtrudeMarker";
|
||||
export * from "./markers/HtmlMarker";
|
||||
export * from "./markers/LineMarker";
|
||||
export * from "./markers/Marker";
|
||||
export * from "./markers/MarkerFillFragmentShader";
|
||||
export * from "./markers/MarkerFillVertexShader";
|
||||
export * from "./markers/MarkerManager";
|
||||
export * from "./markers/MarkerSet";
|
||||
export * from "./markers/PlayerMarkerSet";
|
||||
export * from "./markers/NormalMarkerManager";
|
||||
export * from "./markers/ObjectMarker";
|
||||
export * from "./markers/PlayerMarker";
|
||||
export * from "./markers/PlayerMarkerManager";
|
||||
export * from "./markers/PlayerMarkerSet";
|
||||
export * from "./markers/PoiMarker";
|
||||
export * from "./markers/ShapeMarker";
|
||||
|
||||
export * from "./controls/map/MapControls";
|
||||
export * from "./controls/freeflight/FreeFlightControls";
|
||||
export * from "./skybox/SkyFragmentShader";
|
||||
export * from "./skybox/SkyVertexShader";
|
||||
export * from "./skybox/SkyboxScene";
|
||||
|
||||
export * from "./util/CSS2DRenderer";
|
||||
export * from "./util/CombinedCamera";
|
||||
export * from "./util/LineShader";
|
||||
export * from "./util/Stats";
|
||||
export * from "./util/Utils";
|
||||
|
||||
export * from "./BlueMapApp";
|
||||
export * from "./MainMenu";
|
||||
export * from "./MapViewer";
|
||||
export * from "./PopupMarker";
|
||||
export * from "./Utils";
|
||||
|
||||
/**
|
||||
* @param event {object}
|
||||
* @return {boolean} - whether the event has been consumed (true) or not (false)
|
||||
*/
|
||||
Object3D.prototype.onClick = function(event) {
|
||||
|
||||
if (this.parent){
|
||||
Object3D.prototype.onClick = function (event) {
|
||||
if (this.parent) {
|
||||
if (!Array.isArray(event.eventStack)) event.eventStack = [];
|
||||
event.eventStack.push(this);
|
||||
|
||||
|
@ -48,7 +48,7 @@ export class BlueMapApp {
|
||||
|
||||
this.mapViewer = new MapViewer(rootElement, this.events);
|
||||
|
||||
this.mapControls = new MapControls(this.mapViewer.renderer.domElement);
|
||||
this.mapControls = new MapControls(this.mapViewer.renderer.domElement, rootElement);
|
||||
this.freeFlightControls = new FreeFlightControls(this.mapViewer.renderer.domElement);
|
||||
|
||||
/** @type {PlayerMarkerManager} */
|
||||
@ -251,6 +251,9 @@ export class BlueMapApp {
|
||||
let map = this.mapsMap.get(mapId);
|
||||
if (!map) return Promise.reject(`There is no map with the id "${mapId}" loaded!`);
|
||||
|
||||
if (this.playerMarkerManager) this.playerMarkerManager.dispose();
|
||||
if (this.markerFileManager) this.markerFileManager.dispose();
|
||||
|
||||
await this.mapViewer.switchMap(map)
|
||||
|
||||
if (resetCamera) this.resetCamera();
|
||||
@ -353,10 +356,8 @@ export class BlueMapApp {
|
||||
}
|
||||
|
||||
initPlayerMarkerManager() {
|
||||
if (this.playerMarkerManager){
|
||||
this.playerMarkerManager.clear();
|
||||
if (this.playerMarkerManager)
|
||||
this.playerMarkerManager.dispose()
|
||||
}
|
||||
|
||||
const map = this.mapViewer.map;
|
||||
if (!map) return;
|
||||
@ -369,16 +370,13 @@ export class BlueMapApp {
|
||||
})
|
||||
.catch(e => {
|
||||
alert(this.events, e, "warning");
|
||||
this.playerMarkerManager.clear();
|
||||
this.playerMarkerManager.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
initMarkerFileManager() {
|
||||
if (this.markerFileManager) {
|
||||
this.markerFileManager.clear();
|
||||
if (this.markerFileManager)
|
||||
this.markerFileManager.dispose();
|
||||
}
|
||||
|
||||
const map = this.mapViewer.map;
|
||||
if (!map) return;
|
||||
@ -390,7 +388,6 @@ export class BlueMapApp {
|
||||
})
|
||||
.catch(e => {
|
||||
alert(this.events, e, "warning");
|
||||
this.markerFileManager.clear();
|
||||
this.markerFileManager.dispose();
|
||||
});
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import {MathUtils, Vector2} from "three";
|
||||
import {MathUtils, Vector2, Vector3} from "three";
|
||||
import {Manager, Pan, DIRECTION_ALL} from "hammerjs";
|
||||
import {animate, EasingFunctions} from "../../util/Utils";
|
||||
import {KeyMoveControls} from "./keyboard/KeyMoveControls";
|
||||
@ -32,9 +32,12 @@ import {MouseAngleControls} from "./mouse/MouseAngleControls";
|
||||
import {KeyHeightControls} from "./keyboard/KeyHeightControls";
|
||||
import {TouchPanControls} from "./touch/TouchPanControls";
|
||||
import {reactive} from "vue";
|
||||
import {DEG2RAD} from "three/src/math/MathUtils";
|
||||
|
||||
export class FreeFlightControls {
|
||||
|
||||
static _beforeMoveTemp = new Vector3();
|
||||
|
||||
/**
|
||||
* @param target {Element}
|
||||
*/
|
||||
@ -43,7 +46,7 @@ export class FreeFlightControls {
|
||||
this.manager = null;
|
||||
|
||||
this.data = reactive({
|
||||
|
||||
followingPlayer: null
|
||||
});
|
||||
|
||||
this.hammer = new Manager(this.target);
|
||||
@ -99,12 +102,32 @@ export class FreeFlightControls {
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
FreeFlightControls._beforeMoveTemp.copy(this.manager.position);
|
||||
let beforeMoveRot = this.manager.rotation;
|
||||
let beforeMoveAngle = this.manager.angle;
|
||||
|
||||
this.keyMove.update(delta, map);
|
||||
this.keyHeight.update(delta, map);
|
||||
this.mouseRotate.update(delta, map);
|
||||
this.mouseAngle.update(delta, map);
|
||||
this.touchPan.update(delta, map);
|
||||
|
||||
// if moved, stop following the marker and give back control
|
||||
if (this.data.followingPlayer && (
|
||||
!FreeFlightControls._beforeMoveTemp.equals(this.manager.position) ||
|
||||
beforeMoveRot !== this.manager.rotation ||
|
||||
beforeMoveAngle !== this.manager.angle
|
||||
)) {
|
||||
this.stopFollowingPlayerMarker();
|
||||
}
|
||||
|
||||
// follow player marker
|
||||
if (this.data.followingPlayer) {
|
||||
this.manager.position.copy(this.data.followingPlayer.position);
|
||||
this.manager.rotation = (this.data.followingPlayer.rotation.yaw - 180) * DEG2RAD;
|
||||
this.manager.angle = -(this.data.followingPlayer.rotation.pitch - 90) * DEG2RAD;
|
||||
}
|
||||
|
||||
this.manager.angle = MathUtils.clamp(this.manager.angle, 0, Math.PI);
|
||||
this.manager.distance = 0;
|
||||
this.manager.ortho = 0;
|
||||
@ -133,6 +156,19 @@ export class FreeFlightControls {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param marker {object}
|
||||
*/
|
||||
followPlayerMarker(marker) {
|
||||
if (marker.isPlayerMarker) marker = marker.data;
|
||||
this.data.followingPlayer = marker;
|
||||
this.keyMove.deltaPosition.set(0, 0);
|
||||
}
|
||||
|
||||
stopFollowingPlayerMarker() {
|
||||
this.data.followingPlayer = null;
|
||||
}
|
||||
|
||||
onWheel = evt => {
|
||||
evt.preventDefault();
|
||||
|
||||
|
@ -66,11 +66,13 @@ export class KeyHeightControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,4 +122,9 @@ export class KeyHeightControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
}
|
||||
|
||||
}
|
@ -78,11 +78,13 @@ export class KeyMoveControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,4 +154,11 @@ export class KeyMoveControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
}
|
||||
|
||||
}
|
@ -50,9 +50,11 @@ export class MapControls {
|
||||
|
||||
/**
|
||||
* @param rootElement {Element}
|
||||
* @param scrollCaptureElement {Element}
|
||||
*/
|
||||
constructor(rootElement) {
|
||||
constructor(rootElement, scrollCaptureElement) {
|
||||
this.rootElement = rootElement;
|
||||
this.scrollCaptureElement = scrollCaptureElement;
|
||||
|
||||
this.data = reactive({
|
||||
followingPlayer: null
|
||||
@ -68,7 +70,7 @@ export class MapControls {
|
||||
this.mouseMove = new MouseMoveControls(this.rootElement, 1.5,0.3);
|
||||
this.mouseRotate = new MouseRotateControls(this.rootElement, 6, 0.3);
|
||||
this.mouseAngle = new MouseAngleControls(this.rootElement, 3, 0.3);
|
||||
this.mouseZoom = new MouseZoomControls(this.rootElement, 1, 0.2);
|
||||
this.mouseZoom = new MouseZoomControls(this.scrollCaptureElement, 1, 0.2);
|
||||
|
||||
this.keyMove = new KeyMoveControls(this.rootElement, 0.025, 0.2);
|
||||
this.keyRotate = new KeyRotateControls(this.rootElement, 0.06, 0.15);
|
||||
|
@ -67,11 +67,13 @@ export class KeyAngleControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,4 +123,9 @@ export class KeyAngleControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
}
|
||||
|
||||
}
|
@ -78,11 +78,13 @@ export class KeyMoveControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,4 +154,11 @@ export class KeyMoveControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
}
|
||||
|
||||
}
|
@ -67,11 +67,13 @@ export class KeyRotateControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,4 +123,9 @@ export class KeyRotateControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
}
|
||||
|
||||
}
|
@ -65,11 +65,13 @@ export class KeyZoomControls {
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
window.addEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
window.removeEventListener("blur", this.onStop)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,4 +121,9 @@ export class KeyZoomControls {
|
||||
}
|
||||
}
|
||||
|
||||
onStop = evt => {
|
||||
this.in = false;
|
||||
this.out = false;
|
||||
}
|
||||
|
||||
}
|
@ -83,7 +83,8 @@ export class MarkerManager {
|
||||
*/
|
||||
update() {
|
||||
return this.loadMarkerFile()
|
||||
.then(markerFileData => this.updateFromData(markerFileData));
|
||||
.then(markerFileData => this.updateFromData(markerFileData))
|
||||
.catch(() => this.clear());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,13 +30,14 @@ import {LineMarker} from "./LineMarker";
|
||||
import {HtmlMarker} from "./HtmlMarker";
|
||||
import {PoiMarker} from "./PoiMarker";
|
||||
import {reactive} from "vue";
|
||||
import {getLocalStorage, setLocalStorage} from "../Utils";
|
||||
|
||||
export class MarkerSet extends Scene {
|
||||
|
||||
/**
|
||||
* @param id {string}
|
||||
*/
|
||||
constructor(id) {
|
||||
constructor(id, data = null) {
|
||||
super();
|
||||
Object.defineProperty(this, 'isMarkerSet', {value: true});
|
||||
|
||||
@ -58,6 +59,9 @@ export class MarkerSet extends Scene {
|
||||
return this.toggleable ||
|
||||
this.markers.filter(marker => marker.listed).length > 0 ||
|
||||
this.markerSets.filter(markerSet => markerSet.listed).length > 0
|
||||
},
|
||||
saveState: () => {
|
||||
setLocalStorage(this.localStorageKey("visible"), this.visible);
|
||||
}
|
||||
});
|
||||
|
||||
@ -65,6 +69,19 @@ export class MarkerSet extends Scene {
|
||||
get() { return this.data.visible },
|
||||
set(value) { this.data.visible = value }
|
||||
});
|
||||
|
||||
if (data) {
|
||||
this.updateFromData(data);
|
||||
}
|
||||
|
||||
if (this.data.toggleable) {
|
||||
let storedVisible = getLocalStorage(this.localStorageKey("visible"));
|
||||
if (storedVisible !== undefined) {
|
||||
this.visible = !!storedVisible;
|
||||
} else if (this.data.defaultHide) {
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateFromData(data) {
|
||||
@ -108,18 +125,14 @@ export class MarkerSet extends Scene {
|
||||
updateMarkerSetFromData(markerSetId, data) {
|
||||
let markerSet = this.markerSets.get(markerSetId);
|
||||
|
||||
// create new if not existent
|
||||
if (!markerSet) {
|
||||
markerSet = new MarkerSet(markerSetId);
|
||||
// create new if not existent
|
||||
markerSet = new MarkerSet(markerSetId, data);
|
||||
this.add(markerSet);
|
||||
|
||||
if (data.defaultHidden) {
|
||||
markerSet.visible = false;
|
||||
}
|
||||
} else {
|
||||
// update
|
||||
markerSet.updateFromData(data);
|
||||
}
|
||||
|
||||
// update
|
||||
markerSet.updateFromData(data);
|
||||
}
|
||||
|
||||
updateMarkersFromData(data = {}, ignore = []) {
|
||||
@ -174,8 +187,8 @@ export class MarkerSet extends Scene {
|
||||
* Removes all markers and marker-sets
|
||||
*/
|
||||
clear() {
|
||||
[...this.data.markerSets].forEach(markerSet => this.remove(markerSet));
|
||||
[...this.data.markers].forEach(marker => this.remove(marker));
|
||||
[...this.markerSets.values()].forEach(markerSet => this.remove(markerSet));
|
||||
[...this.markers.values()].forEach(marker => this.remove(marker));
|
||||
}
|
||||
|
||||
add(...object) {
|
||||
@ -220,4 +233,8 @@ export class MarkerSet extends Scene {
|
||||
});
|
||||
}
|
||||
|
||||
localStorageKey(key) {
|
||||
return "bluemap-markerset-" + encodeURIComponent(this.data.id) + "-" + key;
|
||||
}
|
||||
|
||||
}
|
@ -48,4 +48,8 @@ export class NormalMarkerManager extends MarkerManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.root.updateMarkerSetsFromData({}, [PLAYER_MARKER_SET_ID, "bm-popup-set"]);
|
||||
}
|
||||
|
||||
}
|
@ -41,6 +41,10 @@ export class PlayerMarker extends Marker {
|
||||
this.data.playerUuid = playerUuid;
|
||||
this.data.name = playerUuid;
|
||||
this.data.playerHead = playerHead;
|
||||
this.data.rotation = {
|
||||
pitch: 0,
|
||||
yaw: 0
|
||||
};
|
||||
|
||||
this.elementObject = new CSS2DObject(htmlToElement(`
|
||||
<div id="bm-marker-${this.data.id}" class="bm-marker-${this.data.type}">
|
||||
@ -102,24 +106,34 @@ export class PlayerMarker extends Marker {
|
||||
|
||||
// animate position update
|
||||
let pos = markerData.position || {};
|
||||
let rot = markerData.rotation || {};
|
||||
if (!this.position.x && !this.position.y && !this.position.z) {
|
||||
this.position.set(
|
||||
pos.x || 0,
|
||||
(pos.y || 0) + 1.8,
|
||||
pos.z || 0
|
||||
);
|
||||
this.data.rotation.pitch = rot.pitch || 0;
|
||||
this.data.rotation.yaw = rot.yaw || 0;
|
||||
} else {
|
||||
let startPos = {
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
z: this.position.z,
|
||||
pitch: this.data.rotation.pitch,
|
||||
yaw: this.data.rotation.yaw,
|
||||
}
|
||||
let deltaPos = {
|
||||
x: (pos.x || 0) - startPos.x,
|
||||
y: ((pos.y || 0) + 1.8) - startPos.y,
|
||||
z: (pos.z || 0) - startPos.z,
|
||||
pitch: (rot.pitch || 0) - startPos.pitch,
|
||||
yaw: (rot.yaw || 0) - startPos.yaw
|
||||
}
|
||||
if (deltaPos.x || deltaPos.y || deltaPos.z) {
|
||||
while (deltaPos.yaw > 180) deltaPos.yaw -= 360;
|
||||
while (deltaPos.yaw < -180) deltaPos.yaw += 360;
|
||||
|
||||
if (deltaPos.x || deltaPos.y || deltaPos.z || deltaPos.pitch || deltaPos.yaw) {
|
||||
animate(progress => {
|
||||
let ease = EasingFunctions.easeInOutCubic(progress);
|
||||
this.position.set(
|
||||
@ -127,7 +141,9 @@ export class PlayerMarker extends Marker {
|
||||
startPos.y + deltaPos.y * ease || 0,
|
||||
startPos.z + deltaPos.z * ease || 0
|
||||
);
|
||||
}, 500);
|
||||
this.data.rotation.pitch = startPos.pitch + deltaPos.pitch * ease || 0;
|
||||
this.data.rotation.yaw = startPos.yaw + deltaPos.yaw * ease || 0;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,4 +78,8 @@ export class PlayerMarkerManager extends MarkerManager {
|
||||
return this.getPlayerMarkerSet().getPlayerMarker(playerUuid)
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.getPlayerMarkerSet(false).clear();
|
||||
}
|
||||
|
||||
}
|
@ -29,8 +29,8 @@ import {PlayerMarker} from "./PlayerMarker";
|
||||
|
||||
export class PlayerMarkerSet extends MarkerSet {
|
||||
|
||||
constructor(id, playerheadsUrl) {
|
||||
super(id);
|
||||
constructor(id, playerheadsUrl, data = null) {
|
||||
super(id, data);
|
||||
this.data.label = "Player";
|
||||
this.data.toggleable = true;
|
||||
this.data.defaultHide = false;
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
import * as Vue from 'vue';
|
||||
import App from './App.vue';
|
||||
import * as BlueMap from "./js/BlueMap";
|
||||
import {BlueMapApp} from "./js/BlueMapApp";
|
||||
import {i18nModule, loadLanguageSettings} from "./i18n";
|
||||
|
||||
@ -38,6 +39,7 @@ async function load() {
|
||||
try {
|
||||
const bluemap = new BlueMapApp(document.getElementById("map-container"));
|
||||
window.bluemap = bluemap;
|
||||
window.BlueMap = BlueMap;
|
||||
|
||||
// init vue
|
||||
const vue = Vue.createApp(App, {
|
||||
|
@ -27,6 +27,8 @@
|
||||
.bm-marker-html {
|
||||
position: relative;
|
||||
|
||||
user-select: none;
|
||||
|
||||
.bm-marker-poi-label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -219,7 +219,7 @@ public synchronized void saveMarkerState() {
|
||||
|
||||
public synchronized void savePlayerState() {
|
||||
try (
|
||||
OutputStream out = storage.writeMeta(id, META_FILE_PLAYERS);
|
||||
OutputStream out = storage.writeMeta(id, META_FILE_PLAYERS)
|
||||
) {
|
||||
out.write("{}".getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception ex) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
public class MapRenderState {
|
||||
|
||||
private final Map<Vector2i, Long> regionRenderTimes;
|
||||
private transient long latestRenderTime = -1;
|
||||
|
||||
public MapRenderState() {
|
||||
regionRenderTimes = new HashMap<>();
|
||||
@ -44,6 +45,13 @@ public MapRenderState() {
|
||||
|
||||
public synchronized void setRenderTime(Vector2i regionPos, long renderTime) {
|
||||
regionRenderTimes.put(regionPos, renderTime);
|
||||
|
||||
if (latestRenderTime != -1) {
|
||||
if (renderTime > latestRenderTime)
|
||||
latestRenderTime = renderTime;
|
||||
else
|
||||
latestRenderTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized long getRenderTime(Vector2i regionPos) {
|
||||
@ -52,6 +60,19 @@ public synchronized long getRenderTime(Vector2i regionPos) {
|
||||
else return renderTime;
|
||||
}
|
||||
|
||||
public long getLatestRenderTime() {
|
||||
if (latestRenderTime == -1) {
|
||||
synchronized (this) {
|
||||
latestRenderTime = regionRenderTimes.values().stream()
|
||||
.mapToLong(Long::longValue)
|
||||
.max()
|
||||
.orElse(-1);
|
||||
}
|
||||
}
|
||||
|
||||
return latestRenderTime;
|
||||
}
|
||||
|
||||
public synchronized void reset() {
|
||||
regionRenderTimes.clear();
|
||||
}
|
||||
|
@ -55,7 +55,9 @@ public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag) {
|
||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||
|
||||
String status = levelData.getString("Status");
|
||||
this.isGenerated = status.equals("full");
|
||||
this.isGenerated = status.equals("full") ||
|
||||
status.equals("fullchunk") ||
|
||||
status.equals("postprocessed");
|
||||
this.hasLight = isGenerated;
|
||||
|
||||
this.inhabitedTime = levelData.getLong("InhabitedTime");
|
||||
|
@ -0,0 +1,13 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql;
|
||||
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.MySQLDialect;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
public class MySQLStorage extends SQLStorage{
|
||||
|
||||
public MySQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
super(MySQLDialect.INSTANCE, config);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.storage.CompressedInputStream;
|
||||
import de.bluecolored.bluemap.core.storage.Compression;
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect;
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.PostgresDialect;
|
||||
import de.bluecolored.bluemap.core.util.WrappedOutputStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PostgreSQLStorage extends SQLStorage {
|
||||
|
||||
public PostgreSQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
super(PostgresDialect.INSTANCE, config);
|
||||
}
|
||||
|
||||
public PostgreSQLStorage(Dialect dialect, SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
super(dialect, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
return new WrappedOutputStream(compression.compress(byteOut), () -> {
|
||||
int mapFK = getMapFK(mapId);
|
||||
int tileCompressionFK = getMapTileCompressionFK(compression);
|
||||
|
||||
recoveringConnection(connection -> {
|
||||
executeUpdate(connection, this.dialect.writeMapTile(),
|
||||
mapFK,
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY(),
|
||||
tileCompressionFK,
|
||||
byteOut.toByteArray()
|
||||
);
|
||||
}, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream writeMeta(String mapId, String name) {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
return new WrappedOutputStream(byteOut, () -> {
|
||||
int mapFK = getMapFK(mapId);
|
||||
recoveringConnection(connection -> {
|
||||
executeUpdate(connection, this.dialect.writeMeta(),
|
||||
mapFK,
|
||||
name,
|
||||
byteOut.toByteArray()
|
||||
);
|
||||
}, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
|
||||
try {
|
||||
byte[] data = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection, this.dialect.readMapTile(),
|
||||
mapId,
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY(),
|
||||
compression.getTypeId()
|
||||
);
|
||||
|
||||
if (result.next()) {
|
||||
return result.getBytes(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, 2);
|
||||
|
||||
if (data == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(data);
|
||||
return Optional.of(new CompressedInputStream(inputStream, compression));
|
||||
} catch (SQLException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> readMeta(String mapId, String name) throws IOException {
|
||||
try {
|
||||
byte[] data = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection, this.dialect.readMeta(),
|
||||
mapId,
|
||||
escapeMetaName(name)
|
||||
);
|
||||
if (result.next()) {
|
||||
return result.getBytes(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, 2);
|
||||
|
||||
if (data == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(data);
|
||||
return Optional.of(inputStream);
|
||||
} catch (SQLException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -30,6 +30,8 @@
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.storage.*;
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType;
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect;
|
||||
import de.bluecolored.bluemap.core.util.WrappedOutputStream;
|
||||
import org.apache.commons.dbcp2.*;
|
||||
import org.apache.commons.pool2.ObjectPool;
|
||||
@ -48,10 +50,12 @@
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SQLStorage extends Storage {
|
||||
public abstract class SQLStorage extends Storage {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final Compression hiresCompression;
|
||||
|
||||
protected final Dialect dialect;
|
||||
protected final Compression hiresCompression;
|
||||
|
||||
private final LoadingCache<String, Integer> mapFKs = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
@ -62,9 +66,9 @@ public class SQLStorage extends Storage {
|
||||
|
||||
private volatile boolean closed;
|
||||
|
||||
public SQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
public SQLStorage(Dialect dialect, SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
this.dialect = dialect;
|
||||
this.closed = false;
|
||||
|
||||
try {
|
||||
if (config.getDriverClass().isPresent()) {
|
||||
if (config.getDriverJar().isPresent()) {
|
||||
@ -115,9 +119,7 @@ public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IO
|
||||
byteOut.writeTo(blobOut);
|
||||
}
|
||||
|
||||
executeUpdate(connection,
|
||||
"REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
executeUpdate(connection, this.dialect.writeMapTile(),
|
||||
mapFK,
|
||||
lod,
|
||||
tile.getX(),
|
||||
@ -139,17 +141,7 @@ public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector
|
||||
try {
|
||||
byte[] data = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT t.`data` " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?",
|
||||
this.dialect.readMapTile(),
|
||||
mapId,
|
||||
lod,
|
||||
tile.getX(),
|
||||
@ -179,17 +171,7 @@ public Optional<TileInfo> readMapTileInfo(final String mapId, int lod, final Vec
|
||||
try {
|
||||
TileInfo tileInfo = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT t.`changed`, LENGTH(t.`data`) as 'size' " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?",
|
||||
this.dialect.readMapTileInfo(),
|
||||
mapId,
|
||||
lod,
|
||||
tile.getX(),
|
||||
@ -238,15 +220,7 @@ public long getLastModified() {
|
||||
public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
try {
|
||||
recoveringConnection(connection ->
|
||||
executeUpdate(connection,
|
||||
"DELETE t " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ?",
|
||||
executeUpdate(connection,this.dialect.deleteMapTile(),
|
||||
mapId,
|
||||
lod,
|
||||
tile.getX(),
|
||||
@ -271,8 +245,7 @@ public OutputStream writeMeta(String mapId, String name) {
|
||||
}
|
||||
|
||||
executeUpdate(connection,
|
||||
"REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " +
|
||||
"VALUES (?, ?, ?)",
|
||||
this.dialect.writeMeta(),
|
||||
mapFK,
|
||||
escapeMetaName(name),
|
||||
dataBlob
|
||||
@ -289,12 +262,7 @@ public Optional<InputStream> readMeta(String mapId, String name) throws IOExcept
|
||||
try {
|
||||
byte[] data = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT t.`value` " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?",
|
||||
this.dialect.readMeta(),
|
||||
mapId,
|
||||
escapeMetaName(name)
|
||||
);
|
||||
@ -319,12 +287,7 @@ public Optional<MetaInfo> readMetaInfo(String mapId, String name) throws IOExcep
|
||||
try {
|
||||
MetaInfo tileInfo = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT LENGTH(t.`value`) as 'size' " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?",
|
||||
this.dialect.readMetaSize(),
|
||||
mapId,
|
||||
escapeMetaName(name)
|
||||
);
|
||||
@ -361,12 +324,7 @@ public void deleteMeta(String mapId, String name) throws IOException {
|
||||
try {
|
||||
recoveringConnection(connection ->
|
||||
executeUpdate(connection,
|
||||
"DELETE t " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?",
|
||||
this.dialect.deleteMeta(),
|
||||
mapId,
|
||||
escapeMetaName(name)
|
||||
), 2);
|
||||
@ -381,28 +339,18 @@ public void purgeMap(String mapId, Function<ProgressInfo, Boolean> onProgress) t
|
||||
try {
|
||||
recoveringConnection(connection -> {
|
||||
executeUpdate(connection,
|
||||
"DELETE t " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ?",
|
||||
this.dialect.purgeMapTile(),
|
||||
mapId
|
||||
);
|
||||
|
||||
executeUpdate(connection,
|
||||
"DELETE t " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ?",
|
||||
this.dialect.purgeMapMeta(),
|
||||
mapId
|
||||
);
|
||||
|
||||
|
||||
executeUpdate(connection,
|
||||
"DELETE " +
|
||||
"FROM `bluemap_map` " +
|
||||
"WHERE `map_id` = ?",
|
||||
this.dialect.purgeMap(),
|
||||
mapId
|
||||
);
|
||||
}, 2);
|
||||
@ -420,7 +368,7 @@ public Collection<String> collectMapIds() throws IOException {
|
||||
try {
|
||||
return recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT `map_id` FROM `bluemap_map`"
|
||||
this.dialect.selectMapIds()
|
||||
);
|
||||
Collection<String> mapIds = new ArrayList<>();
|
||||
while (result.next()) {
|
||||
@ -440,15 +388,10 @@ public void initialize() throws IOException {
|
||||
// initialize and get schema-version
|
||||
String schemaVersionString = recoveringConnection(connection -> {
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `bluemap_storage_meta` (" +
|
||||
"`key` varchar(255) NOT NULL, " +
|
||||
"`value` varchar(255) DEFAULT NULL, " +
|
||||
"PRIMARY KEY (`key`)" +
|
||||
")");
|
||||
this.dialect.initializeStorageMeta());
|
||||
|
||||
ResultSet result = executeQuery(connection,
|
||||
"SELECT `value` FROM `bluemap_storage_meta` " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.selectStorageMeta(),
|
||||
"schema_version"
|
||||
);
|
||||
|
||||
@ -456,8 +399,7 @@ public void initialize() throws IOException {
|
||||
return result.getString("value");
|
||||
} else {
|
||||
executeUpdate(connection,
|
||||
"INSERT INTO `bluemap_storage_meta` (`key`, `value`) " +
|
||||
"VALUES (?, ?)",
|
||||
this.dialect.insertStorageMeta(),
|
||||
"schema_version", "0"
|
||||
);
|
||||
return "0";
|
||||
@ -482,51 +424,22 @@ public void initialize() throws IOException {
|
||||
recoveringConnection(connection -> {
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`map_id` VARCHAR(255) NOT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE INDEX `map_id` (`map_id`)" +
|
||||
");"
|
||||
this.dialect.initializeMap()
|
||||
);
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_tile_compression` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`compression` VARCHAR(255) NOT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE INDEX `compression` (`compression`)" +
|
||||
");"
|
||||
this.dialect.initializeMapTileCompression()
|
||||
);
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_meta` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`key` varchar(255) NOT NULL," +
|
||||
"`value` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `key`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_meta_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
")");
|
||||
this.dialect.initializeMapMeta());
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_tile` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`lod` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`x` INT NOT NULL," +
|
||||
"`z` INT NOT NULL," +
|
||||
"`compression` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`changed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
||||
"`data` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `lod`, `x`, `z`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_compression` FOREIGN KEY (`compression`) REFERENCES `bluemap_map_tile_compression` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
");"
|
||||
this.dialect.initializeMapTile()
|
||||
);
|
||||
|
||||
executeUpdate(connection,
|
||||
"UPDATE `bluemap_storage_meta` " +
|
||||
"SET `value` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.updateStorageMeta(),
|
||||
"3", "schema_version"
|
||||
);
|
||||
}, 2);
|
||||
@ -544,36 +457,27 @@ public void initialize() throws IOException {
|
||||
|
||||
// delete potential files that are already in the new format to avoid constraint-issues
|
||||
executeUpdate(connection,
|
||||
"DELETE FROM `bluemap_map_meta`" +
|
||||
"WHERE `key` IN (?, ?, ?)",
|
||||
this.dialect.deleteMapMeta(),
|
||||
"settings.json", "textures.json", ".rstate"
|
||||
);
|
||||
|
||||
// rename files
|
||||
executeUpdate(connection,
|
||||
"UPDATE `bluemap_map_meta` " +
|
||||
"SET `key` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.updateMapMeta(),
|
||||
"settings.json", "settings"
|
||||
);
|
||||
executeUpdate(connection,
|
||||
"UPDATE `bluemap_map_meta` " +
|
||||
"SET `key` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.updateMapMeta(),
|
||||
"textures.json", "textures"
|
||||
);
|
||||
executeUpdate(connection,
|
||||
"UPDATE `bluemap_map_meta` " +
|
||||
"SET `key` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.updateMapMeta(),
|
||||
".rstate", "render_state"
|
||||
);
|
||||
|
||||
// update schemaVersion
|
||||
executeUpdate(connection,
|
||||
"UPDATE `bluemap_storage_meta` " +
|
||||
"SET `value` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
this.dialect.updateStorageMeta(),
|
||||
"3", "schema_version"
|
||||
);
|
||||
}, 2);
|
||||
@ -603,32 +507,31 @@ public void close() throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
private ResultSet executeQuery(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException {
|
||||
// we only use this prepared statement once, but the DB-Driver caches those and reuses them
|
||||
PreparedStatement statement = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
statement.setObject(i+1, parameters[i]);
|
||||
}
|
||||
return statement.executeQuery();
|
||||
protected ResultSet executeQuery(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException {
|
||||
return prepareStatement(connection, sql, parameters).executeQuery();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
private int executeUpdate(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException {
|
||||
protected int executeUpdate(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException {
|
||||
return prepareStatement(connection, sql, parameters).executeUpdate();
|
||||
}
|
||||
|
||||
private PreparedStatement prepareStatement(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException {
|
||||
// we only use this prepared statement once, but the DB-Driver caches those and reuses them
|
||||
PreparedStatement statement = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
statement.setObject(i+1, parameters[i]);
|
||||
statement.setObject(i + 1, parameters[i]);
|
||||
}
|
||||
return statement.executeUpdate();
|
||||
return statement;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void recoveringConnection(ConnectionConsumer action, int tries) throws SQLException, IOException {
|
||||
protected void recoveringConnection(ConnectionConsumer action, int tries) throws SQLException, IOException {
|
||||
recoveringConnection((ConnectionFunction<Void>) action, tries);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private <R> R recoveringConnection(ConnectionFunction<R> action, int tries) throws SQLException, IOException {
|
||||
protected <R> R recoveringConnection(ConnectionFunction<R> action, int tries) throws SQLException, IOException {
|
||||
SQLException sqlException = null;
|
||||
|
||||
try {
|
||||
@ -657,7 +560,7 @@ private <R> R recoveringConnection(ConnectionFunction<R> action, int tries) thro
|
||||
throw sqlException;
|
||||
}
|
||||
|
||||
private int getMapFK(String mapId) throws SQLException {
|
||||
protected int getMapFK(String mapId) throws SQLException {
|
||||
try {
|
||||
return Objects.requireNonNull(mapFKs.get(mapId));
|
||||
} catch (CompletionException ex) {
|
||||
@ -670,7 +573,7 @@ private int getMapFK(String mapId) throws SQLException {
|
||||
}
|
||||
}
|
||||
|
||||
private int getMapTileCompressionFK(Compression compression) throws SQLException {
|
||||
int getMapTileCompressionFK(Compression compression) throws SQLException {
|
||||
try {
|
||||
return Objects.requireNonNull(mapTileCompressionFKs.get(compression));
|
||||
} catch (CompletionException ex) {
|
||||
@ -698,9 +601,7 @@ private int lookupFK(String table, String idField, String valueField, String val
|
||||
return recoveringConnection(connection -> {
|
||||
int key;
|
||||
ResultSet result = executeQuery(connection,
|
||||
//language=SQL
|
||||
"SELECT `" + idField + "` FROM `" + table + "` " +
|
||||
"WHERE `" + valueField + "` = ?",
|
||||
this.dialect.lookupFK(table,idField,valueField),
|
||||
value
|
||||
);
|
||||
|
||||
@ -708,8 +609,7 @@ private int lookupFK(String table, String idField, String valueField, String val
|
||||
key = result.getInt("id");
|
||||
} else {
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO `" + table + "` (`" + valueField + "`) " +
|
||||
"VALUES (?)",
|
||||
this.dialect.insertFK(table,valueField),
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
);
|
||||
statement.setString(1, value);
|
||||
@ -774,6 +674,12 @@ private DataSource createDataSource(ConnectionFactory connectionFactory, int max
|
||||
return new PoolingDataSource<>(connectionPool);
|
||||
}
|
||||
|
||||
public static SQLStorage create(SQLStorageSettings settings) throws Exception {
|
||||
String dbUrl = settings.getConnectionUrl();
|
||||
String provider = dbUrl.strip().split(":", 3)[1];
|
||||
return DialectType.getStorage(provider,settings);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConnectionConsumer extends ConnectionFunction<Void> {
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql;
|
||||
|
||||
import de.bluecolored.bluemap.core.storage.sql.dialect.SqliteDialect;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
public class SQLiteStorage extends PostgreSQLStorage {
|
||||
|
||||
public SQLiteStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException {
|
||||
super(SqliteDialect.INSTANCE, config);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql.dialect;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
public interface Dialect {
|
||||
@Language("sql")
|
||||
String writeMapTile();
|
||||
|
||||
@Language("sql")
|
||||
String readMapTile();
|
||||
|
||||
@Language("sql")
|
||||
String readMapTileInfo();
|
||||
|
||||
@Language("sql")
|
||||
String deleteMapTile();
|
||||
|
||||
@Language("sql")
|
||||
String writeMeta();
|
||||
|
||||
@Language("sql")
|
||||
String readMeta();
|
||||
|
||||
@Language("sql")
|
||||
String readMetaSize();
|
||||
|
||||
@Language("sql")
|
||||
String deleteMeta();
|
||||
|
||||
@Language("sql")
|
||||
String purgeMapTile();
|
||||
|
||||
@Language("sql")
|
||||
String purgeMapMeta();
|
||||
|
||||
@Language("sql")
|
||||
String purgeMap();
|
||||
|
||||
@Language("sql")
|
||||
String selectMapIds();
|
||||
|
||||
@Language("sql")
|
||||
String initializeStorageMeta();
|
||||
|
||||
@Language("sql")
|
||||
String selectStorageMeta();
|
||||
|
||||
@Language("sql")
|
||||
String insertStorageMeta();
|
||||
|
||||
@Language("sql")
|
||||
String initializeMap();
|
||||
|
||||
@Language("sql")
|
||||
String initializeMapTileCompression();
|
||||
|
||||
@Language("sql")
|
||||
String initializeMapMeta();
|
||||
|
||||
@Language("sql")
|
||||
String initializeMapTile();
|
||||
|
||||
@Language("sql")
|
||||
String updateStorageMeta(); // can be use twice in init
|
||||
|
||||
@Language("sql")
|
||||
String deleteMapMeta();
|
||||
|
||||
@Language("sql")
|
||||
String updateMapMeta(); // can be used twice in init
|
||||
|
||||
@Language("sql")
|
||||
String lookupFK(String table, String idField, String valueField);
|
||||
|
||||
@Language("sql")
|
||||
String insertFK(String table, String valueField);
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql.dialect;
|
||||
|
||||
import de.bluecolored.bluemap.core.storage.sql.*;
|
||||
|
||||
public enum DialectType {
|
||||
|
||||
MYSQL (MySQLStorage::new, "mysql"),
|
||||
MARIADB (MySQLStorage::new, "mariadb"),
|
||||
POSTGRESQL (PostgreSQLStorage::new, "postgresql"),
|
||||
SQLITE (SQLiteStorage::new, "sqlite");
|
||||
|
||||
private static final DialectType FALLBACK = MYSQL;
|
||||
|
||||
private final SQLStorageFactory storageFactory;
|
||||
private final String dialectName;
|
||||
|
||||
DialectType(SQLStorageFactory storageFactory, String dialectName) {
|
||||
this.storageFactory = storageFactory;
|
||||
this.dialectName = dialectName;
|
||||
}
|
||||
public String getDialectName() {
|
||||
return dialectName;
|
||||
}
|
||||
|
||||
public static SQLStorage getStorage(String dialectName, SQLStorageSettings settings) throws Exception {
|
||||
for (DialectType dialect : values()) {
|
||||
if (dialect.getDialectName().equals(dialectName)) {
|
||||
return dialect.storageFactory.provide(settings);
|
||||
}
|
||||
}
|
||||
|
||||
// unknown dialect, use fallback
|
||||
return FALLBACK.storageFactory.provide(settings);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SQLStorageFactory {
|
||||
SQLStorage provide(SQLStorageSettings config) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql.dialect;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
public class MySQLDialect implements Dialect {
|
||||
|
||||
public static final MySQLDialect INSTANCE = new MySQLDialect();
|
||||
|
||||
private MySQLDialect() {};
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String writeMapTile() {
|
||||
return "REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String readMapTile() {
|
||||
return "SELECT t.`data` " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String readMapTileInfo() {
|
||||
return "SELECT t.`changed`, LENGTH(t.`data`) as 'size' " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String deleteMapTile() {
|
||||
return "DELETE t " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String writeMeta() {
|
||||
return "REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " +
|
||||
"VALUES (?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String readMeta() {
|
||||
return "SELECT t.`value` " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String readMetaSize() {
|
||||
return "SELECT LENGTH(t.`value`) as 'size' " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String deleteMeta() {
|
||||
return "DELETE t " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String purgeMapTile() {
|
||||
return "DELETE t " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String purgeMapMeta() {
|
||||
return "DELETE t " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String purgeMap() {
|
||||
return "DELETE " +
|
||||
"FROM `bluemap_map` " +
|
||||
"WHERE `map_id` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String selectMapIds() {
|
||||
return "SELECT `map_id` FROM `bluemap_map`";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String initializeStorageMeta() {
|
||||
return "CREATE TABLE IF NOT EXISTS `bluemap_storage_meta` (" +
|
||||
"`key` varchar(255) NOT NULL, " +
|
||||
"`value` varchar(255) DEFAULT NULL, " +
|
||||
"PRIMARY KEY (`key`)" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String selectStorageMeta() {
|
||||
return "SELECT `value` FROM `bluemap_storage_meta` " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String insertStorageMeta() {
|
||||
return "INSERT INTO `bluemap_storage_meta` (`key`, `value`) " +
|
||||
"VALUES (?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String initializeMap() {
|
||||
return "CREATE TABLE `bluemap_map` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`map_id` VARCHAR(255) NOT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE INDEX `map_id` (`map_id`)" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String initializeMapTileCompression() {
|
||||
return "CREATE TABLE `bluemap_map_tile_compression` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`compression` VARCHAR(255) NOT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE INDEX `compression` (`compression`)" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String initializeMapMeta() {
|
||||
return "CREATE TABLE `bluemap_map_meta` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`key` varchar(255) NOT NULL," +
|
||||
"`value` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `key`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_meta_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String initializeMapTile() {
|
||||
return "CREATE TABLE `bluemap_map_tile` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`lod` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`x` INT NOT NULL," +
|
||||
"`z` INT NOT NULL," +
|
||||
"`compression` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`changed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
||||
"`data` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `lod`, `x`, `z`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_compression` FOREIGN KEY (`compression`) REFERENCES `bluemap_map_tile_compression` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String updateStorageMeta() {
|
||||
return "UPDATE `bluemap_storage_meta` " +
|
||||
"SET `value` = ? " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String deleteMapMeta() {
|
||||
return "DELETE FROM `bluemap_map_meta`" +
|
||||
"WHERE `key` IN (?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String updateMapMeta() {
|
||||
return "UPDATE `bluemap_map_meta` " +
|
||||
"SET `key` = ? " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String lookupFK(String table, String idField, String valueField) {
|
||||
return "SELECT `" + idField + "` FROM `" + table + "` " +
|
||||
"WHERE `" + valueField + "` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("MySQL")
|
||||
public String insertFK(String table, String valueField) {
|
||||
return "INSERT INTO `" + table + "` (`" + valueField + "`) " +
|
||||
"VALUES (?)";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql.dialect;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
public class PostgresDialect implements Dialect {
|
||||
|
||||
public static final PostgresDialect INSTANCE = new PostgresDialect();
|
||||
|
||||
private PostgresDialect() {};
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String writeMapTile() {
|
||||
return "INSERT INTO bluemap_map_tile (map, lod, x, z, compression, data) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?) " +
|
||||
"ON CONFLICT (map, lod, x, z) DO UPDATE SET compression = EXCLUDED.compression, data = EXCLUDED.data";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String readMapTile() {
|
||||
return "SELECT t.data " +
|
||||
"FROM bluemap_map_tile t " +
|
||||
" INNER JOIN bluemap_map m " +
|
||||
" ON t.map = m.id " +
|
||||
" INNER JOIN bluemap_map_tile_compression c " +
|
||||
" ON t.compression = c.id " +
|
||||
"WHERE m.map_id = ? " +
|
||||
"AND t.lod = ? " +
|
||||
"AND t.x = ? " +
|
||||
"AND t.z = ? " +
|
||||
"AND c.compression = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String readMapTileInfo() {
|
||||
return "SELECT t.changed, OCTET_LENGTH(t.data) as size " +
|
||||
"FROM bluemap_map_tile t " +
|
||||
" INNER JOIN bluemap_map m " +
|
||||
" ON t.map = m.id " +
|
||||
" INNER JOIN bluemap_map_tile_compression c " +
|
||||
" ON t.compression = c.id " +
|
||||
"WHERE m.map_id = ? " +
|
||||
"AND t.lod = ? " +
|
||||
"AND t.x = ? " +
|
||||
"AND t.z = ? " +
|
||||
"AND c.compression = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String deleteMapTile() {
|
||||
return "DELETE FROM bluemap_map_tile t " +
|
||||
"USING bluemap_map m " +
|
||||
"WHERE t.map = m.id " +
|
||||
"AND m.map_id = ? " +
|
||||
"AND t.lod = ? " +
|
||||
"AND t.x = ? " +
|
||||
"AND t.z = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String writeMeta() {
|
||||
return "INSERT INTO bluemap_map_meta (map, key, value) " +
|
||||
"VALUES (?, ?, ?) " +
|
||||
"ON CONFLICT (map, key) DO UPDATE SET value = EXCLUDED.value";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String readMeta() {
|
||||
return "SELECT t.value " +
|
||||
"FROM bluemap_map_meta t " +
|
||||
" INNER JOIN bluemap_map m " +
|
||||
" ON t.map = m.id " +
|
||||
"WHERE m.map_id = ? " +
|
||||
"AND t.key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String readMetaSize() {
|
||||
return "SELECT OCTET_LENGTH(t.value) as size " +
|
||||
"FROM bluemap_map_meta t " +
|
||||
" INNER JOIN bluemap_map m " +
|
||||
" ON t.map = m.id " +
|
||||
"WHERE m.map_id = ? " +
|
||||
"AND t.key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String deleteMeta() {
|
||||
return "DELETE FROM bluemap_map_meta t " +
|
||||
"USING bluemap_map m " +
|
||||
"WHERE t.map = m.id " +
|
||||
"AND m.map_id = ? " +
|
||||
"AND t.key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String purgeMapTile() {
|
||||
return "DELETE FROM bluemap_map_tile t " +
|
||||
"USING bluemap_map m " +
|
||||
"WHERE t.map = m.id " +
|
||||
"AND m.map_id = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String purgeMapMeta() {
|
||||
return "DELETE FROM bluemap_map_meta t " +
|
||||
"USING bluemap_map m " +
|
||||
"WHERE t.map = m.id " +
|
||||
"AND m.map_id = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String purgeMap() {
|
||||
return "DELETE FROM bluemap_map " +
|
||||
"WHERE map_id = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String selectMapIds() {
|
||||
return "SELECT map_id FROM bluemap_map";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String initializeStorageMeta() {
|
||||
return "CREATE TABLE IF NOT EXISTS bluemap_storage_meta (" +
|
||||
"key varchar(255) PRIMARY KEY, " +
|
||||
"value varchar(255)" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String selectStorageMeta() {
|
||||
return "SELECT value FROM bluemap_storage_meta " +
|
||||
"WHERE key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String insertStorageMeta() {
|
||||
return "INSERT INTO bluemap_storage_meta (key, value) " +
|
||||
"VALUES (?, ?) " +
|
||||
"ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String initializeMap() {
|
||||
return "CREATE TABLE IF NOT EXISTS bluemap_map (" +
|
||||
"id SERIAL PRIMARY KEY, " +
|
||||
"map_id VARCHAR(255) UNIQUE NOT NULL" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String initializeMapTileCompression() {
|
||||
return "CREATE TABLE IF NOT EXISTS bluemap_map_tile_compression (" +
|
||||
"id SERIAL PRIMARY KEY, " +
|
||||
"compression VARCHAR(255) UNIQUE NOT NULL" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String initializeMapMeta() {
|
||||
return "CREATE TABLE IF NOT EXISTS bluemap_map_meta (" +
|
||||
"map SMALLINT REFERENCES bluemap_map(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " +
|
||||
"key varchar(255) NOT NULL, " +
|
||||
"value BYTEA NOT NULL, " +
|
||||
"PRIMARY KEY (map, key)" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String initializeMapTile() {
|
||||
return "CREATE TABLE IF NOT EXISTS bluemap_map_tile (" +
|
||||
"map SMALLINT REFERENCES bluemap_map(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " +
|
||||
"lod SMALLINT NOT NULL, " +
|
||||
"x INT NOT NULL, " +
|
||||
"z INT NOT NULL, " +
|
||||
"compression SMALLINT REFERENCES bluemap_map_tile_compression(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " +
|
||||
"changed TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
||||
"data BYTEA NOT NULL, " +
|
||||
"PRIMARY KEY (map, lod, x, z)" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String updateStorageMeta() {
|
||||
return "UPDATE bluemap_storage_meta " +
|
||||
"SET value = ? " +
|
||||
"WHERE key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String deleteMapMeta() {
|
||||
return "DELETE FROM bluemap_map_meta " +
|
||||
"WHERE key IN (?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String updateMapMeta() {
|
||||
return "UPDATE bluemap_map_meta " +
|
||||
"SET key = ? " +
|
||||
"WHERE key = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String lookupFK(String table, String idField, String valueField) {
|
||||
return "SELECT " + idField + " FROM " + table +
|
||||
" WHERE " + valueField + " = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("PostgreSQL")
|
||||
public String insertFK(String table, String valueField) {
|
||||
return "INSERT INTO " + table + " (" + valueField + ") " +
|
||||
"VALUES (?)";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package de.bluecolored.bluemap.core.storage.sql.dialect;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
public class SqliteDialect implements Dialect {
|
||||
|
||||
public static final SqliteDialect INSTANCE = new SqliteDialect();
|
||||
|
||||
private SqliteDialect() {}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String writeMapTile() {
|
||||
return "REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String readMapTile() {
|
||||
return "SELECT t.`data` " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String readMapTileInfo() {
|
||||
return "SELECT t.`changed`, LENGTH(t.`data`) as 'size' " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String deleteMapTile() {
|
||||
return "DELETE FROM `bluemap_map_tile` " +
|
||||
"WHERE `map` IN( " +
|
||||
" SELECT `id` " +
|
||||
" FROM `bluemap_map` " +
|
||||
" WHERE `map_id` = ? " +
|
||||
" LIMIT 1 " +
|
||||
") " +
|
||||
"AND `lod` = ? " +
|
||||
"AND `x` = ? " +
|
||||
"AND `z` = ? ";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String writeMeta() {
|
||||
return "REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " +
|
||||
"VALUES (?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String readMeta() {
|
||||
return "SELECT t.`value` " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String readMetaSize() {
|
||||
return "SELECT LENGTH(t.`value`) as 'size' " +
|
||||
"FROM `bluemap_map_meta` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND t.`key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String deleteMeta() {
|
||||
return "DELETE FROM `bluemap_map_meta` " +
|
||||
"WHERE `map` IN( " +
|
||||
" SELECT `id` " +
|
||||
" FROM `bluemap_map` " +
|
||||
" WHERE `map_id` = ? " +
|
||||
" LIMIT 1 " +
|
||||
") " +
|
||||
"AND `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String purgeMapTile() {
|
||||
return "DELETE FROM `bluemap_map_tile` " +
|
||||
"WHERE `map` IN( " +
|
||||
" SELECT `id` " +
|
||||
" FROM `bluemap_map` " +
|
||||
" WHERE `map_id` = ? " +
|
||||
" LIMIT 1 " +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String purgeMapMeta() {
|
||||
return "DELETE FROM `bluemap_map_meta` " +
|
||||
"WHERE `map` IN( " +
|
||||
" SELECT `id` " +
|
||||
" FROM `bluemap_map` " +
|
||||
" WHERE `map_id` = ? " +
|
||||
" LIMIT 1 " +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String purgeMap() {
|
||||
return "DELETE " +
|
||||
"FROM `bluemap_map` " +
|
||||
"WHERE `map_id` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String selectMapIds() {
|
||||
return "SELECT `map_id` FROM `bluemap_map`";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String initializeStorageMeta() {
|
||||
return "CREATE TABLE IF NOT EXISTS `bluemap_storage_meta` (" +
|
||||
"`key` varchar(255) NOT NULL, " +
|
||||
"`value` varchar(255) DEFAULT NULL, " +
|
||||
"PRIMARY KEY (`key`)" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String selectStorageMeta() {
|
||||
return "SELECT `value` FROM `bluemap_storage_meta` " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String insertStorageMeta() {
|
||||
return "INSERT INTO `bluemap_storage_meta` (`key`, `value`) " +
|
||||
"VALUES (?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String initializeMap() {
|
||||
return "CREATE TABLE `bluemap_map` (" +
|
||||
"`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
|
||||
"`map_id` VARCHAR(255) NOT NULL," +
|
||||
"UNIQUE (`map_id`)" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String initializeMapTileCompression() {
|
||||
return "CREATE TABLE `bluemap_map_tile_compression` (" +
|
||||
"`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
|
||||
"`compression` VARCHAR(255) NOT NULL," +
|
||||
"UNIQUE (`compression`)" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String initializeMapMeta() {
|
||||
return "CREATE TABLE `bluemap_map_meta` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`key` varchar(255) NOT NULL," +
|
||||
"`value` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `key`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_meta_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String initializeMapTile() {
|
||||
return "CREATE TABLE `bluemap_map_tile` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`lod` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`x` INT NOT NULL," +
|
||||
"`z` INT NOT NULL," +
|
||||
"`compression` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`changed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," +
|
||||
"`data` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `lod`, `x`, `z`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_compression` FOREIGN KEY (`compression`) REFERENCES `bluemap_map_tile_compression` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
");";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String updateStorageMeta() {
|
||||
return "UPDATE `bluemap_storage_meta` " +
|
||||
"SET `value` = ? " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String deleteMapMeta() {
|
||||
return "DELETE FROM `bluemap_map_meta`" +
|
||||
"WHERE `key` IN (?, ?, ?)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String updateMapMeta() {
|
||||
return "UPDATE `bluemap_map_meta` " +
|
||||
"SET `key` = ? " +
|
||||
"WHERE `key` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String lookupFK(String table, String idField, String valueField) {
|
||||
return "SELECT `" + idField + "` FROM `" + table + "` " +
|
||||
"WHERE `" + valueField + "` = ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Language("sqlite")
|
||||
public String insertFK(String table, String valueField) {
|
||||
return "INSERT INTO `" + table + "` (`" + valueField + "`) " +
|
||||
"VALUES (?)";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -54,7 +54,9 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.BindException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -204,12 +206,24 @@ public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOExc
|
||||
HttpRequestHandler handler = new BlueMapResponseModifier(routingRequestHandler);
|
||||
if (verbose) handler = new LoggingRequestHandler(handler);
|
||||
|
||||
HttpServer webServer = new HttpServer(handler);
|
||||
webServer.bind(new InetSocketAddress(
|
||||
config.resolveIp(),
|
||||
config.getPort()
|
||||
));
|
||||
webServer.start();
|
||||
try {
|
||||
HttpServer webServer = new HttpServer(handler);
|
||||
webServer.bind(new InetSocketAddress(
|
||||
config.resolveIp(),
|
||||
config.getPort()
|
||||
));
|
||||
webServer.start();
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to resolve the ip in your webserver-config.\n" +
|
||||
"Check if that is correctly configured.", ex);
|
||||
} catch (BindException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to bind to the configured address.\n" +
|
||||
"This usually happens when the configured port (" + config.getPort() + ") is already in use by some other program.", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException("BlueMap failed to initialize the webserver.\n" +
|
||||
"Check your webserver-config if everything is configured correctly.\n" +
|
||||
"(Make sure you DON'T use the same port for bluemap that you also use for your minecraft server)", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user