Finished private Minecraft chat, lots of bug fixes #42
9 changed files with 230 additions and 57 deletions
24
.travis.yml
24
.travis.yml
|
@ -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
10
deploy.sh
Normal 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
|
6
pom.xml
6
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
9
src/main/java/buttondevteam/discordplugin/IMCPlayer.java
Normal file
9
src/main/java/buttondevteam/discordplugin/IMCPlayer.java
Normal 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();
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue