Merge pull request #108 from TBMCPlugins/dev

1.14 support, better error handling
This commit is contained in:
Norbi Peti 2019-10-30 19:43:30 +01:00 committed by GitHub
commit 30e2da094a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 836 additions and 2141 deletions

View file

@ -9,7 +9,7 @@ before_install: | # Wget BuildTools and run if cached folder not found
fi fi
language: java language: java
jdk: jdk:
- oraclejdk8 - oraclejdk11
sudo: true sudo: true
deploy: deploy:
# deploy develop to the staging environment # deploy develop to the staging environment

38
pom.xml
View file

@ -2,6 +2,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>CorePOM</artifactId>
<version>master-SNAPSHOT</version>
</parent>
<groupId>com.github.TBMCPlugins</groupId> <groupId>com.github.TBMCPlugins</groupId>
<artifactId>DiscordPlugin</artifactId> <artifactId>DiscordPlugin</artifactId>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
@ -34,26 +40,10 @@
</resources> </resources>
<finalName>DiscordPlugin</finalName> <finalName>DiscordPlugin</finalName>
<plugins> <plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- <compilerArgs>
<arg>-processor</arg>
<arg>buttondevteam.buttonproc.ButtonProcessor, lombok.core.AnnotationProcessor</arg>
</compilerArgs> -->
<!-- <annotationProcessors>
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</annotationProcessor>
<annotationProcessor>buttondevteam.buttonproc.ButtonProcessor</annotationProcessor>
</annotationProcessors> -->
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version> <version>3.2.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@ -148,7 +138,7 @@
</repository> --> </repository> -->
<repository> <repository>
<id>Essentials</id> <id>Essentials</id>
<url>http://repo.ess3.net/content/repositories/essrel/</url> <url>https://ci.ender.zone/plugin/repository/everything/</url>
</repository> </repository>
<repository> <repository>
<id>projectlombok.org</id> <id>projectlombok.org</id>
@ -174,7 +164,7 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.12-R0.1-SNAPSHOT</version> <version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -183,11 +173,17 @@
<version>1.12.2-R0.1-SNAPSHOT</version> <version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.spigotmc.</groupId>
<artifactId>spigot</artifactId>
<version>1.14.4-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>
<artifactId>discord4j-core</artifactId> <artifactId>discord4j-core</artifactId>
<version>3.0.6</version> <version>3.0.10</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 --> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
<dependency> <dependency>
@ -217,7 +213,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.16.16</version> <version>1.18.10</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- <dependency> <!-- <dependency>

View file

@ -163,4 +163,8 @@ public final class DPUtils {
return getMessageChannel(config.getPath(), config.get()); return getMessageChannel(config.getPath(), config.get());
} }
public static <T> Mono<T> ignoreError(Mono<T> mono) {
return mono.onErrorResume(t -> Mono.empty());
}
} }

View file

@ -1,25 +1,258 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule; import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer; import buttondevteam.discordplugin.playerfaker.DiscordInventory;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User; import discord4j.core.object.entity.User;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Delegate;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.ServerOperator;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.UUID; import java.util.UUID;
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> { import static org.mockito.Answers.RETURNS_DEFAULTS;
private static int nextEntityId = 10000;
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener; public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer<DiscordConnectedPlayer> {
private @Getter VCMDWrapper vanillaCmdListener;
@Getter @Getter
@Setter @Setter
private boolean loggedIn = false; private boolean loggedIn = false;
public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { @Delegate(excludes = ServerOperator.class)
super(user, channel, nextEntityId++, uuid, mcname, module); private PermissibleBase origPerm;
vanillaCmdListener = new VanillaCommandListener<>(this);
private @Getter String name;
private @Getter OfflinePlayer basePlayer;
@Getter
@Setter
private PermissibleBase perm;
private Location location;
private final MinecraftChatModule module;
@Getter
private final UUID uniqueId;
/**
* The parameters must match with {@link #create(User, MessageChannel, UUID, String, MinecraftChatModule)}
*/
protected DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname,
MinecraftChatModule module) {
super(user, channel);
location = Bukkit.getWorlds().get(0).getSpawnLocation();
origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid));
name = mcname;
this.module = module;
uniqueId = uuid;
displayName = mcname;
try {
vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this));
if (vanillaCmdListener.getListener() == null)
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
} catch (NoClassDefFoundError e) {
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
}
} }
/**
* For testing
*/
protected DiscordConnectedPlayer(User user, MessageChannel channel) {
super(user, channel);
module = null;
uniqueId = UUID.randomUUID();
}
public void setOp(boolean value) { //CraftPlayer-compatible implementation
this.origPerm.setOp(value);
this.perm.recalculatePermissions();
}
public boolean isOp() { return this.origPerm.isOp(); }
@Override
public boolean teleport(Location location) {
if (module.allowFakePlayerTeleports().get())
this.location = location;
return true;
}
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
if (module.allowFakePlayerTeleports().get())
this.location = location;
return true;
}
@Override
public boolean teleport(Entity destination) {
if (module.allowFakePlayerTeleports().get())
this.location = destination.getLocation();
return true;
}
@Override
public boolean teleport(Entity destination, PlayerTeleportEvent.TeleportCause cause) {
if (module.allowFakePlayerTeleports().get())
this.location = destination.getLocation();
return true;
}
@Override
public Location getLocation(Location loc) {
if (loc != null) {
loc.setWorld(getWorld());
loc.setX(location.getX());
loc.setY(location.getY());
loc.setZ(location.getZ());
loc.setYaw(location.getYaw());
loc.setPitch(location.getPitch());
}
return loc;
}
@Override
public Server getServer() {
return Bukkit.getServer();
}
@Override
public void sendRawMessage(String message) {
sendMessage(message);
}
@Override
public void chat(String msg) {
Bukkit.getPluginManager()
.callEvent(new AsyncPlayerChatEvent(true, this, msg, new HashSet<>(Bukkit.getOnlinePlayers())));
}
@Override
public World getWorld() {
return Bukkit.getWorlds().get(0);
}
@Override
public boolean isOnline() {
return true;
}
@Override
public Location getLocation() {
return new Location(getWorld(), location.getX(), location.getY(), location.getZ(),
location.getYaw(), location.getPitch());
}
@Override
public double getMaxHealth() {
return 20;
}
@Override
public Player getPlayer() {
return this;
}
@Getter
@Setter
private String displayName;
@Override
public AttributeInstance getAttribute(Attribute attribute) {
return new AttributeInstance() {
@Override
public Attribute getAttribute() {
return attribute;
}
@Override
public double getBaseValue() {
return getDefaultValue();
}
@Override
public void setBaseValue(double value) {
}
@Override
public Collection<AttributeModifier> getModifiers() {
return Collections.emptyList();
}
@Override
public void addModifier(AttributeModifier modifier) {
}
@Override
public void removeModifier(AttributeModifier modifier) {
}
@Override
public double getValue() {
return getDefaultValue();
}
@Override
public double getDefaultValue() {
return 20; //Works for max health, should be okay for the rest
}
};
}
@Override
public GameMode getGameMode() {
return GameMode.SPECTATOR;
}
public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname,
MinecraftChatModule module) {
return Mockito.mock(DiscordConnectedPlayer.class,
getSettings().useConstructor(user, channel, uuid, mcname, module));
}
public static DiscordConnectedPlayer createTest() {
return Mockito.mock(DiscordConnectedPlayer.class, getSettings().useConstructor(null, null));
}
private static MockSettings getSettings() {
return Mockito.withSettings()
.defaultAnswer(invocation -> {
try {
if (!Modifier.isAbstract(invocation.getMethod().getModifiers()))
return invocation.callRealMethod();
if (PlayerInventory.class.isAssignableFrom(invocation.getMethod().getReturnType()))
return Mockito.mock(DiscordInventory.class, Mockito.withSettings().extraInterfaces(PlayerInventory.class));
if (Inventory.class.isAssignableFrom(invocation.getMethod().getReturnType()))
return new DiscordInventory();
return RETURNS_DEFAULTS.answer(invocation);
} catch (Exception e) {
System.err.println("Error in mocked player!");
e.printStackTrace();
return RETURNS_DEFAULTS.answer(invocation);
}
});
}
} }

View file

