1.14 support, better error handling #108
10 changed files with 164 additions and 18 deletions
140
src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
Executable file → Normal file
140
src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
Executable file → Normal file
|
@ -1,29 +1,155 @@
|
|||
package buttondevteam.discordplugin;
|
||||
|
||||
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
|
||||
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
|
||||
import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
|
||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||
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.entity.Entity;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.ServerOperator;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> {
|
||||
private static int nextEntityId = 10000;
|
||||
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener;
|
||||
public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer<DiscordConnectedPlayer> {
|
||||
private @Getter VCMDWrapper<DiscordConnectedPlayer> vanillaCmdListener;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean loggedIn = false;
|
||||
|
||||
public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) {
|
||||
super(user, channel, nextEntityId++, uuid, mcname, module);
|
||||
@Delegate(excludes = ServerOperator.class)
|
||||
private PermissibleBase origPerm;
|
||||
|
||||
private @Getter String name;
|
||||
|
||||
private @Getter OfflinePlayer basePlayer;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private PermissibleBase perm;
|
||||
|
||||
private Location location = Bukkit.getWorlds().get(0).getSpawnLocation();
|
||||
|
||||
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);
|
||||
origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid));
|
||||
name = mcname;
|
||||
this.module = module;
|
||||
uniqueId = uuid;
|
||||
displayName = mcname;
|
||||
try {
|
||||
vanillaCmdListener = new VanillaCommandListener<>(this);
|
||||
vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener<>(this));
|
||||
} catch (NoClassDefFoundError e) {
|
||||
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String displayName;
|
||||
|
||||
public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname,
|
||||
MinecraftChatModule module) {
|
||||
return Mockito.mock(DiscordConnectedPlayer.class, Mockito.withSettings()
|
||||
.defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(user, channel, uuid, mcname, module));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package buttondevteam.discordplugin;
|
||||
|
||||
import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
|
||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||
import discord4j.core.object.entity.MessageChannel;
|
||||
import discord4j.core.object.entity.User;
|
||||
|
@ -36,13 +37,13 @@ import java.util.*;
|
|||
public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {
|
||||
|
||||
protected Player player;
|
||||
private @Getter VanillaCommandListener<DiscordPlayerSender> vanillaCmdListener;
|
||||
private @Getter VCMDWrapper<DiscordPlayerSender> vanillaCmdListener;
|
||||
|
||||
public DiscordPlayerSender(User user, MessageChannel channel, Player player) {
|
||||
super(user, channel);
|
||||
this.player = player;
|
||||
try {
|
||||
vanillaCmdListener = new VanillaCommandListener<DiscordPlayerSender>(this);
|
||||
vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener<DiscordPlayerSender>(this, player));
|
||||
} catch (NoClassDefFoundError e) {
|
||||
DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error.");
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package buttondevteam.discordplugin;
|
||||
|
||||
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
|
||||
import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface IMCPlayer<T extends DiscordSenderBase & IMCPlayer<T>> extends Player {
|
||||
VanillaCommandListener<T> getVanillaCmdListener();
|
||||
VCMDWrapper<T> getVanillaCmdListener();
|
||||
}
|
||||
|
|
|
@ -15,9 +15,12 @@ public class GeneralEventBroadcasterModule extends Component<DiscordPlugin> {
|
|||
PlayerListWatcher.hookUp();
|
||||
DPUtils.getLogger().info("Finished hooking into the player list");
|
||||
hooked = true;
|
||||
} catch (Exception | NoClassDefFoundError e) {
|
||||
} catch (Exception 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
|
||||
|
@ -29,8 +32,9 @@ public class GeneralEventBroadcasterModule extends Component<DiscordPlugin> {
|
|||
else
|
||||
DPUtils.getLogger().info("Didn't have the player list hooked.");
|
||||
hooked = false;
|
||||
} catch (Exception | NoClassDefFoundError e) {
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("Error while hacking the player list!", e);
|
||||
} catch (NoClassDefFoundError ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ public class ChannelconCommand extends ICommand2DC {
|
|||
return true;
|
||||
}
|
||||
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
|
||||
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
|
||||
|
|
|
@ -26,7 +26,7 @@ public class MCChatPrivate {
|
|||
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
|
||||
val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class);
|
||||
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);
|
||||
if (p == null)// Player is offline - If the player is online, that takes precedence
|
||||
MCChatUtils.callLoginEvents(sender);
|
||||
|
|
|
@ -180,7 +180,10 @@ public class MCChatUtils {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -104,7 +104,7 @@ public class MinecraftChatModule extends Component<DiscordPlugin> {
|
|||
if (!mcch.isPresent() || ch == null || user == null || groupid == null)
|
||||
continue;
|
||||
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()));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package buttondevteam.discordplugin.playerfaker;
|
||||
|
||||
import buttondevteam.discordplugin.DiscordSenderBase;
|
||||
import buttondevteam.discordplugin.IMCPlayer;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class VCMDWrapper<T extends DiscordSenderBase & IMCPlayer<T>> {
|
||||
@Getter //Needed to mock the player
|
||||
private final VanillaCommandListener<T> listener;
|
||||
}
|
|
@ -87,7 +87,7 @@ public class VanillaCommandListener<T extends DiscordSenderBase & IMCPlayer<T>>
|
|||
if (!vcmd.testPermission(sender))
|
||||
return true;
|
||||
|
||||
ICommandListener icommandlistener = sender.getVanillaCmdListener();
|
||||
ICommandListener icommandlistener = sender.getVanillaCmdListener().getListener();
|
||||
String[] args = cmdstr.split(" ");
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue