mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 10:35:16 +01:00
Implement playermarkers on the web-app
This commit is contained in:
parent
b6d358a097
commit
c4f0256ca0
@ -35,7 +35,6 @@
|
||||
import de.bluecolored.bluemap.common.MapType;
|
||||
import de.bluecolored.bluemap.common.RenderManager;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||
|
||||
public class MapUpdateHandler implements ServerEventListener {
|
||||
|
||||
@ -135,20 +134,5 @@ public void flushTileBuffer() {
|
||||
updateBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerJoin(UUID playerUuid) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerLeave(UUID playerUuid) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChatMessage(Text message) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,7 @@
|
||||
import de.bluecolored.bluemap.common.RenderManager;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||
import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater;
|
||||
import de.bluecolored.bluemap.core.config.ConfigManager;
|
||||
import de.bluecolored.bluemap.core.config.MainConfig;
|
||||
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
|
||||
@ -86,6 +87,7 @@ public class Plugin {
|
||||
private Map<String, MapType> maps;
|
||||
|
||||
private MapUpdateHandler updateHandler;
|
||||
private PlayerSkinUpdater skinUpdater;
|
||||
|
||||
private RenderManager renderManager;
|
||||
private BlueMapWebServer webServer;
|
||||
@ -269,6 +271,10 @@ public synchronized void load() throws IOException, ParseResourceException {
|
||||
this.updateHandler = new MapUpdateHandler(this);
|
||||
serverInterface.registerListener(updateHandler);
|
||||
|
||||
//start skin updater
|
||||
this.skinUpdater = new PlayerSkinUpdater(config.getWebRoot().resolve("assets").resolve("playerheads").toFile());
|
||||
serverInterface.registerListener(skinUpdater);
|
||||
|
||||
//create/update webfiles
|
||||
WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot());
|
||||
if (webFilesManager.needsUpdate()) {
|
||||
|
@ -33,16 +33,16 @@
|
||||
|
||||
public interface ServerEventListener {
|
||||
|
||||
void onWorldSaveToDisk(UUID world);
|
||||
default void onWorldSaveToDisk(UUID world) {};
|
||||
|
||||
void onBlockChange(UUID world, Vector3i blockPos);
|
||||
default void onBlockChange(UUID world, Vector3i blockPos) {};
|
||||
|
||||
void onChunkFinishedGeneration(UUID world, Vector2i chunkPos);
|
||||
default void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) {};
|
||||
|
||||
void onPlayerJoin(UUID playerUuid);
|
||||
default void onPlayerJoin(UUID playerUuid) {};
|
||||
|
||||
void onPlayerLeave(UUID playerUuid);
|
||||
default void onPlayerLeave(UUID playerUuid) {};
|
||||
|
||||
void onChatMessage(Text message);
|
||||
default void onChatMessage(Text message) {};
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 java.awt.Graphics2D;
|
||||
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.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
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) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (lastUpdate > 0 && lastUpdate + 600000 > now) return; // only update if skin is older than 10 minutes
|
||||
|
||||
lastUpdate = now;
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Future<BufferedImage> futureSkin = loadSkin();
|
||||
BufferedImage skin = futureSkin.get(10, TimeUnit.SECONDS);
|
||||
BufferedImage head = createHead(skin);
|
||||
ImageIO.write(head, "png", new File(storageFolder, uuid.toString() + ".png"));
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
Logger.global.logWarning("Failed to load player-skin from mojang-servers: " + e);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to write player-head image!", e);
|
||||
} catch (InterruptedException ignore) {}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public BufferedImage createHead(BufferedImage skinTexture) {
|
||||
BufferedImage head = new BufferedImage(8, 8, skinTexture.getType());
|
||||
|
||||
BufferedImage layer1 = skinTexture.getSubimage(8, 8, 8, 8);
|
||||
BufferedImage layer2 = skinTexture.getSubimage(40, 8, 8, 8);
|
||||
|
||||
try {
|
||||
Graphics2D g = head.createGraphics();
|
||||
g.drawImage(layer1, 0, 0, null);
|
||||
g.drawImage(layer2, 0, 0, null);
|
||||
} catch (Throwable t) { // There might be problems with headless servers when loading the graphics class
|
||||
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.");
|
||||
|
||||
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 (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 (IllegalStateException | ClassCastException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||
|
||||
public class PlayerSkinUpdater implements ServerEventListener {
|
||||
|
||||
private File storageFolder;
|
||||
|
||||
private Map<UUID, PlayerSkin> skins;
|
||||
|
||||
public PlayerSkinUpdater(File storageFolder) {
|
||||
this.storageFolder = storageFolder;
|
||||
this.skins = new ConcurrentHashMap<>();
|
||||
|
||||
this.storageFolder.mkdirs();
|
||||
}
|
||||
|
||||
public void updateSkin(UUID playerUuid) {
|
||||
PlayerSkin skin = skins.get(playerUuid);
|
||||
|
||||
if (skin == null) {
|
||||
skin = new PlayerSkin(playerUuid);
|
||||
skins.put(playerUuid, skin);
|
||||
}
|
||||
|
||||
skin.update(storageFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerJoin(UUID playerUuid) {
|
||||
updateSkin(playerUuid);
|
||||
}
|
||||
|
||||
}
|
@ -198,9 +198,7 @@ public synchronized Texture loadTexture(FileAccess fileAccess, String path) thro
|
||||
|
||||
//crop off animation frames
|
||||
if (image.getHeight() > image.getWidth()){
|
||||
BufferedImage cropped = new BufferedImage(image.getWidth(), image.getWidth(), image.getType());
|
||||
image.copyData(cropped.getRaster());
|
||||
image = cropped;
|
||||
image = image.getSubimage(0, 0, image.getWidth(), image.getWidth());
|
||||
}
|
||||
|
||||
//check halfTransparency
|
||||
|
BIN
BlueMapCore/src/main/webroot/assets/playerheads/alex.png
Normal file
BIN
BlueMapCore/src/main/webroot/assets/playerheads/alex.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
BlueMapCore/src/main/webroot/assets/playerheads/steve.png
Normal file
BIN
BlueMapCore/src/main/webroot/assets/playerheads/steve.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
@ -60,9 +60,10 @@ import { stringToImage, pathFromCoords } from './utils.js';
|
||||
import {cachePreventionNr, getCookie, setCookie} from "./utils";
|
||||
|
||||
export default class BlueMap {
|
||||
constructor(element, dataRoot) {
|
||||
constructor(element, dataRoot = "data/", liveApiRoot = "live/") {
|
||||
this.element = $('<div class="bluemap-container"></div>').appendTo(element)[0];
|
||||
this.dataRoot = dataRoot;
|
||||
this.liveApiRoot = liveApiRoot;
|
||||
this.locationHash = '';
|
||||
this.cacheSuffix = '';
|
||||
|
||||
|
@ -3,6 +3,7 @@ import $ from "jquery";
|
||||
import ToggleButton from "../ui/ToggleButton";
|
||||
import Label from "../ui/Label";
|
||||
import {cachePreventionNr} from "../utils";
|
||||
import PlayerMarkerSet from "./PlayerMarkerSet";
|
||||
|
||||
export default class MarkerManager {
|
||||
|
||||
@ -10,14 +11,23 @@ export default class MarkerManager {
|
||||
this.blueMap = blueMap;
|
||||
this.ui = ui;
|
||||
|
||||
this.markerData = null;
|
||||
this.liveData = null;
|
||||
this.markerSets = [];
|
||||
|
||||
this.playerMarkerSet = null;
|
||||
|
||||
this.readyPromise =
|
||||
this.loadMarkerData()
|
||||
.catch(ignore => {
|
||||
if (this.blueMap.debugInfo) console.debug("Failed load markers:", ignore);
|
||||
})
|
||||
.then(this.loadMarkers);
|
||||
Promise.all([
|
||||
this.loadMarkerData()
|
||||
.catch(ignore => {
|
||||
if (this.blueMap.debugInfo) console.debug("Failed load markers:", ignore);
|
||||
}),
|
||||
this.checkLiveAPI()
|
||||
.then(this.initializePlayerMarkers)
|
||||
])
|
||||
.then(this.loadMarkers)
|
||||
.then(this.updatePlayerMarkerLoop);
|
||||
|
||||
$(document).on('bluemap-map-change', this.onBlueMapMapChange);
|
||||
}
|
||||
@ -41,6 +51,25 @@ export default class MarkerManager {
|
||||
});
|
||||
}
|
||||
|
||||
checkLiveAPI() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.blueMap.fileLoader.load(this.blueMap.liveApiRoot + 'players?' + cachePreventionNr(),
|
||||
liveData => {
|
||||
try {
|
||||
this.liveData = JSON.parse(liveData);
|
||||
resolve();
|
||||
} catch (e){
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
xhr => {},
|
||||
error => {
|
||||
reject();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
loadMarkers = () => {
|
||||
if (this.markerData && this.markerData.markerSets) {
|
||||
this.markerData.markerSets.forEach(setData => {
|
||||
@ -49,12 +78,27 @@ export default class MarkerManager {
|
||||
}
|
||||
};
|
||||
|
||||
initializePlayerMarkers = () => {
|
||||
if (this.liveData){
|
||||
this.playerMarkerSet = new PlayerMarkerSet(this.blueMap);
|
||||
this.markerSets.push(this.playerMarkerSet);
|
||||
}
|
||||
};
|
||||
|
||||
update(){
|
||||
this.markerSets.forEach(markerSet => {
|
||||
markerSet.update();
|
||||
});
|
||||
}
|
||||
|
||||
updatePlayerMarkerLoop = () => {
|
||||
if (this.playerMarkerSet){
|
||||
this.playerMarkerSet.updateLive();
|
||||
}
|
||||
|
||||
setTimeout(this.updatePlayerMarkerLoop, 2000);
|
||||
};
|
||||
|
||||
addMenuElements(menu){
|
||||
let addedLabel = false;
|
||||
this.markerSets.forEach(markerSet => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import Marker from "./Marker";
|
||||
import {CSS2DObject} from "./CSS2DRenderer";
|
||||
import {Vector3} from "three";
|
||||
|
||||
import POI from "../../../assets/poi.svg";
|
||||
|
||||
|
90
BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js
Normal file
90
BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js
Normal file
@ -0,0 +1,90 @@
|
||||
import $ from 'jquery';
|
||||
import Marker from "./Marker";
|
||||
import {CSS2DObject} from "./CSS2DRenderer";
|
||||
|
||||
export default class PlayerMarker extends Marker {
|
||||
|
||||
constructor(blueMap, markerSet, markerData, playerUuid, worldUuid) {
|
||||
super(blueMap, markerSet, markerData);
|
||||
|
||||
this.online = false;
|
||||
this.player = playerUuid;
|
||||
this.world = worldUuid;
|
||||
|
||||
this.animationRunning = false;
|
||||
this.lastFrame = -1;
|
||||
}
|
||||
|
||||
setVisible(visible){
|
||||
this.visible = visible && this.online && this.world === this.blueMap.settings.maps[this.blueMap.map].world;
|
||||
|
||||
this.blueMap.updateFrame = true;
|
||||
|
||||
if (!this.renderObject){
|
||||
let iconElement = $(`<div class="marker-player"><img src="assets/playerheads/${this.player}.png" onerror="this.onerror=null;this.src='assets/playerheads/steve.png';"><div class="nameplate">${this.label}</div></div>`);
|
||||
iconElement.find("img").click(this.onClick);
|
||||
|
||||
this.renderObject = new CSS2DObject(iconElement[0]);
|
||||
this.renderObject.position.copy(this.position);
|
||||
this.renderObject.onBeforeRender = (renderer, scene, camera) => {
|
||||
let distanceSquared = this.position.distanceToSquared(camera.position);
|
||||
if (distanceSquared > 1000000) {
|
||||
iconElement.addClass("distant");
|
||||
} else {
|
||||
iconElement.removeClass("distant");
|
||||
}
|
||||
|
||||
this.updateRenderObject(this.renderObject, scene, camera);
|
||||
};
|
||||
}
|
||||
|
||||
if (this.visible) {
|
||||
this.blueMap.hudScene.add(this.renderObject);
|
||||
} else {
|
||||
this.blueMap.hudScene.remove(this.renderObject);
|
||||
}
|
||||
}
|
||||
|
||||
updatePosition = () => {
|
||||
if (this.renderObject && !this.renderObject.position.equals(this.position)) {
|
||||
if (this.visible) {
|
||||
if (!this.animationRunning) {
|
||||
this.animationRunning = true;
|
||||
requestAnimationFrame(this.moveAnimation);
|
||||
}
|
||||
} else {
|
||||
this.renderObject.position.copy(this.position);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
moveAnimation = (time) => {
|
||||
let delta = time - this.lastFrame;
|
||||
if (this.lastFrame === -1){
|
||||
delta = 20;
|
||||
}
|
||||
this.lastFrame = time;
|
||||
|
||||
if (this.renderObject && !this.renderObject.position.equals(this.position)) {
|
||||
this.renderObject.position.x += (this.position.x - this.renderObject.position.x) * 0.01 * delta;
|
||||
this.renderObject.position.y += (this.position.y - this.renderObject.position.y) * 0.01 * delta;
|
||||
this.renderObject.position.z += (this.position.z - this.renderObject.position.z) * 0.01 * delta;
|
||||
|
||||
if (this.renderObject.position.distanceToSquared(this.position) < 0.001) {
|
||||
this.renderObject.position.copy(this.position);
|
||||
}
|
||||
|
||||
this.blueMap.updateFrame = true;
|
||||
|
||||
requestAnimationFrame(this.moveAnimation);
|
||||
} else {
|
||||
this.animationRunning = false;
|
||||
this.lastFrame = -1;
|
||||
}
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
|
||||
}
|
||||
|
||||
}
|
85
BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js
Normal file
85
BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js
Normal file
@ -0,0 +1,85 @@
|
||||
import POIMarker from "./POIMarker";
|
||||
import ShapeMarker from "./ShapeMarker";
|
||||
import {cachePreventionNr} from "../utils";
|
||||
import PlayerMarker from "./PlayerMarker";
|
||||
import {Vector3} from "three";
|
||||
|
||||
export default class PlayerMarkerSet {
|
||||
|
||||
constructor(blueMap) {
|
||||
this.blueMap = blueMap;
|
||||
this.id = "bluemap-live-players";
|
||||
this.label = "players";
|
||||
this.toggleable = true;
|
||||
this.defaultHide = false;
|
||||
this.marker = [];
|
||||
this.markerMap = {};
|
||||
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.marker.forEach(marker => {
|
||||
marker.setVisible(this.visible);
|
||||
});
|
||||
}
|
||||
|
||||
async updateLive(){
|
||||
await new Promise((resolve, reject) => {
|
||||
this.blueMap.fileLoader.load(this.blueMap.liveApiRoot + 'players?' + cachePreventionNr(),
|
||||
liveData => {
|
||||
try {
|
||||
liveData = JSON.parse(liveData);
|
||||
resolve(liveData);
|
||||
} catch (e){
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
xhr => {},
|
||||
error => {
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
}).then((liveData) => {
|
||||
this.updateWith(liveData)
|
||||
}).catch((e) => {
|
||||
console.error("Failed to update player-markers!", e);
|
||||
});
|
||||
}
|
||||
|
||||
updateWith(liveData){
|
||||
this.marker.forEach(marker => {
|
||||
marker.nowOnline = false;
|
||||
});
|
||||
|
||||
for(let i = 0; i < liveData.players.length; i++){
|
||||
let player = liveData.players[i];
|
||||
let marker = this.markerMap[player.uuid];
|
||||
|
||||
if (!marker){
|
||||
marker = new PlayerMarker(this.blueMap, this, {
|
||||
type: "playermarker",
|
||||
map: null,
|
||||
position: player.position,
|
||||
label: player.name,
|
||||
link: null,
|
||||
newTab: false
|
||||
}, player.uuid, player.world);
|
||||
|
||||
this.markerMap[player.uuid] = marker;
|
||||
this.marker.push(marker);
|
||||
}
|
||||
|
||||
marker.nowOnline = true;
|
||||
marker.position = new Vector3(player.position.x, player.position.y + 1.5, player.position.z);
|
||||
marker.updatePosition();
|
||||
}
|
||||
|
||||
this.marker.forEach(marker => {
|
||||
if (marker.nowOnline !== marker.online){
|
||||
marker.online = marker.nowOnline;
|
||||
marker.setVisible(this.visible);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bluemap-container .marker-poi {
|
||||
.bluemap-container .marker-poi, .bluemap-container .marker-player {
|
||||
pointer-events: none;
|
||||
|
||||
> * {
|
||||
@ -86,4 +86,39 @@
|
||||
> img {
|
||||
filter: drop-shadow(1px 1px 3px #0008);
|
||||
}
|
||||
}
|
||||
|
||||
.bluemap-container .marker-player {
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.nameplate {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
transform: translate(-50%, -110%);
|
||||
background: rgba(50, 50, 50, 0.75);
|
||||
padding: 0.2em 0.3em;
|
||||
color: white;
|
||||
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&.distant {
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.nameplate {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user