@ -1,6 +1,6 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User; import discord4j.core.object.entity.User;
import lombok.Getter; import lombok.Getter;
@ -36,12 +36,18 @@ import java.util.*;
public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> { public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {
protected Player player; protected Player player;
private @Getter VanillaCommandListener<DiscordPlayerSender> vanillaCmdListener; private @Getter VCMDWrapper vanillaCmdListener;
public DiscordPlayerSender(User user, MessageChannel channel, Player player) { public DiscordPlayerSender(User user, MessageChannel channel, Player player) {
super(user, channel); super(user, channel);
this.player = player; this.player = player;
vanillaCmdListener = new VanillaCommandListener<DiscordPlayerSender>(this); try {
vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, player));
if (vanillaCmdListener.getListener() == null)
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
} catch (NoClassDefFoundError e) {
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
}
} }
@Override @Override
@ -298,10 +304,6 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<
return player.addAttachment(plugin); return player.addAttachment(plugin);
} }
public Block getTargetBlock(HashSet<Byte> transparent, int maxDistance) {
return player.getTargetBlock(transparent, maxDistance);
}
public World getWorld() { public World getWorld() {
return player.getWorld(); return player.getWorld();
} }
@ -350,10 +352,6 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<
player.setCompassTarget(loc); player.setCompassTarget(loc);
} }
public List<Block> getLastTwoTargetBlocks(HashSet<Byte> transparent, int maxDistance) {
return player.getLastTwoTargetBlocks(transparent, maxDistance);
}
public Location getCompassTarget() { public Location getCompassTarget() {
return player.getCompassTarget(); return player.getCompassTarget();
} }
@ -1092,11 +1090,21 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<
} }
public void hidePlayer(Player player) { public void hidePlayer(Player player) {
player.hidePlayer(player); this.player.hidePlayer(player);
}
@Override
public void hidePlayer(Plugin plugin, Player player) {
this.player.hidePlayer(plugin, player);
} }
public void showPlayer(Player player) { public void showPlayer(Player player) {
player.showPlayer(player); this.player.showPlayer(player);
}
@Override
public void showPlayer(Plugin plugin, Player player) {
this.player.showPlayer(plugin, player);
} }
public boolean canSee(Player player) { public boolean canSee(Player player) {

View file

@ -143,13 +143,17 @@ public class DiscordPlugin extends ButtonPlugin {
private void handleReady(List<GuildCreateEvent> event) { private void handleReady(List<GuildCreateEvent> event) {
try { try {
if (mainServer != null) { //This is not the first ready event if (mainServer != null) { //This is not the first ready event
getLogger().info("Ready event already handled"); getLogger().info("Ready event already handled"); //TODO: It should probably handle disconnections
return; return;
} }
mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards
getCommand2MC().registerCommand(new DiscordMCCommand()); //Register so that the reset command works
if (mainServer == null) { if (mainServer == null) {
if (event.size() == 0) { if (event.size() == 0) {
getLogger().severe("Main server not found! Invite the bot and do /discord reset"); getLogger().severe("Main server not found! Invite the bot and do /discord reset");
dc.getApplicationInfo().subscribe(info -> {
getLogger().severe("Click here: https://discordapp.com/oauth2/authorize?client_id=" + info.getId().asString() + "&scope=bot&permissions=268509264");
});
saveConfig(); //Put default there saveConfig(); //Put default there
return; //We should have all guilds by now, no need to retry return; //We should have all guilds by now, no need to retry
} }
@ -205,7 +209,6 @@ public class DiscordPlugin extends ButtonPlugin {
CommonListeners.register(dc.getEventDispatcher()); CommonListeners.register(dc.getEventDispatcher());
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
getCommand2MC().registerCommand(new DiscordMCCommand());
TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
? ((DiscordSenderBase) sender).getChromaUser() : null)); ? ((DiscordSenderBase) sender).getChromaUser() : null));
@ -266,8 +269,8 @@ public class DiscordPlugin extends ButtonPlugin {
try { try {
SafeMode = true; // Stop interacting with Discord SafeMode = true; // Stop interacting with Discord
ChromaBot.delete(); ChromaBot.delete();
timings.printElapsed("Updating presence..."); //timings.printElapsed("Updating presence...");
dc.updatePresence(Presence.idle(Activity.playing("Chromacraft"))).block(); //No longer using the same account for testing //dc.updatePresence(Presence.idle(Activity.playing("logging out"))).block(); //No longer using the same account for testing
timings.printElapsed("Logging out..."); timings.printElapsed("Logging out...");
dc.logout().block(); dc.logout().block();
mainServer = null; //Allow ReadyEvent again mainServer = null; //Allow ReadyEvent again

View file

@ -12,6 +12,7 @@ import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import reactor.core.publisher.Mono;
import java.util.Set; import java.util.Set;
@ -23,7 +24,8 @@ public class DiscordSender extends DiscordSenderBase implements CommandSender {
public DiscordSender(User user, MessageChannel channel) { public DiscordSender(User user, MessageChannel channel) {
super(user, channel); super(user, channel);
val def = "Discord user"; val def = "Discord user";
name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId()).blockOptional().map(Member::getDisplayName).orElse(def); name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId())
.onErrorResume(t -> Mono.empty()).blockOptional().map(Member::getDisplayName).orElse(def);
} }
public DiscordSender(User user, MessageChannel channel, String name) { public DiscordSender(User user, MessageChannel channel, String name) {

View file

@ -1,8 +1,8 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public interface IMCPlayer<T extends DiscordSenderBase & IMCPlayer<T>> extends Player { public interface IMCPlayer<T> extends Player {
VanillaCommandListener<T> getVanillaCmdListener(); VCMDWrapper getVanillaCmdListener();
} }

View file

@ -12,24 +12,29 @@ public class GeneralEventBroadcasterModule extends Component<DiscordPlugin> {
@Override @Override
protected void enable() { protected void enable() {
try { try {
PlayerListWatcher.hookUp(); PlayerListWatcher.hookUpDown(true);
DPUtils.getLogger().info("Finished hooking into the player list"); DPUtils.getLogger().info("Finished hooking into the player list");
hooked = true; hooked = true;
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e); TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e);
} catch (NoClassDefFoundError e) {
DPUtils.getLogger().warning("Error while hacking the player list! Disable this module if you're on an incompatible version.");
} }
} }
@Override @Override
protected void disable() { protected void disable() {
try { try {
if (PlayerListWatcher.hookDown()) if (!hooked) return;
if (PlayerListWatcher.hookUpDown(false))
DPUtils.getLogger().info("Finished unhooking the player list!"); DPUtils.getLogger().info("Finished unhooking the player list!");
else else
DPUtils.getLogger().info("Didn't have the player list hooked."); DPUtils.getLogger().info("Didn't have the player list hooked.");
hooked = false; hooked = false;
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e); TBMCCoreAPI.SendException("Error while hacking the player list!", e);
} catch (NoClassDefFoundError ignored) {
} }
} }
} }

View file

@ -2,25 +2,21 @@ package buttondevteam.discordplugin.broadcaster;
import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import com.mojang.authlib.GameProfile;
import lombok.val; import lombok.val;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.mockito.Mockito;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer; import org.mockito.invocation.InvocationOnMock;
import org.bukkit.craftbukkit.v1_12_R1.util.CraftChatMessage; import org.mockito.stubbing.Answer;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.objenesis.ObjenesisStd;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import java.util.UUID;
public class PlayerListWatcher extends DedicatedPlayerList { public class PlayerListWatcher {
private DedicatedPlayerList plist; private static Object plist;
private static Object mock;
public PlayerListWatcher(DedicatedServer minecraftserver) { /*public PlayerListWatcher(DedicatedServer minecraftserver) {
super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis
} }
@ -38,7 +34,7 @@ public class PlayerListWatcher extends DedicatedPlayerList {
} }
@Override @Override
public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overriden method public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overridden method
plist.getServer().sendMessage(ichatbasecomponent); plist.getServer().sendMessage(ichatbasecomponent);
ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT; ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT;
@ -57,306 +53,114 @@ public class PlayerListWatcher extends DedicatedPlayerList {
for (IChatBaseComponent component : iChatBaseComponents) { for (IChatBaseComponent component : iChatBaseComponents) {
sendMessage(component, true); sendMessage(component, true);
} }
} }*/
static void hookUp() throws Exception { static boolean hookUpDown(boolean up) throws Exception {
Field conf = CraftServer.class.getDeclaredField("console"); val csc = Bukkit.getServer().getClass();
Field conf = csc.getDeclaredField("console");
conf.setAccessible(true); conf.setAccessible(true);
val server = (MinecraftServer) conf.get(Bukkit.getServer()); val server = conf.get(Bukkit.getServer());
val plw = new ObjenesisStd().newInstance(PlayerListWatcher.class); // Cannot call super constructor val nms = server.getClass().getPackage().getName();
plw.plist = (DedicatedPlayerList) server.getPlayerList(); val dplc = Class.forName(nms + ".DedicatedPlayerList");
plw.maxPlayers = plw.plist.getMaxPlayers(); val currentPL = server.getClass().getMethod("getPlayerList").invoke(server);
Field plf = plw.getClass().getField("players"); if (up) {
plf.setAccessible(true); val icbcl = Class.forName(nms + ".IChatBaseComponent");
Field modf = plf.getClass().getDeclaredField("modifiers"); val sendMessage = server.getClass().getMethod("sendMessage", icbcl);
modf.setAccessible(true); val cmtcl = Class.forName(nms + ".ChatMessageType");
modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); val systemType = cmtcl.getDeclaredField("SYSTEM").get(null);
plf.set(plw, plw.plist.players); val chatType = cmtcl.getDeclaredField("CHAT").get(null);
server.a(plw);
Field pllf = CraftServer.class.getDeclaredField("playerList");
pllf.setAccessible(true);
pllf.set(Bukkit.getServer(), plw);
}
static boolean hookDown() throws Exception { val obc = csc.getPackage().getName();
Field conf = CraftServer.class.getDeclaredField("console"); val ccmcl = Class.forName(obc + ".util.CraftChatMessage");
conf.setAccessible(true); val fixComponent = ccmcl.getMethod("fixComponent", icbcl);
val server = (MinecraftServer) conf.get(Bukkit.getServer()); val ppoc = Class.forName(nms + ".PacketPlayOutChat");
val plist = (DedicatedPlayerList) server.getPlayerList(); val ppocC = Class.forName(nms + ".PacketPlayOutChat").getConstructor(icbcl, cmtcl);
if (!(plist instanceof PlayerListWatcher)) val sendAll = dplc.getMethod("sendAll", Class.forName(nms + ".Packet"));
return false; Method tpt;
server.a(((PlayerListWatcher) plist).plist); try {
Field pllf = CraftServer.class.getDeclaredField("playerList"); tpt = icbcl.getMethod("toPlainText");
} catch (NoSuchMethodException e) {
tpt = icbcl.getMethod("getString");
}
val toPlainText = tpt;
mock = Mockito.mock(dplc, new Answer() { // Cannot call super constructor
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
final Method method = invocation.getMethod();
if (!method.getName().equals("sendMessage")) {
if (method.getName().equals("sendAll")) {
sendAll(invocation.getArgument(0));
return null;
}
return method.invoke(plist, invocation.getArguments());
}
val args = invocation.getArguments();
val params = method.getParameterTypes();
if (params.length == 0) {
TBMCCoreAPI.SendException("Found a strange method",
new Exception("Found a sendMessage() method without arguments."));
return null;
}
if (params[0].getSimpleName().equals("IChatBaseComponent[]"))
for (val arg : (Object[]) args[0])
sendMessage(arg, true);
else if (params[0].getSimpleName().equals("IChatBaseComponent"))
if (params.length > 1 && params[1].getSimpleName().equalsIgnoreCase("boolean"))
sendMessage(args[0], (Boolean) args[1]);
else
sendMessage(args[0], true);
else
TBMCCoreAPI.SendException("Found a method with interesting params",
new Exception("Found a sendMessage(" + params[0].getSimpleName() + ") method"));
return null;
}
private void sendMessage(Object chatComponent, boolean system) {
try { //Converted to use reflection
sendMessage.invoke(server, chatComponent);
Object chatmessagetype = system ? systemType : chatType;
// CraftBukkit start - we run this through our processor first so we can get web links etc
this.sendAll(ppocC.newInstance(fixComponent.invoke(null, chatComponent), chatmessagetype));
// CraftBukkit end
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occurred while passing a vanilla message through the player list", e);
}
}
private void sendAll(Object packet) {
try { // Some messages get sent by directly constructing a packet
sendAll.invoke(plist, packet);
if (packet.getClass() == ppoc) {
Field msgf = ppoc.getDeclaredField("a");
msgf.setAccessible(true);
MCChatUtils.forAllMCChat(MCChatUtils.send((String) toPlainText.invoke(msgf.get(packet))));
}
} catch (Exception e) {
TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e);
}
}
});
plist = currentPL;
for (var plc = dplc; plc != null; plc = plc.getSuperclass()) { //Set all fields
for (var f : plc.getDeclaredFields()) {
f.setAccessible(true);
Field modf = f.getClass().getDeclaredField("modifiers");
modf.setAccessible(true);
modf.set(f, f.getModifiers() & ~Modifier.FINAL);
f.set(mock, f.get(plist));
}
}
}
try {
server.getClass().getMethod("a", dplc).invoke(server, up ? mock : plist);
} catch (NoSuchMethodException e) {
server.getClass().getMethod("a", Class.forName(server.getClass().getPackage().getName() + ".PlayerList"))
.invoke(server, up ? mock : plist);
}
Field pllf = csc.getDeclaredField("playerList");
pllf.setAccessible(true); pllf.setAccessible(true);
pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist); pllf.set(Bukkit.getServer(), up ? mock : plist);
return true; return true;
} }
public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) {
plist.a(entityhuman, ichatbasecomponent);
}
public void a(EntityPlayer entityplayer, int i) {
plist.a(entityplayer, i);
}
public void a(EntityPlayer entityplayer, WorldServer worldserver) {
plist.a(entityplayer, worldserver);
}
public NBTTagCompound a(EntityPlayer entityplayer) {
return plist.a(entityplayer);
}
public void a(int i) {
plist.a(i);
}
public void a(NetworkManager networkmanager, EntityPlayer entityplayer) {
plist.a(networkmanager, entityplayer);
}
public void a(Packet<?> packet, int i) {
plist.a(packet, i);
}
public EntityPlayer a(UUID uuid) {
return plist.a(uuid);
}
public void addOp(GameProfile gameprofile) {
plist.addOp(gameprofile);
}
public void addWhitelist(GameProfile gameprofile) {
plist.addWhitelist(gameprofile);
}
public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) {
return plist.attemptLogin(loginlistener, gameprofile, hostname);
}
public String b(boolean flag) {
return plist.b(flag);
}
public void b(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) {
plist.b(entityhuman, ichatbasecomponent);
}
public void b(EntityPlayer entityplayer, WorldServer worldserver) {
plist.b(entityplayer, worldserver);
}
public List<EntityPlayer> b(String s) {
return plist.b(s);
}
public Location calculateTarget(Location enter, World target) {
return plist.calculateTarget(enter, target);
}
public void changeDimension(EntityPlayer entityplayer, int i, TeleportCause cause) {
plist.changeDimension(entityplayer, i, cause);
}
public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) {
plist.changeWorld(entity, i, worldserver, worldserver1);
}
public int d() {
return plist.d();
}
public void d(EntityPlayer entityplayer) {
plist.d(entityplayer);
}
public String disconnect(EntityPlayer entityplayer) {
return plist.disconnect(entityplayer);
}
public boolean equals(Object obj) {
return plist.equals(obj);
}
public String[] f() {
return plist.f();
}
public void f(EntityPlayer entityplayer) {
plist.f(entityplayer);
}
public boolean f(GameProfile gameprofile) {
return plist.f(gameprofile);
}
public GameProfile[] g() {
return plist.g();
}
public boolean getHasWhitelist() {
return plist.getHasWhitelist();
}
public IpBanList getIPBans() {
return plist.getIPBans();
}
public int getMaxPlayers() {
return plist.getMaxPlayers();
}
public OpList getOPs() {
return plist.getOPs();
}
public EntityPlayer getPlayer(String s) {
return plist.getPlayer(s);
}
public int getPlayerCount() {
return plist.getPlayerCount();
}
public GameProfileBanList getProfileBans() {
return plist.getProfileBans();
}
public String[] getSeenPlayers() {
return plist.getSeenPlayers();
}
public DedicatedServer getServer() {
return plist.getServer();
}
public WhiteList getWhitelist() {
return plist.getWhitelist();
}
public String[] getWhitelisted() {
return plist.getWhitelisted();
}
public AdvancementDataPlayer h(EntityPlayer entityplayer) {
return plist.h(entityplayer);
}
public int hashCode() {
return plist.hashCode();
}
public boolean isOp(GameProfile gameprofile) {
return plist.isOp(gameprofile);
}
public boolean isWhitelisted(GameProfile gameprofile) {
return plist.isWhitelisted(gameprofile);
}
public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag, Location location,
boolean avoidSuffocation) {
return plist.moveToWorld(entityplayer, i, flag, location, avoidSuffocation);
}
public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag) {
return plist.moveToWorld(entityplayer, i, flag);
}
public String[] n() {
return plist.n();
}
public void onPlayerJoin(EntityPlayer entityplayer, String joinMessage) {
plist.onPlayerJoin(entityplayer, joinMessage);
}
public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) {
return plist.processLogin(gameprofile, player);
}
public void reload() {
plist.reload();
}
public void reloadWhitelist() {
plist.reloadWhitelist();
}
public void removeOp(GameProfile gameprofile) {
plist.removeOp(gameprofile);
}
public void removeWhitelist(GameProfile gameprofile) {
plist.removeWhitelist(gameprofile);
}
public void repositionEntity(Entity entity, Location exit, boolean portal) {
plist.repositionEntity(entity, exit, portal);
}
public int s() {
return plist.s();
}
public void savePlayers() {
plist.savePlayers();
}
@SuppressWarnings("rawtypes")
public void sendAll(Packet packet, EntityHuman entityhuman) {
plist.sendAll(packet, entityhuman);
}
@SuppressWarnings("rawtypes")
public void sendAll(Packet packet, World world) {
plist.sendAll(packet, world);
}
public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i,
Packet<?> packet) {
plist.sendPacketNearby(entityhuman, d0, d1, d2, d3, i, packet);
}
public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) {
plist.sendScoreboard(scoreboardserver, entityplayer);
}
public void setHasWhitelist(boolean flag) {
plist.setHasWhitelist(flag);
}
public void setPlayerFileData(WorldServer[] aworldserver) {
plist.setPlayerFileData(aworldserver);
}
public NBTTagCompound t() {
return plist.t();
}
public void tick() {
plist.tick();
}
public String toString() {
return plist.toString();
}
public void u() {
plist.u();
}
public void updateClient(EntityPlayer entityplayer) {
plist.updateClient(entityplayer);
}
public List<EntityPlayer> v() {
return plist.v();
}
public ServerStatisticManager getStatisticManager(EntityPlayer entityhuman) {
return plist.getStatisticManager(entityhuman);
}
} }

