Implement live-api for bukkit

This commit is contained in:
Blue (Lukas Rieger) 2020-08-07 17:29:28 +02:00
parent c4f0256ca0
commit 7982765586
4 changed files with 225 additions and 7 deletions

View File

@ -0,0 +1,138 @@
/*
* 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.bukkit;
import java.lang.ref.WeakReference;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.potion.PotionEffectType;
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;
public class BukkitPlayer implements Player {
private static final Map<GameMode, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameMode.class);
static {
GAMEMODE_MAP.put(GameMode.ADVENTURE, Gamemode.ADVENTURE);
GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL);
GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR);
}
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 WeakReference<org.bukkit.entity.Player> delegate;
public BukkitPlayer(org.bukkit.entity.Player delegate) {
this.uuid = delegate.getUniqueId();
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;
}
/**
* API access, only call on server thread!
*/
public void update() {
org.bukkit.entity.Player player = delegate.get();
if (player == null) {
player = Bukkit.getPlayer(uuid);
if (player == null) {
this.online = false;
return;
}
delegate = new WeakReference<>(player);
}
this.gamemode = GAMEMODE_MAP.get(player.getGameMode());
this.invisible = player.hasPotionEffect(PotionEffectType.INVISIBILITY);
this.name = Text.of(player.getName());
this.online = player.isOnline();
Location location = player.getLocation();
this.position = new Vector3d(location.getX(), location.getY(), location.getZ());
this.sneaking = player.isSneaking();
this.world = player.getWorld().getUID();
}
}

View File

@ -27,11 +27,15 @@
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -40,6 +44,11 @@
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.CommandMap; import org.bukkit.command.CommandMap;
import org.bukkit.command.defaults.BukkitCommand; import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.Plugin;
@ -48,7 +57,7 @@
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;
public class BukkitPlugin extends JavaPlugin implements ServerInterface { public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener {
private static BukkitPlugin instance; private static BukkitPlugin instance;
@ -56,8 +65,15 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface {
private EventForwarder eventForwarder; private EventForwarder eventForwarder;
private BukkitCommands commands; private BukkitCommands commands;
private int playerUpdateIndex = 0;
private Map<UUID, Player> onlinePlayerMap;
private List<BukkitPlayer> onlinePlayerList;
public BukkitPlugin() { public BukkitPlugin() {
Logger.global = new JavaLogger(getLogger()); Logger.global = new JavaLogger(getLogger());
this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
this.eventForwarder = new EventForwarder(); this.eventForwarder = new EventForwarder();
this.bluemap = new Plugin("bukkit", this); this.bluemap = new Plugin("bukkit", this);
@ -77,6 +93,7 @@ public void onEnable() {
} }
//register events //register events
getServer().getPluginManager().registerEvents(this, this);
getServer().getPluginManager().registerEvents(eventForwarder, this); getServer().getPluginManager().registerEvents(eventForwarder, this);
//register commands //register commands
@ -96,6 +113,9 @@ public void onEnable() {
//tab completions //tab completions
getServer().getPluginManager().registerEvents(commands, this); getServer().getPluginManager().registerEvents(commands, this);
//start updating players
getServer().getScheduler().runTaskTimer(this, this::updateSomePlayers, 1, 1);
//load bluemap //load bluemap
getServer().getScheduler().runTaskAsynchronously(this, () -> { getServer().getScheduler().runTaskAsynchronously(this, () -> {
try { try {
@ -111,6 +131,7 @@ public void onEnable() {
@Override @Override
public void onDisable() { public void onDisable() {
Logger.global.logInfo("Stopping..."); Logger.global.logInfo("Stopping...");
getServer().getScheduler().cancelTasks(this);
bluemap.unload(); bluemap.unload();
Logger.global.logInfo("Saved and stopped!"); Logger.global.logInfo("Saved and stopped!");
} }
@ -182,17 +203,52 @@ public Plugin getBlueMap() {
public static BukkitPlugin getInstance() { public static BukkitPlugin getInstance() {
return instance; return instance;
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent evt) {
BukkitPlayer player = new BukkitPlayer(evt.getPlayer());
onlinePlayerMap.put(evt.getPlayer().getUniqueId(), player);
onlinePlayerList.add(player);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(PlayerQuitEvent evt) {
UUID playerUUID = evt.getPlayer().getUniqueId();
onlinePlayerMap.remove(playerUUID);
synchronized (onlinePlayerList) {
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID));
}
}
@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

@ -42,6 +42,9 @@
import org.bukkit.event.block.BlockGrowEvent; import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkPopulateEvent; import org.bukkit.event.world.ChunkPopulateEvent;
import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldSaveEvent;
@ -49,6 +52,7 @@
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.common.plugin.text.Text;
public class EventForwarder implements Listener { public class EventForwarder implements Listener {
@ -129,5 +133,21 @@ public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) {
Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ());
listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos)); listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos));
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId()));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(PlayerQuitEvent evt) {
listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId()));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerChat(AsyncPlayerChatEvent evt) {
String message = String.format(evt.getFormat(), evt.getPlayer().getDisplayName(), evt.getMessage());
listeners.forEach(l -> l.onChatMessage(Text.of(message)));
}
} }

View File

@ -29,6 +29,7 @@
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -91,7 +92,7 @@ public SpongePlugin(org.slf4j.Logger logger) {
Logger.global = new Slf4jLogger(logger); Logger.global = new Slf4jLogger(logger);
this.onlinePlayerMap = new ConcurrentHashMap<>(); this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = new ArrayList<>(); this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
this.bluemap = new Plugin("sponge", this); this.bluemap = new Plugin("sponge", this);
this.commands = new SpongeCommands(bluemap); this.commands = new SpongeCommands(bluemap);
@ -131,6 +132,7 @@ public void onServerStart(GameStartingServerEvent evt) {
@Listener @Listener
public void onServerStop(GameStoppingEvent evt) { public void onServerStop(GameStoppingEvent evt) {
Logger.global.logInfo("Stopping..."); Logger.global.logInfo("Stopping...");
Sponge.getScheduler().getScheduledTasks(this).forEach(t -> t.cancel());
bluemap.unload(); bluemap.unload();
Logger.global.logInfo("Saved and stopped!"); Logger.global.logInfo("Saved and stopped!");
} }
@ -160,7 +162,9 @@ public void onPlayerJoin(ClientConnectionEvent.Join evt) {
public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) { public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) {
UUID playerUUID = evt.getTargetEntity().getUniqueId(); UUID playerUUID = evt.getTargetEntity().getUniqueId();
onlinePlayerMap.remove(playerUUID); onlinePlayerMap.remove(playerUUID);
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); synchronized (onlinePlayerList) {
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID));
}
} }
@Override @Override