Preparations for public host

This commit adds a lot of new features, optimizations and bugfixes to prepare the bot to be hosted publicly. Updates include a prefix changing command, fixes with box generation to prevent softlocks in later levels, and making edits to game messages instead of sending a new message after each update. Smaller optimizations were made as well, such as removing games from the HashMap when a user quits to save memory.
This commit is contained in:
PocketMars
2020-06-30 09:40:28 -05:00
parent 89bd9a8f30
commit 79a09651ab
4 changed files with 113 additions and 26 deletions
+18 -2
View File
@@ -2,22 +2,38 @@ import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;
import javax.security.auth.login.LoginException; import javax.security.auth.login.LoginException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
public class Bot { public class Bot {
static String prefix = "!"; static HashMap<Guild, String> prefixes = new HashMap<Guild, String>();
public static void main(String[] args) throws LoginException, IOException { public static void main(String[] args) throws LoginException, IOException {
JDABuilder builder = new JDABuilder(AccountType.BOT); JDABuilder builder = new JDABuilder(AccountType.BOT);
String token = new String(Files.readAllBytes(Paths.get("token.txt"))); String token = new String(Files.readAllBytes(Paths.get("token.txt")));
builder.setToken(token); builder.setToken(token);
builder.setStatus(OnlineStatus.ONLINE); builder.setStatus(OnlineStatus.ONLINE);
builder.setActivity(Activity.playing("!play to play Sokoban!")); builder.setActivity(Activity.playing("@Sokobot for info!"));
builder.addEventListeners(new Commands()); builder.addEventListeners(new Commands());
builder.build(); builder.build();
} }
static void setPrefix(Guild guild, String prefix)
{
prefixes.put(guild, prefix);
}
static String getPrefix(Guild guild)
{
if (!prefixes.containsKey(guild))
{
return "!";
}
return prefixes.get(guild);
}
} }
+74 -17
View File
@@ -1,6 +1,10 @@
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -11,7 +15,15 @@ import java.util.HashMap;
public class Commands extends ListenerAdapter { public class Commands extends ListenerAdapter {
HashMap<User, Game> games = new HashMap<User, Game>(); HashMap<User, Game> games = new HashMap<User, Game>();
ArrayList<String> commands = new ArrayList<String>(Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r", Bot.prefix + "play", Bot.prefix + "continue", Bot.prefix + "stop")); ArrayList<String> commandsPrefix = new ArrayList<String>(Arrays.asList("play", "continue", "stop"));
ArrayList<String> commandsNoPrefix = new ArrayList<String>(Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r"));
public void onGuildLeave(GuildLeaveEvent event) //removes bot's stored prefix for a server if removed from that server
{
if (Bot.prefixes.containsKey(event.getGuild()))
{
Bot.prefixes.remove(event.getGuild());
}
}
public void onGuildMessageReceived(GuildMessageReceivedEvent event) public void onGuildMessageReceived(GuildMessageReceivedEvent event)
{ {
if (event.getAuthor().isBot() && event.getMessage().getEmbeds().get(0).getTitle().charAt(0) == 'L') if (event.getAuthor().isBot() && event.getMessage().getEmbeds().get(0).getTitle().charAt(0) == 'L')
@@ -21,26 +33,55 @@ public class Commands extends ListenerAdapter {
event.getMessage().addReaction("U+2B06").queue(); event.getMessage().addReaction("U+2B06").queue();
event.getMessage().addReaction("U+2B07").queue(); event.getMessage().addReaction("U+2B07").queue();
event.getMessage().addReaction("U+1F504").queue(); event.getMessage().addReaction("U+1F504").queue();
if (games.containsKey(event.getJDA().getUserById(event.getMessage().getEmbeds().get(0).getFields().get(0).getValue().substring(10, event.getMessage().getEmbeds().get(0).getFields().get(0).getValue().length() - 1))))
{
games.get(event.getJDA().getUserById(event.getMessage().getEmbeds().get(0).getFields().get(0).getValue().substring(10, event.getMessage().getEmbeds().get(0).getFields().get(0).getValue().length() - 1))).setGameMessage(event.getMessage());
}
return; return;
} }
String[] args = event.getMessage().getContentRaw().split("\\s+");
if (args[0].toLowerCase().equals(Bot.prefix + "info")) String[] args = event.getMessage().getContentRaw().split("\\s+");
if (args[0].toLowerCase().equals(Bot.getPrefix(event.getGuild()) + "prefix"))
{ {
event.getChannel().sendMessage(info().build()).queue(); if (event.getMember().hasPermission(Permission.ADMINISTRATOR)) {
if (args.length == 2 && args[1].length() == 1) {
Bot.setPrefix(event.getGuild(), args[1].toLowerCase());
event.getChannel().sendMessage("Prefix successfully changed to ``" + Bot.getPrefix(event.getGuild()) + "``.").queue();
} else {
event.getChannel().sendMessage("``" + args[1] + "`` is not a valid prefix!").queue();
}
event.getMessage().delete().queue();
}
else
{
event.getChannel().sendMessage(event.getAuthor().getAsMention() + ", you do not have permission to use this command.").queue();
}
} }
else if (commands.contains(args[0].toLowerCase())) else if ((commandsNoPrefix.contains(args[0].toLowerCase())) || (Character.toString(args[0].toLowerCase().charAt(0)).equals(Bot.getPrefix(event.getGuild())) && commandsPrefix.contains(args[0].toLowerCase().substring(1))))
{ {
if (!games.containsKey(event.getAuthor())) if (!games.containsKey(event.getAuthor()))
{ {
games.put(event.getAuthor(), new Game()); games.put(event.getAuthor(), new Game(event.getAuthor()));
} }
String userInput = args[0].toLowerCase(); String userInput = args[0].toLowerCase();
if (Character.toString(userInput.charAt(0)).equals(Bot.prefix)) if (Character.toString(userInput.charAt(0)).equals(Bot.getPrefix(event.getGuild())))
{ {
userInput = userInput.substring(1, userInput.length()); userInput = userInput.substring(1, userInput.length());
} }
games.get(event.getAuthor()).run(event.getChannel(), userInput); games.get(event.getAuthor()).run(event.getGuild(), event.getChannel(), userInput);
if (userInput.equals("stop")) //remove game from hashmap when player quits
{
if (games.containsKey(event.getAuthor()))
{
games.remove(event.getAuthor());
}
}
event.getMessage().delete().queue();
}
else if (args[0].toLowerCase().equals(Bot.getPrefix(event.getGuild()) + "info") || event.getMessage().getMentionedUsers().get(0).equals(event.getJDA().getSelfUser()))
{
event.getChannel().sendMessage(info(event.getGuild()).build()).queue();
event.getMessage().delete().queue();
} }
} }
public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) { public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) {
@@ -50,8 +91,9 @@ public class Commands extends ListenerAdapter {
} }
if (!games.containsKey(event.getMember().getUser())) if (!games.containsKey(event.getMember().getUser()))
{ {
games.put(event.getMember().getUser(), new Game()); games.put(event.getMember().getUser(), new Game(event.getMember().getUser()));
} }
boolean reactionCommand = true;
String userInput = ""; String userInput = "";
switch (event.getReactionEmote().toString()) switch (event.getReactionEmote().toString())
{ {
@@ -70,10 +112,16 @@ public class Commands extends ListenerAdapter {
case "RE:U+1f504": case "RE:U+1f504":
userInput = "r"; userInput = "r";
break; break;
default:
reactionCommand = false;
} }
games.get(event.getMember().getUser()).run(event.getChannel(), userInput); if (reactionCommand)
{
games.get(event.getMember().getUser()).run(event.getGuild(), event.getChannel(), userInput);
}
event.getReaction().removeReaction(event.getUser()).queue();
} }
EmbedBuilder info() EmbedBuilder info(Guild guild)
{ {
EmbedBuilder info = new EmbedBuilder(); EmbedBuilder info = new EmbedBuilder();
info.setTitle("Sokobot"); info.setTitle("Sokobot");
@@ -82,25 +130,34 @@ public class Commands extends ListenerAdapter {
info.setColor(0xdd2e53); info.setColor(0xdd2e53);
info.addField("How to Play", "You are a **Sokoban** :flushed:.\nYour job is to push **boxes** :brown_square: on top of their **destinations** :negative_squared_cross_mark:.", false); info.addField("How to Play", "You are a **Sokoban** :flushed:.\nYour job is to push **boxes** :brown_square: on top of their **destinations** :negative_squared_cross_mark:.", false);
info.addField("Features", ":white_small_square:**Infinite levels**\nThe maps in Sokobot are randomly generated, increasing in difficulty as you progress.\n:white_small_square:**Varied controls**\nSokobot has multiple control options to improve the player's experience, including reactions and wasd commands!\n:white_small_square:**Simultaneous games**\nThanks to the power of Java HashMaps:tm:, multiple users can use the bot at the same time without interfering with one another.", false); info.addField("Features", ":white_small_square:**Infinite levels**\nThe maps in Sokobot are randomly generated, increasing in difficulty as you progress.\n:white_small_square:**Varied controls**\nSokobot has multiple control options to improve the player's experience, including reactions and wasd commands!\n:white_small_square:**Simultaneous games**\nThanks to the power of Java HashMaps:tm:, multiple users can use the bot at the same time without interfering with one another.", false);
info.addField("Commands", ("``" + Bot.prefix + "play`` can be used to start a game if you are not currently in one.\n``" + Bot.prefix + "stop`` can be used to stop your active game at any time.\n``" + Bot.prefix + "info`` provides some useful details about the bot and rules of the game."), false); info.addField("Commands", ("``" + Bot.getPrefix(guild) + "play`` can be used to start a game if you are not currently in one.\n``" + Bot.getPrefix(guild) + "stop`` can be used to stop your active game at any time.\n``" + Bot.getPrefix(guild) + "info`` provides some useful details about the bot and rules of the game."), false);
info.addField("Source code", "https://github.com/PolyMarsDev/Sokobot", false); info.addField("Source code", "https://github.com/PolyMarsDev/Sokobot", false);
info.addField("Latest build", "https://github.com/PolyMarsDev/Sokobot/releases", false); info.addField("Latest build", "https://github.com/PolyMarsDev/Sokobot/releases", false);
info.setFooter("created by PolyMars", "https://avatars0.githubusercontent.com/u/51007356?s=460&u=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4"); info.setFooter("created by PolyMars", "https://avatars0.githubusercontent.com/u/51007356?s=460&u=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4");
return info; return info;
} }
public static void sendGameEmbed(MessageChannel channel, String level, String game) public static void sendGameEmbed(MessageChannel channel, String level, String game, User user)
{ {
EmbedBuilder embed = new EmbedBuilder(); EmbedBuilder embed = new EmbedBuilder();
embed.setTitle("Level " + level); embed.setTitle("Level " + level);
embed.setDescription(game); embed.setDescription(game);
embed.setFooter("Enter direction (up, down, left, right, or wasd) or r to reset"); embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "Player: " + user.getAsMention(), false);
channel.sendMessage(embed.build()).queue(); channel.sendMessage(embed.build()).queue();
} }
public static void sendWinEmbed(MessageChannel channel, String level) public static void updateGameEmbed(Message message, String level, String game, User user)
{
EmbedBuilder embed = new EmbedBuilder();
embed.setTitle("Level " + level);
embed.setDescription(game);
embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "Player: " + user.getAsMention(), false);
message.editMessage(embed.build()).queue();
}
public static void sendWinEmbed(Guild guild, Message message, String level)
{ {
EmbedBuilder embed = new EmbedBuilder(); EmbedBuilder embed = new EmbedBuilder();
embed.setTitle("You win!"); embed.setTitle("You win!");
embed.setDescription("Type ``" + Bot.prefix + "continue`` to continue to Level " + level + " or ``" + Bot.prefix + "stop`` to quit "); embed.setDescription("Type ``" + Bot.getPrefix(guild) + "continue`` to continue to Level " + level + " or ``" + Bot.getPrefix(guild) + "stop`` to quit ");
channel.sendMessage(embed.build()).queue(); embed.setFooter("You can also press any reaction to continue.");
message.editMessage(embed.build()).queue();
} }
} }
+20 -6
View File
@@ -1,11 +1,24 @@
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
public class Game { public class Game {
public Message gameMessage;
public User user;
boolean gameActive = false; boolean gameActive = false;
public int level = 1; public int level = 1;
int width = 9; int width = 9;
int height = 6; int height = 6;
Grid grid = new Grid(width, height, level); Grid grid = new Grid(width, height, level);
public Game(User user)
{
this.user = user;
}
public void setGameMessage(Message message)
{
gameMessage = message;
}
public void newGame(MessageChannel channel) public void newGame(MessageChannel channel)
{ {
if (!gameActive) if (!gameActive)
@@ -16,15 +29,16 @@ public class Game {
grid = new Grid(width, height, level); grid = new Grid(width, height, level);
gameActive = true; gameActive = true;
Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString()); Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString(), user);
} }
} }
public void run(MessageChannel channel, String userInput) public void run(Guild guild, MessageChannel channel, String userInput)
{ {
if (userInput.equals("stop") && gameActive) if (userInput.equals("stop") && gameActive)
{ {
channel.sendMessage("Thanks for playing!").queue(); channel.sendMessage("Thanks for playing, " + user.getAsMention() + "!").queue();
gameActive = false; gameActive = false;
} }
if (userInput.equals("play") && !gameActive) if (userInput.equals("play") && !gameActive)
@@ -53,7 +67,7 @@ public class Game {
grid.reset(); grid.reset();
} }
if (!grid.hasWon()) { //need to check again if (!grid.hasWon()) { //need to check again
Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString()); Commands.updateGameEmbed(gameMessage, String.valueOf(level), grid.toString(), user);
} }
} }
if (grid.hasWon()) { if (grid.hasWon()) {
@@ -65,7 +79,7 @@ public class Game {
{ {
height += 1; height += 1;
} }
Commands.sendWinEmbed(channel, String.valueOf(level)); Commands.sendWinEmbed(guild, gameMessage, String.valueOf(level));
grid = new Grid(width, height, level); grid = new Grid(width, height, level);
} }
} }
+1 -1
View File
@@ -97,7 +97,7 @@ public class Grid
int y = rand.nextInt(height - 4) + 2; int y = rand.nextInt(height - 4) + 2;
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
{ {
while ((x == boxes[j].getX() && y == boxes[j].getY()) || (x == 2 && y == 2)) while ((x == boxes[j].getX() && y == boxes[j].getY()) || (x == 2 && y == 2) || (x - 1 == boxes[j].getX() && y == boxes[j].getY()) || (x + 1 == boxes[j].getX() && y == boxes[j].getY()))
{ {
x = rand.nextInt(width - 4) + 2; x = rand.nextInt(width - 4) + 2;
y = rand.nextInt(height - 4) + 2; y = rand.nextInt(height - 4) + 2;