View file

@ -19,7 +19,7 @@ public class DebugCommand extends ICommand2DC {
.flatMap(m -> DiscordPlugin.plugin.modRole().get() .flatMap(m -> DiscordPlugin.plugin.modRole().get()
.map(mr -> m.getRoleIds().stream().anyMatch(r -> r.equals(mr.getId()))) .map(mr -> m.getRoleIds().stream().anyMatch(r -> r.equals(mr.getId())))
.switchIfEmpty(Mono.fromSupplier(() -> DiscordPlugin.mainServer.getOwnerId().asLong() == m.getId().asLong()))) //Role not found .switchIfEmpty(Mono.fromSupplier(() -> DiscordPlugin.mainServer.getOwnerId().asLong() == m.getId().asLong()))) //Role not found
.subscribe(success -> { .onErrorReturn(false).subscribe(success -> {
if (success) if (success)
sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled")); sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
else else

View file

@ -50,28 +50,28 @@ public class ExceptionListenerModule extends Component<DiscordPlugin> implements
private static void SendException(Throwable e, String sourcemessage) { private static void SendException(Throwable e, String sourcemessage) {
if (instance == null) return; if (instance == null) return;
try { try {
Mono<MessageChannel> channel = getChannel(); getChannel().flatMap(channel -> {
assert channel != null; Mono<Role> coderRole;
Mono<Role> coderRole; if (channel instanceof GuildChannel)
if (channel instanceof GuildChannel) coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get();
coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get(); else
else coderRole = Mono.empty();
coderRole = Mono.empty(); return coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder()
coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder() : new StringBuilder(role.getMention()).append("\n"))
: new StringBuilder(role.getMention()).append("\n")) .defaultIfEmpty(new StringBuilder())
.defaultIfEmpty(new StringBuilder()) .flatMap(sb -> {
.flatMap(sb -> { sb.append(sourcemessage).append("\n");
sb.append(sourcemessage).append("\n"); sb.append("```").append("\n");
sb.append("```").append("\n"); String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n"))
String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")) .filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam."))
.filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam.")) .collect(Collectors.joining("\n"));
.collect(Collectors.joining("\n")); if (sb.length() + stackTrace.length() >= 1980)
if (sb.length() + stackTrace.length() >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length());
stackTrace = stackTrace.substring(0, 1980 - sb.length()); sb.append(stackTrace).append("\n");
sb.append(stackTrace).append("\n"); sb.append("```");
sb.append("```"); return channel.createMessage(sb.toString());
return channel.flatMap(ch -> ch.createMessage(sb.toString())); });
}).subscribe(); }).subscribe();
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
} }

View file

@ -12,6 +12,7 @@ import lombok.val;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.event.server.ServerCommandEvent;
import reactor.core.publisher.Mono;
public class MCListener implements Listener { public class MCListener implements Listener {
@EventHandler @EventHandler
@ -33,13 +34,16 @@ public class MCListener implements Listener {
DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class); DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals("")) if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
return; return;
User user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block(); val userOpt = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).onErrorResume(t -> Mono.empty()).blockOptional();
if (user == null) return; if (!userOpt.isPresent()) return;
User user = userOpt.get();
e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator()); e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator());
Member member = user.asMember(DiscordPlugin.mainServer.getId()).block(); val memberOpt = user.asMember(DiscordPlugin.mainServer.getId()).onErrorResume(t -> Mono.empty()).blockOptional();
if (member == null) return; if (!memberOpt.isPresent()) return;
val pr = member.getPresence().block(); Member member = memberOpt.get();
if (pr == null) return; val prOpt = member.getPresence().blockOptional();
if (!prOpt.isPresent()) return;
val pr = prOpt.get();
e.addInfo(pr.getStatus().toString()); e.addInfo(pr.getStatus().toString());
if (pr.getActivity().isPresent()) { if (pr.getActivity().isPresent()) {
val activity = pr.getActivity().get(); val activity = pr.getActivity().get();

View file

@ -31,7 +31,7 @@ import java.util.stream.Collectors;
"Use the ID (command) of the channel, for example `g` for the global chat.", // "Use the ID (command) of the channel, for example `g` for the global chat.", //
"To remove a connection use @ChromaBot channelcon remove in the channel.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", //
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", // "Mentioning the bot is needed in this case because the / prefix only works in #bot.", //
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" // "Invite link: <Unknown>" //
}) })
@RequiredArgsConstructor @RequiredArgsConstructor
public class ChannelconCommand extends ICommand2DC { public class ChannelconCommand extends ICommand2DC {
@ -90,6 +90,10 @@ public class ChannelconCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
public boolean def(Command2DCSender sender, String channelID) { public boolean def(Command2DCSender sender, String channelID) {
val message = sender.getMessage(); val message = sender.getMessage();
if (!module.allowCustomChat().get()) {
sender.sendMessage("channel connection is not allowed on this Minecraft server.");
return true;
}
if (checkPerms(message)) return true; if (checkPerms(message)) return true;
if (MCChatCustom.hasCustomChat(message.getChannelId())) if (MCChatCustom.hasCustomChat(message.getChannelId()))
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it."); return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.");
@ -107,7 +111,7 @@ public class ChannelconCommand extends ICommand2DC {
return true; return true;
} }
val channel = message.getChannel().block(); val channel = message.getChannel().block();
DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module); DiscordConnectedPlayer dcp = DiscordConnectedPlayer.create(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module);
//Using a fake player with no login/logout, should be fine for this event //Using a fake player with no login/logout, should be fine for this event
String groupid = chan.get().getGroupID(dcp); String groupid = chan.get().getGroupID(dcp);
if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later
@ -151,7 +155,7 @@ public class ChannelconCommand extends ICommand2DC {
"Use the ID (command) of the channel, for example `g` for the global chat.", // "Use the ID (command) of the channel, for example `g` for the global chat.", //
"To remove a connection use @ChromaBot channelcon remove in the channel.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", //
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", // "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", //
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" // TODO: Set correct client ID "Invite link: <https://discordapp.com/oauth2/authorize?client_id=" + module.clientID + "&scope=bot&permissions=268509264>"
}; };
} }
} }

