Attempts at mocking the server, fixes
This commit is contained in:
parent
d784d8b1e2
commit
666f05ff12
10 changed files with 111 additions and 98 deletions
40
pom.xml
40
pom.xml
|
@ -21,21 +21,8 @@
|
||||||
<testSourceDirectory>target/generated-test-sources/delombok</testSourceDirectory> -->
|
<testSourceDirectory>target/generated-test-sources/delombok</testSourceDirectory> -->
|
||||||
<sourceDirectory>src/main/java</sourceDirectory>
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
|
||||||
<directory>src</directory>
|
|
||||||
<excludes>
|
|
||||||
<exclude>**/*.java</exclude>
|
|
||||||
</excludes>
|
|
||||||
</resource>
|
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
<includes>
|
|
||||||
<include>*.properties</include>
|
|
||||||
<include>*.yml</include>
|
|
||||||
<include>*.csv</include>
|
|
||||||
<include>*.txt</include>
|
|
||||||
</includes>
|
|
||||||
<filtering>true</filtering>
|
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<finalName>Chroma-Discord</finalName>
|
<finalName>Chroma-Discord</finalName>
|
||||||
|
@ -68,28 +55,6 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>3.0.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy</id>
|
|
||||||
<phase>compile</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-resources</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>target</outputDirectory>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>resources</directory>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<!-- <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId>
|
<!-- <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId>
|
||||||
<version>1.16.16.0</version> <executions> <execution> <id>delombok</id> <phase>generate-sources</phase>
|
<version>1.16.16.0</version> <executions> <execution> <id>delombok</id> <phase>generate-sources</phase>
|
||||||
<goals> <goal>delombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
<goals> <goal>delombok</goal> </goals> <configuration> <addOutputDirectory>false</addOutputDirectory>
|
||||||
|
@ -241,6 +206,11 @@
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-inline</artifactId>
|
||||||
|
<version>3.5.10</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import buttondevteam.discordplugin.exceptions.ExceptionListenerModule;
|
||||||
import buttondevteam.discordplugin.fun.FunModule;
|
import buttondevteam.discordplugin.fun.FunModule;
|
||||||
import buttondevteam.discordplugin.listeners.CommonListeners;
|
import buttondevteam.discordplugin.listeners.CommonListeners;
|
||||||
import buttondevteam.discordplugin.listeners.MCListener;
|
import buttondevteam.discordplugin.listeners.MCListener;
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatPrivate;
|
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||||
import buttondevteam.discordplugin.mccommands.DiscordMCCommand;
|
import buttondevteam.discordplugin.mccommands.DiscordMCCommand;
|
||||||
|
@ -260,7 +259,6 @@ public class DiscordPlugin extends ButtonPlugin {
|
||||||
public void pluginDisable() {
|
public void pluginDisable() {
|
||||||
Timings timings = new Timings();
|
Timings timings = new Timings();
|
||||||
timings.printElapsed("Actual disable start (logout)");
|
timings.printElapsed("Actual disable start (logout)");
|
||||||
MCChatPrivate.logoutAll();
|
|
||||||
timings.printElapsed("Config setup");
|
timings.printElapsed("Config setup");
|
||||||
getConfig().set("serverup", false);
|
getConfig().set("serverup", false);
|
||||||
if (ChromaBot.getInstance() == null) return; //Failed to load
|
if (ChromaBot.getInstance() == null) return; //Failed to load
|
||||||
|
|
|
@ -3,24 +3,17 @@ package buttondevteam.discordplugin.broadcaster;
|
||||||
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PlayerListWatcher {
|
public class PlayerListWatcher {
|
||||||
|
@ -28,45 +21,6 @@ public class PlayerListWatcher {
|
||||||
private static Object mock;
|
private static Object mock;
|
||||||
private static MethodHandle fHandle; //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16
|
private static MethodHandle fHandle; //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16
|
||||||
|
|
||||||
/*public PlayerListWatcher(DedicatedServer minecraftserver) {
|
|
||||||
super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendAll(Packet<?> packet) {
|
|
||||||
plist.sendAll(packet);
|
|
||||||
try { // Some messages get sent by directly constructing a packet
|
|
||||||
if (packet instanceof PacketPlayOutChat) {
|
|
||||||
Field msgf = PacketPlayOutChat.class.getDeclaredField("a");
|
|
||||||
msgf.setAccessible(true);
|
|
||||||
MCChatUtils.forAllMCChat(MCChatUtils.send(((IChatBaseComponent) msgf.get(packet)).toPlainText()));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overridden method
|
|
||||||
plist.getServer().sendMessage(ichatbasecomponent);
|
|
||||||
ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT;
|
|
||||||
|
|
||||||
// CraftBukkit start - we run this through our processor first so we can get web links etc
|
|
||||||
this.sendAll(new PacketPlayOutChat(CraftChatMessage.fixComponent(ichatbasecomponent), chatmessagetype));
|
|
||||||
// CraftBukkit end
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(IChatBaseComponent ichatbasecomponent) { // Needed so it calls the overriden method
|
|
||||||
this.sendMessage(ichatbasecomponent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overridden method
|
|
||||||
for (IChatBaseComponent component : iChatBaseComponents) {
|
|
||||||
sendMessage(component, true);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
static boolean hookUpDown(boolean up) throws Exception {
|
static boolean hookUpDown(boolean up) throws Exception {
|
||||||
val csc = Bukkit.getServer().getClass();
|
val csc = Bukkit.getServer().getClass();
|
||||||
Field conf = csc.getDeclaredField("console");
|
Field conf = csc.getDeclaredField("console");
|
||||||
|
|
|
@ -186,6 +186,7 @@ public class MCChatListener implements Listener {
|
||||||
* @param wait Wait 5 seconds for the threads to stop
|
* @param wait Wait 5 seconds for the threads to stop
|
||||||
*/
|
*/
|
||||||
public static void stop(boolean wait) {
|
public static void stop(boolean wait) {
|
||||||
|
MCChatPrivate.logoutAll();
|
||||||
if (sendthread != null) sendthread.interrupt();
|
if (sendthread != null) sendthread.interrupt();
|
||||||
if (recthread != null) recthread.interrupt();
|
if (recthread != null) recthread.interrupt();
|
||||||
try {
|
try {
|
||||||
|
@ -203,7 +204,6 @@ public class MCChatListener implements Listener {
|
||||||
MCChatPrivate.lastmsgPerUser.clear();
|
MCChatPrivate.lastmsgPerUser.clear();
|
||||||
MCChatCustom.lastmsgCustom.clear();
|
MCChatCustom.lastmsgCustom.clear();
|
||||||
MCChatUtils.lastmsgfromd.clear();
|
MCChatUtils.lastmsgfromd.clear();
|
||||||
MCChatUtils.ConnectedSenders.clear();
|
|
||||||
MCChatUtils.UnconnectedSenders.clear();
|
MCChatUtils.UnconnectedSenders.clear();
|
||||||
recthread = sendthread = null;
|
recthread = sendthread = null;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
|
@ -28,11 +28,13 @@ public class MCChatPrivate {
|
||||||
if (start) {
|
if (start) {
|
||||||
val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID(), op.getName(), mcm);
|
val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID(), op.getName(), mcm);
|
||||||
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
|
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
|
||||||
if (p == null)// Player is offline - If the player is online, that takes precedence
|
MCChatUtils.LoggedInPlayers.put(mcp.getUUID(), sender);
|
||||||
|
if (p == null) // Player is offline - If the player is online, that takes precedence
|
||||||
MCChatUtils.callLoginEvents(sender);
|
MCChatUtils.callLoginEvents(sender);
|
||||||
} else {
|
} else {
|
||||||
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user);
|
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user);
|
||||||
assert sender != null;
|
assert sender != null;
|
||||||
|
MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId());
|
||||||
if (p == null // Player is offline - If the player is online, that takes precedence
|
if (p == null // Player is offline - If the player is online, that takes precedence
|
||||||
&& sender.isLoggedIn()) //Don't call the quit event if login failed
|
&& sender.isLoggedIn()) //Don't call the quit event if login failed
|
||||||
MCChatUtils.callLogoutEvent(sender, true);
|
MCChatUtils.callLogoutEvent(sender, true);
|
||||||
|
|
|
@ -28,10 +28,7 @@ import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -49,8 +46,8 @@ public class MCChatUtils {
|
||||||
* May contain P<DiscordID> as key for public chat
|
* May contain P<DiscordID> as key for public chat
|
||||||
*/
|
*/
|
||||||
public static final HashMap<String, HashMap<Snowflake, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
|
public static final HashMap<String, HashMap<Snowflake, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
|
||||||
static @Nullable
|
public static final HashMap<UUID, DiscordConnectedPlayer> LoggedInPlayers = new HashMap<>();
|
||||||
LastMsgData lastmsgdata;
|
static @Nullable LastMsgData lastmsgdata;
|
||||||
static LongObjectHashMap<Message> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
|
static LongObjectHashMap<Message> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
|
||||||
private static MinecraftChatModule module;
|
private static MinecraftChatModule module;
|
||||||
private static final HashMap<Class<? extends Event>, HashSet<String>> staticExcludedPlugins = new HashMap<>();
|
private static final HashMap<Class<? extends Event>, HashSet<String>> staticExcludedPlugins = new HashMap<>();
|
||||||
|
@ -283,7 +280,6 @@ public class MCChatUtils {
|
||||||
* @param only Flips the operation and <b>includes</b> the listed plugins
|
* @param only Flips the operation and <b>includes</b> the listed plugins
|
||||||
* @param plugins The plugins to exclude. Not case sensitive.
|
* @param plugins The plugins to exclude. Not case sensitive.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public static void callEventExcluding(Event event, boolean only, String... plugins) { // Copied from Spigot-API and modified a bit
|
public static void callEventExcluding(Event event, boolean only, String... plugins) { // Copied from Spigot-API and modified a bit
|
||||||
if (event.isAsynchronous()) {
|
if (event.isAsynchronous()) {
|
||||||
if (Thread.holdsLock(Bukkit.getPluginManager())) {
|
if (Thread.holdsLock(Bukkit.getPluginManager())) {
|
||||||
|
|
|
@ -40,9 +40,9 @@ class MCListener implements Listener {
|
||||||
return;
|
return;
|
||||||
if (e.getPlayer() instanceof DiscordConnectedPlayer)
|
if (e.getPlayer() instanceof DiscordConnectedPlayer)
|
||||||
return;
|
return;
|
||||||
MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
|
var dcp = MCChatUtils.LoggedInPlayers.get(e.getPlayer().getUniqueId());
|
||||||
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
if (dcp != null)
|
||||||
.ifPresent(dcp -> MCChatUtils.callLogoutEvent(dcp, false));
|
MCChatUtils.callLogoutEvent(dcp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
@ -75,9 +75,7 @@ class MCListener implements Listener {
|
||||||
MCChatUtils.OnlineSenders.entrySet()
|
MCChatUtils.OnlineSenders.entrySet()
|
||||||
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
|
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin,
|
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin,
|
||||||
() -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
|
() -> Optional.ofNullable(MCChatUtils.LoggedInPlayers.get(e.getPlayer().getUniqueId())).ifPresent(MCChatUtils::callLoginEvents));
|
||||||
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
|
|
||||||
.ifPresent(MCChatUtils::callLoginEvents));
|
|
||||||
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
|
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
|
||||||
ChromaBot.getInstance()::updatePlayerList, 5);
|
ChromaBot.getInstance()::updatePlayerList, 5);
|
||||||
final String message = e.getQuitMessage();
|
final String message = e.getQuitMessage();
|
||||||
|
|
|
@ -5,6 +5,7 @@ import buttondevteam.core.component.channel.Channel;
|
||||||
import buttondevteam.discordplugin.DPUtils;
|
import buttondevteam.discordplugin.DPUtils;
|
||||||
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
import buttondevteam.discordplugin.DiscordConnectedPlayer;
|
||||||
import buttondevteam.discordplugin.DiscordPlugin;
|
import buttondevteam.discordplugin.DiscordPlugin;
|
||||||
|
import buttondevteam.discordplugin.playerfaker.ServerWatcher;
|
||||||
import buttondevteam.discordplugin.playerfaker.perm.LPInjector;
|
import buttondevteam.discordplugin.playerfaker.perm.LPInjector;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
|
@ -29,6 +30,7 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public class MinecraftChatModule extends Component<DiscordPlugin> {
|
public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
private @Getter MCChatListener listener;
|
private @Getter MCChatListener listener;
|
||||||
|
private ServerWatcher serverWatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
|
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
|
||||||
|
@ -113,6 +115,11 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
return getConfig().getData("enableVanillaCommands", true);
|
return getConfig().getData("enableVanillaCommands", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether players logged on from Discord should be recognised by other plugins. Some plugins might break if it's turned off.
|
||||||
|
*/
|
||||||
|
public final ConfigData<Boolean> addFakePlayersToBukkit = getConfig().getData("addFakePlayersToBukkit", true);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enable() {
|
protected void enable() {
|
||||||
if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono()))
|
if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono()))
|
||||||
|
@ -156,10 +163,22 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
||||||
log("No LuckPerms, not injecting");
|
log("No LuckPerms, not injecting");
|
||||||
//e.printStackTrace();
|
//e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try { //TODO: Config ^^
|
||||||
|
serverWatcher = new ServerWatcher();
|
||||||
|
serverWatcher.enableDisable(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to hack the server (object)!", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void disable() {
|
protected void disable() {
|
||||||
|
try {
|
||||||
|
serverWatcher.enableDisable(false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to restore the server object!", e);
|
||||||
|
}
|
||||||
val chcons = MCChatCustom.getCustomChats();
|
val chcons = MCChatCustom.getCustomChats();
|
||||||
val chconsc = getConfig().getConfig().createSection("chcons");
|
val chconsc = getConfig().getConfig().createSection("chcons");
|
||||||
for (val chcon : chcons) {
|
for (val chcon : chcons) {
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package buttondevteam.discordplugin.playerfaker;
|
||||||
|
|
||||||
|
import buttondevteam.discordplugin.mcchat.MCChatUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ServerWatcher {
|
||||||
|
private List<Player> playerList;
|
||||||
|
private final List<Player> fakePlayers = new ArrayList<>();
|
||||||
|
private Server origServer;
|
||||||
|
|
||||||
|
public void enableDisable(boolean enable) throws Exception {
|
||||||
|
var serverField = Bukkit.class.getDeclaredField("server");
|
||||||
|
serverField.setAccessible(true);
|
||||||
|
if (enable) {
|
||||||
|
var serverClass = Bukkit.getServer().getClass();
|
||||||
|
var mock = Mockito.mock(serverClass, Mockito.withSettings()
|
||||||
|
.stubOnly().defaultAnswer(invocation -> {
|
||||||
|
var method = invocation.getMethod();
|
||||||
|
int pc = method.getParameterCount();
|
||||||
|
Player player = null;
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "getPlayer":
|
||||||
|
if (pc == 1 && method.getParameterTypes()[0] == UUID.class)
|
||||||
|
player = MCChatUtils.LoggedInPlayers.get(invocation.<UUID>getArgument(0));
|
||||||
|
break;
|
||||||
|
case "getPlayerExact":
|
||||||
|
if (pc == 1) {
|
||||||
|
final String argument = invocation.getArgument(0);
|
||||||
|
player = MCChatUtils.LoggedInPlayers.values().stream()
|
||||||
|
.filter(dcp -> dcp.getName().equalsIgnoreCase(argument)).findAny().orElse(null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "getOnlinePlayers":
|
||||||
|
if (playerList == null) {
|
||||||
|
@SuppressWarnings("unchecked") var list = (List<Player>) invocation.callRealMethod();
|
||||||
|
playerList = new AppendListView<>(list, fakePlayers);
|
||||||
|
}
|
||||||
|
return playerList;
|
||||||
|
}
|
||||||
|
if (player != null)
|
||||||
|
return player;
|
||||||
|
return invocation.callRealMethod();
|
||||||
|
}));
|
||||||
|
var originalServer = serverField.get(null);
|
||||||
|
for (var field : serverClass.getFields()) //Copy public fields, private fields aren't accessible directly anyways
|
||||||
|
field.set(mock, field.get(originalServer));
|
||||||
|
serverField.set(null, mock);
|
||||||
|
origServer = (Server) originalServer;
|
||||||
|
} else if (origServer != null)
|
||||||
|
serverField.set(null, origServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class AppendListView<T> extends AbstractSequentialList<T> {
|
||||||
|
private final List<T> originalList;
|
||||||
|
private final List<T> additionalList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<T> listIterator(int i) {
|
||||||
|
int os = originalList.size();
|
||||||
|
return i < os ? originalList.listIterator(i) : additionalList.listIterator(i - os);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return originalList.size() + additionalList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
mock-maker-inline
|
Loading…
Reference in a new issue