Finished private Minecraft chat, lots of bug fixes #42

Merged
NorbiPeti merged 8 commits from dev into master 2017-07-12 23:48:03 +00:00
9 changed files with 230 additions and 57 deletions
Showing only changes of commit fd14bf1954 - Show all commits

View file

@ -1,4 +1,26 @@
cache:
directories:
- $HOME/.m2/repository/org/bukkit/craftbukkit
before_install: | # Wget BuildTools and run if cached folder not found
if [ ! -d "$HOME/.m2/repository/org/bukkit/craftbukkit/1.12-R0.1-SNAPSHOT" ]; then
wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
# grep so that download counts don't appear in log files
- java -jar BuildTools.jar --rev 1.12 | grep -vE "[^/ ]*/[^/ ]*\s*KB\s*$" | grep -v "^\s*$"
fi
language: java
jdk:
- oraclejdk8
sudo: true
deploy:
# deploy develop to the staging environment
- provider: script
script: chmod +x deploy.sh && sh deploy.sh staging
on:
branch: dev
skip_cleanup: true
# deploy master to production
- provider: script
script: chmod +x deploy.sh && sh deploy.sh production
on:
branch: master
skip_cleanup: true

10
deploy.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
FILENAME=$(find target/ -maxdepth 1 ! -name '*original*' -name '*.jar')
echo Found file: $FILENAME
if [ $1 = 'production' ]; then
echo Production mode
echo $UPLOAD_KEY > upload_key
chmod 400 upload_key
yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/plugins
fi

View file

@ -150,6 +150,12 @@
<version>1.12-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.12-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.austinv11</groupId>
<artifactId>Discord4j</artifactId>

View file