View file

@ -9,6 +9,7 @@ import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import discord4j.core.object.entity.PrivateChannel; import discord4j.core.object.entity.PrivateChannel;
import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
@CommandClass(helpText = { @CommandClass(helpText = {
@ -17,10 +18,17 @@ import lombok.val;
"It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", // "It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", //
"You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." // "You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." //
}) })
@RequiredArgsConstructor
public class MCChatCommand extends ICommand2DC { public class MCChatCommand extends ICommand2DC {
private final MinecraftChatModule module;
@Command2.Subcommand @Command2.Subcommand
public boolean def(Command2DCSender sender) { public boolean def(Command2DCSender sender) {
if (!module.allowPrivateChat().get()) {
sender.sendMessage("using the private chat is not allowed on this Minecraft server.");
return true;
}
val message = sender.getMessage(); val message = sender.getMessage();
val channel = message.getChannel().block(); val channel = message.getChannel().block();
@SuppressWarnings("OptionalGetWithoutIsPresent") val author = message.getAuthor().get(); @SuppressWarnings("OptionalGetWithoutIsPresent") val author = message.getAuthor().get();

View file

@ -10,6 +10,7 @@ import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.listeners.CommandListener; import buttondevteam.discordplugin.listeners.CommandListener;
import buttondevteam.discordplugin.listeners.CommonListeners; import buttondevteam.discordplugin.listeners.CommonListeners;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener14;
import buttondevteam.discordplugin.util.Timings; import buttondevteam.discordplugin.util.Timings;
import buttondevteam.lib.*; import buttondevteam.lib.*;
import buttondevteam.lib.chat.ChatMessage; import buttondevteam.lib.chat.ChatMessage;
@ -78,7 +79,7 @@ public class MCChatListener implements Listener {
final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " // final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " //
+ ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") // + ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
+ (DPUtils.sanitizeStringNoEscape(ThorpeUtils.getDisplayName(e.getSender()))); + (DPUtils.sanitizeStringNoEscape(ChromaUtils.getDisplayName(e.getSender())));
val color = e.getChannel().Color().get(); val color = e.getChannel().Color().get();
final Consumer<EmbedCreateSpec> embed = ecs -> { final Consumer<EmbedCreateSpec> embed = ecs -> {
ecs.setDescription(e.getMessage()).setColor(new Color(color.getRed(), ecs.setDescription(e.getMessage()).setColor(new Color(color.getRed(),
@ -232,8 +233,8 @@ public class MCChatListener implements Listener {
timings.printElapsed("Filter 1"); timings.printElapsed("Filter 1");
return !(ev.getMessage().getChannelId().asLong() != module.chatChannel().get().asLong() return !(ev.getMessage().getChannelId().asLong() != module.chatChannel().get().asLong()
&& !(channel instanceof PrivateChannel && !(channel instanceof PrivateChannel
&& author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false) && author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false))
&& !hasCustomChat)); //Chat isn't enabled on this channel && !hasCustomChat); //Chat isn't enabled on this channel
}).filter(channel -> { }).filter(channel -> {
timings.printElapsed("Filter 2"); timings.printElapsed("Filter 2");
return !(channel instanceof PrivateChannel //Only in private chat return !(channel instanceof PrivateChannel //Only in private chat
@ -277,10 +278,11 @@ public class MCChatListener implements Listener {
for (User u : event.getMessage().getUserMentions().toIterable()) { //TODO: Role mentions for (User u : event.getMessage().getUserMentions().toIterable()) { //TODO: Role mentions
dmessage = dmessage.replace(u.getMention(), "@" + u.getUsername()); // TODO: IG Formatting dmessage = dmessage.replace(u.getMention(), "@" + u.getUsername()); // TODO: IG Formatting
val m = u.asMember(DiscordPlugin.mainServer.getId()).block(); val m = u.asMember(DiscordPlugin.mainServer.getId()).onErrorResume(t -> Mono.empty()).blockOptional();
if (m != null) { if (m.isPresent()) {
final String nick = m.getDisplayName(); val mm = m.get();
dmessage = dmessage.replace(m.getNicknameMention(), "@" + nick); final String nick = mm.getDisplayName();
dmessage = dmessage.replace(mm.getNicknameMention(), "@" + nick);
} }
} }
for (GuildChannel ch : event.getGuild().flux().flatMap(Guild::getChannels).toIterable()) { for (GuildChannel ch : event.getGuild().flux().flatMap(Guild::getChannels).toIterable()) {
@ -321,7 +323,8 @@ public class MCChatListener implements Listener {
return; return;
} }
val ev = new TBMCCommandPreprocessEvent(dsender, dmessage); val ev = new TBMCCommandPreprocessEvent(dsender, dmessage);
Bukkit.getPluginManager().callEvent(ev); Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () ->
Bukkit.getPluginManager().callEvent(ev));
if (ev.isCancelled()) if (ev.isCancelled())
return; return;
int spi = cmdlowercased.indexOf(' '); int spi = cmdlowercased.indexOf(' ');
@ -338,8 +341,18 @@ public class MCChatListener implements Listener {
if (clmd != null) { if (clmd != null) {
channel.set(clmd.mcchannel); //Hack to send command in the channel channel.set(clmd.mcchannel); //Hack to send command in the channel
} //TODO: Permcheck isn't implemented for commands } //TODO: Permcheck isn't implemented for commands
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); try {
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased); String mcpackage = Bukkit.getServer().getClass().getPackage().getName();
if (mcpackage.contains("1_12"))
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
else if (mcpackage.contains("1_14"))
VanillaCommandListener14.runBukkitOrVanillaCommand(dsender, cmd);
else
Bukkit.dispatchCommand(dsender, cmd);
} catch (NoClassDefFoundError e) {
TBMCCoreAPI.SendException("A class is not found when trying to run command " + cmd + "!", e);
}
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmd);
if (clmd != null) if (clmd != null)
channel.set(chtmp); channel.set(chtmp);
}); });

View file

@ -26,7 +26,7 @@ public class MCChatPrivate {
val op = Bukkit.getOfflinePlayer(mcp.getUUID()); val op = Bukkit.getOfflinePlayer(mcp.getUUID());
val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class); val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class);
if (start) { if (start) {
val sender = new DiscordConnectedPlayer(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 if (p == null)// Player is offline - If the player is online, that takes precedence
MCChatUtils.callLoginEvents(sender); MCChatUtils.callLoginEvents(sender);

View file

@ -10,7 +10,6 @@ import discord4j.core.object.entity.*;
import discord4j.core.object.util.Snowflake; import discord4j.core.object.util.Snowflake;
import io.netty.util.collection.LongObjectHashMap; import io.netty.util.collection.LongObjectHashMap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -53,7 +52,8 @@ public class MCChatUtils {
private static HashMap<Class<? extends Event>, HashSet<String>> staticExcludedPlugins = new HashMap<>(); private static HashMap<Class<? extends Event>, HashSet<String>> staticExcludedPlugins = new HashMap<>();
public static void updatePlayerList() { public static void updatePlayerList() {
if (notEnabled()) return; val mod = getModule();
if (mod == null || !mod.showPlayerListOnDC().get()) return;
if (lastmsgdata != null) if (lastmsgdata != null)
updatePL(lastmsgdata); updatePL(lastmsgdata);
MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL); MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
@ -180,7 +180,10 @@ public class MCChatUtils {
} }
public static Consumer<Mono<MessageChannel>> send(String message) { public static Consumer<Mono<MessageChannel>> send(String message) {
return ch -> ch.flatMap(mc -> mc.createMessage(DPUtils.sanitizeString(message))).subscribe(); return ch -> ch.flatMap(mc -> {
resetLastMessage(mc);
return mc.createMessage(DPUtils.sanitizeString(message));
}).subscribe();
} }
public static void forAllowedMCChat(Consumer<Mono<MessageChannel>> action, TBMCSystemChatEvent event) { public static void forAllowedMCChat(Consumer<Mono<MessageChannel>> action, TBMCSystemChatEvent event) {

View file

@ -18,13 +18,15 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerCommandSendEvent;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result; import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.server.BroadcastMessageEvent; import org.bukkit.event.server.BroadcastMessageEvent;
import org.bukkit.event.server.TabCompleteEvent;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@RequiredArgsConstructor @RequiredArgsConstructor
@ -50,11 +52,13 @@ class MCListener implements Listener {
final Player p = e.getPlayer(); final Player p = e.getPlayer();
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class); DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
if (dp != null) { if (dp != null) {
val user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block(); DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).flatMap(user -> user.getPrivateChannel().flatMap(chan -> module.chatChannelMono().flatMap(cc -> {
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, Objects.requireNonNull(user).getPrivateChannel().block(), p)); //TODO: Don't block new DiscordPlayerSender(user, chan, p));
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, module.chatChannelMono().block(), p)); //Stored per-channel new DiscordPlayerSender(user, cc, p)); //Stored per-channel
return Mono.empty();
}))).subscribe();
} }
final String message = e.GetPlayer().PlayerName().get() + " joined the game"; final String message = e.GetPlayer().PlayerName().get() + " joined the game";
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
@ -114,7 +118,7 @@ class MCListener implements Listener {
final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class) final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
.getAs(DiscordPlayer.class); .getAs(DiscordPlayer.class);
if (p == null) return; if (p == null) return;
DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID())) DPUtils.ignoreError(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID()))
.flatMap(user -> user.asMember(DiscordPlugin.mainServer.getId())) .flatMap(user -> user.asMember(DiscordPlugin.mainServer.getId()))
.flatMap(user -> role.flatMap(r -> { .flatMap(user -> role.flatMap(r -> {
if (e.getValue()) if (e.getValue())
@ -127,7 +131,7 @@ class MCListener implements Listener {
if (modlog != null) if (modlog != null)
return modlog.flatMap(ch -> ch.createMessage(msg)); return modlog.flatMap(ch -> ch.createMessage(msg));
return Mono.empty(); return Mono.empty();
})).subscribe(); }))).subscribe();
} }
@EventHandler @EventHandler
@ -154,4 +158,26 @@ class MCListener implements Listener {
public void onNickChange(NickChangeEvent event) { public void onNickChange(NickChangeEvent event) {
MCChatUtils.updatePlayerList(); MCChatUtils.updatePlayerList();
} }
@EventHandler
public void onTabComplete(TabCompleteEvent event) {
int i = event.getBuffer().lastIndexOf(' ');
String t = event.getBuffer().substring(i + 1); //0 if not found
//System.out.println("Last token: " + t);
if (!t.startsWith("@"))
return;
String token = t.substring(1);
//System.out.println("Token: " + token);
val x = DiscordPlugin.mainServer.getMembers()
.flatMap(m -> Flux.just(m.getUsername(), m.getNickname().orElse("")))
.filter(s -> s.startsWith(token))
.map(s -> "@" + s)
.doOnNext(event.getCompletions()::add).blockLast();
//System.out.println("Finished - last: " + x);
}
@EventHandler
public void onCommandSend(PlayerCommandSendEvent event) {
event.getCommands().add("g");
}
} }

View file

@ -77,14 +77,44 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
return getConfig().getData("allowFakePlayerTeleports", false); return getConfig().getData("allowFakePlayerTeleports", false);
} }
/**
* If this is on, each chat channel will have a player list in their description.
* It only gets added if there's no description yet or there are (at least) two lines of "----" following each other.
* Note that it will replace <b>everything</b> between the first and last "----" but it will only detect exactly four dashes.
* So if you want to use dashes for something else in the description, make sure it's either less or more dashes in one line.
*/
public ConfigData<Boolean> showPlayerListOnDC() {
return getConfig().getData("showPlayerListOnDC", true);
}
/**
* This setting controls whether custom chat connections can be <i>created</i> (existing connections will always work).
* Custom chat connections can be created using the channelcon command and they allow players to display town chat in a Discord channel for example.
* See the channelcon command for more details.
*/
public ConfigData<Boolean> allowCustomChat() {
return getConfig().getData("allowCustomChat", true);
}
/**
* This setting allows you to control if players can DM the bot to log on the server from Discord.
* This allows them to both chat and perform any command they can in-game.
*/
public ConfigData<Boolean> allowPrivateChat() {
return getConfig().getData("allowPrivateChat", true);
}
String clientID;
@Override @Override
protected void enable() { protected void enable() {
if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono())) if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono()))
return; return;
DiscordPlugin.dc.getApplicationInfo().subscribe(info -> clientID = info.getId().asString());
listener = new MCChatListener(this); listener = new MCChatListener(this);
TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin());
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled
getPlugin().getManager().registerCommand(new MCChatCommand()); getPlugin().getManager().registerCommand(new MCChatCommand(this));
getPlugin().getManager().registerCommand(new ChannelconCommand(this)); getPlugin().getManager().registerCommand(new ChannelconCommand(this));
val chcons = getConfig().getConfig().getConfigurationSection("chcons"); val chcons = getConfig().getConfig().getConfigurationSection("chcons");
@ -104,7 +134,7 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
if (!mcch.isPresent() || ch == null || user == null || groupid == null) if (!mcch.isPresent() || ch == null || user == null || groupid == null)
continue; continue;
Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase) Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase)
val dcp = new DiscordConnectedPlayer(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this); val dcp = DiscordConnectedPlayer.create(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this);
MCChatCustom.addCustomChat((MessageChannel) ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet())); MCChatCustom.addCustomChat((MessageChannel) ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet()));
}); });
} }

