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)
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)
@ -123,7 +123,7 @@ public void onBlockChange(BlockFertilizeEvent evt) {
private synchronized void onBlockChange(Location loc) {
UUID world = loc.getWorld().getUID();
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)
@ -131,23 +131,23 @@ public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) {
Chunk chunk = evt.getChunk();
UUID world = chunk.getWorld().getUID();
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)
public void onPlayerJoin(PlayerJoinEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId()));
public synchronized void onPlayerJoin(PlayerJoinEvent evt) {
for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(PlayerQuitEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId()));
public synchronized void onPlayerLeave(PlayerQuitEvent evt) {
for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId());
}
@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());
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);
}
public void addEventListener(ServerEventListener listener) {
public synchronized void addEventListener(ServerEventListener listener) {
this.eventListeners.add(listener);
}
public void removeAllListeners() {
public synchronized void removeAllListeners() {
this.eventListeners.clear();
}
@ -94,45 +94,47 @@ public ActionResult onBlockAttack(PlayerEntity player, World world, Hand hand, B
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());
try {
UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onBlockChange(uuid, position));
for (ServerEventListener listener : eventListeners) listener.onBlockChange(uuid, position);
} 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 {
UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onWorldSaveToDisk(uuid));
for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid);
} 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 {
UUID uuid = mod.getUUIDForWorld(world);
eventListeners.forEach(e -> e.onChunkFinishedGeneration(uuid, chunkPos));
for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos);
} 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;
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;
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 MinecraftServer serverInstance = null;
private Map<File, UUID> worldUuids;
private Map<File, UUID> worldUUIDs;
private FabricEventForwarder eventForwarder;
private LoadingCache<ServerWorld, UUID> worldUuidCache;
@ -82,7 +82,7 @@ public FabricMod() {
pluginInstance = new Plugin("fabric", this);
this.worldUuids = new ConcurrentHashMap<>();
this.worldUUIDs = new ConcurrentHashMap<>();
this.eventForwarder = new FabricEventForwarder(this);
this.worldUuidCache = CacheBuilder.newBuilder()
.weakKeys()
@ -111,7 +111,7 @@ public void onInitialize() {
try {
pluginInstance.load();
Logger.global.logInfo("BlueMap loaded!");
if (pluginInstance.isLoaded()) Logger.global.logInfo("BlueMap loaded!");
} catch (IOException | ParseResourceException e) {
Logger.global.logError("Failed to load bluemap!", e);
}
@ -145,10 +145,10 @@ public void unregisterAllListeners() {
public UUID getUUIDForWorld(File worldFolder) throws IOException {
worldFolder = worldFolder.getCanonicalFile();
UUID uuid = worldUuids.get(worldFolder);
UUID uuid = worldUUIDs.get(worldFolder);
if (uuid == null) {
uuid = UUID.randomUUID();
worldUuids.put(worldFolder, uuid);
worldUUIDs.put(worldFolder, 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.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR);
GAMEMODE_MAP.put(GameMode.NOT_SET, Gamemode.SURVIVAL);
}
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() {
ServerPlayerEntity player = delegate.get();
@ -139,7 +140,7 @@ public void update() {
this.invisible = invis != null && invis.getDuration() > 0;
this.name = Text.of(player.getName().getString());
this.online = !player.removed;
this.online = true;
Vec3d pos = player.getPos();
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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import org.apache.logging.log4j.LogManager;
import com.flowpowered.math.vector.Vector3i;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
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.ServerInterface;
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.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.event.TickEvent.ServerTickEvent;
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.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
@ -61,19 +65,28 @@
@Mod(Plugin.PLUGIN_ID)
public class ForgeMod implements ServerInterface {
private Plugin bluemap;
private Commands<CommandSource> commands;
private Map<String, UUID> worldUUIDs;
private Collection<ServerEventListener> eventListeners;
private Plugin pluginInstance = null;
private MinecraftServer serverInstance = null;
private Map<File, UUID> worldUUIDs;
private ForgeEventForwarder eventForwarder;
private LoadingCache<ServerWorld, UUID> worldUuidCache;
private int playerUpdateIndex = 0;
private Map<UUID, Player> onlinePlayerMap;
private List<ForgePlayer> onlinePlayerList;
public ForgeMod() {
Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME));
this.bluemap = new Plugin("forge", this);
this.worldUUIDs = new HashMap<>();
this.eventListeners = new ArrayList<>(1);
this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
this.pluginInstance = new Plugin("forge", this);
this.worldUUIDs = new ConcurrentHashMap<>();
this.eventForwarder = new ForgeEventForwarder(this);
this.worldUuidCache = CacheBuilder.newBuilder()
.weakKeys()
.maximumSize(1000)
@ -91,106 +104,54 @@ public UUID load(ServerWorld key) throws Exception {
public void onServerStarting(FMLServerStartingEvent event) {
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
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(() -> {
try {
Logger.global.logInfo("Loading...");
bluemap.load();
if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!");
} catch (Throwable t) {
Logger.global.logError("Failed to load!", t);
try {
pluginInstance.load();
if (pluginInstance.isLoaded()) Logger.global.logInfo("Loaded!");
} catch (IOException | ParseResourceException e) {
Logger.global.logError("Failed to load bluemap!", e);
}
}).start();
}
private void registerWorld(ServerWorld world) throws IOException {
getUUIDForWorld(world);
@SubscribeEvent
public void onServerStopping(FMLServerStoppingEvent event) {
pluginInstance.unload();
Logger.global.logInfo("BlueMap unloaded!");
}
@SubscribeEvent
public void onServerStopping(FMLServerStoppingEvent event) {
Logger.global.logInfo("Stopping...");
bluemap.unload();
Logger.global.logInfo("Saved and stopped!");
public void onTick(ServerTickEvent evt) {
updateSomePlayers();
}
@Override
public void registerListener(ServerEventListener listener) {
eventListeners.add(listener);
eventForwarder.addEventListener(listener);
}
@Override
public void unregisterAllListeners() {
eventListeners.clear();
}
@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) {}
eventForwarder.removeAllListeners();
}
@Override
public UUID getUUIDForWorld(File worldFolder) throws IOException {
synchronized (worldUUIDs) {
String key = worldFolder.getCanonicalPath();
worldFolder = worldFolder.getCanonicalFile();
UUID uuid = worldUUIDs.get(key);
UUID uuid = worldUUIDs.get(worldFolder);
if (uuid == null) {
throw new IOException("There is no world with this folder loaded: " + worldFolder.getPath());
uuid = UUID.randomUUID();
worldUUIDs.put(worldFolder, uuid);
}
return uuid;
}
}
public UUID getUUIDForWorld(ServerWorld world) throws IOException {
try {
@ -203,8 +164,7 @@ public UUID getUUIDForWorld(ServerWorld world) throws IOException {
}
private UUID loadUUIDForWorld(ServerWorld world) throws IOException {
synchronized (worldUUIDs) {
String key = getFolderForWorld(world).getPath();
File key = getFolderForWorld(world);
UUID uuid = worldUUIDs.get(key);
if (uuid == null) {
@ -214,7 +174,6 @@ private UUID loadUUIDForWorld(ServerWorld world) throws IOException {
return uuid;
}
}
private File getFolderForWorld(ServerWorld world) throws IOException {
File worldFolder = world.getSaveHandler().getWorldDirectory();
@ -232,20 +191,59 @@ public File getConfigFolder() {
return new File("config/bluemap");
}
public Commands<CommandSource> getCommands() {
return commands;
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 MinecraftServer getServer() {
return this.serverInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {
// TODO Implement
return Collections.emptyList();
return onlinePlayerMap.values();
}
@Override
public Optional<Player> getPlayer(UUID uuid) {
// TODO Implement
return Optional.empty();
return Optional.ofNullable(onlinePlayerMap.get(uuid));
}
/**
* 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;
}
}
}