Fix ServerWatcher, mcchat works

This commit is contained in:
Norbi Peti 2020-09-11 22:27:22 +02:00
parent 666f05ff12
commit cd2132ba45
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
5 changed files with 59 additions and 9 deletions

13
pom.xml
View file

@ -114,6 +114,10 @@
<id>Reactor-Tools</id> <id>Reactor-Tools</id>
<url>https://repo.spring.io/milestone</url> <url>https://repo.spring.io/milestone</url>
</repository> --> </repository> -->
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -141,6 +145,12 @@
<version>1.14.4-R0.1-SNAPSHOT</version> <version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <!-- From patched_1.16.3.jar -->
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper</artifactId>
<version>1.16.3-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J --> <!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
<dependency> <dependency>
<groupId>com.discord4j</groupId> <groupId>com.discord4j</groupId>
@ -201,10 +211,11 @@
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>3.0.0</version> <version>3.5.10</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>

View file

@ -35,6 +35,7 @@ import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.mockito.internal.util.MockUtil;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.awt.*; import java.awt.*;
@ -101,6 +102,17 @@ public class DiscordPlugin extends ButtonPlugin {
return getIConfig().getData("inviteLink", ""); return getIConfig().getData("inviteLink", "");
} }
@Override
public void onLoad() { //Needed by ServerWatcher
var thread = Thread.currentThread();
getLogger().info("Setting context class loader for " + thread);
var cl = thread.getContextClassLoader();
thread.setContextClassLoader(getClassLoader());
MockUtil.isMock(null); //Load MockUtil to load Mockito plugins
getLogger().info("Restoring context class loader");
thread.setContextClassLoader(cl);
}
@Override @Override
public void pluginEnable() { public void pluginEnable() {
try { try {

View file

@ -1,12 +1,14 @@
package buttondevteam.discordplugin.playerfaker; package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import com.destroystokyo.paper.profile.CraftPlayerProfile;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
public class ServerWatcher { public class ServerWatcher {
@ -19,8 +21,17 @@ public class ServerWatcher {
serverField.setAccessible(true); serverField.setAccessible(true);
if (enable) { if (enable) {
var serverClass = Bukkit.getServer().getClass(); var serverClass = Bukkit.getServer().getClass();
var mock = Mockito.mock(serverClass, Mockito.withSettings() //var mockMaker = new InlineByteBuddyMockMaker();
.stubOnly().defaultAnswer(invocation -> { //System.setProperty("net.bytebuddy.experimental", "true");
/*try {
var resources = cl.getResources("mockito-extensions/" + MockMaker.class.getName());
System.out.println("Found resources: " + resources);
Iterables.toIterable(resources).forEach(resource -> System.out.println("Resource: " + resource));
} catch (IOException e) {
throw new IllegalStateException("Failed to load " + MockMaker.class, e);
}*/
var settings = Mockito.withSettings().stubOnly()
.defaultAnswer(invocation -> {
var method = invocation.getMethod(); var method = invocation.getMethod();
int pc = method.getParameterCount(); int pc = method.getParameterCount();
Player player = null; Player player = null;
@ -38,17 +49,33 @@ public class ServerWatcher {
break; break;
case "getOnlinePlayers": case "getOnlinePlayers":
if (playerList == null) { if (playerList == null) {
@SuppressWarnings("unchecked") var list = (List<Player>) invocation.callRealMethod(); @SuppressWarnings("unchecked") var list = (List<Player>) method.invoke(origServer, invocation.getArguments());
playerList = new AppendListView<>(list, fakePlayers); playerList = new AppendListView<>(list, fakePlayers);
} }
return playerList; return playerList;
case "createProfile": //Paper's method, casts the player to a CraftPlayer
if (pc == 2) {
UUID uuid = invocation.getArgument(0);
String name = invocation.getArgument(1);
player = uuid != null ? MCChatUtils.LoggedInPlayers.get(uuid) : null;
if (player == null && name != null)
player = MCChatUtils.LoggedInPlayers.values().stream()
.filter(dcp -> dcp.getName().equalsIgnoreCase(name)).findAny().orElse(null);
if (player != null)
return new CraftPlayerProfile(player.getUniqueId(), player.getName());
}
break;
} }
if (player != null) if (player != null)
return player; return player;
return invocation.callRealMethod(); return method.invoke(origServer, invocation.getArguments());
})); });
//var mock = mockMaker.createMock(settings, MockHandlerFactory.createMockHandler(settings));
//thread.setContextClassLoader(cl);
var mock = Mockito.mock(serverClass, settings);
var originalServer = serverField.get(null); var originalServer = serverField.get(null);
for (var field : serverClass.getFields()) //Copy public fields, private fields aren't accessible directly anyways for (var field : serverClass.getFields()) //Copy public fields, private fields aren't accessible directly anyways
if (!Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
field.set(mock, field.get(originalServer)); field.set(mock, field.get(originalServer));
serverField.set(null, mock); serverField.set(null, mock);
origServer = (Server) originalServer; origServer = (Server) originalServer;

View file

@ -79,7 +79,7 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
var server = Bukkit.getServer(); var server = Bukkit.getServer();
var cmap = (SimpleCommandMap) server.getClass().getMethod("getCommandMap").invoke(server); var cmap = (SimpleCommandMap) server.getClass().getMethod("getCommandMap").invoke(server);
val cmd = cmap.getCommand(cmdstr.split(" ")[0].toLowerCase()); val cmd = cmap.getCommand(cmdstr.split(" ")[0].toLowerCase());
if (!(dsender instanceof Player) || !vcwcl.isAssignableFrom(cmd.getClass())) if (!(dsender instanceof Player) || cmd == null || !vcwcl.isAssignableFrom(cmd.getClass()))
return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds
if (!(dsender instanceof IMCPlayer)) if (!(dsender instanceof IMCPlayer))

View file

@ -1 +1 @@
mock-maker-inline org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker