Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@
import net.digitalingot.feather.serverapi.bukkit.ui.rpc.RpcService;
import net.digitalingot.feather.serverapi.bukkit.update.UpdateNotifier;
import net.digitalingot.feather.serverapi.bukkit.waypoint.BukkitWaypointService;
import org.jetbrains.annotations.NotNull;
import org.bukkit.plugin.java.JavaPlugin;

public class FeatherBukkitPlugin extends JavaPlugin {
private PlayerTaskScheduler playerTaskScheduler;

@Override
public void onDisable() {
super.onDisable();
}

@Override
public void onEnable() {
this.playerTaskScheduler = new PlayerTaskScheduler(this);

BukkitEventService eventService = new BukkitEventService(this);
BukkitPlayerService playerService = new BukkitPlayerService(this);

Expand All @@ -37,4 +42,11 @@ public void onEnable() {

super.onEnable();
}

public @NotNull PlayerTaskScheduler getPlayerTaskScheduler() {
if (this.playerTaskScheduler == null) {
throw new IllegalStateException("Player task scheduler not initialized");
}
return this.playerTaskScheduler;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package net.digitalingot.feather.serverapi.bukkit;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Consumer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class PlayerTaskScheduler {
private static final Runnable NO_OP = () -> {};

@NotNull private final FeatherBukkitPlugin plugin;
private final boolean regionSchedulerSupported;
@Nullable private final Method isOwnedByCurrentRegionMethod;
@Nullable private final Method getSchedulerMethod;
@Nullable private final Method runMethod;
@Nullable private final Method runDelayedMethod;

public PlayerTaskScheduler(@NotNull FeatherBukkitPlugin plugin) {
this.plugin = plugin;

this.isOwnedByCurrentRegionMethod =
findMethod(Bukkit.getServer().getClass(), "isOwnedByCurrentRegion", Entity.class);
this.getSchedulerMethod = findMethod(Player.class, "getScheduler");

Class<?> schedulerType =
this.getSchedulerMethod != null ? this.getSchedulerMethod.getReturnType() : null;

this.runMethod = findMethod(schedulerType, "run", 3);
this.runDelayedMethod = findMethod(schedulerType, "runDelayed", 4);
this.regionSchedulerSupported =
this.getSchedulerMethod != null && this.runMethod != null && this.runDelayedMethod != null;
}

public void execute(@NotNull Player player, @NotNull Runnable task) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(task, "task");

if (shouldRunInline(player)) {
task.run();
return;
}

if (!player.isOnline()) {
return;
}

if (this.regionSchedulerSupported) {
invoke(
this.runMethod,
invoke(this.getSchedulerMethod, player),
this.plugin,
consumer(player, task),
NO_OP);
return;
}

Bukkit.getScheduler().runTask(this.plugin, () -> runIfOnline(player, task));
}

public void executeLater(@NotNull Player player, long delayTicks, @NotNull Runnable task) {
Objects.requireNonNull(player, "player");
Objects.requireNonNull(task, "task");

if (delayTicks <= 0L) {
execute(player, task);
return;
}

if (!player.isOnline()) {
return;
}

if (this.regionSchedulerSupported) {
invoke(
this.runDelayedMethod,
invoke(this.getSchedulerMethod, player),
this.plugin,
consumer(player, task),
NO_OP,
delayTicks);
return;
}

Bukkit.getScheduler().runTaskLater(this.plugin, () -> runIfOnline(player, task), delayTicks);
}

private boolean shouldRunInline(@NotNull Player player) {
if (!player.isOnline()) {
return false;
}

if (this.regionSchedulerSupported) {
return this.isOwnedByCurrentRegionMethod != null
&& Boolean.TRUE.equals(invoke(this.isOwnedByCurrentRegionMethod, Bukkit.getServer(), player));
}

return Bukkit.isPrimaryThread();
}

private static void runIfOnline(@NotNull Player player, @NotNull Runnable task) {
if (player.isOnline()) {
task.run();
}
}

private static Consumer<Object> consumer(@NotNull Player player, @NotNull Runnable task) {
return ignored -> runIfOnline(player, task);
}

@Nullable
private static Method findMethod(@Nullable Class<?> type, @NotNull String name, int parameterCount) {
if (type == null) {
return null;
}

for (Method method : type.getMethods()) {
if (method.getName().equals(name) && method.getParameterTypes().length == parameterCount) {
return method;
}
}

return null;
}

@Nullable
private static Method findMethod(
@Nullable Class<?> type, @NotNull String name, @NotNull Class<?>... parameterTypes) {
if (type == null) {
return null;
}

try {
return type.getMethod(name, parameterTypes);
} catch (NoSuchMethodException ignored) {
return null;
}
}

private static Object invoke(@Nullable Method method, @Nullable Object instance, Object... args) {
if (method == null) {
throw new IllegalStateException("Required scheduler method is not available");
}

try {
return method.invoke(instance, args);
} catch (IllegalAccessException | InvocationTargetException exception) {
throw new IllegalStateException(
"Failed to invoke scheduler method '" + method.getName() + "'", exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package net.digitalingot.feather.serverapi.bukkit.messaging;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import net.digitalingot.feather.serverapi.api.model.FeatherMod;
import net.digitalingot.feather.serverapi.api.model.Platform;
Expand Down Expand Up @@ -135,10 +135,8 @@ public void sendMessage(Collection<FeatherPlayer> recipients, Message<?> message
List<byte[]> fragments = MessageFragmenter.CLIENT_BOUND.fragment(message);

for (FeatherPlayer recipient : recipients) {
for (byte[] data : fragments) {
sendPluginMessage(
((BukkitFeatherPlayer) recipient).getPlayer(), CHANNEL_FRAGMENTED, data);
}
sendPluginMessages(
((BukkitFeatherPlayer) recipient).getPlayer(), CHANNEL_FRAGMENTED, fragments);
}
} else {
for (FeatherPlayer recipient : recipients) {
Expand All @@ -151,21 +149,32 @@ public void sendMessage(Player player, Message<?> message) {
byte[] encoded = MessageEncoder.CLIENT_BOUND.encode(message);

if (encoded.length > Messenger.MAX_MESSAGE_SIZE) {
for (byte[] data : MessageFragmenter.CLIENT_BOUND.fragment(message)) {
sendPluginMessage(player, CHANNEL_FRAGMENTED, data);
}
sendPluginMessages(player, CHANNEL_FRAGMENTED, MessageFragmenter.CLIENT_BOUND.fragment(message));
} else {
sendPluginMessage(player, CHANNEL, encoded);
}
}

private void sendPluginMessage(@NotNull Player player, @NotNull String channel, byte[] data) {
player.sendPluginMessage(this.plugin, channel, data);
sendPluginMessages(player, channel, java.util.Collections.singletonList(data));
}

private void sendPluginMessages(
@NotNull Player player, @NotNull String channel, @NotNull List<byte[]> dataMessages) {
this.plugin
.getPlayerTaskScheduler()
.execute(
player,
() -> {
for (byte[] data : dataMessages) {
player.sendPluginMessage(this.plugin, channel, data);
}
});
}

private static class Handshaking implements Listener {
private final BukkitMessagingService messagingService;
private final Map<UUID, HandshakeState> handshakes = new HashMap<>();
private final Map<UUID, HandshakeState> handshakes = new ConcurrentHashMap<>();
private final UpdateNotifier updateNotifier;

public Handshaking(BukkitMessagingService messagingService, UpdateNotifier updateNotifier) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package net.digitalingot.feather.serverapi.bukkit.player;

import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import net.digitalingot.feather.serverapi.api.model.FeatherMod;
import net.digitalingot.feather.serverapi.api.player.FeatherPlayer;
Expand All @@ -24,7 +25,7 @@ public class BukkitFeatherPlayer implements FeatherPlayer {
@NotNull private final Player player;
@NotNull private final BukkitMessagingService messagingService;
@NotNull private final PlayerMessageHandler messageHandler;
private final Set<FeatherMod> blockedMods = Sets.newHashSet();
private final Set<FeatherMod> blockedMods = ConcurrentHashMap.newKeySet();

public BukkitFeatherPlayer(
@NotNull Player player,
Expand Down Expand Up @@ -63,7 +64,8 @@ public void unblockMods(@NotNull Collection<@NotNull FeatherMod> mods) {

@Override
public CompletableFuture<@NotNull Collection<@NotNull FeatherMod>> getBlockedMods() {
return CompletableFuture.completedFuture(Collections.unmodifiableCollection(this.blockedMods));
return CompletableFuture.completedFuture(
Collections.unmodifiableSet(new HashSet<>(this.blockedMods)));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.digitalingot.feather.serverapi.api.player.FeatherPlayer;
import net.digitalingot.feather.serverapi.api.player.PlayerService;
import net.digitalingot.feather.serverapi.bukkit.FeatherBukkitPlugin;
Expand All @@ -19,7 +19,7 @@

public class BukkitPlayerService implements PlayerService, Listener {
private final PluginManager pluginManager;
private final Map<UUID, BukkitFeatherPlayer> players = new HashMap<>();
private final Map<UUID, BukkitFeatherPlayer> players = new ConcurrentHashMap<>();

public BukkitPlayerService(FeatherBukkitPlugin plugin) {
this.pluginManager = plugin.getServer().getPluginManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import net.digitalingot.feather.serverapi.api.FeatherAPI;
import net.digitalingot.feather.serverapi.api.meta.ServerListBackground;
Expand Down Expand Up @@ -47,7 +48,7 @@ class PlayerMessageHandler implements ServerMessageHandler {
})
.build();

private int requestId = 0;
private final AtomicInteger requestId = new AtomicInteger();

public PlayerMessageHandler(BukkitFeatherPlayer player, RpcService rpcService) {
this.player = player;
Expand Down Expand Up @@ -135,7 +136,7 @@ public void handle(C2SRequestServerBackground serverBackground) {
}

public @NotNull CompletableFuture<@NotNull Collection<@NotNull FeatherMod>> requestEnabledMods() {
int id = this.requestId++;
int id = this.requestId.getAndIncrement();
CompletableFuture<@NotNull Collection<@NotNull FeatherMod>> future = new CompletableFuture<>();
//noinspection UnstableApiUsage
this.pendingModsRequests.put(id, future);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.digitalingot.feather.serverapi.bukkit.ui;

import com.google.common.collect.Maps;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.digitalingot.feather.serverapi.api.player.FeatherPlayer;
import net.digitalingot.feather.serverapi.api.ui.UIPage;
import net.digitalingot.feather.serverapi.api.ui.UIService;
Expand All @@ -23,7 +23,7 @@ public class BukkitUIService implements UIService {
@NotNull private final BukkitMessagingService messagingService;
@NotNull private final RpcService rpcService;

private final Map<String, BukkitUIPage> registeredPages = Maps.newHashMap();
private final Map<String, BukkitUIPage> registeredPages = new ConcurrentHashMap<>();

public BukkitUIService(
@NotNull BukkitMessagingService messagingService, @NotNull RpcService rpcService) {
Expand All @@ -42,11 +42,10 @@ private static Plugin validatePlugin(Object plugin) {
public UIPage registerPage(@NotNull Object pluginObject, @NotNull String url) {
Plugin plugin = validatePlugin(pluginObject);
String pluginName = plugin.getName();
if (this.registeredPages.containsKey(pluginName)) {
BukkitUIPage page = new BukkitUIPage(plugin, url);
if (this.registeredPages.putIfAbsent(pluginName, page) != null) {
throw new IllegalArgumentException("Page already exists");
}
BukkitUIPage page = new BukkitUIPage(plugin, url);
this.registeredPages.put(pluginName, page);
return page;
}

Expand Down
Loading