View file

@ -1,308 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Vector;
import java.util.*;
@Getter
@Setter
@SuppressWarnings("deprecated")
public abstract class DiscordEntity extends DiscordSenderBase implements Entity {
protected DiscordEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
super(user, channel);
this.entityId = entityId;
uniqueId = uuid;
this.module = module;
}
private HashMap<String, MetadataValue> metadata = new HashMap<String, MetadataValue>();
private Location location = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
private Vector velocity;
private final int entityId;
private EntityDamageEvent lastDamageCause;
private final Set<String> scoreboardTags = new HashSet<String>();
private final UUID uniqueId;
private final MinecraftChatModule module;
@Override
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
metadata.put(metadataKey, newMetadataValue);
}
@Override
public List<MetadataValue> getMetadata(String metadataKey) {
return Collections.singletonList(metadata.get(metadataKey)); // Who needs multiple data anyways
}
@Override
public boolean hasMetadata(String metadataKey) {
return metadata.containsKey(metadataKey);
}
@Override
public void removeMetadata(String metadataKey, Plugin owningPlugin) {
metadata.remove(metadataKey);
}
@Override
public Location getLocation(Location loc) {
if (loc != null) {
loc.setWorld(getWorld());
loc.setX(location.getX());
loc.setY(location.getY());
loc.setZ(location.getZ());
loc.setYaw(location.getYaw());
loc.setPitch(location.getPitch());
}
return loc;
}
@Override
public double getHeight() {
return 0;
}
@Override
public double getWidth() {
return 0;
}
@Override
public boolean isOnGround() {
return false;
}
@Override
public World getWorld() {
return location.getWorld();
}
@Override
public boolean teleport(Location location) {
if (module.allowFakePlayerTeleports().get())
this.location = location;
return true;
}
@Override
public boolean teleport(Location location, TeleportCause cause) {
if (module.allowFakePlayerTeleports().get())
this.location = location;
return true;
}
@Override
public boolean teleport(Entity destination) {
if (module.allowFakePlayerTeleports().get())
this.location = destination.getLocation();
return true;
}
@Override
public boolean teleport(Entity destination, TeleportCause cause) {
if (module.allowFakePlayerTeleports().get())
this.location = destination.getLocation();
return true;
}
@Override
public List<Entity> getNearbyEntities(double x, double y, double z) {
return Collections.emptyList();
}
@Override
public int getFireTicks() {
return 0;
}
@Override
public int getMaxFireTicks() {
return 0;
}
@Override
public void setFireTicks(int ticks) {
}
@Override
public void remove() {
}
@Override
public boolean isDead() { // Impossible to kill
return false;
}
@Override
public boolean isValid() {
return true;
}
@Override
public Server getServer() {
return Bukkit.getServer();
}
@Override
public Entity getPassenger() {
return null;
}
@Override
public boolean setPassenger(Entity passenger) {
return false;
}
@Override
public List<Entity> getPassengers() {
return Collections.emptyList();
}
@Override
public boolean addPassenger(Entity passenger) {
return false;
}
@Override
public boolean removePassenger(Entity passenger) { // Don't support passengers
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean eject() {
return false;
}
@Override
public float getFallDistance() {
return 0;
}
@Override
public void setFallDistance(float distance) {
}
@Override
public int getTicksLived() {
return 1;
}
@Override
public void setTicksLived(int value) {
}
@Override
public void playEffect(EntityEffect type) {
}
@Override
public boolean isInsideVehicle() {
return false;
}
@Override
public boolean leaveVehicle() {
return false;
}
@Override
public Entity getVehicle() { // Don't support vehicles
return null;
}
@Override
public void setCustomNameVisible(boolean flag) {
}
@Override
public boolean isCustomNameVisible() {
return true;
}
@Override
public void setGlowing(boolean flag) {
}
@Override
public boolean isGlowing() {
return false;
}
@Override
public void setInvulnerable(boolean flag) {
}
@Override
public boolean isInvulnerable() {
return true;
}
@Override
public boolean isSilent() {
return true;
}
@Override
public void setSilent(boolean flag) {
}
@Override
public boolean hasGravity() {
return false;
}
@Override
public void setGravity(boolean gravity) {
}
@Override
public int getPortalCooldown() {
return 0;
}
@Override
public void setPortalCooldown(int cooldown) {
}
@Override
public boolean addScoreboardTag(String tag) {
return scoreboardTags.add(tag);
}
@Override
public boolean removeScoreboardTag(String tag) {
return scoreboardTags.remove(tag);
}
@Override
public PistonMoveReaction getPistonMoveReaction() {
return PistonMoveReaction.IGNORE;
}
@Override
public Entity.Spigot spigot() {
return new Entity.Spigot();
}
}

View file