@ -3,14 +3,18 @@ package buttondevteam.discordplugin;
import java.util.UUID;
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import lombok.Getter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
public class DiscordConnectedPlayer extends DiscordFakePlayer {
private static int nextEntityId = 0;
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> {
private static int nextEntityId = 10000;
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener;
public DiscordConnectedPlayer(IUser user, IChannel channel, UUID uuid) {
super(user, channel, nextEntityId++, uuid);
vanillaCmdListener = new VanillaCommandListener<>(this);
}
}

View file

@ -31,20 +31,33 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.util.Vector;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import lombok.Getter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
@SuppressWarnings("deprecation")
public class DiscordPlayerSender extends DiscordSenderBase implements Player {
public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {
protected Player player;
// protected @Delegate(excludes = ProjectileSource.class) Player player;
// protected @Delegate(excludes = { ProjectileSource.class, Permissible.class }) Player player;
// protected @Delegate(excludes = { ProjectileSource.class, CommandSender.class }) Player player;
private @Getter VanillaCommandListener<DiscordPlayerSender> vanillaCmdListener;
public DiscordPlayerSender(IUser user, IChannel channel, Player player) {
super(user, channel);
this.player = player;
vanillaCmdListener = new VanillaCommandListener<DiscordPlayerSender>(this);
}
@Override
public void sendMessage(String message) {
player.sendMessage(message);
super.sendMessage(message);
}
@Override
public void sendMessage(String[] messages) {
player.sendMessage(messages);
super.sendMessage(messages);
}
@Override

View file

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

View file

@ -18,6 +18,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import buttondevteam.discordplugin.*;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import buttondevteam.lib.*;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.TBMCChatAPI;
@ -36,50 +37,53 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
return;
if (e.getSender() instanceof DiscordSender || e.getSender() instanceof DiscordPlayerSender)
return;
synchronized (this) {
final String authorPlayer = DiscordPlugin.sanitizeString(e.getSender() instanceof Player //
? ((Player) e.getSender()).getDisplayName() //
: e.getSender().getName());
final EmbedBuilder embed = new EmbedBuilder().withAuthorName(authorPlayer).withDescription(e.getMessage())
.withColor(new Color(e.getChannel().color.getRed(), e.getChannel().color.getGreen(),
e.getChannel().color.getBlue()));
if (e.getSender() instanceof Player)
embed.withAuthorIcon("https://minotar.net/avatar/" + ((Player) e.getSender()).getName() + "/32.png");
final long nanoTime = System.nanoTime();
Consumer<LastMsgData> doit = lastmsgdata -> {
final EmbedObject embedObject = embed.build();
String msg = lastmsgdata.channel.isPrivate() ? DiscordPlugin.sanitizeString(e.getChannel().DisplayName)
: "";
if (lastmsgdata.message == null || lastmsgdata.message.isDeleted()
|| !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName())
|| lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
|| !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
lastmsgdata.message = DiscordPlugin.sendMessageToChannel(lastmsgdata.channel, msg, embedObject);
lastmsgdata.time = nanoTime;
lastmsgdata.mcchannel = e.getChannel();
lastmsgdata.content = embedObject.description;
} else
try {
lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n"
+ embedObject.description;// The message object doesn't get updated
final LastMsgData _lastmsgdata = lastmsgdata;
DiscordPlugin.perform(() -> _lastmsgdata.message.edit(msg, embedObject));
} catch (MissingPermissionsException | DiscordException e1) {
TBMCCoreAPI.SendException("An error occured while editing chat message!", e1);
}
};
if (e.getChannel().equals(Channel.GlobalChat))
doit.accept(
lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel) : lastmsgdata);
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
synchronized (this) {
final String authorPlayer = DiscordPlugin.sanitizeString(e.getSender() instanceof Player //
? ((Player) e.getSender()).getDisplayName() //
: e.getSender().getName());
final EmbedBuilder embed = new EmbedBuilder().withAuthorName(authorPlayer)
.withDescription(e.getMessage()).withColor(new Color(e.getChannel().color.getRed(),
e.getChannel().color.getGreen(), e.getChannel().color.getBlue()));
if (e.getSender() instanceof Player)
embed.withAuthorIcon(
"https://minotar.net/avatar/" + ((Player) e.getSender()).getName() + "/32.png");
final long nanoTime = System.nanoTime();
Consumer<LastMsgData> doit = lastmsgdata -> {
final EmbedObject embedObject = embed.build();
String msg = lastmsgdata.channel.isPrivate()
? DiscordPlugin.sanitizeString(e.getChannel().DisplayName) : "";
if (lastmsgdata.message == null || lastmsgdata.message.isDeleted()
|| !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName())
|| lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
|| !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
lastmsgdata.message = DiscordPlugin.sendMessageToChannel(lastmsgdata.channel, msg, embedObject);
lastmsgdata.time = nanoTime;
lastmsgdata.mcchannel = e.getChannel();
lastmsgdata.content = embedObject.description;
} else
try {
lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n"
+ embedObject.description;// The message object doesn't get updated
final LastMsgData _lastmsgdata = lastmsgdata;
DiscordPlugin.perform(() -> _lastmsgdata.message.edit(msg, embedObject));
} catch (MissingPermissionsException | DiscordException e1) {
TBMCCoreAPI.SendException("An error occured while editing chat message!", e1);
}
};
if (e.getChannel().equals(Channel.GlobalChat))
doit.accept(lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel)
: lastmsgdata);
for (LastMsgData data : lastmsgPerUser) {
final IUser iUser = data.channel.getUsersHere().stream()
.filter(u -> u.getLongID() != u.getClient().getOurUser().getLongID()).findFirst().get(); // Doesn't support group DMs
final DiscordPlayer user = DiscordPlayer.getUser(iUser.getStringID(), DiscordPlayer.class);
if (user.isMinecraftChatEnabled() && e.shouldSendTo(getSender(data.channel, iUser, user)))
doit.accept(data);
for (LastMsgData data : lastmsgPerUser) {
final IUser iUser = data.channel.getUsersHere().stream()
.filter(u -> u.getLongID() != u.getClient().getOurUser().getLongID()).findFirst().get(); // Doesn't support group DMs
final DiscordPlayer user = DiscordPlayer.getUser(iUser.getStringID(), DiscordPlayer.class);
if (user.isMinecraftChatEnabled() && e.shouldSendTo(getSender(data.channel, iUser, user)))
doit.accept(data);
}
}
} // TODO: Author URL
}); // TODO: Author URL
}
private static class LastMsgData {
@ -235,7 +239,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
dsender.sendMessage("Stop it. You know the answer.");
lastlist = 0;
} else
Bukkit.dispatchCommand(dsender, cmd);
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
lastlistp = (short) Bukkit.getOnlinePlayers().size();
} else {
if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0)
@ -273,7 +277,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
val key = (channel.isPrivate() ? "" : "P") + author.getStringID();
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
() -> Optional.ofNullable(OnlineSenders.get(key)), // Find first non-null
() -> Optional.ofNullable(ConnectedSenders.get(author.getStringID())), // This doesn't support it
() -> Optional.ofNullable(ConnectedSenders.get(key)), // This doesn't support the public chat, but it'll always return null for it
() -> Optional.ofNullable(UnconnectedSenders.get(key)), () -> {
val dsender = new DiscordSender(author, channel);
UnconnectedSenders.put(key, dsender);

View file

@ -11,13 +11,10 @@ import org.bukkit.entity.*;
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 buttondevteam.discordplugin.DiscordPlugin;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Delegate;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
@ -25,9 +22,7 @@ import sx.blah.discord.handle.obj.IUser;
public class DiscordFakePlayer extends DiscordHumanEntity implements Player {
protected DiscordFakePlayer(IUser user, IChannel channel, int entityId, UUID uuid) {
super(user, channel, entityId, uuid);
perm = new PermissibleBase(new ServerOperator() {
private @Getter @Setter boolean op;
});
perm = new PermissibleBase(Bukkit.getOfflinePlayer(uuid));
}
@Delegate

View file

@ -0,0 +1,110 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.Arrays;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_12_R1.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import lombok.Getter;
import lombok.val;
import net.minecraft.server.v1_12_R1.ChatMessage;
import net.minecraft.server.v1_12_R1.CommandException;
import net.minecraft.server.v1_12_R1.EnumChatFormat;
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
import net.minecraft.server.v1_12_R1.ICommandListener;
import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.World;
public class VanillaCommandListener<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 VanillaCommandListener(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 VanillaCommandListener(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 MinecraftServer C_() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
@Override
public boolean a(int oplevel, String cmd) {
// return oplevel <= 2; // Value from CommandBlockListenerAbstract, found what it is in EntityPlayer - Wait, that'd always allow OP commands
return oplevel == 0 || player.isOp();
}
@Override
public String getName() {
return player.getName();
}
@Override
public World getWorld() {
return ((CraftWorld) player.getWorld()).getHandle();
}
@Override
public void sendMessage(IChatBaseComponent arg0) {
player.sendMessage(arg0.toPlainText());
if (bukkitplayer != null)
((CraftPlayer) bukkitplayer).getHandle().sendMessage(arg0);
}
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;
ICommandListener icommandlistener = sender.getVanillaCmdListener();
String[] args = cmdstr.split(" ");
args = Arrays.copyOfRange(args, 1, args.length);
try {
vcmd.dispatchVanillaCommand(sender, icommandlistener, args);
} catch (CommandException commandexception) {
// Taken from CommandHandler
ChatMessage chatmessage = new ChatMessage(commandexception.getMessage(), commandexception.getArgs());
chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
icommandlistener.sendMessage(chatmessage);
}
return true;
}
}