Add stuff

This commit is contained in:
Lukas Rieger (Blue) 2022-12-13 16:54:31 +01:00
parent 6e78587d6f
commit 6d53bce0dd
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
63 changed files with 1131 additions and 358 deletions

@ -1 +1 @@
Subproject commit 3949ebcb565ecf44bd106daf69582d76d8cfbca1
Subproject commit 5b4093e404de85f98a1ce620282d1d5314f49c90

@ -1 +1 @@
Subproject commit df59cdfc78227c45f0826b4112d80586bd54454f
Subproject commit e6059e90512c6550d7a63bca17be29f53ab10efc

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common;
import de.bluecolored.bluemap.common.config.*;

View File

@ -75,6 +75,8 @@ public class BlueMapService implements Closeable {
private final Map<Path, String> worldIds;
private final Map<String, Storage> storages;
private volatile WebFilesManager webFilesManager;
private Map<String, World> worlds;
private Map<String, BmMap> maps;
@ -130,9 +132,20 @@ public String getWorldId(Path worldFolder) throws IOException {
}
}
public WebFilesManager getWebFilesManager() {
if (webFilesManager == null) {
synchronized (this) {
if (webFilesManager == null)
webFilesManager = new WebFilesManager(configs.getWebappConfig().getWebroot());
}
}
return webFilesManager;
}
public synchronized void createOrUpdateWebApp(boolean force) throws ConfigurationException {
try {
WebFilesManager webFilesManager = new WebFilesManager(configs.getWebappConfig().getWebroot());
WebFilesManager webFilesManager = getWebFilesManager();
// update web-app files
if (force || webFilesManager.filesNeedUpdate()) {
@ -142,6 +155,7 @@ public synchronized void createOrUpdateWebApp(boolean force) throws Configuratio
// update settings.json
if (!configs.getWebappConfig().isUpdateSettingsFile()) {
webFilesManager.loadSettings();
webFilesManager.addFrom(configs.getWebappConfig());
} else {
webFilesManager.setFrom(configs.getWebappConfig());
}

View File

@ -87,10 +87,22 @@ public void removeMap(String mapId) {
this.settings.maps.remove(mapId);
}
public Set<String> getScripts() {
return this.settings.scripts;
}
public Set<String> getStyles() {
return this.settings.styles;
}
public void setFrom(WebappConfig webappConfig) {
this.settings.setFrom(webappConfig);
}
public void addFrom(WebappConfig webappConfig) {
this.settings.addFrom(webappConfig);
}
public boolean filesNeedUpdate() {
return !Files.exists(webRoot.resolve("index.html"));
}
@ -156,6 +168,8 @@ private static class Settings {
private int lowresSliderMin = 500;
private Set<String> maps = new HashSet<>();
private Set<String> scripts = new HashSet<>();
private Set<String> styles = new HashSet<>();
public void setFrom(WebappConfig config) {
this.useCookies = config.isUseCookies();
@ -165,16 +179,26 @@ public void setFrom(WebappConfig config) {
this.minZoomDistance = config.getMinZoomDistance();
this.maxZoomDistance = config.getMaxZoomDistance();
this.hiresSliderMax = config.getHiresSliderMax();
this.hiresSliderDefault = config.getHiresSliderDefault();
this.hiresSliderMin = config.getHiresSliderMin();
this.lowresSliderMax = config.getLowresSliderMax();
this.lowresSliderDefault = config.getLowresSliderDefault();
this.lowresSliderMin = config.getLowresSliderMin();
this.styles.clear();
this.scripts.clear();
addFrom(config);
}
public void addFrom(WebappConfig config) {
this.scripts.addAll(config.getScripts());
this.styles.addAll(config.getStyles());
}
}
}

View File

@ -0,0 +1,72 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import de.bluecolored.bluemap.api.AssetStorage;
import de.bluecolored.bluemap.core.storage.Storage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;
public class AssetStorageImpl implements AssetStorage {
private static final String ASSET_PATH = "live/assets/";
private final Storage storage;
private final String mapId;
public AssetStorageImpl(Storage storage, String mapId) {
this.storage = storage;
this.mapId = mapId;
}
@Override
public OutputStream writeAsset(String name) throws IOException {
return storage.writeMeta(mapId, ASSET_PATH + name);
}
@Override
public Optional<InputStream> readAsset(String name) throws IOException {
return storage.readMeta(mapId, ASSET_PATH + name);
}
@Override
public boolean assetExists(String name) throws IOException {
return storage.readMetaInfo(mapId, ASSET_PATH + name).isPresent();
}
@Override
public String getAssetUrl(String name) {
return "maps/" + mapId + "/" + Storage.escapeMetaName(ASSET_PATH + name);
}
@Override
public void deleteAsset(String name) throws IOException {
storage.deleteMeta(mapId, ASSET_PATH + name);
}
}

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import com.github.benmanes.caffeine.cache.Caffeine;
@ -46,6 +70,11 @@ public WebAppImpl getWebApp() {
return new WebAppImpl(plugin);
}
@Override
public de.bluecolored.bluemap.api.plugin.Plugin getPlugin() {
return new PluginImpl(plugin);
}
@Override
public Collection<BlueMapMap> getMaps() {
return plugin.getMaps().values().stream()

View File

@ -1,8 +1,33 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import com.flowpowered.math.vector.Vector2i;
import de.bluecolored.bluemap.api.BlueMapMap;
import de.bluecolored.bluemap.api.BlueMapWorld;
import de.bluecolored.bluemap.api.AssetStorage;
import de.bluecolored.bluemap.api.markers.MarkerSet;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
@ -52,6 +77,11 @@ public BlueMapWorld getWorld() {
return world;
}
@Override
public AssetStorage getAssetStorage() {
return new AssetStorageImpl(unpack(map).getStorage(), getId());
}
@Override
public Map<String, MarkerSet> getMarkerSets() {
return unpack(map).getMarkerSets();

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import de.bluecolored.bluemap.api.BlueMapMap;

View File

@ -0,0 +1,39 @@
package de.bluecolored.bluemap.common.api;
import de.bluecolored.bluemap.api.plugin.PlayerIconFactory;
import de.bluecolored.bluemap.api.plugin.SkinProvider;
import de.bluecolored.bluemap.common.plugin.Plugin;
public class PluginImpl implements de.bluecolored.bluemap.api.plugin.Plugin {
private final Plugin plugin;
public PluginImpl(Plugin plugin) {
this.plugin = plugin;
}
@Override
public SkinProvider getSkinProvider() {
return plugin.getSkinUpdater().getSkinProvider();
}
@Override
public void setSkinProvider(SkinProvider skinProvider) {
plugin.getSkinUpdater().setSkinProvider(skinProvider);
}
@Override
public PlayerIconFactory getPlayerMarkerIconFactory() {
return plugin.getSkinUpdater().getPlayerMarkerIconFactory();
}
@Override
public void setPlayerMarkerIconFactory(PlayerIconFactory playerMarkerIconFactory) {
plugin.getSkinUpdater().setPlayerMarkerIconFactory(playerMarkerIconFactory);
}
public Plugin getPlugin() {
return plugin;
}
}

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import com.flowpowered.math.vector.Vector2i;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.api;
import de.bluecolored.bluemap.api.WebApp;
@ -42,6 +66,16 @@ public boolean getPlayerVisibility(UUID player) {
return !plugin.getPluginState().isPlayerHidden(player);
}
@Override
public void registerScript(String url) {
plugin.getBlueMap().getWebFilesManager().getScripts().add(url);
}
@Override
public void registerStyle(String url) {
plugin.getBlueMap().getWebFilesManager().getStyles().add(url);
}
@Override
public String createImage(BufferedImage image, String path) throws IOException {
path = path.replaceAll("[^a-zA-Z0-9_.\\-/]", "_");

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.api.debug.DebugDump;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import java.util.HashMap;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.api.debug.DebugDump;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import com.flowpowered.math.vector.Vector2i;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.api.debug.DebugDump;

View File

@ -1,10 +1,36 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.api.debug.DebugDump;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
@DebugDump
@ -35,6 +61,8 @@ public class WebappConfig {
private int lowresSliderDefault = 2000;
private int lowresSliderMin = 500;
private Set<String> scripts = new HashSet<>();
private Set<String> styles = new HashSet<>();
public boolean isEnabled() {
return enabled;
}
@ -95,4 +123,12 @@ public int getLowresSliderMin() {
return lowresSliderMin;
}
public Set<String> getScripts() {
return scripts;
}
public Set<String> getStyles() {
return styles;
}
}

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.api.debug.DebugDump;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.config.typeserializer;
import com.flowpowered.math.vector.Vector2i;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.live;
import de.bluecolored.bluemap.api.gson.MarkerGson;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.live;
import com.google.gson.stream.JsonWriter;
@ -20,7 +44,7 @@ public class LivePlayersDataSupplier implements Supplier<String> {
@Nullable private final String worldId;
private final Predicate<UUID> playerFilter;
public LivePlayersDataSupplier(ServerInterface server, PluginConfig config, String worldId, Predicate<UUID> playerFilter) {
public LivePlayersDataSupplier(ServerInterface server, PluginConfig config, @Nullable String worldId, Predicate<UUID> playerFilter) {
this.server = server;
this.config = config;
this.worldId = worldId;
@ -46,8 +70,10 @@ public String get() {
if (config.isHideSneaking() && player.isSneaking()) continue;
if (config.getHiddenGameModes().contains(player.getGamemode().getId())) continue;
if (config.isHideDifferentWorld() && !isCorrectWorld) continue;
if (player.getSkyLight() < config.getHideBelowSkyLight()) continue;
if (player.getBlockLight() < config.getHideBelowBlockLight()) continue;
if (
player.getSkyLight() < config.getHideBelowSkyLight() &&
player.getBlockLight() < config.getHideBelowBlockLight()
) continue;
if (!this.playerFilter.test(player.getUuid())) continue;
json.beginObject();

View File

@ -45,7 +45,6 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.metrics.Metrics;
import de.bluecolored.bluemap.core.storage.MetaType;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.world.World;
@ -223,19 +222,17 @@ public void load() throws IOException {
}
//update webapp and settings
blueMap.createOrUpdateWebApp(false);
if (webappConfig.isEnabled())
blueMap.createOrUpdateWebApp(false);
//start skin updater
if (pluginConfig.isLivePlayerMarkers()) {
this.skinUpdater = new PlayerSkinUpdater(
webappConfig.getWebroot().resolve("assets").resolve("playerheads").toFile(),
webappConfig.getWebroot().resolve("assets").resolve("steve.png").toFile()
);
this.skinUpdater = new PlayerSkinUpdater(this);
serverInterface.registerListener(skinUpdater);
}
//init timer
daemonTimer = new Timer("BlueMap-Plugin-Daemon-Timer", true);
daemonTimer = new Timer("BlueMap-Plugin-DaemonTimer", true);
//periodically save
TimerTask saveTask = new TimerTask() {
@ -318,6 +315,9 @@ public void run() {
this.api = new BlueMapAPIImpl(this);
this.api.register();
//save webapp settings again (for api-registered scripts and styles)
this.getBlueMap().getWebFilesManager().saveSettings();
//done
loaded = true;
}
@ -438,7 +438,7 @@ public void savePlayerStates() {
Predicate.not(pluginState::isPlayerHidden)
);
try (
OutputStream out = map.getStorage().writeMeta(map.getId(), MetaType.PLAYERS);
OutputStream out = map.getStorage().writeMeta(map.getId(), BmMap.META_FILE_PLAYERS);
Writer writer = new OutputStreamWriter(out)
) {
writer.write(dataSupplier.get());
@ -555,6 +555,10 @@ public String getImplementationType() {
return implementationType;
}
public PlayerSkinUpdater getSkinUpdater() {
return skinUpdater;
}
private void initFileWatcherTasks() {
for (BmMap map : maps.values()) {
if (pluginState.getMapState(map).isUpdateEnabled()) {

View File

@ -114,9 +114,10 @@ public void init() {
LiteralCommandNode<S> debugCommand =
literal("debug")
.requires(requirements("bluemap.debug"))
.requires(requirementsUnloaded("bluemap.debug"))
.then(literal("block")
.requires(requirements("bluemap.debug"))
.executes(this::debugBlockCommand)
.then(argument("world", StringArgumentType.string()).suggests(new WorldSuggestionProvider<>(plugin))
@ -126,12 +127,14 @@ public void init() {
.executes(this::debugBlockCommand))))))
.then(literal("flush")
.requires(requirements("bluemap.debug"))
.executes(this::debugFlushCommand)
.then(argument("world", StringArgumentType.string()).suggests(new WorldSuggestionProvider<>(plugin))
.executes(this::debugFlushCommand)))
.then(literal("cache")
.requires(requirements("bluemap.debug"))
.executes(this::debugClearCacheCommand))
@ -397,7 +400,7 @@ public int reloadCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.RED, "There was an error reloading BlueMap! See the console for details!"));
}
}).start();
}, "BlueMap-Plugin-ReloadCommand").start();
return 1;
}
@ -448,7 +451,7 @@ public int debugFlushCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details..."));
Logger.global.logError("Unexpected exception trying to save the world!", ex);
}
}).start();
}, "BlueMap-Plugin-DebugFlushCommand").start();
return 1;
}
@ -502,7 +505,7 @@ public int debugBlockCommand(CommandContext<S> context) {
Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block),
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow)
));
}).start();
}, "BlueMap-Plugin-DebugBlockCommand").start();
return 1;
}
@ -534,7 +537,7 @@ public int stopCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads stopped!"));
plugin.save();
}).start();
}, "BlueMap-Plugin-StopCommand").start();
} else {
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already stopped!"));
return 0;
@ -554,7 +557,7 @@ public int startCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!"));
plugin.save();
}).start();
}, "BlueMap-Plugin-StartCommand").start();
} else {
source.sendMessage(Text.of(TextColor.RED, "Render-Threads are already running!"));
return 0;
@ -595,7 +598,7 @@ public int freezeCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.GRAY, "Any currently scheduled updates for this map have been cancelled."));
plugin.save();
}).start();
}, "BlueMap-Plugin-FreezeCommand").start();
} else {
source.sendMessage(Text.of(TextColor.RED, "This map is already frozen!"));
return 0;
@ -627,7 +630,7 @@ public int unfreezeCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.GREEN, "Map ", TextColor.WHITE, mapString, TextColor.GREEN, " is no longer frozen and will be automatically updated!"));
plugin.save();
}).start();
}, "BlueMap-Plugin-UnfreezeCommand").start();
} else {
source.sendMessage(Text.of(TextColor.RED, "This map is not frozen!"));
return 0;
@ -737,7 +740,7 @@ public int updateCommand(CommandContext<S> context, boolean force) {
source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details..."));
Logger.global.logError("Unexpected exception trying to save the world!", ex);
}
}).start();
}, "BlueMap-Plugin-UpdateCommand").start();
return 1;
}
@ -810,7 +813,7 @@ public int purgeCommand(CommandContext<S> context) {
source.sendMessage(Text.of(TextColor.RED, "There was an error trying to purge '" + map.getId() + "', see console for details."));
Logger.global.logError("Failed to purge map '" + map.getId() + "'!", e);
}
}).start();
}, "BlueMap-Plugin-PurgeCommand").start();
return 1;
}