@ -1,733 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Delegate;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.map.MapView;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.ServerOperator;
import org.bukkit.plugin.Plugin;
import org.bukkit.scoreboard.Scoreboard;
import java.net.InetSocketAddress;
import java.util.*;
@SuppressWarnings("deprecation")
public class DiscordFakePlayer extends DiscordHumanEntity implements Player {
protected DiscordFakePlayer(User user, MessageChannel channel, int entityId, UUID uuid, String mcname, MinecraftChatModule module) {
super(user, channel, entityId, uuid, module);
origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid));
name = mcname;
}
@Delegate(excludes = ServerOperator.class)
private PermissibleBase origPerm;
private @Getter String name;
private @Getter OfflinePlayer basePlayer;
@Getter
@Setter
private PermissibleBase perm;
public void setOp(boolean value) { //CraftPlayer-compatible implementation
this.origPerm.setOp(value);
this.perm.recalculatePermissions();
}
public boolean isOp() { return this.origPerm.isOp(); }
@Override
public EntityType getType() {
return EntityType.PLAYER;
}
@Override
public String getCustomName() {
return user.getUsername();
}
@Override
public void setCustomName(String name) {
}
@Override
public boolean isConversing() {
return false;
}
@Override
public void acceptConversationInput(String input) {
}
@Override
public boolean beginConversation(Conversation conversation) {
return false;
}
@Override
public void abandonConversation(Conversation conversation) {
}
@Override
public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
}
@Override
public boolean isOnline() {
return true;// Let's pretend
}
@Override
public boolean isBanned() {
return false;
}
@Override
public boolean isWhitelisted() {
return true;
}
@Override
public void setWhitelisted(boolean value) {
}
@Override
public Player getPlayer() {
return this;
}
@Override
public long getFirstPlayed() {
return 0;
}
@Override
public long getLastPlayed() {
return 0;
}
@Override
public boolean hasPlayedBefore() {
return false;
}
@Override
public Map<String, Object> serialize() {
return new HashMap<>();
}
@Override
public void sendPluginMessage(Plugin source, String channel, byte[] message) {
}
@Override
public Set<String> getListeningPluginChannels() {
return Collections.emptySet();
}
@Override
public String getDisplayName() {
return Objects.requireNonNull(user.asMember(DiscordPlugin.mainServer.getId()).block()).getDisplayName();
}
@Override
public void setDisplayName(String name) {
}
@Override
public String getPlayerListName() {
return getName();
}
@Override
public void setPlayerListName(String name) {
}
@Override
public void setCompassTarget(Location loc) {
}
@Override
public Location getCompassTarget() {
return new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
}
@Override
public InetSocketAddress getAddress() {
return null;
}
@Override
public void sendRawMessage(String message) {
sendMessage(message);
}
@Override
public void kickPlayer(String message) {
}
@Override
public void chat(String msg) {
Bukkit.getPluginManager()
.callEvent(new AsyncPlayerChatEvent(true, this, msg, new HashSet<>(Bukkit.getOnlinePlayers())));
}
@Override
public boolean performCommand(String command) {
return Bukkit.getServer().dispatchCommand(this, command);
}
@Override
public boolean isSneaking() {
return false;
}
@Override
public void setSneaking(boolean sneak) {
}
@Override
public boolean isSprinting() {
return false;
}
@Override
public void setSprinting(boolean sprinting) {
}
@Override
public void saveData() {
}
@Override
public void loadData() {
}
@Override
public void setSleepingIgnored(boolean isSleeping) {
}
@Override
public boolean isSleepingIgnored() {
return false;
}
@Override
public void playNote(Location loc, byte instrument, byte note) {
}
@Override
public void playNote(Location loc, Instrument instrument, Note note) {
}
@Override
public void playSound(Location location, Sound sound, float volume, float pitch) {
}
@Override
public void playSound(Location location, String sound, float volume, float pitch) {
}
@Override
public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) {
}
@Override
public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) {
}
@Override
public void stopSound(Sound sound) {
}
@Override
public void stopSound(String sound) {
}
@Override
public void stopSound(Sound sound, SoundCategory category) {
}
@Override
public void stopSound(String sound, SoundCategory category) {
}
@Override
public void playEffect(Location loc, Effect effect, int data) {
}
@Override
public <T> void playEffect(Location loc, Effect effect, T data) {
}
@Override
public void sendBlockChange(Location loc, Material material, byte data) {
}
@Override
public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) {
return false;
}
@Override
public void sendBlockChange(Location loc, int material, byte data) {
}
@Override
public void sendSignChange(Location loc, String[] lines) throws IllegalArgumentException {
}
@Override
public void sendMap(MapView map) {
}
@Override
public void updateInventory() {
}
@Override
public void awardAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
}
@Override
public void removeAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
}
@Override
public boolean hasAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
return false;
}
@Override
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic) throws IllegalArgumentException {
}
@Override
public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
}
@Override
public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException {
}
@Override
public int getStatistic(Statistic statistic) throws IllegalArgumentException {
return 0;
}
@Override
public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
}
@Override
public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
return 0;
}
@Override
public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
}
@Override
public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException {
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
}
@Override
public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
return 0;
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType, int amount)
throws IllegalArgumentException {
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
}
@Override
public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
}
@Override
public void setPlayerTime(long time, boolean relative) {
}
@Override
public long getPlayerTime() {
return 0;
}
@Override
public long getPlayerTimeOffset() {
return 0;
}
@Override
public boolean isPlayerTimeRelative() {
return false;
}
@Override
public void resetPlayerTime() {
}
@Override
public void setPlayerWeather(WeatherType type) {
}
@Override
public WeatherType getPlayerWeather() {
return null;
}
@Override
public void resetPlayerWeather() {
}
@Override
public void giveExp(int amount) {
}
@Override
public void giveExpLevels(int amount) {
}
@Override
public float getExp() {
return 0;
}
@Override
public void setExp(float exp) {
}
@Override
public int getLevel() {
return 0;
}
@Override
public void setLevel(int level) {
}
@Override
public int getTotalExperience() {
return 0;
}
@Override
public void setTotalExperience(int exp) {
}
@Override
public float getExhaustion() {
return 0;
}
@Override
public void setExhaustion(float value) {
}
@Override
public float getSaturation() {
return 0;
}
@Override
public void setSaturation(float value) {
}
@Override
public int getFoodLevel() {
return 0;
}
@Override
public void setFoodLevel(int value) {
}
@Override
public Location getBedSpawnLocation() {
return null;
}
@Override
public void setBedSpawnLocation(Location location) {
}
@Override
public void setBedSpawnLocation(Location location, boolean force) {
}
@Override
public boolean getAllowFlight() {
return false;
}
@Override
public void setAllowFlight(boolean flight) {
}
@Override
public void hidePlayer(Player player) {
}
@Override
public void showPlayer(Player player) {
}
@Override
public boolean canSee(Player player) { // Nobody can see them
return false;
}
@Override
public boolean isFlying() {
return false;
}
@Override
public void setFlying(boolean value) {
}
@Override
public void setFlySpeed(float value) throws IllegalArgumentException {
}
@Override
public void setWalkSpeed(float value) throws IllegalArgumentException {
}
@Override
public float getFlySpeed() {
return 0;
}
@Override
public float getWalkSpeed() {
return 0;
}
@Override
public void setTexturePack(String url) {
}
@Override
public void setResourcePack(String url) {
}
@Override
public void setResourcePack(String url, byte[] hash) {
}
@Override
public Scoreboard getScoreboard() {
return null;
}
@Override
public void setScoreboard(Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException {
}
@Override
public boolean isHealthScaled() {
return false;
}
@Override
public void setHealthScaled(boolean scale) {
}
@Override
public void setHealthScale(double scale) throws IllegalArgumentException {
}
@Override
public double getHealthScale() {
return 1;
}
@Override
public Entity getSpectatorTarget() {
return null;
}
@Override
public void setSpectatorTarget(Entity entity) {
}
@Override
public void sendTitle(String title, String subtitle) {
}
@Override
public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
}
@Override
public void resetTitle() {
}
@Override
public void spawnParticle(Particle particle, Location location, int count) {
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count) {
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, T data) {
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, T data) {
}
@Override
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
double offsetZ) {
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
double offsetY, double offsetZ) {
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
double offsetZ, T data) {
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
double offsetY, double offsetZ, T data) {
}
@Override
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
double offsetZ, double extra) {
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
double offsetY, double offsetZ, double extra) {
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
double offsetZ, double extra, T data) {
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
double offsetY, double offsetZ, double extra, T data) {
}
@Override
public AdvancementProgress getAdvancementProgress(Advancement advancement) { // TODO: Test
return null;
}
@Override
public String getLocale() {
return null;
}
@Override
public Player.Spigot spigot() {
return new Player.Spigot();
}
}

View file

@ -1,168 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Villager;
import org.bukkit.inventory.*;
import org.bukkit.inventory.InventoryView.Property;
import java.util.UUID;
public abstract class DiscordHumanEntity extends DiscordLivingEntity implements HumanEntity {
protected DiscordHumanEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
super(user, channel, entityId, uuid, module);
}
private PlayerInventory inv = new DiscordPlayerInventory(this);
@Override
public PlayerInventory getInventory() {
return inv;
}
private Inventory enderchest = new DiscordInventory(this);
@Override
public Inventory getEnderChest() {
return enderchest;
}
@Override
public MainHand getMainHand() {
return MainHand.RIGHT;
}
@Override
public boolean setWindowProperty(Property prop, int value) {
return false;
}
@Override
public InventoryView getOpenInventory() { // TODO: Test
return null;
}
@Override
public InventoryView openInventory(Inventory inventory) {
return null;
}
@Override
public InventoryView openWorkbench(Location location, boolean force) {
return null;
}
@Override
public InventoryView openEnchanting(Location location, boolean force) {
return null;
}
@Override
public void openInventory(InventoryView inventory) {
}
@Override
public InventoryView openMerchant(Villager trader, boolean force) {
return null;
}
@Override
public InventoryView openMerchant(Merchant merchant, boolean force) {
return null;
}
@Override
public void closeInventory() {
}
@Override
public ItemStack getItemInHand() { // TODO: Test all ItemStack methods
return null;
}
@Override
public void setItemInHand(ItemStack item) {
}
@Override
public ItemStack getItemOnCursor() {
return null;
}
@Override
public void setItemOnCursor(ItemStack item) {
}
@Override
public boolean hasCooldown(Material material) {
return false;
}
@Override
public int getCooldown(Material material) {
return 0;
}
@Override
public void setCooldown(Material material, int ticks) {
}
@Override
public boolean isSleeping() {
return false;
}
@Override
public int getSleepTicks() {
return 0;
}
@Override
public GameMode getGameMode() {
return GameMode.SPECTATOR;
}
@Override
public void setGameMode(GameMode mode) {
}
@Override
public boolean isBlocking() {
return false;
}
@Override
public boolean isHandRaised() {
return false;
}
@Override
public int getExpToLevel() {
return 0;
}
@Override
public Entity getShoulderEntityLeft() {
return null;
}
@Override
public void setShoulderEntityLeft(Entity entity) {
}
@Override
public Entity getShoulderEntityRight() {
return null;
}
@Override
public void setShoulderEntityRight(Entity entity) {
}
}

View file

@ -1,110 +1,110 @@
package buttondevteam.discordplugin.playerfaker; package buttondevteam.discordplugin.playerfaker;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
public class DiscordInventory implements Inventory { public class DiscordInventory implements Inventory {
public DiscordInventory(DiscordHumanEntity holder) { private ItemStack[] items = new ItemStack[27];
this.holder = holder; private List<ItemStack> itemStacks = Arrays.asList(items);
} @Getter
@Setter
public int maxStackSize;
private static ItemStack emptyStack = new ItemStack(Material.AIR, 0);
@Override @Override
public int getSize() { public int getSize() {
return 0; return items.length;
}
@Override
public int getMaxStackSize() {
return 0;
}
@Override
public void setMaxStackSize(int size) {
} }
@Override @Override
public String getName() { public String getName() {
return "Player inventory"; return "Discord inventory";
} }
@Override @Override
public ItemStack getItem(int index) { public ItemStack getItem(int index) {
return null; if (index >= items.length)
return emptyStack;
else
return items[index];
} }
@Override @Override
public HashMap<Integer, ItemStack> addItem(ItemStack... items) throws IllegalArgumentException { // Can't add anything public void setItem(int index, ItemStack item) {
return new HashMap<>( if (index < items.length)
IntStream.range(0, items.length).boxed().collect(Collectors.toMap(i -> i, i -> items[i]))); items[index] = item;
}
@Override
public HashMap<Integer, ItemStack> addItem(ItemStack... items) throws IllegalArgumentException {
return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything
} }
@Override @Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) throws IllegalArgumentException { public HashMap<Integer, ItemStack> removeItem(ItemStack... items) throws IllegalArgumentException {
return new HashMap<>( return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything
IntStream.range(0, items.length).boxed().collect(Collectors.toMap(i -> i, i -> items[i])));
} }
@Override @Override
public ItemStack[] getContents() { public ItemStack[] getContents() {
return new ItemStack[0]; return items;
} }
@Override @Override
public void setContents(ItemStack[] items) throws IllegalArgumentException { public void setContents(ItemStack[] items) throws IllegalArgumentException {
if (items.length > 0) this.items = items;
throw new IllegalArgumentException("This inventory does not support items");
} }
@Override @Override
public ItemStack[] getStorageContents() { public ItemStack[] getStorageContents() {
return new ItemStack[0]; return items;
} }
@Override @Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
if (items.length > 0) this.items = items;
throw new IllegalArgumentException("This inventory does not support items");
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean contains(int materialId) { public boolean contains(int materialId) {
return false; return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId);
} }
@Override @Override
public boolean contains(Material material) throws IllegalArgumentException { public boolean contains(Material material) throws IllegalArgumentException {
return false; return itemStacks.stream().anyMatch(is -> is.getType() == material);
} }
@Override @Override
public boolean contains(ItemStack item) { public boolean contains(ItemStack item) {
return false; return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == item.getAmount());
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean contains(int materialId, int amount) { public boolean contains(int materialId, int amount) {
return false; return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId && is.getAmount() == amount);
} }
@Override @Override
public boolean contains(Material material, int amount) throws IllegalArgumentException { public boolean contains(Material material, int amount) throws IllegalArgumentException {
return false; return itemStacks.stream().anyMatch(is -> is.getType() == material && is.getAmount() == amount);
} }
@Override @Override
public boolean contains(ItemStack item, int amount) { public boolean contains(ItemStack item, int amount) { //Not correct implementation but whatever
return false; return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == amount);
} }
@Override @Override
@ -161,52 +161,48 @@ public class DiscordInventory implements Inventory {
@Override @Override
public void clear(int index) { public void clear(int index) {
if (index < items.length)
items[index] = null;
} }
@Override @Override
public void clear() { public void clear() {
Arrays.fill(items, null);
} }
@Override @Override
public List<HumanEntity> getViewers() { public List<HumanEntity> getViewers() {
return new ArrayList<>(0); return Collections.emptyList();
} }
@Override @Override
public String getTitle() { public String getTitle() {
return "Player inventory"; return "Discord inventory";
} }
@Override @Override
public InventoryType getType() { public InventoryType getType() {
return InventoryType.PLAYER; return InventoryType.CHEST;
} }
private ListIterator<ItemStack> iterator = new ArrayList<ItemStack>(0).listIterator(); @Override
public InventoryHolder getHolder() {
return null;
}
@SuppressWarnings("NullableProblems")
@Override @Override
public ListIterator<ItemStack> iterator() { public ListIterator<ItemStack> iterator() {
return iterator; return itemStacks.listIterator();
} }
@Override @Override
public ListIterator<ItemStack> iterator(int index) { public ListIterator<ItemStack> iterator(int index) {
return iterator; return itemStacks.listIterator(index);
} }
@Override @Override
public Location getLocation() { public Location getLocation() {
return holder.getLocation(); return null;
}
@Override
public void setItem(int index, ItemStack item) {
}
private HumanEntity holder;
@Override
public HumanEntity getHolder() {
return holder;
} }
} }

