1.14 support, better error handling #108

Merged
NorbiPeti merged 22 commits from dev into master 2019-10-30 18:43:31 +00:00
10 changed files with 164 additions and 18 deletions
Showing only changes of commit 7db3b17090 - Show all commits

View 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));
}
}

View file

@ -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.");
}

View file

@ -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();
}

View file

@ -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) {
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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) {

View file

@ -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()));
});
}

View file

@ -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;
}

View file

@ -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 {