Add forge live-implementation, improve fabric implementation and fix possible CME

This commit is contained in:
Blue (Lukas Rieger) 2020-08-14 20:16:06 +02:00
parent 59c3f8adc5
commit e72fb4f21a
7 changed files with 410 additions and 137 deletions

View File

@ -72,7 +72,7 @@ public synchronized void removeAllListeners() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public synchronized void onWorldSaveToDisk(WorldSaveEvent evt) { public synchronized void onWorldSaveToDisk(WorldSaveEvent evt) {
listeners.forEach(l -> l.onWorldSaveToDisk(evt.getWorld().getUID())); for (ServerEventListener listener : listeners) listener.onWorldSaveToDisk(evt.getWorld().getUID());
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@ -123,7 +123,7 @@ public void onBlockChange(BlockFertilizeEvent evt) {
private synchronized void onBlockChange(Location loc) { private synchronized void onBlockChange(Location loc) {
UUID world = loc.getWorld().getUID(); UUID world = loc.getWorld().getUID();
Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
listeners.forEach(l -> l.onBlockChange(world, pos)); for (ServerEventListener listener : listeners) listener.onBlockChange(world, pos);
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@ -131,23 +131,23 @@ public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) {
Chunk chunk = evt.getChunk(); Chunk chunk = evt.getChunk();
UUID world = chunk.getWorld().getUID(); UUID world = chunk.getWorld().getUID();
Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ());
listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos)); for (ServerEventListener listener : listeners) listener.onChunkFinishedGeneration(world, chunkPos);
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent evt) { public synchronized void onPlayerJoin(PlayerJoinEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId());
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(PlayerQuitEvent evt) { public synchronized void onPlayerLeave(PlayerQuitEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId());
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerChat(AsyncPlayerChatEvent evt) { public synchronized void onPlayerChat(AsyncPlayerChatEvent evt) {
String message = String.format(evt.getFormat(), evt.getPlayer().getDisplayName(), evt.getMessage()); String message = String.format(evt.getFormat(), evt.getPlayer().getDisplayName(), evt.getMessage());
listeners.forEach(l -> l.onChatMessage(Text.of(message))); for (ServerEventListener listener : listeners) listener.onChatMessage(Text.of(message));
} }
} }

View File

@ -69,11 +69,11 @@ public FabricEventForwarder(FabricMod mod) {
PlayerLeaveCallback.EVENT.register(this::onPlayerLeave); PlayerLeaveCallback.EVENT.register(this::onPlayerLeave);
} }
public void addEventListener(ServerEventListener listener) { public synchronized void addEventListener(ServerEventListener listener) {
this.eventListeners.add(listener); this.eventListeners.add(listener);
} }
public void removeAllListeners() { public synchronized void removeAllListeners() {
this.eventListeners.clear(); this.eventListeners.clear();
} }
@ -94,45 +94,47 @@ public ActionResult onBlockAttack(PlayerEntity player, World world, Hand hand, B
return ActionResult.PASS; return ActionResult.PASS;
} }
public void onBlockChange(ServerWorld world, BlockPos blockPos) { public synchronized void onBlockChange(ServerWorld world, BlockPos blockPos) {
Vector3i position = new Vector3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()); Vector3i position = new Vector3i(blockPos.getX(), blockPos.getY(), blockPos.getZ());
try { try {
UUID uuid = mod.getUUIDForWorld(world); UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onBlockChange(uuid, position)); for (ServerEventListener listener : eventListeners) listener.onBlockChange(uuid, position);
} catch (IOException e) { } catch (IOException e) {
Logger.global.logError("Failed to get UUID for world: " + world, e); Logger.global.noFloodError("Failed to get the UUID for a world!", e);
} }
} }
public void onWorldSave(ServerWorld world) { public synchronized void onWorldSave(ServerWorld world) {
try { try {
UUID uuid = mod.getUUIDForWorld(world); UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onWorldSaveToDisk(uuid)); for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid);
} catch (IOException e) { } catch (IOException e) {
Logger.global.logError("Failed to get UUID for world: " + world, e); Logger.global.noFloodError("Failed to get the UUID for a world!", e);
} }
} }
public void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { public synchronized void onChunkFinalize(ServerWorld world, Vector2i chunkPos) {
try { try {
UUID uuid = mod.getUUIDForWorld(world); UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onChunkFinishedGeneration(uuid, chunkPos)); for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos);
} catch (IOException e) { } catch (IOException e) {
Logger.global.logError("Failed to get UUID for world: " + world, e); Logger.global.noFloodError("Failed to get the UUID for a world!", e);
} }
} }
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) {
if (this.mod.getServer() != server) return; if (this.mod.getServer() != server) return;
this.eventListeners.forEach(l -> l.onPlayerJoin(player.getUuid())); UUID uuid = player.getUuid();
for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid);
} }
public void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) { public synchronized void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) {
if (this.mod.getServer() != server) return; if (this.mod.getServer() != server) return;
this.eventListeners.forEach(l -> l.onPlayerLeave(player.getUuid())); UUID uuid = player.getUuid();
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
} }
} }