View File

@ -0,0 +1,35 @@
package de.bluecolored.bluemap.common.plugin.skins;
import de.bluecolored.bluemap.api.plugin.PlayerIconFactory;
import de.bluecolored.bluemap.core.logger.Logger;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.UUID;
public class DefaultPlayerIconFactory implements PlayerIconFactory {
@Override
public BufferedImage apply(UUID uuid, BufferedImage in) {
BufferedImage head;
BufferedImage layer1 = in.getSubimage(8, 8, 8, 8);
BufferedImage layer2 = in.getSubimage(40, 8, 8, 8);
try {
head = new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = head.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(layer1, 4, 4, 40, 40, null);
g.drawImage(layer2, 0, 0, 48, 48, null);
} catch (Throwable t) { // There might be problems with headless servers when loading the graphics class, so we catch every exception and error on purpose here
Logger.global.noFloodWarning("headless-graphics-fail",
"Could not access Graphics2D to render player-skin texture. Try adding '-Djava.awt.headless=true' to your startup flags or ignore this warning.");
head = new BufferedImage(8, 8, in.getType());
layer1.copyData(head.getRaster());
}
return head;
}
}

View File

@ -0,0 +1,67 @@
package de.bluecolored.bluemap.common.plugin.skins;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import de.bluecolored.bluemap.api.plugin.SkinProvider;
import de.bluecolored.bluemap.core.logger.Logger;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
public class MojangSkinProvider implements SkinProvider {
@Override
public Optional<BufferedImage> load(UUID playerUUID) throws IOException {
try (Reader reader = requestProfileJson(playerUUID)) {
JsonParser parser = new JsonParser();
String textureInfoJson = readTextureInfoJson(parser.parse(reader));
String textureUrl = readTextureUrl(parser.parse(textureInfoJson));
return Optional.of(ImageIO.read(new URL(textureUrl)));
} catch (IOException ex) {
Logger.global.logDebug("Failed to load skin from mojang for player: '" + playerUUID + "' - " + ex);
return Optional.empty();
}
}
private Reader requestProfileJson(UUID playerUUID) throws IOException {
URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + playerUUID);
return new InputStreamReader(url.openStream());
}
private String readTextureInfoJson(JsonElement json) throws IOException {
try {
JsonArray properties = json.getAsJsonObject().getAsJsonArray("properties");
for (JsonElement element : properties) {
if (element.getAsJsonObject().get("name").getAsString().equals("textures")) {
return new String(Base64.getDecoder().decode(element.getAsJsonObject().get("value").getAsString().getBytes()));
}
}
throw new IOException("No texture info found!");
} catch (NullPointerException | IllegalStateException | ClassCastException e) {
throw new IOException(e);
}
}
private String readTextureUrl(JsonElement json) throws IOException {
try {
return json.getAsJsonObject()
.getAsJsonObject("textures")
.getAsJsonObject("SKIN")
.get("url").getAsString();
} catch (NullPointerException | IllegalStateException | ClassCastException e) {
throw new IOException(e);
}
}
}