View file

@ -1,298 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import discord4j.core.object.entity.MessageChannel;
import discord4j.core.object.entity.User;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import java.util.*;
public abstract class DiscordLivingEntity extends DiscordEntity implements LivingEntity {
protected DiscordLivingEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
super(user, channel, entityId, uuid, module);
}
private @Getter EntityEquipment equipment = new DiscordEntityEquipment(this);
@Getter
@Setter
private static class DiscordEntityEquipment implements EntityEquipment {
private float leggingsDropChance;
private ItemStack leggings;
private float itemInOffHandDropChance;
private ItemStack itemInOffHand;
private float itemInMainHandDropChance;
private ItemStack itemInMainHand;
private float itemInHandDropChance;
private ItemStack itemInHand;
private float helmetDropChance;
private ItemStack helmet;
private float chestplateDropChance;
private ItemStack chestplate;
private float bootsDropChance;
private ItemStack boots;
private ItemStack[] armorContents = new ItemStack[0]; // TODO
private final Entity holder;
public DiscordEntityEquipment(Entity holder) {
this.holder = holder;
}
@Override
public void clear() {
armorContents = new ItemStack[0];
}
}
@Override
public AttributeInstance getAttribute(Attribute attribute) { // We don't support any attributes
return null;
}
@Override
public void damage(double amount) {
}
@Override
public void damage(double amount, Entity source) {
}
@Override
public double getHealth() {
return getMaxHealth();
}
@Override
public void setHealth(double health) {
}
@Override
public double getMaxHealth() {
return 100;
}
@Override
public void setMaxHealth(double health) {
}
@Override
public void resetMaxHealth() {
}
@Override
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile) {
return null;
}
@Override
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) {
return null;
}
@Override
public double getEyeHeight() {
return 0;
}
@Override
public double getEyeHeight(boolean ignoreSneaking) {
return 0;
}
@Override
public Location getEyeLocation() {
return getLocation();
}
@Override
public List<Block> getLineOfSight(Set<Material> transparent, int maxDistance) {
return Arrays.asList();
}
@Override
public Block getTargetBlock(HashSet<Byte> transparent, int maxDistance) {
return null;
}
@Override
public Block getTargetBlock(Set<Material> transparent, int maxDistance) {
return null;
}
@Override
public List<Block> getLastTwoTargetBlocks(HashSet<Byte> transparent, int maxDistance) {
return Arrays.asList();
}
@Override
public List<Block> getLastTwoTargetBlocks(Set<Material> transparent, int maxDistance) {
return Arrays.asList();
}
@Override
public int getRemainingAir() {
return 100;
}
@Override
public void setRemainingAir(int ticks) {
}
@Override
public int getMaximumAir() {
return 100;
}
@Override
public void setMaximumAir(int ticks) {
}
@Override
public int getMaximumNoDamageTicks() {
return 100;
}
@Override
public void setMaximumNoDamageTicks(int ticks) {
}
@Override
public double getLastDamage() {
return 0;
}
@Override
public void setLastDamage(double damage) {
}
@Override
public int getNoDamageTicks() {
return 100;
}
@Override
public void setNoDamageTicks(int ticks) {
}
@Override
public Player getKiller() {
return null;
}
@Override
public boolean addPotionEffect(PotionEffect effect) {
return false;
}
@Override
public boolean addPotionEffect(PotionEffect effect, boolean force) {
return false;
}
@Override
public boolean addPotionEffects(Collection<PotionEffect> effects) {
return false;
}
@Override
public boolean hasPotionEffect(PotionEffectType type) {
return false;
}
@Override
public PotionEffect getPotionEffect(PotionEffectType type) {
return null;
}
@Override
public void removePotionEffect(PotionEffectType type) {
}
@Override
public Collection<PotionEffect> getActivePotionEffects() {
return Arrays.asList();
}
@Override
public boolean hasLineOfSight(Entity other) {
return false;
}
@Override
public boolean getRemoveWhenFarAway() {
return false;
}
@Override
public void setRemoveWhenFarAway(boolean remove) {
}
@Override
public void setCanPickupItems(boolean pickup) {
}
@Override
public boolean getCanPickupItems() {
return false;
}
@Override
public boolean isLeashed() {
return false;
}
@Override
public Entity getLeashHolder() throws IllegalStateException {
throw new IllegalStateException();
}
@Override
public boolean setLeashHolder(Entity holder) {
return false;
}
@Override
public boolean isGliding() {
return false;
}
@Override
public void setGliding(boolean gliding) {
}
@Override
public void setAI(boolean ai) {
}
@Override
public boolean hasAI() {
return false;
}
@Override
public void setCollidable(boolean collidable) {
}
@Override
public boolean isCollidable() {
return false;
}
}

View file

@ -1,105 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
public class DiscordPlayerInventory extends DiscordInventory implements PlayerInventory {
public DiscordPlayerInventory(DiscordHumanEntity holder) {
super(holder);
}
@Override
public ItemStack[] getArmorContents() {
return new ItemStack[0];
}
@Override
public ItemStack[] getExtraContents() {
return new ItemStack[0];
}
@Override
public ItemStack getHelmet() {
return null;
}
@Override
public ItemStack getChestplate() {
return null;
}
@Override
public ItemStack getLeggings() {
return null;
}
@Override
public ItemStack getBoots() {
return null;
}
@Override
public void setArmorContents(ItemStack[] items) {
}
@Override
public void setExtraContents(ItemStack[] items) {
}
@Override
public void setHelmet(ItemStack helmet) {
}
@Override
public void setChestplate(ItemStack chestplate) {
}
@Override
public void setLeggings(ItemStack leggings) {
}
@Override
public void setBoots(ItemStack boots) {
}
@Override
public ItemStack getItemInMainHand() {
return null;
}
@Override
public void setItemInMainHand(ItemStack item) {
}
@Override
public ItemStack getItemInOffHand() {
return null;
}
@Override
public void setItemInOffHand(ItemStack item) {
}
@Override
public ItemStack getItemInHand() {
return null;
}
@Override
public void setItemInHand(ItemStack stack) {
}
@Override
public int getHeldItemSlot() {
return 0;
}
@Override
public void setHeldItemSlot(int slot) {
}
@Override
public int clear(int id, int data) {
return 0;
}
}

View file

@ -0,0 +1,39 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@RequiredArgsConstructor
public class VCMDWrapper {
@Getter //Needed to mock the player
private final Object listener;
/**
* This constructor will only send raw vanilla messages to the sender in plain text.
*
* @param player The Discord sender player (the wrapper)
*/
public static <T extends DiscordSenderBase & IMCPlayer<T>> Object createListener(T player) {
return createListener(player, null);
}
/**
* This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player.
*
* @param player The Discord sender player (the wrapper)
* @param bukkitplayer The Bukkit player to send the raw message to
*/
public static <T extends DiscordSenderBase & IMCPlayer<T>> Object createListener(T player, Player bukkitplayer) {
String mcpackage = Bukkit.getServer().getClass().getPackage().getName();
if (mcpackage.contains("1_12"))
return bukkitplayer == null ? new VanillaCommandListener<>(player) : new VanillaCommandListener<>(player, bukkitplayer);
else if (mcpackage.contains("1_14"))
return bukkitplayer == null ? new VanillaCommandListener14<>(player) : new VanillaCommandListener14<>(player, bukkitplayer);
else
return null;
}
}

View file

@ -87,7 +87,7 @@ public class VanillaCommandListener<T extends DiscordSenderBase & IMCPlayer<T>>
if (!vcmd.testPermission(sender)) if (!vcmd.testPermission(sender))
return true; return true;
ICommandListener icommandlistener = sender.getVanillaCmdListener(); ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
String[] args = cmdstr.split(" "); String[] args = cmdstr.split(" ");
args = Arrays.copyOfRange(args, 1, args.length); args = Arrays.copyOfRange(args, 1, args.length);
try { try {

View file

@ -0,0 +1,106 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import lombok.Getter;
import lombok.val;
import net.minecraft.server.v1_14_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.v1_14_R1.CraftServer;
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_14_R1.command.ProxiedNativeCommandSender;
import org.bukkit.craftbukkit.v1_14_R1.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.Arrays;
public class VanillaCommandListener14<T extends DiscordSenderBase & IMCPlayer<T>> implements ICommandListener {
private @Getter T player;
private Player bukkitplayer;
/**
* This constructor will only send raw vanilla messages to the sender in plain text.
*
* @param player The Discord sender player (the wrapper)
*/
public VanillaCommandListener14(T player) {
this.player = player;
this.bukkitplayer = null;
}
/**
* This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player.
*
* @param player The Discord sender player (the wrapper)
* @param bukkitplayer The Bukkit player to send the raw message to
*/
public VanillaCommandListener14(T player, Player bukkitplayer) {
this.player = player;
this.bukkitplayer = bukkitplayer;
if (!(bukkitplayer instanceof CraftPlayer))
throw new ClassCastException("bukkitplayer must be a Bukkit player!");
}
@Override
public void sendMessage(IChatBaseComponent arg0) {
player.sendMessage(arg0.getString());
if (bukkitplayer != null)
((CraftPlayer) bukkitplayer).getHandle().sendMessage(arg0);
}
@Override
public boolean shouldSendSuccess() {
return true;
}
@Override
public boolean shouldSendFailure() {
return true;
}
@Override
public boolean shouldBroadcastCommands() {
return true; //Broadcast to in-game admins
}
@Override
public CommandSender getBukkitSender(CommandListenerWrapper commandListenerWrapper) {
return player;
}
public static boolean runBukkitOrVanillaCommand(DiscordSenderBase dsender, String cmdstr) {
val cmd = ((CraftServer) Bukkit.getServer()).getCommandMap().getCommand(cmdstr.split(" ")[0].toLowerCase());
if (!(dsender instanceof Player) || !(cmd instanceof VanillaCommandWrapper))
return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds
if (!(dsender instanceof IMCPlayer))
throw new ClassCastException(
"dsender needs to implement IMCPlayer to use vanilla commands as it implements Player.");
IMCPlayer<?> sender = (IMCPlayer<?>) dsender; // Don't use val on recursive interfaces :P
val vcmd = (VanillaCommandWrapper) cmd;
if (!vcmd.testPermission(sender))
return true;
val world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0),
new Vec2F(0, 0), world, 0, sender.getName(),
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);
val pncs = new ProxiedNativeCommandSender(wrapper, sender, sender);
String[] args = cmdstr.split(" ");
args = Arrays.copyOfRange(args, 1, args.length);
try {
return vcmd.execute(pncs, cmd.getLabel(), args);
} catch (CommandException commandexception) {
// Taken from CommandHandler
ChatMessage chatmessage = new ChatMessage(commandexception.getMessage(), commandexception.a());
chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
icommandlistener.sendMessage(chatmessage);
}
return true;
}
}