View File

@ -65,7 +65,7 @@ public class FabricMod implements ModInitializer, ServerInterface {
private Plugin pluginInstance = null; private Plugin pluginInstance = null;
private MinecraftServer serverInstance = null; private MinecraftServer serverInstance = null;
private Map<File, UUID> worldUuids; private Map<File, UUID> worldUUIDs;
private FabricEventForwarder eventForwarder; private FabricEventForwarder eventForwarder;
private LoadingCache<ServerWorld, UUID> worldUuidCache; private LoadingCache<ServerWorld, UUID> worldUuidCache;
@ -82,7 +82,7 @@ public FabricMod() {
pluginInstance = new Plugin("fabric", this); pluginInstance = new Plugin("fabric", this);
this.worldUuids = new ConcurrentHashMap<>(); this.worldUUIDs = new ConcurrentHashMap<>();
this.eventForwarder = new FabricEventForwarder(this); this.eventForwarder = new FabricEventForwarder(this);
this.worldUuidCache = CacheBuilder.newBuilder() this.worldUuidCache = CacheBuilder.newBuilder()
.weakKeys() .weakKeys()
@ -111,7 +111,7 @@ public void onInitialize() {
try { try {
pluginInstance.load(); pluginInstance.load();
Logger.global.logInfo("BlueMap loaded!"); if (pluginInstance.isLoaded()) Logger.global.logInfo("BlueMap loaded!");
} catch (IOException | ParseResourceException e) { } catch (IOException | ParseResourceException e) {
Logger.global.logError("Failed to load bluemap!", e); Logger.global.logError("Failed to load bluemap!", e);
} }
@ -145,10 +145,10 @@ public void unregisterAllListeners() {
public UUID getUUIDForWorld(File worldFolder) throws IOException { public UUID getUUIDForWorld(File worldFolder) throws IOException {
worldFolder = worldFolder.getCanonicalFile(); worldFolder = worldFolder.getCanonicalFile();
UUID uuid = worldUuids.get(worldFolder); UUID uuid = worldUUIDs.get(worldFolder);
if (uuid == null) { if (uuid == null) {
uuid = UUID.randomUUID(); uuid = UUID.randomUUID();
worldUuids.put(worldFolder, uuid); worldUUIDs.put(worldFolder, uuid);
} }
return uuid; return uuid;

View File

@ -52,6 +52,7 @@ public class FabricPlayer implements Player {
GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL); GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL);
GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE); GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR); GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR);
GAMEMODE_MAP.put(GameMode.NOT_SET, Gamemode.SURVIVAL);
} }
private UUID uuid; private UUID uuid;
@ -115,7 +116,7 @@ public Gamemode getGamemode() {
} }
/** /**
* API access, only call on server thread! * Only call on server thread!
*/ */
public void update() { public void update() {
ServerPlayerEntity player = delegate.get(); ServerPlayerEntity player = delegate.get();
@ -139,7 +140,7 @@ public void update() {
this.invisible = invis != null && invis.getDuration() > 0; this.invisible = invis != null && invis.getDuration() > 0;
this.name = Text.of(player.getName().getString()); this.name = Text.of(player.getName().getString());
this.online = !player.removed; this.online = true;
Vec3d pos = player.getPos(); Vec3d pos = player.getPos();
this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ());

View File

@ -0,0 +1,116 @@
/*
* 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.forge;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.core.logger.Logger;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
public class ForgeEventForwarder {
private ForgeMod mod;
private Collection<ServerEventListener> eventListeners;
public ForgeEventForwarder(ForgeMod mod) {
this.mod = mod;
this.eventListeners = new ArrayList<>(1);
MinecraftForge.EVENT_BUS.register(this);
}
public synchronized void addEventListener(ServerEventListener listener) {
this.eventListeners.add(listener);
}
public synchronized void removeAllListeners() {
this.eventListeners.clear();
}
@SubscribeEvent
public void onBlockBreak(BlockEvent.BreakEvent evt) {
onBlockChange(evt);
}
@SubscribeEvent
public void onBlockPlace(BlockEvent.EntityPlaceEvent evt) {
onBlockChange(evt);
}
private synchronized void onBlockChange(BlockEvent evt) {
if (!(evt.getWorld() instanceof ServerWorld)) return;
try {
UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld());
Vector3i position = new Vector3i(
evt.getPos().getX(),
evt.getPos().getY(),
evt.getPos().getZ()
);
for (ServerEventListener listener : eventListeners) listener.onBlockChange(world, position);
} catch (IOException e) {
Logger.global.noFloodError("Failed to get the UUID for a world!", e);
}
}
@SubscribeEvent
public synchronized void onWorldSave(WorldEvent.Save evt) {
if (!(evt.getWorld() instanceof ServerWorld)) return;
try {
UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld());
for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(world);
} catch (IOException e) {
Logger.global.noFloodError("Failed to get the UUID for a world!", e);
}
}
@SubscribeEvent
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
UUID uuid = evt.getPlayer().getUniqueID();
for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid);
}
@SubscribeEvent
public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) {
UUID uuid = evt.getPlayer().getUniqueID();
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
}
}

View File

@ -29,15 +29,15 @@
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import com.flowpowered.math.vector.Vector3i;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
@ -48,11 +48,15 @@
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import net.minecraft.command.CommandSource; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.server.ServerWorld; import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.TickEvent.ServerTickEvent;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
@ -60,20 +64,29 @@
@Mod(Plugin.PLUGIN_ID) @Mod(Plugin.PLUGIN_ID)
public class ForgeMod implements ServerInterface { public class ForgeMod implements ServerInterface {
private Plugin pluginInstance = null;
private MinecraftServer serverInstance = null;
private Plugin bluemap; private Map<File, UUID> worldUUIDs;
private Commands<CommandSource> commands; private ForgeEventForwarder eventForwarder;
private Map<String, UUID> worldUUIDs;
private Collection<ServerEventListener> eventListeners;
private LoadingCache<ServerWorld, UUID> worldUuidCache; private LoadingCache<ServerWorld, UUID> worldUuidCache;
private int playerUpdateIndex = 0;
private Map<UUID, Player> onlinePlayerMap;
private List<ForgePlayer> onlinePlayerList;
public ForgeMod() { public ForgeMod() {
Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)); Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME));
this.bluemap = new Plugin("forge", this); this.onlinePlayerMap = new ConcurrentHashMap<>();
this.worldUUIDs = new HashMap<>(); this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
this.eventListeners = new ArrayList<>(1);
this.pluginInstance = new Plugin("forge", this);
this.worldUUIDs = new ConcurrentHashMap<>();
this.eventForwarder = new ForgeEventForwarder(this);
this.worldUuidCache = CacheBuilder.newBuilder() this.worldUuidCache = CacheBuilder.newBuilder()
.weakKeys() .weakKeys()
.maximumSize(1000) .maximumSize(1000)
@ -83,113 +96,61 @@ public UUID load(ServerWorld key) throws Exception {
return loadUUIDForWorld(key); return loadUUIDForWorld(key);
} }
}); });
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) { public void onServerStarting(FMLServerStartingEvent event) {
this.worldUUIDs.clear(); this.worldUUIDs.clear();
for (ServerWorld world : event.getServer().getWorlds()) {
try {
registerWorld(world);
} catch (IOException e) {
Logger.global.logError("Failed to register world: " + world.getProviderName(), e);
}
try {
world.save(null, false, false);
} catch (Throwable t) {
Logger.global.logError("Failed to save world: " + world.getProviderName(), t);
}
}
//register commands //register commands
this.commands = new Commands<>(bluemap, event.getCommandDispatcher(), forgeSource -> new ForgeCommandSource(this, bluemap, forgeSource)); new Commands<>(pluginInstance, event.getCommandDispatcher(), forgeSource -> new ForgeCommandSource(this, pluginInstance, forgeSource));
new Thread(() -> { new Thread(() -> {
Logger.global.logInfo("Loading...");
try { try {
Logger.global.logInfo("Loading..."); pluginInstance.load();
bluemap.load(); if (pluginInstance.isLoaded()) Logger.global.logInfo("Loaded!");
if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!"); } catch (IOException | ParseResourceException e) {
} catch (Throwable t) { Logger.global.logError("Failed to load bluemap!", e);
Logger.global.logError("Failed to load!", t);
} }
}).start(); }).start();
} }
private void registerWorld(ServerWorld world) throws IOException {
getUUIDForWorld(world);
}
@SubscribeEvent @SubscribeEvent
public void onServerStopping(FMLServerStoppingEvent event) { public void onServerStopping(FMLServerStoppingEvent event) {
Logger.global.logInfo("Stopping..."); pluginInstance.unload();
bluemap.unload(); Logger.global.logInfo("BlueMap unloaded!");
Logger.global.logInfo("Saved and stopped!");
} }
@SubscribeEvent
public void onTick(ServerTickEvent evt) {
updateSomePlayers();
}
@Override @Override
public void registerListener(ServerEventListener listener) { public void registerListener(ServerEventListener listener) {
eventListeners.add(listener); eventForwarder.addEventListener(listener);
} }
@Override @Override
public void unregisterAllListeners() { public void unregisterAllListeners() {
eventListeners.clear(); eventForwarder.removeAllListeners();
}
@SubscribeEvent
public void onBlockBreak(BlockEvent.BreakEvent evt) {
onBlockChange(evt);
}
@SubscribeEvent
public void onBlockPlace(BlockEvent.EntityPlaceEvent evt) {
onBlockChange(evt);
}
private void onBlockChange(BlockEvent evt) {
if (!(evt.getWorld() instanceof ServerWorld)) return;
try {
UUID world = getUUIDForWorld((ServerWorld) evt.getWorld());
Vector3i position = new Vector3i(
evt.getPos().getX(),
evt.getPos().getY(),
evt.getPos().getZ()
);
for (ServerEventListener listener : eventListeners) listener.onBlockChange(world, position);
} catch (IOException ignore) {}
}
@SubscribeEvent
public void onWorldSave(WorldEvent.Save evt) {
if (!(evt.getWorld() instanceof ServerWorld)) return;
try {
UUID world = getUUIDForWorld((ServerWorld) evt.getWorld());
for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(world);
} catch (IOException ignore) {}
} }
@Override @Override
public UUID getUUIDForWorld(File worldFolder) throws IOException { public UUID getUUIDForWorld(File worldFolder) throws IOException {
synchronized (worldUUIDs) { worldFolder = worldFolder.getCanonicalFile();
String key = worldFolder.getCanonicalPath();
UUID uuid = worldUUIDs.get(worldFolder);
UUID uuid = worldUUIDs.get(key); if (uuid == null) {
if (uuid == null) { uuid = UUID.randomUUID();
throw new IOException("There is no world with this folder loaded: " + worldFolder.getPath()); worldUUIDs.put(worldFolder, uuid);
}
return uuid;
} }
return uuid;
} }
public UUID getUUIDForWorld(ServerWorld world) throws IOException { public UUID getUUIDForWorld(ServerWorld world) throws IOException {
@ -203,17 +164,15 @@ public UUID getUUIDForWorld(ServerWorld world) throws IOException {
} }
private UUID loadUUIDForWorld(ServerWorld world) throws IOException { private UUID loadUUIDForWorld(ServerWorld world) throws IOException {
synchronized (worldUUIDs) { File key = getFolderForWorld(world);
String key = getFolderForWorld(world).getPath();
UUID uuid = worldUUIDs.get(key);
UUID uuid = worldUUIDs.get(key); if (uuid == null) {
if (uuid == null) { uuid = UUID.randomUUID();
uuid = UUID.randomUUID(); worldUUIDs.put(key, uuid);
worldUUIDs.put(key, uuid);
}
return uuid;
} }
return uuid;
} }
private File getFolderForWorld(ServerWorld world) throws IOException { private File getFolderForWorld(ServerWorld world) throws IOException {
@ -231,21 +190,60 @@ private File getFolderForWorld(ServerWorld world) throws IOException {
public File getConfigFolder() { public File getConfigFolder() {
return new File("config/bluemap"); return new File("config/bluemap");
} }
public void onPlayerJoin(PlayerLoggedInEvent evt) {
PlayerEntity playerInstance = evt.getPlayer();
if (!(playerInstance instanceof ServerPlayerEntity)) return;
ForgePlayer player = new ForgePlayer(this, (ServerPlayerEntity) playerInstance);
onlinePlayerMap.put(player.getUuid(), player);
onlinePlayerList.add(player);
}
public void onPlayerLeave(PlayerLoggedOutEvent evt) {
PlayerEntity player = evt.getPlayer();
if (!(player instanceof ServerPlayerEntity)) return;
UUID playerUUID = player.getUniqueID();
onlinePlayerMap.remove(playerUUID);
synchronized (onlinePlayerList) {
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID));
}
}
public Commands<CommandSource> getCommands() { public MinecraftServer getServer() {
return commands; return this.serverInstance;
} }
@Override @Override
public Collection<Player> getOnlinePlayers() { public Collection<Player> getOnlinePlayers() {
// TODO Implement return onlinePlayerMap.values();
return Collections.emptyList();
} }
@Override @Override
public Optional<Player> getPlayer(UUID uuid) { public Optional<Player> getPlayer(UUID uuid) {
// TODO Implement return Optional.ofNullable(onlinePlayerMap.get(uuid));
return Optional.empty(); }
/**
* Only update some of the online players each tick to minimize performance impact on the server-thread.
* Only call this method on the server-thread.
*/
private void updateSomePlayers() {
int onlinePlayerCount = onlinePlayerList.size();
if (onlinePlayerCount == 0) return;
int playersToBeUpdated = onlinePlayerCount / 20; //with 20 tps, each player is updated once a second
if (playersToBeUpdated == 0) playersToBeUpdated = 1;
for (int i = 0; i < playersToBeUpdated; i++) {
playerUpdateIndex++;
if (playerUpdateIndex >= 20 && playerUpdateIndex >= onlinePlayerCount) playerUpdateIndex = 0;
if (playerUpdateIndex < onlinePlayerCount) {
onlinePlayerList.get(i).update();
}
}
} }
} }