View File

@ -1,159 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.skins;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.*;
@DebugDump
public class PlayerSkin {
private final UUID uuid;
private long lastUpdate;
public PlayerSkin(UUID uuid) {
this.uuid = uuid;
this.lastUpdate = -1;
}
public void update(File storageFolder, File fallback) {
long now = System.currentTimeMillis();
if (lastUpdate > 0 && lastUpdate + 600000 > now) return; // only update if skin is older than 10 minutes
lastUpdate = now;
new Thread(() -> {
BufferedImage head = null;
try {
Future<BufferedImage> futureSkin = loadSkin();
BufferedImage skin = futureSkin.get(10, TimeUnit.SECONDS);
head = createHead(skin);
} catch (ExecutionException | TimeoutException e) {
Logger.global.logDebug("Failed to load player-skin from mojang-servers: " + e);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
return;
}
try {
if (head == null) head = ImageIO.read(fallback);
ImageIO.write(head, "png", new File(storageFolder, uuid.toString() + ".png"));
} catch (IOException e) {
Logger.global.logError("Failed to write player-head image!", e);
}
}).start();
}
public BufferedImage createHead(BufferedImage skinTexture) {
BufferedImage head;
BufferedImage layer1 = skinTexture.getSubimage(8, 8, 8, 8);
BufferedImage layer2 = skinTexture.getSubimage(40, 8, 8, 8);
try {
head = new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = head.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(layer1, 4, 4, 40, 40, null);
g.drawImage(layer2, 0, 0, 48, 48, null);
} catch (Throwable t) { // There might be problems with headless servers when loading the graphics class, so we catch every exception and error on purpose here
Logger.global.noFloodWarning("headless-graphics-fail",
"Could not access Graphics2D to render player-skin texture. Try adding '-Djava.awt.headless=true' to your startup flags or ignore this warning.");
head = new BufferedImage(8, 8, skinTexture.getType());
layer1.copyData(head.getRaster());
}
return head;
}
public Future<BufferedImage> loadSkin() {
CompletableFuture<BufferedImage> image = new CompletableFuture<>();
new Thread(() -> {
try {
JsonParser parser = new JsonParser();
try (Reader reader = requestProfileJson()) {
String textureInfoJson = readTextureInfoJson(parser.parse(reader));
String textureUrl = readTextureUrl(parser.parse(textureInfoJson));
image.complete(ImageIO.read(new URL(textureUrl)));
}
} catch (IOException e) {
image.completeExceptionally(e);
}
}).start();
return image;
}
private Reader requestProfileJson() throws IOException {
URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + this.uuid);
return new InputStreamReader(url.openStream());
}
private String readTextureInfoJson(JsonElement json) throws IOException {
try {
JsonArray properties = json.getAsJsonObject().getAsJsonArray("properties");
for (JsonElement element : properties) {
if (element.getAsJsonObject().get("name").getAsString().equals("textures")) {
return new String(Base64.getDecoder().decode(element.getAsJsonObject().get("value").getAsString().getBytes()));
}
}
throw new IOException("No texture info found!");
} catch (NullPointerException | IllegalStateException | ClassCastException e) {
throw new IOException(e);
}
}
private String readTextureUrl(JsonElement json) throws IOException {
try {
return json.getAsJsonObject()
.getAsJsonObject("textures")
.getAsJsonObject("SKIN")
.get("url").getAsString();
} catch (NullPointerException | IllegalStateException | ClassCastException e) {
throw new IOException(e);
}
}
}