View file

@ -1,8 +1,8 @@
package buttondevteam.discordplugin.playerfaker.perm; package buttondevteam.discordplugin.playerfaker.perm;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import me.lucko.luckperms.bukkit.LPBukkitBootstrap; import me.lucko.luckperms.bukkit.LPBukkitBootstrap;
import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.bukkit.LPBukkitPlugin;
@ -87,10 +87,10 @@ public final class LPInjector implements Listener { //Disable login event for Lu
/* Called when the player starts logging into the server. /* Called when the player starts logging into the server.
At this point, the users data should be present and loaded. */ At this point, the users data should be present and loaded. */
if (!(e.getPlayer() instanceof DiscordFakePlayer)) if (!(e.getPlayer() instanceof DiscordConnectedPlayer))
return; //Normal players must be handled by the plugin return; //Normal players must be handled by the plugin
final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer(); final DiscordConnectedPlayer player = (DiscordConnectedPlayer) e.getPlayer();
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
plugin.getLogger().info("Processing login for " + player.getUniqueId() + " - " + player.getName()); plugin.getLogger().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
@ -155,10 +155,10 @@ public final class LPInjector implements Listener { //Disable login event for Lu
// Wait until the last priority to unload, so plugins can still perform permission checks on this event // Wait until the last priority to unload, so plugins can still perform permission checks on this event
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent e) { public void onPlayerQuit(PlayerQuitEvent e) {
if (!(e.getPlayer() instanceof DiscordFakePlayer)) if (!(e.getPlayer() instanceof DiscordConnectedPlayer))
return; return;
final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer(); final DiscordConnectedPlayer player = (DiscordConnectedPlayer) e.getPlayer();
connectionListener.handleDisconnect(player.getUniqueId()); connectionListener.handleDisconnect(player.getUniqueId());
@ -183,7 +183,7 @@ public final class LPInjector implements Listener { //Disable login event for Lu
} }
//me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector //me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector
private void inject(DiscordFakePlayer player, LPPermissible newPermissible, PermissibleBase oldPermissible) throws IllegalAccessException, InvocationTargetException { private void inject(DiscordConnectedPlayer player, LPPermissible newPermissible, PermissibleBase oldPermissible) throws IllegalAccessException, InvocationTargetException {
// seems we have already injected into this player. // seems we have already injected into this player.
if (oldPermissible instanceof LPPermissible) { if (oldPermissible instanceof LPPermissible) {
@ -207,7 +207,7 @@ public final class LPInjector implements Listener { //Disable login event for Lu
player.setPerm(newPermissible); player.setPerm(newPermissible);
} }
private void uninject(DiscordFakePlayer player, boolean dummy) throws Exception { private void uninject(DiscordConnectedPlayer player, boolean dummy) throws Exception {
// gets the players current permissible. // gets the players current permissible.
PermissibleBase permissible = player.getPerm(); PermissibleBase permissible = player.getPerm();

View file

@ -3,6 +3,7 @@ package buttondevteam.discordplugin.role;
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ReadOnlyConfigData; import buttondevteam.lib.architecture.ReadOnlyConfigData;
import discord4j.core.event.domain.role.RoleCreateEvent; import discord4j.core.event.domain.role.RoleCreateEvent;
@ -26,7 +27,7 @@ public class GameRoleModule extends Component<DiscordPlugin> {
@Override @Override
protected void enable() { protected void enable() {
getPlugin().getManager().registerCommand(new RoleCommand(this)); getPlugin().getManager().registerCommand(new RoleCommand(this));
GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(this::isGameRole).map(Role::getName).collect(Collectors.toList()).block(); GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(r -> isGameRole(r, false)).map(Role::getName).collect(Collectors.toList()).block();
} }
@Override @Override
@ -46,7 +47,7 @@ public class GameRoleModule extends Component<DiscordPlugin> {
if (roleEvent instanceof RoleCreateEvent) { if (roleEvent instanceof RoleCreateEvent) {
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
Role role=((RoleCreateEvent) roleEvent).getRole(); Role role=((RoleCreateEvent) roleEvent).getRole();
grm.isGameRole(role).flatMap(b -> { grm.isGameRole(role, false).flatMap(b -> {
if (!b) if (!b)
return Mono.empty(); //Deleted or not a game role return Mono.empty(); //Deleted or not a game role
GameRoles.add(role.getName()); GameRoles.add(role.getName());
@ -67,7 +68,7 @@ public class GameRoleModule extends Component<DiscordPlugin> {
return; return;
} }
Role or=event.getOld().get(); Role or=event.getOld().get();
grm.isGameRole(event.getCurrent()).flatMap(b -> { grm.isGameRole(event.getCurrent(), true).flatMap(b -> {
if (!b) { if (!b) {
if (GameRoles.remove(or.getName()) && logChannel != null) if (GameRoles.remove(or.getName()) && logChannel != null)
return logChannel.flatMap(ch -> ch.createMessage("Removed " + or.getName() + " as a game role because it's color changed.")); return logChannel.flatMap(ch -> ch.createMessage("Removed " + or.getName() + " as a game role because it's color changed."));
@ -88,12 +89,25 @@ public class GameRoleModule extends Component<DiscordPlugin> {
} }
} }
private Mono<Boolean> isGameRole(Role r) { private Mono<Boolean> isGameRole(Role r, boolean debugMC) {
if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong()) boolean debug = debugMC && r.getName().equalsIgnoreCase("Minecraft");
if (debug) TBMCCoreAPI.sendDebugMessage("Checking if Minecraft is a game role...");
if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong()) {
if (debug) TBMCCoreAPI.sendDebugMessage("Not in the main server: " + r.getGuildId().asString());
return Mono.just(false); //Only allow on the main server return Mono.just(false); //Only allow on the main server
}
val rc = new Color(149, 165, 166, 0); val rc = new Color(149, 165, 166, 0);
return Mono.just(r.getColor().equals(rc)).filter(b -> b).flatMap(b -> if (debug) TBMCCoreAPI.sendDebugMessage("Game role color: " + rc + " - MC color: " + r.getColor());
DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId())).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles return Mono.just(r.getColor().equals(rc))
.defaultIfEmpty(false); .doAfterSuccessOrError((b, e) -> {
if (debug) TBMCCoreAPI.sendDebugMessage("1. b: " + b + " - e: " + e);
}).filter(b -> b).flatMap(b ->
DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId()))
.doAfterSuccessOrError((m, e) -> {
if (debug) TBMCCoreAPI.sendDebugMessage("2. m: " + m.getDisplayName() + " e: " + e);
}).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles
.doAfterSuccessOrError((b, e) -> {
if (debug) TBMCCoreAPI.sendDebugMessage("3. b: " + b + " - e: " + e);
}).defaultIfEmpty(false);
} }
} }

View file

@ -8,6 +8,7 @@ import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import discord4j.core.object.entity.Role; import discord4j.core.object.entity.Role;
import lombok.val; import lombok.val;
import reactor.core.publisher.Mono;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -31,7 +32,8 @@ public class RoleCommand extends ICommand2DC {
return true; return true;
try { try {
sender.getMessage().getAuthorAsMember() sender.getMessage().getAuthorAsMember()
.subscribe(m -> m.addRole(role.getId()).subscribe(r -> sender.sendMessage("added role."))); .flatMap(m -> m.addRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("added role."))))
.subscribe();
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while adding role!", e); TBMCCoreAPI.SendException("Error while adding role!", e);
sender.sendMessage("an error occured while adding the role."); sender.sendMessage("an error occured while adding the role.");
@ -49,7 +51,8 @@ public class RoleCommand extends ICommand2DC {
return true; return true;
try { try {
sender.getMessage().getAuthorAsMember() sender.getMessage().getAuthorAsMember()
.subscribe(m -> m.removeRole(role.getId()).subscribe(r -> sender.sendMessage("removed role."))); .flatMap(m -> m.removeRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("removed role."))))
.subscribe();
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while removing role!", e); TBMCCoreAPI.SendException("Error while removing role!", e);
sender.sendMessage("an error occured while removing the role."); sender.sendMessage("an error occured while removing the role.");

View file

@ -1,8 +1,8 @@
name: Thorpe-Discord name: Chroma-Discord
main: buttondevteam.discordplugin.DiscordPlugin main: buttondevteam.discordplugin.DiscordPlugin
version: 1.0 version: 1.0
author: NorbiPeti author: NorbiPeti
depend: [ThorpeCore] depend: [ChromaCore]
commands: commands:
discord: discord:
website: 'https://github.com/TBMCPlugins/DiscordPlugin' website: 'https://github.com/TBMCPlugins/DiscordPlugin'

View file

@ -1,34 +1,40 @@
package buttondevteam.DiscordPlugin; package buttondevteam.DiscordPlugin;
import junit.framework.Test; import buttondevteam.discordplugin.DiscordConnectedPlayer;
import junit.framework.TestCase; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestCase;
import junit.framework.TestSuite;
/** import org.bukkit.attribute.Attribute;
* Unit test for simple App. import org.bukkit.entity.Player;
*/
public class AppTest extends TestCase { /**
/** * Unit test for simple App.
* Create the test case */
* public class AppTest extends TestCase {
* @param testName /**
* name of the test case * Create the test case
*/ *
public AppTest(String testName) { * @param testName
super(testName); * name of the test case
} */
public AppTest(String testName) {
/** super(testName);
* @return the suite of tests being tested }
*/
public static Test suite() { /**
return new TestSuite(AppTest.class); * @return the suite of tests being tested
} */
public static Test suite() {
/** return new TestSuite(AppTest.class);
* Rigourous Test :-) }
*/
public void testApp() { /**
assertTrue(true); * Rigourous Test :-)
} */
} public void testApp() {
Player dcp = DiscordConnectedPlayer.createTest();
double h = dcp.getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue(); ; ;
System.out.println(h);
}
}