View File

@ -0,0 +1,156 @@
/*
* 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.forge;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
import com.flowpowered.math.vector.Vector3d;
import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode;
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
import de.bluecolored.bluemap.common.plugin.text.Text;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameType;
public class ForgePlayer implements Player {
private static final UUID UNKNOWN_WORLD_UUID = UUID.randomUUID();
private static final Map<GameType, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameType.class);
static {
GAMEMODE_MAP.put(GameType.ADVENTURE, Gamemode.ADVENTURE);
GAMEMODE_MAP.put(GameType.SURVIVAL, Gamemode.SURVIVAL);
GAMEMODE_MAP.put(GameType.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameType.SPECTATOR, Gamemode.SPECTATOR);
GAMEMODE_MAP.put(GameType.NOT_SET, Gamemode.SURVIVAL);
}
private UUID uuid;
private Text name;
private UUID world;
private Vector3d position;
private boolean online;
private boolean sneaking;
private boolean invisible;
private Gamemode gamemode;
private ForgeMod mod;
private WeakReference<ServerPlayerEntity> delegate;
public ForgePlayer(ForgeMod mod, ServerPlayerEntity delegate) {
this.uuid = delegate.getUniqueID();
this.mod = mod;
this.delegate = new WeakReference<>(delegate);
update();
}
@Override
public UUID getUuid() {
return this.uuid;
}
@Override
public Text getName() {
return this.name;
}
@Override
public UUID getWorld() {
return this.world;
}
@Override
public Vector3d getPosition() {
return this.position;
}
@Override
public boolean isOnline() {
return this.online;
}
@Override
public boolean isSneaking() {
return this.sneaking;
}
@Override
public boolean isInvisible() {
return this.invisible;
}
@Override
public Gamemode getGamemode() {
return this.gamemode;
}
/**
* Only call on server thread!
*/
public void update() {
ServerPlayerEntity player = delegate.get();
if (player == null) {
MinecraftServer server = mod.getServer();
if (server != null) {
player = server.getPlayerList().getPlayerByUUID(uuid);
}
if (player == null) {
this.online = false;
return;
}
delegate = new WeakReference<>(player);
}
this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameType());
EffectInstance invis = player.getActivePotionEffect(Effects.INVISIBILITY);
this.invisible = invis != null && invis.getDuration() > 0;
this.name = Text.of(player.getName().getString());
this.online = true;
Vec3d pos = player.getPositionVec();
this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ());
this.sneaking = player.isSneaking();
try {
this.world = mod.getUUIDForWorld(player.getServerWorld());
} catch (IOException e) {
this.world = UNKNOWN_WORLD_UUID;
}
}
}