View File

@ -24,62 +24,105 @@
*/
package de.bluecolored.bluemap.common.plugin.skins;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.api.debug.DebugDump;
import org.apache.commons.io.FileUtils;
import de.bluecolored.bluemap.api.plugin.PlayerIconFactory;
import de.bluecolored.bluemap.api.plugin.SkinProvider;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import java.io.File;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@DebugDump
public class PlayerSkinUpdater implements ServerEventListener {
private File storageFolder;
private File defaultSkin;
private final Plugin plugin;
private final Map<UUID, Long> skinUpdates;
private final Map<UUID, PlayerSkin> skins;
private SkinProvider skinProvider;
private PlayerIconFactory playerMarkerIconFactory;
public PlayerSkinUpdater(File storageFolder, File defaultSkin) throws IOException {
this.storageFolder = storageFolder;
this.defaultSkin = defaultSkin;
this.skins = new ConcurrentHashMap<>();
public PlayerSkinUpdater(Plugin plugin) {
this.plugin = plugin;
this.skinUpdates = new ConcurrentHashMap<>();
FileUtils.forceMkdir(this.storageFolder);
skinProvider = new MojangSkinProvider();
playerMarkerIconFactory = new DefaultPlayerIconFactory();
}
public void updateSkin(UUID playerUuid) {
PlayerSkin skin = skins.get(playerUuid);
public CompletableFuture<Void> updateSkin(final UUID playerUuid) {
if (skin == null) {
skin = new PlayerSkin(playerUuid);
skins.put(playerUuid, skin);
}
// only update if last update was longer then an hour ago
long lastUpdate = skinUpdates.getOrDefault(playerUuid, 0L);
long now = System.currentTimeMillis();
if (now - lastUpdate < TimeUnit.HOURS.toMillis(1)) return CompletableFuture.completedFuture(null);
skinUpdates.put(playerUuid, now);
skin.update(storageFolder, defaultSkin);
// do the update async
return CompletableFuture.supplyAsync(() -> {
try {
return skinProvider.load(playerUuid);
} catch (IOException e) {
throw new CompletionException("The skin provider threw an exception while loading the skin for UUID: '" + playerUuid + "'!", e);
}
}, BlueMap.THREAD_POOL).thenAcceptAsync(skin -> {
if (skin.isEmpty()) {
Logger.global.logDebug("No player-skin provided for UUID: " + playerUuid);
return;
}
Map<String, BmMap> maps = plugin.getMaps();
if (maps == null) {
Logger.global.logDebug("Could not update skin, since the plugin seems not to be ready.");
return;
}
BufferedImage playerHead = playerMarkerIconFactory.apply(playerUuid, skin.get());
for (BmMap map : maps.values()) {
try (OutputStream out = map.getStorage().writeMeta(map.getId(), "live/assets/playerheads/" + playerUuid + ".png")) {
ImageIO.write(playerHead, "png", out);
} catch (IOException ex) {
Logger.global.logError("Failed to write player skin to storage: " + playerUuid, ex);
}
}
}, BlueMap.THREAD_POOL);
}
@Override
public void onPlayerJoin(UUID playerUuid) {
updateSkin(playerUuid);
updateSkin(playerUuid).exceptionally(ex -> {
Logger.global.logError("Failed to update player skin: " + playerUuid, ex);
return null;
});
}
public File getStorageFolder() {
return storageFolder;
public SkinProvider getSkinProvider() {
return skinProvider;
}
public void setStorageFolder(File storageFolder) {
this.storageFolder = storageFolder;
public void setSkinProvider(SkinProvider skinProvider) {
this.skinProvider = Objects.requireNonNull(skinProvider, "skinProvider can not be null");
}
public File getDefaultSkin() {
return defaultSkin;
public PlayerIconFactory getPlayerMarkerIconFactory() {
return playerMarkerIconFactory;
}
public void setDefaultSkin(File defaultSkin) {
this.defaultSkin = defaultSkin;
public void setPlayerMarkerIconFactory(PlayerIconFactory playerMarkerIconFactory) {
this.playerMarkerIconFactory = Objects.requireNonNull(playerMarkerIconFactory, "playerMarkerIconFactory can not be null");
}
}

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.rendermanager;
import de.bluecolored.bluemap.core.map.BmMap;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.serverinterface;
import java.nio.file.Path;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.serverinterface;
import de.bluecolored.bluemap.api.debug.DebugDump;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.web;
import de.bluecolored.bluemap.common.webserver.HttpRequest;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.web;
import de.bluecolored.bluemap.common.webserver.HttpRequest;

View File

@ -25,6 +25,7 @@
package de.bluecolored.bluemap.common.web;
import com.flowpowered.math.vector.Vector2i;
import de.bluecolored.bluemap.api.ContentTypeRegistry;
import de.bluecolored.bluemap.common.webserver.HttpRequest;
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
import de.bluecolored.bluemap.common.webserver.HttpResponse;
@ -35,10 +36,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -77,13 +75,13 @@ public HttpResponse handle(HttpRequest request) {
int lod = Integer.parseInt(tileMatcher.group(1));
int x = Integer.parseInt(tileMatcher.group(2).replace("/", ""));
int z = Integer.parseInt(tileMatcher.group(3).replace("/", ""));
Optional<TileData> optTileData = mapStorage.readMapTileData(mapId, lod, new Vector2i(x, z));
Optional<TileInfo> optTileInfo = mapStorage.readMapTileInfo(mapId, lod, new Vector2i(x, z));
if (optTileData.isPresent()) {
TileData tileData = optTileData.get();
if (optTileInfo.isPresent()) {
TileInfo tileInfo = optTileInfo.get();
// check e-tag
String eTag = calculateETag(path, tileData);
String eTag = calculateETag(path, tileInfo);
Set<String> etagStringSet = request.getHeader("If-None-Match");
if (!etagStringSet.isEmpty()){
if(etagStringSet.iterator().next().equals(eTag)) {
@ -92,7 +90,7 @@ public HttpResponse handle(HttpRequest request) {
}
// check modified-since
long lastModified = tileData.getLastModified();
long lastModified = tileInfo.getLastModified();
Set<String> modStringSet = request.getHeader("If-Modified-Since");
if (!modStringSet.isEmpty()){
try {
@ -103,7 +101,7 @@ public HttpResponse handle(HttpRequest request) {
} catch (IllegalArgumentException ignored){}
}
CompressedInputStream compressedIn = tileData.readMapTile();
CompressedInputStream compressedIn = tileInfo.readMapTile();
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
response.addHeader("ETag", eTag);
if (lastModified > 0)
@ -118,23 +116,13 @@ public HttpResponse handle(HttpRequest request) {
}
// provide meta-data
MetaType metaType = null;
for (MetaType mt : MetaType.values()) {
if (mt.getFilePath().equals(path)) {
metaType = mt;
break;
}
}
if (metaType != null) {
Optional<CompressedInputStream> optIn = mapStorage.readMeta(mapId, metaType);
if (optIn.isPresent()) {
CompressedInputStream compressedIn = optIn.get();
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
response.addHeader("Content-Type", metaType.getContentType());
writeToResponse(compressedIn, response, request);
return response;
}
Optional<InputStream> optIn = mapStorage.readMeta(mapId, path);
if (optIn.isPresent()) {
CompressedInputStream compressedIn = new CompressedInputStream(optIn.get(), Compression.NONE);
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
response.addHeader("Content-Type", ContentTypeRegistry.fromFileName(path));
writeToResponse(compressedIn, response, request);
return response;
}
} catch (NumberFormatException | NoSuchElementException ignore){
@ -148,8 +136,8 @@ public HttpResponse handle(HttpRequest request) {
return response;
}
private String calculateETag(String path, TileData tileData) {
return Long.toHexString(tileData.getSize()) + Integer.toHexString(path.hashCode()) + Long.toHexString(tileData.getLastModified());
private String calculateETag(String path, TileInfo tileInfo) {
return Long.toHexString(tileInfo.getSize()) + Integer.toHexString(path.hashCode()) + Long.toHexString(tileInfo.getLastModified());
}
private void writeToResponse(CompressedInputStream data, HttpResponse response, HttpRequest request) throws IOException {

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.web;
import de.bluecolored.bluemap.common.webserver.*;

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.webserver;
import java.io.IOException;

View File

@ -59,7 +59,7 @@ remove-caves-below-y: ${remove-caves-below-y}
# Defaults to 10000 (disabled)
cave-detection-ocean-floor: -5
# With this value set to true, BlueMap uses the block-light value instead of the sky-light value to "detect caves".
# With this value set to true, BlueMap also uses the block-light value (additionally to the sky-light) to "detect caves".
# (See: remove-caves-below-y)
# Changing this value requires a re-render of the map.
# Default is false

View File

@ -10,7 +10,7 @@ live-player-markers: true
# A list of gamemodes that will prevent a player from appearing on the map.
# Possible values are: survival, creative, spectator, adventure
hidden-gamemodes: [
hidden-game-modes: [
"spectator"
]
@ -28,7 +28,10 @@ hide-invisible: true
hide-sneaking: false
# Hides the player if they are in a sky or block-light level below the given number.
# E.g. if you set this to 1, then the player will be hidden on the map if they are in absolute darkness
# BOTH values have to be below the threshold for the player to be hidden!
# E.g. if you set both to 1, then the player will be hidden on the map if they are in absolute darkness
# Or, if you want players only be visible on the surface you set the sky-threshold to something between 1 and 15
# and the block-threshold to 16
# Default is 0 (don't hide the player)
hide-below-sky-light: 0
hide-below-block-light: 0

View File

@ -53,3 +53,15 @@ hires-slider-min: 0
lowres-slider-max: 7000
lowres-slider-default: 2000
lowres-slider-min: 500
# Here you can add URLs to custom scripts (js) so they will be loaded by the webapp
# You can place them somewhere in bluemap's webroot and add the (relative) link here
scripts: [
#"js/my-custom-script.js"
]
# Here you can add URLs to custom styles (css) so they will be loaded by the webapp
# You can place them somewhere in bluemap's webroot and add the (relative) link here
styles: [
#"css/my-custom-style.css"
]

View File

@ -1,4 +1,5 @@
import java.io.IOException
import java.util.concurrent.TimeoutException
plugins {
java
@ -12,7 +13,11 @@ fun String.runCommand(): String = ProcessBuilder(split("\\s(?=(?:[^'\"`]*(['\"`]
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
.apply { waitFor(60, TimeUnit.SECONDS) }
.apply {
if (!waitFor(10, TimeUnit.SECONDS)) {
throw TimeoutException("Failed to execute command: '" + this@runCommand + "'")
}
}
.run {
val error = errorStream.bufferedReader().readText().trim()
if (error.isNotEmpty()) {

View File

@ -34,8 +34,6 @@
import de.bluecolored.bluemap.core.map.lowres.LowresTileManager;
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.storage.CompressedInputStream;
import de.bluecolored.bluemap.core.storage.MetaType;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.world.World;
@ -50,6 +48,12 @@
@DebugDump
public class BmMap {
public static final String META_FILE_SETTINGS = "settings.json";
public static final String META_FILE_TEXTURES = "textures.json";
public static final String META_FILE_RENDER_STATE = ".rstate";
public static final String META_FILE_MARKERS = "live/players.json";
public static final String META_FILE_PLAYERS = "live/players.json";
private final String id;
private final String name;
private final String worldId;
@ -134,7 +138,7 @@ public synchronized void save() {
// only save texture gallery if not present in storage
try {
if (storage.readMeta(id, MetaType.TEXTURES).isEmpty())
if (storage.readMeta(id, META_FILE_TEXTURES).isEmpty())
saveTextureGallery();
} catch (IOException e) {
Logger.global.logError("Failed to read texture gallery", e);
@ -142,9 +146,9 @@ public synchronized void save() {
}
private void loadRenderState() throws IOException {
Optional<CompressedInputStream> rstateData = storage.readMeta(id, MetaType.RENDER_STATE);
Optional<InputStream> rstateData = storage.readMeta(id, META_FILE_RENDER_STATE);
if (rstateData.isPresent()) {
try (InputStream in = rstateData.get().decompress()){
try (InputStream in = rstateData.get()){
this.renderState.load(in);
} catch (IOException ex) {
Logger.global.logWarning("Failed to load render-state for map '" + getId() + "': " + ex);
@ -153,7 +157,7 @@ private void loadRenderState() throws IOException {
}
public synchronized void saveRenderState() {
try (OutputStream out = storage.writeMeta(id, MetaType.RENDER_STATE)) {
try (OutputStream out = storage.writeMeta(id, META_FILE_RENDER_STATE)) {
this.renderState.save(out);
} catch (IOException ex){
Logger.global.logError("Failed to save render-state for map: '" + this.id + "'!", ex);
@ -162,9 +166,9 @@ public synchronized void saveRenderState() {
private TextureGallery loadTextureGallery() throws IOException {
TextureGallery gallery = null;
Optional<CompressedInputStream> texturesData = storage.readMeta(id, MetaType.TEXTURES);
Optional<InputStream> texturesData = storage.readMeta(id, META_FILE_TEXTURES);
if (texturesData.isPresent()) {
try (InputStream in = texturesData.get().decompress()){
try (InputStream in = texturesData.get()){
gallery = TextureGallery.readTexturesFile(in);
} catch (IOException ex) {
Logger.global.logError("Failed to load textures for map '" + getId() + "'!", ex);
@ -174,7 +178,7 @@ private TextureGallery loadTextureGallery() throws IOException {
}
private void saveTextureGallery() {
try (OutputStream out = storage.writeMeta(id, MetaType.TEXTURES)) {
try (OutputStream out = storage.writeMeta(id, META_FILE_TEXTURES)) {
this.textureGallery.writeTexturesFile(this.resourcePack, out);
} catch (IOException ex) {
Logger.global.logError("Failed to save textures for map '" + getId() + "'!", ex);
@ -188,7 +192,7 @@ public synchronized void resetTextureGallery() {
private void saveMapSettings() {
try (
OutputStream out = storage.writeMeta(id, MetaType.SETTINGS);
OutputStream out = storage.writeMeta(id, META_FILE_SETTINGS);
Writer writer = new OutputStreamWriter(out)
) {
ResourcesGson.addAdapter(new GsonBuilder())
@ -202,7 +206,7 @@ private void saveMapSettings() {
public synchronized void saveMarkerState() {
try (
OutputStream out = storage.writeMeta(id, MetaType.MARKERS);
OutputStream out = storage.writeMeta(id, META_FILE_MARKERS);
Writer writer = new OutputStreamWriter(out)
) {
MarkerGson.INSTANCE.toJson(this.markerSets, writer);

View File

@ -201,7 +201,7 @@ private void createElementFace(Element element, Direction faceDir, VectorM3f c0,
int blockLight = Math.max(blockLightData.getBlockLight(), facedLightData.getBlockLight());
// filter out faces that are in a "cave" that should not be rendered
if (isCave && (renderSettings.isCaveDetectionUsesBlockLight() ? blockLight : sunLight) == 0f) return;
if (isCave && (renderSettings.isCaveDetectionUsesBlockLight() ? Math.max(blockLight, sunLight) : sunLight) == 0f) return;
// initialize the faces
blockModel.initialize();

View File

@ -41,7 +41,7 @@ public class Metrics {
private static final String METRICS_REPORT_URL = "https://metrics.bluecolored.de/bluemap/";
public static void sendReportAsync(String implementation) {
new Thread(() -> sendReport(implementation)).start();
new Thread(() -> sendReport(implementation), "BlueMap-Plugin-SendMetricsReport").start();
}
public static void sendReport(String implementation) {

View File

@ -0,0 +1,12 @@
package de.bluecolored.bluemap.core.storage;
import java.io.IOException;
import java.io.InputStream;
public interface MetaInfo {
InputStream readMeta() throws IOException;
long getSize();
}

View File

@ -1,56 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.storage;
public enum MetaType {
TEXTURES ("textures", "textures.json", "application/json"),
SETTINGS ("settings", "settings.json", "application/json"),
MARKERS ("markers", "live/markers", "application/json"),
PLAYERS ("players", "live/players", "application/json"),
RENDER_STATE ("render_state", ".rstate", "application/octet-stream");
private final String typeId;
private final String filePath;
private final String contentType;
MetaType(String typeId, String filePath, String contentType) {
this.typeId = typeId;
this.filePath = filePath;
this.contentType = contentType;
}
public String getTypeId() {
return typeId;
}
public String getFilePath() {
return filePath;
}
public String getContentType() {
return contentType;
}
}

View File

@ -28,6 +28,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;
@ -39,15 +40,17 @@ public abstract class Storage implements Closeable {
public abstract Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector2i tile) throws IOException;
public abstract Optional<TileData> readMapTileData(String mapId, int lod, Vector2i tile) throws IOException;
public abstract Optional<TileInfo> readMapTileInfo(String mapId, int lod, Vector2i tile) throws IOException;
public abstract void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException;
public abstract OutputStream writeMeta(String mapId, MetaType metaType) throws IOException;
public abstract OutputStream writeMeta(String mapId, String name) throws IOException;
public abstract Optional<CompressedInputStream> readMeta(String mapId, MetaType metaType) throws IOException;
public abstract Optional<InputStream> readMeta(String mapId, String name) throws IOException;
public abstract void deleteMeta(String mapId, MetaType metaType) throws IOException;
public abstract Optional<MetaInfo> readMetaInfo(String mapId, String name) throws IOException;
public abstract void deleteMeta(String mapId, String name) throws IOException;
public abstract void purgeMap(String mapId) throws IOException;
@ -115,4 +118,8 @@ public Storage getStorage() {
}
public static String escapeMetaName(String name) {
return name.replaceAll("[^\\w\\d.\\-_/]", "_").replace("..", "_.");
}
}

View File

@ -26,7 +26,7 @@
import java.io.IOException;
public interface TileData {
public interface TileInfo {
CompressedInputStream readMapTile() throws IOException;

View File

@ -97,7 +97,7 @@ public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector
}
@Override
public Optional<TileData> readMapTileData(String mapId, int lod, Vector2i tile) throws IOException {
public Optional<TileInfo> readMapTileInfo(String mapId, int lod, Vector2i tile) throws IOException {
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
Path file = getFilePath(mapId, lod, tile);
@ -106,7 +106,7 @@ public Optional<TileData> readMapTileData(String mapId, int lod, Vector2i tile)
final long size = Files.size(file);
final long lastModified = Files.getLastModifiedTime(file).toMillis();
return Optional.of(new TileData() {
return Optional.of(new TileInfo() {
@Override
public CompressedInputStream readMapTile() throws IOException {
return FileStorage.this.readMapTile(mapId, lod, tile)
@ -137,8 +137,8 @@ public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOExcepti
}
@Override
public OutputStream writeMeta(String mapId, MetaType metaType) throws IOException {
Path file = getFilePath(mapId).resolve(metaType.getFilePath());
public OutputStream writeMeta(String mapId, String name) throws IOException {
Path file = getMetaFilePath(mapId, name);
OutputStream os = FileHelper.createFilepartOutputStream(file);
os = new BufferedOutputStream(os);
@ -147,20 +147,43 @@ public OutputStream writeMeta(String mapId, MetaType metaType) throws IOExceptio
}
@Override
public Optional<CompressedInputStream> readMeta(String mapId, MetaType metaType) throws IOException {
Path file = getFilePath(mapId).resolve(metaType.getFilePath());
public Optional<InputStream> readMeta(String mapId, String name) throws IOException {
Path file = getMetaFilePath(mapId, name);
if (!Files.exists(file)) return Optional.empty();
InputStream is = Files.newInputStream(file, StandardOpenOption.READ);
is = new BufferedInputStream(is);
return Optional.of(new CompressedInputStream(is, Compression.NONE));
return Optional.of(is);
}
@Override
public void deleteMeta(String mapId, MetaType metaType) throws IOException {
Path file = getFilePath(mapId).resolve(metaType.getFilePath());
public Optional<MetaInfo> readMetaInfo(String mapId, String name) throws IOException {
Path file = getMetaFilePath(mapId, name);
if (!Files.exists(file)) return Optional.empty();
final long size = Files.size(file);
return Optional.of(new MetaInfo() {
@Override
public InputStream readMeta() throws IOException {
return FileStorage.this.readMeta(mapId, name)
.orElseThrow(() -> new IOException("Meta no longer present!"));
}
@Override
public long getSize() {
return size;
}
});
}
@Override
public void deleteMeta(String mapId, String name) throws IOException {
Path file = getMetaFilePath(mapId, name);
Files.deleteIfExists(file);
}
@ -199,4 +222,9 @@ public Path getFilePath(String mapId) {
return root.resolve(mapId);
}
public Path getMetaFilePath(String mapId, String name) {
return getFilePath(mapId).resolve(escapeMetaName(name)
.replace("/", root.getFileSystem().getSeparator()));
}
}

View File

@ -37,10 +37,7 @@
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@ -176,11 +173,11 @@ public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector
}
@Override
public Optional<TileData> readMapTileData(final String mapId, int lod, final Vector2i tile) throws IOException {
public Optional<TileInfo> readMapTileInfo(final String mapId, int lod, final Vector2i tile) throws IOException {
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
try {
TileData tileData = recoveringConnection(connection -> {
TileInfo tileInfo = recoveringConnection(connection -> {
ResultSet result = executeQuery(connection,
//language=SQL
"SELECT t.`changed`, LENGTH(t.`data`) as 'size' " +
@ -205,7 +202,7 @@ public Optional<TileData> readMapTileData(final String mapId, int lod, final Vec
final long lastModified = result.getTimestamp("changed").getTime();
final long size = result.getLong("size");
return new TileData() {
return new TileInfo() {
@Override
public CompressedInputStream readMapTile() throws IOException {
return SQLStorage.this.readMapTile(mapId, lod, tile)
@ -232,7 +229,7 @@ public long getLastModified() {
}
}, 2);
return Optional.ofNullable(tileData);
return Optional.ofNullable(tileInfo);
} catch (SQLException | NoSuchElementException ex) {
throw new IOException(ex);
}
@ -263,7 +260,7 @@ public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOExcepti
}
@Override
public OutputStream writeMeta(String mapId, MetaType metaType) {
public OutputStream writeMeta(String mapId, String name) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
return new WrappedOutputStream(byteOut, () -> {
int mapFK = getMapFK(mapId);
@ -280,7 +277,7 @@ public OutputStream writeMeta(String mapId, MetaType metaType) {
"REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " +
"VALUES (?, ?, ?)",
mapFK,
metaType.getTypeId(),
escapeMetaName(name),
dataBlob
);
} finally {
@ -291,7 +288,7 @@ public OutputStream writeMeta(String mapId, MetaType metaType) {
}
@Override
public Optional<CompressedInputStream> readMeta(String mapId, MetaType metaType) throws IOException {
public Optional<InputStream> readMeta(String mapId, String name) throws IOException {
try {
byte[] data = recoveringConnection(connection -> {
ResultSet result = executeQuery(connection,
@ -303,7 +300,7 @@ public Optional<CompressedInputStream> readMeta(String mapId, MetaType metaType)
"WHERE m.`map_id` = ? " +
"AND t.`key` = ?",
mapId,
metaType.getTypeId()
escapeMetaName(name)
);
if (result.next()) {
@ -322,7 +319,50 @@ public Optional<CompressedInputStream> readMeta(String mapId, MetaType metaType)
}
@Override
public void deleteMeta(String mapId, MetaType metaType) throws IOException {
public Optional<MetaInfo> readMetaInfo(String mapId, String name) throws IOException {
try {
MetaInfo tileInfo = recoveringConnection(connection -> {
ResultSet result = executeQuery(connection,
//language=SQL
"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` = ?",
mapId,
escapeMetaName(name)
);
if (result.next()) {
final long size = result.getLong("size");
return new MetaInfo() {
@Override
public InputStream readMeta() throws IOException {
return SQLStorage.this.readMeta(mapId, name)
.orElseThrow(() -> new IOException("Tile no longer present!"));
}
@Override
public long getSize() {
return size;
}
};
} else {
return null;
}
}, 2);
return Optional.ofNullable(tileInfo);
} catch (SQLException | NoSuchElementException ex) {
throw new IOException(ex);
}
}
@Override
public void deleteMeta(String mapId, String name) throws IOException {
try {
recoveringConnection(connection ->
executeUpdate(connection,
@ -334,7 +374,7 @@ public void deleteMeta(String mapId, MetaType metaType) throws IOException {
"WHERE m.`map_id` = ? " +
"AND t.`key` = ?",
mapId,
metaType.getTypeId()
escapeMetaName(name)
), 2);
} catch (SQLException ex) {
throw new IOException(ex);

View File

@ -69,7 +69,8 @@ public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRende
//metrics report
if (blueMap.getConfigs().getCoreConfig().isMetrics()) Metrics.sendReportAsync("cli");
blueMap.createOrUpdateWebApp(forceGenerateWebapp);
if (blueMap.getConfigs().getWebappConfig().isEnabled())
blueMap.createOrUpdateWebApp(forceGenerateWebapp);
//try load resources
blueMap.getResourcePack();
@ -161,7 +162,7 @@ public void run() {
Logger.global.logInfo("Stopped.");
};
Thread shutdownHook = new Thread(shutdown);
Thread shutdownHook = new Thread(shutdown, "BlueMap-CLI-ShutdownHook");
Runtime.getRuntime().addShutdownHook(shutdownHook);
// wait until done, then shutdown if not watching

View File

@ -104,7 +104,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -106,7 +106,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -106,7 +106,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -106,7 +106,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -107,7 +107,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -107,7 +107,7 @@ public void onInitialize() {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {

View File

@ -119,7 +119,7 @@ public void onServerStarted(FMLServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -119,7 +119,7 @@ public void onServerStarted(FMLServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -125,7 +125,7 @@ public void onServerStarted(FMLServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -124,7 +124,7 @@ public void onServerStarted(FMLServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -124,7 +124,7 @@ public void onServerStarted(ServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -125,7 +125,7 @@ public void onServerStarted(ServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent

View File

@ -125,7 +125,7 @@ public void onServerStarted(ServerStartedEvent event) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}).start();
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent