From 268a46f3f70307dcc47ab56d08659685114ec69b Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Thu, 6 Aug 2020 22:46:27 +0200 Subject: [PATCH 01/10] added database made prefixes database-stored --- src/main/java/Bot.java | 49 ++++++++++++++++- src/main/java/Commands.java | 2 +- src/main/java/Database.java | 107 ++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/main/java/Database.java diff --git a/src/main/java/Bot.java b/src/main/java/Bot.java index 574df11..b5f2480 100644 --- a/src/main/java/Bot.java +++ b/src/main/java/Bot.java @@ -8,6 +8,8 @@ import javax.security.auth.login.LoginException; import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.HashMap; import java.util.Scanner; @@ -15,6 +17,7 @@ public class Bot { static HashMap prefixes = new HashMap<>(); private static ShardManager shardManager; + private static Database database = null; public static void main(String[] args) throws LoginException { String token = null; @@ -22,7 +25,8 @@ public class Bot { File tokenFile = Paths.get("token.txt").toFile(); if (!tokenFile.exists()) { System.out.println("[ERROR] Could not find token.txt file"); - System.out.println("[ERROR] Please create a file called \"token.txt\" in the same folder as the jar " + "file and paste in your bot token."); + System.out.println("[ERROR] Please create a file called \"token.txt\" in the same folder as the jar " + + "file and paste in your bot token."); return; } token = new String(Files.readAllBytes(tokenFile.toPath())); @@ -30,6 +34,17 @@ public class Bot { ex.printStackTrace(); } if (token == null) return; + // database = new Database(Database.DBType.SQLite); + if (database != null) { + if (!database.isConnected()) { + database = null; + System.out.println("[ERROR] Database connection failed. Continuing without database."); + } else { + database.update( + "CREATE TABLE IF NOT EXISTS guildprefix (guildId VARCHAR(18) NOT NULL, prefix VARCHAR(8) NOT " + + "NULL);"); + } + } DefaultShardManagerBuilder builder = new DefaultShardManagerBuilder(token); builder.setStatus(OnlineStatus.ONLINE); builder.setActivity(Activity.playing("@Sokobot for info!")); @@ -54,6 +69,10 @@ public class Bot { if (cmd.equalsIgnoreCase("stop")) { System.out.println("Shutting down..."); shardManager.shutdown(); + if (database != null) { + System.out.println("Disconnecting database..."); + database.disconnect(); + } System.out.println("Bye!"); System.exit(0); return; @@ -65,11 +84,37 @@ public class Bot { return shardManager; } + static void removePrefix(long guildId) { + prefixes.remove(guildId); + if (database != null) { + database.update("DELETE FROM guildprefix WHERE guildId=?;", String.valueOf(guildId)); + } + } + static void setPrefix(Guild guild, String prefix) { prefixes.put(guild.getIdLong(), prefix); + if (database != null) { + database.update("DELETE FROM guildprefix WHERE guildId=?;", guild.getId()); + database.update("INSERT INTO guildprefix VALUES (?, ?);", guild.getId(), prefix); + } } static String getPrefix(Guild guild) { - return prefixes.getOrDefault(guild.getIdLong(), "!"); + if (prefixes.containsKey(guild.getIdLong())) return prefixes.get(guild.getIdLong()); + if (database != null) { + try (ResultSet rs = database.query("SELECT prefix FROM guildprefix WHERE guildId=?;", guild.getId())) { + if (rs.next()) { + String prefix = rs.getString("prefix"); + prefixes.put(guild.getIdLong(), prefix); + return prefix; + } + prefixes.put(guild.getIdLong(), "!"); + return "!"; + } catch (SQLException ex) { + System.out.println("[ERROR] Error at retrieving guild prefix of guild id " + guild.getId() + ": " + ex + .getMessage()); + } + } + return "!"; } } \ No newline at end of file diff --git a/src/main/java/Commands.java b/src/main/java/Commands.java index 69cc57c..ac28e11 100644 --- a/src/main/java/Commands.java +++ b/src/main/java/Commands.java @@ -18,7 +18,7 @@ public class Commands extends ListenerAdapter { @Override public void onGuildLeave(GuildLeaveEvent event) { Guild guild = event.getGuild(); - Bot.prefixes.remove(guild.getIdLong()); + Bot.removePrefix(guild.getIdLong()); } @Override diff --git a/src/main/java/Database.java b/src/main/java/Database.java new file mode 100644 index 0000000..7a43a8d --- /dev/null +++ b/src/main/java/Database.java @@ -0,0 +1,107 @@ +import java.sql.*; + +public class Database { + + enum DBType {MySQL, SQLite} + + /** + * SQLite Data + * Set this data if you use DBType#SQLite + * + * @param filePath This can either be a relative or absolute path. + * ex: sokobot.db + * or: C:/sqlite/db/sokobot.db + */ + private final String filePath = "sokobot.db"; + + /** + * MySQL Data + * Set this data if you use DBType#MySQL + */ + private final String mysql_hostname = "127.0.0.1"; + private final int mysql_port = 3306; + private final String mysql_database = "sokobot"; + private final String mysql_username = "sokobot"; + private final String mysql_password = "$€cUR€_P4sSw0R!)"; + + private Connection con = null; + + public Database(DBType dbType) { + try { + if (dbType == DBType.MySQL) { + con = DriverManager.getConnection( + "jdbc:mysql://" + mysql_hostname + ":" + mysql_port + "/" + mysql_database + + "?autoReconnect=true", mysql_username, mysql_password); + } else if(dbType == DBType.SQLite){ + con = DriverManager.getConnection("jdbc:sqlite:" + filePath); + } + } catch (Exception ex) { + System.out.println("[ERROR] Error at creating database connection: " + ex.getMessage()); + } + } + + public void disconnect() { + try { + con.clearWarnings(); + con.close(); + con = null; + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public Connection getCon() { + return con; + } + + public ResultSet query(String sql, Object... preparedParameters) { + try { + PreparedStatement ps = con.prepareStatement(sql); + int id = 1; + for (Object preparedParameter : preparedParameters) { + ps.setObject(id, preparedParameter); + id++; + } + return ps.executeQuery(); + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + public ResultSet query(String sql) { + try { + ResultSet rs = con.prepareStatement(sql).executeQuery(); + return rs; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + + public void update(String sql, Object... preparedParameters) { + try (PreparedStatement ps = con.prepareStatement(sql)) { + int id = 1; + for (Object preparedParameter : preparedParameters) { + ps.setObject(id, preparedParameter); + id++; + } + ps.executeUpdate(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public void update(String sql) { + try (PreparedStatement ps = con.prepareStatement(sql)) { + ps.executeUpdate(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public boolean isConnected() { + return con != null; + } +} \ No newline at end of file From 5f09809a6c39f8454c9f0e9901fe7782054f8a04 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Thu, 6 Aug 2020 22:49:09 +0200 Subject: [PATCH 02/10] some sqlite fixes/changes --- src/main/java/Database.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/Database.java b/src/main/java/Database.java index 7a43a8d..8a904ef 100644 --- a/src/main/java/Database.java +++ b/src/main/java/Database.java @@ -1,3 +1,4 @@ +import java.io.File; import java.sql.*; public class Database { @@ -9,8 +10,8 @@ public class Database { * Set this data if you use DBType#SQLite * * @param filePath This can either be a relative or absolute path. - * ex: sokobot.db - * or: C:/sqlite/db/sokobot.db + * ex: sokobot.db + * or: C:/sqlite/db/sokobot.db */ private final String filePath = "sokobot.db"; @@ -32,8 +33,16 @@ public class Database { con = DriverManager.getConnection( "jdbc:mysql://" + mysql_hostname + ":" + mysql_port + "/" + mysql_database + "?autoReconnect=true", mysql_username, mysql_password); - } else if(dbType == DBType.SQLite){ + System.out.println("[INFO] Successfully initialized databse connection."); + } else if (dbType == DBType.SQLite) { + File sqliteFile = new File(filePath); + if (!sqliteFile.exists()) { + System.out.println("[INFO] SQLite file \"" + filePath + "\" not found, creating file..."); + boolean create = sqliteFile.createNewFile(); + if (!create) System.out.println("[ERROR] Could not create SQLite file at " + filePath); + } con = DriverManager.getConnection("jdbc:sqlite:" + filePath); + System.out.println("[INFO] Successfully initialized databse connection."); } } catch (Exception ex) { System.out.println("[ERROR] Error at creating database connection: " + ex.getMessage()); From 6c3a422295392d6acf795d894eb55d6a52b5888c Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Fri, 7 Aug 2020 16:22:11 +0200 Subject: [PATCH 03/10] added packages cleaned up messy things fixed emoji not moving when prefix is a direction fixed input message get deleted when game not active fixed more errors maybe something else i forgot --- src/main/java/Commands.java | 221 ------------------ .../{ => me/polymarsdev/sokobot}/Bot.java | 42 +++- .../{ => me/polymarsdev/sokobot}/Game.java | 44 ++-- .../sokobot/commands/GameInputCommand.java | 47 ++++ .../sokobot/commands/InfoCommand.java | 53 +++++ .../sokobot/commands/PrefixCommand.java | 37 +++ .../sokobot/database}/Database.java | 6 +- .../polymarsdev/sokobot/entity/Command.java | 18 ++ .../polymarsdev/sokobot/entity}/Player.java | 4 + .../sokobot/event/CommandEvent.java | 159 +++++++++++++ .../sokobot/listener/CommandListener.java | 122 ++++++++++ .../sokobot/listener/GameListener.java | 68 ++++++ .../polymarsdev/sokobot/objects}/Box.java | 2 + .../sokobot/objects}/Destination.java | 21 +- .../polymarsdev/sokobot/objects}/Grid.java | 5 + .../polymarsdev/sokobot/objects}/Tile.java | 2 + .../me/polymarsdev/sokobot/util/GameUtil.java | 61 +++++ .../polymarsdev/sokobot/util}/Randomizer.java | 2 + src/main/resources/token.txt | 2 +- 19 files changed, 659 insertions(+), 257 deletions(-) delete mode 100644 src/main/java/Commands.java rename src/main/java/{ => me/polymarsdev/sokobot}/Bot.java (73%) rename src/main/java/{ => me/polymarsdev/sokobot}/Game.java (66%) create mode 100644 src/main/java/me/polymarsdev/sokobot/commands/GameInputCommand.java create mode 100644 src/main/java/me/polymarsdev/sokobot/commands/InfoCommand.java create mode 100644 src/main/java/me/polymarsdev/sokobot/commands/PrefixCommand.java rename src/main/java/{ => me/polymarsdev/sokobot/database}/Database.java (95%) create mode 100644 src/main/java/me/polymarsdev/sokobot/entity/Command.java rename src/main/java/{ => me/polymarsdev/sokobot/entity}/Player.java (96%) create mode 100644 src/main/java/me/polymarsdev/sokobot/event/CommandEvent.java create mode 100644 src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java create mode 100644 src/main/java/me/polymarsdev/sokobot/listener/GameListener.java rename src/main/java/{ => me/polymarsdev/sokobot/objects}/Box.java (97%) rename src/main/java/{ => me/polymarsdev/sokobot/objects}/Destination.java (51%) rename src/main/java/{ => me/polymarsdev/sokobot/objects}/Grid.java (97%) rename src/main/java/{ => me/polymarsdev/sokobot/objects}/Tile.java (97%) create mode 100644 src/main/java/me/polymarsdev/sokobot/util/GameUtil.java rename src/main/java/{ => me/polymarsdev/sokobot/util}/Randomizer.java (98%) diff --git a/src/main/java/Commands.java b/src/main/java/Commands.java deleted file mode 100644 index ac28e11..0000000 --- a/src/main/java/Commands.java +++ /dev/null @@ -1,221 +0,0 @@ -import com.vdurmont.emoji.EmojiManager; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.*; -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.react.GuildMessageReactionAddEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; - -import java.util.*; - -public class Commands extends ListenerAdapter { - HashMap games = new HashMap<>(); - ArrayList commandsPrefix = new ArrayList<>(Arrays.asList("play", "continue", "stop")); - ArrayList commandsNoPrefix = new ArrayList<>(Arrays.asList("w", "a", "s", "d", "up", "left", "down", - "right", "r")); - - @Override - public void onGuildLeave(GuildLeaveEvent event) { - Guild guild = event.getGuild(); - Bot.removePrefix(guild.getIdLong()); - } - - @Override - public void onGuildMessageReceived(GuildMessageReceivedEvent event) { - User user = event.getAuthor(); - Member member = event.getMember(); - Message message = event.getMessage(); - TextChannel channel = event.getChannel(); - Guild guild = event.getGuild(); - String prefix = Bot.getPrefix(guild); - if (user.getId().equals(event.getJDA().getSelfUser().getId())) { - List embeds = message.getEmbeds(); - if (embeds.size() > 0) { - MessageEmbed embed = embeds.get(0); - if (embed.getTitle() != null && embed.getTitle().length() > 0) { - if (embed.getTitle().startsWith("Sokobot | Level ")) { - message.addReaction("U+2B05").queue(); - message.addReaction("U+27A1").queue(); - message.addReaction("U+2B06").queue(); - message.addReaction("U+2B07").queue(); - message.addReaction("U+1F504").queue(); - MessageEmbed.Footer footerObject = embed.getFooter(); - if (footerObject != null) { - String footer = footerObject.getText(); - if (footer != null) { - long playerId = Long.parseLong(footer.substring(10, footer.length() - 1)); - if (games.containsKey(playerId)) { - Game game = games.get(playerId); - game.setGameMessage(message); - } - } - } - } - } - } - return; - } - String[] args = message.getContentRaw().split("\\s+"); - if (args.length > 0) { - String arg = args[0].toLowerCase(); - if (arg.equals(prefix + "prefix")) { - if (!hasPermissions(guild, channel)) { - sendInvalidPermissionsMessage(user, channel); - return; - } - if (member.hasPermission(Permission.ADMINISTRATOR)) { - if (args.length == 2 && args[1].length() == 1) { - String newPrefix = args[1].toLowerCase(); - Bot.setPrefix(event.getGuild(), newPrefix); - channel.sendMessage("Prefix successfully changed to ``" + newPrefix + "``.").queue(); - } else channel.sendMessage("The prefix must be one character long!").queue(); - } else - channel.sendMessage(user.getAsMention() + ", you do not have permission to use this " + "command" + ".").queue(); - // No need to delete prefix-set command - // message.delete().queue(); - } else if (((commandsNoPrefix.contains(arg)) || (arg.length() > 0 && Character.toString(arg.charAt(0)).equals(prefix) && commandsPrefix.contains(arg.substring(1))))) { - if (!hasPermissions(guild, channel)) { - sendInvalidPermissionsMessage(user, channel); - return; - } - Game game; - if (!games.containsKey(user.getIdLong())) { - game = new Game(user); - games.put(user.getIdLong(), game); - } else game = games.get(user.getIdLong()); - String userInput = arg; - if (userInput.substring(0, 1).equals(prefix)) userInput = userInput.substring(1); - if (!game.gameActive && userInput.equals("play") && args.length == 2 && EmojiManager.isEmoji(args[1])) { - game.setPlayerEmote(args[1]); - } - game.run(event.getGuild(), channel, userInput); - if (userInput.equals("stop")) games.remove(user.getIdLong()); - if (guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE)) message.delete().queue(); - } else if ((arg.equals(prefix + "info")) || (message.getMentionedUsers().size() > 0 && message.getMentionedUsers().get(0).equals(event.getJDA().getSelfUser()))) { - if (!hasPermissions(guild, channel)) { - sendInvalidPermissionsMessage(user, channel); - return; - } - channel.sendMessage(info(event.getGuild()).build()).queue(); - if (guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE)) message.delete().queue(); - } - } - } - - private static final Collection requiredPermissions = Arrays.asList(Permission.MESSAGE_ADD_REACTION, - Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_MANAGE, Permission.MESSAGE_WRITE); - - private boolean hasPermissions(Guild guild, TextChannel channel) { - Member self = guild.getSelfMember(); - if (self.hasPermission(Permission.ADMINISTRATOR)) return true; - return self.hasPermission(channel, requiredPermissions); - } - - private void sendInvalidPermissionsMessage(User user, TextChannel channel) { - if (channel.canTalk()) { - StringBuilder requiredPermissionsDisplay = new StringBuilder(); - for (Permission requiredPermission : requiredPermissions) { - requiredPermissionsDisplay.append("`").append(requiredPermission.getName()).append("`, "); - } - if (requiredPermissionsDisplay.toString().endsWith(", ")) - requiredPermissionsDisplay = new StringBuilder(requiredPermissionsDisplay.substring(0, - requiredPermissionsDisplay.length() - 2)); - channel.sendMessage(user.getAsMention() + ", I don't have enough permissions to work properly.\nMake " + - "sure I have the following permissions: " + requiredPermissionsDisplay + "\nIf you think this is " - + "an error, please contact a server administrator.").queue(); - } - } - - @Override - public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) { - User user = event.getUser(); - if (user.isBot()) { - return; - } - Guild guild = event.getGuild(); - MessageReaction reaction = event.getReaction(); - TextChannel channel = event.getChannel(); - channel.retrieveMessageById(event.getMessageId()).queue(message -> { - if (message.getAuthor().getId().equals(event.getJDA().getSelfUser().getId())) { - Game game; - if (!games.containsKey(user.getIdLong())) { - game = new Game(user); - games.put(user.getIdLong(), game); - } else game = games.get(user.getIdLong()); - boolean reactionCommand = true; - String userInput = ""; - switch (event.getReactionEmote().toString()) { - case "RE:U+2b05": - userInput = "left"; - break; - case "RE:U+27a1": - userInput = "right"; - break; - case "RE:U+2b06": - userInput = "up"; - break; - case "RE:U+2b07": - userInput = "down"; - break; - case "RE:U+1f504": - userInput = "r"; - break; - default: - reactionCommand = false; - break; - } - if (reactionCommand) game.run(guild, channel, userInput); - reaction.removeReaction(user).queue(); - } - }); - } - - EmbedBuilder info(Guild guild) { - EmbedBuilder info = new EmbedBuilder(); - info.setTitle("Sokobot"); - info.setThumbnail(guild.getSelfMember().getUser().getAvatarUrl()); - info.setDescription("Sokobot is a bot that lets you play Sokoban, the classic box-pushing puzzle game."); - 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("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.\n:white_small_square:**Custom prefixes**\nTo prevent Sokobot from conflicting with other " + "bots, admins can choose any single-character prefix to preface Sokobot's commands.", 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.\n``" + Bot.getPrefix(guild) + "prefix [character]`` can be used to " + "change the prefix the " + "bot responds to."), false); - info.addField("Add to your server", - "https://top.gg/bot/713635251703906336\nSokobot is currently in " + Bot.getShardManager().getGuilds().size() + " servers.", false); - info.addField("Source code", "https://github.com/PolyMarsDev/Sokobot", false); - info.setFooter("created by PolyMars", "https://avatars0.githubusercontent" + ".com/u/51007356?s=460&u" + - "=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4"); - return info; - } - - public static void sendGameEmbed(MessageChannel channel, String level, String game, User user) { - EmbedBuilder embed = new EmbedBuilder(); - embed.setTitle("Sokobot | Level " + level); - embed.setDescription(game); - embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); - embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); - channel.sendMessage(embed.build()).queue(); - } - - public static void updateGameEmbed(Message message, String level, String game, User user) { - EmbedBuilder embed = new EmbedBuilder(); - embed.setTitle("Sokobot | Level " + level); - embed.setDescription(game); - embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); - embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); - message.editMessage(embed.build()).queue(); - } - - public static void sendWinEmbed(Guild guild, Message message, String level) { - EmbedBuilder embed = new EmbedBuilder(); - embed.setTitle("Sokobot | You win!"); - embed.setDescription("Type ``" + Bot.getPrefix(guild) + "continue`` to continue to Level " + level + " or ``" + Bot.getPrefix(guild) + "stop`` to quit "); - embed.setFooter("You can also press any reaction to continue."); - message.editMessage(embed.build()).queue(); - } -} diff --git a/src/main/java/Bot.java b/src/main/java/me/polymarsdev/sokobot/Bot.java similarity index 73% rename from src/main/java/Bot.java rename to src/main/java/me/polymarsdev/sokobot/Bot.java index b5f2480..37f0c32 100644 --- a/src/main/java/Bot.java +++ b/src/main/java/me/polymarsdev/sokobot/Bot.java @@ -1,3 +1,8 @@ +package me.polymarsdev.sokobot; + +import me.polymarsdev.sokobot.database.Database; +import me.polymarsdev.sokobot.listener.CommandListener; +import me.polymarsdev.sokobot.listener.GameListener; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Guild; @@ -16,6 +21,15 @@ import java.util.Scanner; public class Bot { static HashMap prefixes = new HashMap<>(); + /** + * You can enable the database here. + * Set the DB Type to MySQL or SQLite, which you want to use. + * - + * You can configure login data in the Database class. + */ + private static final boolean enableDatabase = false; + private static final Database.DBType dbType = Database.DBType.SQLite; + private static ShardManager shardManager; private static Database database = null; @@ -25,16 +39,27 @@ public class Bot { File tokenFile = Paths.get("token.txt").toFile(); if (!tokenFile.exists()) { System.out.println("[ERROR] Could not find token.txt file"); - System.out.println("[ERROR] Please create a file called \"token.txt\" in the same folder as the jar " - + "file and paste in your bot token."); - return; + System.out.print("Please paste in your bot token: "); + Scanner s = new Scanner(System.in); + token = s.nextLine(); + System.out.println(); + System.out.println("[INFO] Creating token.txt - please wait"); + if (!tokenFile.createNewFile()) { + System.out.println( + "[ERROR] Could not create token.txt - please create this file and paste in your token" + + "."); + s.close(); + return; + } + Files.write(tokenFile.toPath(), token.getBytes()); + s.close(); } token = new String(Files.readAllBytes(tokenFile.toPath())); } catch (Exception ex) { ex.printStackTrace(); } if (token == null) return; - // database = new Database(Database.DBType.SQLite); + if (enableDatabase) database = new Database(dbType); if (database != null) { if (!database.isConnected()) { database = null; @@ -48,13 +73,14 @@ public class Bot { DefaultShardManagerBuilder builder = new DefaultShardManagerBuilder(token); builder.setStatus(OnlineStatus.ONLINE); builder.setActivity(Activity.playing("@Sokobot for info!")); - builder.addEventListeners(new Commands()); + builder.addEventListeners(new GameListener(), new CommandListener()); shardManager = builder.build(); Thread consoleThread = new Thread(() -> { Scanner s = new Scanner(System.in); while (s.hasNextLine()) { processCommand(s.nextLine()); } + s.close(); }); consoleThread.setDaemon(true); consoleThread.setName("Console Thread"); @@ -84,14 +110,14 @@ public class Bot { return shardManager; } - static void removePrefix(long guildId) { + public static void removePrefix(long guildId) { prefixes.remove(guildId); if (database != null) { database.update("DELETE FROM guildprefix WHERE guildId=?;", String.valueOf(guildId)); } } - static void setPrefix(Guild guild, String prefix) { + public static void setPrefix(Guild guild, String prefix) { prefixes.put(guild.getIdLong(), prefix); if (database != null) { database.update("DELETE FROM guildprefix WHERE guildId=?;", guild.getId()); @@ -99,7 +125,7 @@ public class Bot { } } - static String getPrefix(Guild guild) { + public static String getPrefix(Guild guild) { if (prefixes.containsKey(guild.getIdLong())) return prefixes.get(guild.getIdLong()); if (database != null) { try (ResultSet rs = database.query("SELECT prefix FROM guildprefix WHERE guildId=?;", guild.getId())) { diff --git a/src/main/java/Game.java b/src/main/java/me/polymarsdev/sokobot/Game.java similarity index 66% rename from src/main/java/Game.java rename to src/main/java/me/polymarsdev/sokobot/Game.java index c2d30f2..c06d1ce 100644 --- a/src/main/java/Game.java +++ b/src/main/java/me/polymarsdev/sokobot/Game.java @@ -1,3 +1,7 @@ +package me.polymarsdev.sokobot; + +import me.polymarsdev.sokobot.objects.Grid; +import me.polymarsdev.sokobot.util.GameUtil; import net.dv8tion.jda.api.entities.*; public class Game { @@ -32,7 +36,7 @@ public class Game { height = 6; grid = new Grid(width, height, level, playerEmote); gameActive = true; - Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString(), user); + GameUtil.sendGameEmbed(channel, String.valueOf(level), grid.toString(), user); } } @@ -41,27 +45,37 @@ public class Game { channel.sendMessage("Thanks for playing, " + user.getAsMention() + "!").queue(); gameActive = false; } - if (userInput.equals("play") && !gameActive) { newGame(channel); } else if (gameActive) { if (!grid.hasWon()) { String direction = userInput; - if (direction.equals("up") || direction.equals("w")) { - grid.getPlayer().moveUp(); - } else if (direction.equals("down") || direction.equals("s")) { - grid.getPlayer().moveDown(); - } else if (direction.equals("left") || direction.equals("a")) { - grid.getPlayer().moveLeft(); - } else if (direction.equals("right") || direction.equals("d")) { - grid.getPlayer().moveRight(); - } else if (direction.equals("r")) { - grid.reset(); + switch (direction) { + case "up": + case "w": + grid.getPlayer().moveUp(); + break; + case "down": + case "s": + grid.getPlayer().moveDown(); + break; + case "left": + case "a": + grid.getPlayer().moveLeft(); + break; + case "right": + case "d": + grid.getPlayer().moveRight(); + break; + case "r": + grid.reset(); + break; } if (!grid.hasWon()) { TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID); if (textChannel != null) { - textChannel.retrieveMessageById(gameMessageID).queue(gameMessage -> Commands.updateGameEmbed(gameMessage, String.valueOf(level), grid.toString(), user)); + textChannel.retrieveMessageById(gameMessageID).queue(gameMessage -> GameUtil + .updateGameEmbed(gameMessage, String.valueOf(level), grid.toString(), user)); } } } @@ -75,8 +89,8 @@ public class Game { } TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID); if (textChannel != null) { - textChannel.retrieveMessageById(gameMessageID).queue(gameMessage -> Commands.sendWinEmbed(guild, - gameMessage, String.valueOf(level))); + textChannel.retrieveMessageById(gameMessageID) + .queue(gameMessage -> GameUtil.sendWinEmbed(guild, gameMessage, String.valueOf(level))); } grid = new Grid(width, height, level, playerEmote); } diff --git a/src/main/java/me/polymarsdev/sokobot/commands/GameInputCommand.java b/src/main/java/me/polymarsdev/sokobot/commands/GameInputCommand.java new file mode 100644 index 0000000..aab59a6 --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/commands/GameInputCommand.java @@ -0,0 +1,47 @@ +package me.polymarsdev.sokobot.commands; + +import com.vdurmont.emoji.EmojiManager; +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.Game; +import me.polymarsdev.sokobot.entity.Command; +import me.polymarsdev.sokobot.event.CommandEvent; +import me.polymarsdev.sokobot.util.GameUtil; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.User; + +public class GameInputCommand extends Command { + + public GameInputCommand(String name) { + super(name); + } + + @Override + public void execute(CommandEvent event) { + User user = event.getAuthor(); + String[] args = event.getArgs(); + String prefix = Bot.getPrefix(event.getGuild()); + Game game; + if (!GameUtil.hasGame(user.getIdLong())) { + game = new Game(user); + GameUtil.setGame(user.getIdLong(), game); + } else game = GameUtil.getGame(user.getIdLong()); + // + String userInput = this.getName().toLowerCase(); + if (userInput.equals("play")) { + if (!game.gameActive) { + if (args.length > 0 && EmojiManager.isEmoji(args[0])) game.setPlayerEmote(args[0]); + } else { + event.reply(user.getAsMention() + ", you already have an active game.\nUse `" + prefix + + "stop` to stop your current game first."); + } + } + Guild guild = event.getGuild(); + TextChannel channel = event.getTextChannel(); + game.run(event.getGuild(), channel, userInput); + if (userInput.equals("stop")) GameUtil.removeGame(user.getIdLong()); + if (game.gameActive && guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE)) + event.getMessage().delete().queue(); + } +} diff --git a/src/main/java/me/polymarsdev/sokobot/commands/InfoCommand.java b/src/main/java/me/polymarsdev/sokobot/commands/InfoCommand.java new file mode 100644 index 0000000..df47eb2 --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/commands/InfoCommand.java @@ -0,0 +1,53 @@ +package me.polymarsdev.sokobot.commands; + +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.entity.Command; +import me.polymarsdev.sokobot.event.CommandEvent; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; + +public class InfoCommand extends Command { + + public InfoCommand() { + super("info"); + } + + @Override + public void execute(CommandEvent event) { + Guild guild = event.getGuild(); + EmbedBuilder info = new EmbedBuilder(); + final String prefix = Bot.getPrefix(guild); + info.setTitle("Sokobot"); + info.setThumbnail(guild.getSelfMember().getUser().getAvatarUrl()); + info.setDescription("Sokobot is a bot that lets you play Sokoban, the classic box-pushing puzzle game."); + 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("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.\n:white_small_square:**Custom prefixes**\nTo prevent Sokobot from conflicting with other " + + "bots, admins can choose any single-character prefix to preface Sokobot's commands.", false); + info.addField("Commands", + ("``" + prefix + "play`` can be used to start a game if you are not " + "currently in " + + "one.\n``" + prefix + "stop`` can be used to stop your active game at any " + + "time.\n``" + prefix + "info`` provides some useful details about the bot and " + + "rules of " + "the game.\n``" + Bot.getPrefix(guild) + + "prefix [character]`` can be used to " + "change the prefix the " + "bot responds to."), + false); + info.addField("Add to your server", + "https://top.gg/bot/713635251703906336\nSokobot is currently in " + Bot.getShardManager() + .getGuilds().size() + " servers.", false); + /* + // Official Support Server + info.addField("Support / Feedback", + "Official Support Server: https://invite.affluentproductions.org/apserver", false); + */ + info.addField("Source code", "https://github.com/PolyMarsDev/Sokobot", false); + info.setFooter("created by PolyMars", "https://avatars0.githubusercontent" + ".com/u/51007356?s=460&u" + + "=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4"); + event.reply(info.build()); + } +} diff --git a/src/main/java/me/polymarsdev/sokobot/commands/PrefixCommand.java b/src/main/java/me/polymarsdev/sokobot/commands/PrefixCommand.java new file mode 100644 index 0000000..7224d40 --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/commands/PrefixCommand.java @@ -0,0 +1,37 @@ +package me.polymarsdev.sokobot.commands; + +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.entity.Command; +import me.polymarsdev.sokobot.event.CommandEvent; +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.Member; +import net.dv8tion.jda.api.entities.User; + +public class PrefixCommand extends Command { + + public PrefixCommand() { + super("prefix"); + } + + @Override + public void execute(CommandEvent event) { + User user = event.getAuthor(); + Member member = event.getMember(); + String[] args = event.getArgs(); + if (args.length > 0) { + if (!member.hasPermission(Permission.ADMINISTRATOR)) { + event.reply(user.getAsMention() + ", you do not have permission to use this command."); + return; + } + String newPrefix = args[0].toLowerCase(); + if (newPrefix.length() > 1) { + event.reply(user.getAsMention() + ", the prefix must be one character long!"); + return; + } + Bot.setPrefix(event.getGuild(), newPrefix); + event.reply("Prefix successfully changed to ``" + newPrefix + "``."); + } + } +} diff --git a/src/main/java/Database.java b/src/main/java/me/polymarsdev/sokobot/database/Database.java similarity index 95% rename from src/main/java/Database.java rename to src/main/java/me/polymarsdev/sokobot/database/Database.java index 8a904ef..97162d5 100644 --- a/src/main/java/Database.java +++ b/src/main/java/me/polymarsdev/sokobot/database/Database.java @@ -1,15 +1,17 @@ +package me.polymarsdev.sokobot.database; + import java.io.File; import java.sql.*; public class Database { - enum DBType {MySQL, SQLite} + public enum DBType {MySQL, SQLite} /** * SQLite Data * Set this data if you use DBType#SQLite * - * @param filePath This can either be a relative or absolute path. + * field filePath - This can either be a relative or absolute path. * ex: sokobot.db * or: C:/sqlite/db/sokobot.db */ diff --git a/src/main/java/me/polymarsdev/sokobot/entity/Command.java b/src/main/java/me/polymarsdev/sokobot/entity/Command.java new file mode 100644 index 0000000..8d51a6d --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/entity/Command.java @@ -0,0 +1,18 @@ +package me.polymarsdev.sokobot.entity; + +import me.polymarsdev.sokobot.event.CommandEvent; + +public abstract class Command { + + private final String name; + + public Command(String name){ + this.name = name; + } + + public String getName() { + return name; + } + + public abstract void execute(CommandEvent commandEvent); +} diff --git a/src/main/java/Player.java b/src/main/java/me/polymarsdev/sokobot/entity/Player.java similarity index 96% rename from src/main/java/Player.java rename to src/main/java/me/polymarsdev/sokobot/entity/Player.java index aea126d..fead62e 100644 --- a/src/main/java/Player.java +++ b/src/main/java/me/polymarsdev/sokobot/entity/Player.java @@ -1,3 +1,7 @@ +package me.polymarsdev.sokobot.entity; + +import me.polymarsdev.sokobot.objects.Grid; + public class Player { int x = 0; diff --git a/src/main/java/me/polymarsdev/sokobot/event/CommandEvent.java b/src/main/java/me/polymarsdev/sokobot/event/CommandEvent.java new file mode 100644 index 0000000..fa3b7f7 --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/event/CommandEvent.java @@ -0,0 +1,159 @@ +package me.polymarsdev.sokobot.event; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class CommandEvent { + private static final int MAX_MESSAGES = 2; + + private final GuildMessageReceivedEvent event; + private String[] args; + + public CommandEvent(GuildMessageReceivedEvent event, String[] args) { + this.event = event; + this.args = args; + } + + public String[] getArgs() { + return args; + } + + public GuildMessageReceivedEvent getEvent() { + return event; + } + + public List getMentionedMembers() { + List mentionedMembers = new ArrayList<>(event.getMessage().getMentionedMembers()); + if (event.getMessage().getContentRaw().startsWith("<@!" + event.getJDA().getSelfUser().getId() + ">")) + mentionedMembers.remove(0); + return mentionedMembers; + } + + public void reply(String message) { + sendMessage(event.getChannel(), message); + } + + public void reply(String message, Consumer success) { + sendMessage(event.getChannel(), message, success); + } + + public void reply(String message, Consumer success, Consumer failure) { + sendMessage(event.getChannel(), message, success, failure); + } + + public void reply(MessageEmbed embed) { + event.getChannel().sendMessage(embed).queue(); + } + + public void reply(MessageEmbed embed, Consumer success) { + event.getChannel().sendMessage(embed).queue(success); + } + + public void reply(MessageEmbed embed, Consumer success, Consumer failure) { + event.getChannel().sendMessage(embed).queue(success, failure); + } + + public void reply(Message message) { + event.getChannel().sendMessage(message).queue(); + } + + public void reply(Message message, Consumer success) { + event.getChannel().sendMessage(message).queue(success); + } + + public void reply(Message message, Consumer success, Consumer failure) { + event.getChannel().sendMessage(message).queue(success, failure); + } + + public void reply(File file, String filename) { + event.getChannel().sendFile(file, filename).queue(); + } + + public void reply(String message, File file, String filename) { + String msg = message == null ? null : splitMessage(message).get(0); + if (msg == null) event.getChannel().sendFile(file, filename).queue(); + else event.getChannel().sendMessage(msg).addFile(file, filename).queue(); + } + + private void sendMessage(MessageChannel chan, String message) { + ArrayList messages = splitMessage(message); + for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) { + chan.sendMessage(messages.get(i)).queue(); + } + } + + private void sendMessage(MessageChannel chan, String message, Consumer success) { + ArrayList messages = splitMessage(message); + for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) { + if (i + 1 == MAX_MESSAGES || i + 1 == messages.size()) { + chan.sendMessage(messages.get(i)).queue(success); + } else { + chan.sendMessage(messages.get(i)).queue(); + } + } + } + + private void sendMessage(MessageChannel chan, String message, Consumer success, + Consumer failure) { + ArrayList messages = splitMessage(message); + for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) { + if (i + 1 == MAX_MESSAGES || i + 1 == messages.size()) { + chan.sendMessage(messages.get(i)).queue(success, failure); + } else { + chan.sendMessage(messages.get(i)).queue(); + } + } + } + + private static ArrayList splitMessage(String stringtoSend) { + ArrayList msgs = new ArrayList<>(); + if (stringtoSend != null) { + stringtoSend = stringtoSend.replace("@everyone", "@\u0435veryone").replace("@here", "@h\u0435re").trim(); + while (stringtoSend.length() > 2000) { + int leeway = 2000 - (stringtoSend.length() % 2000); + int index = stringtoSend.lastIndexOf("\n", 2000); + if (index < leeway) index = stringtoSend.lastIndexOf(" ", 2000); + if (index < leeway) index = 2000; + String temp = stringtoSend.substring(0, index).trim(); + if (!temp.equals("")) msgs.add(temp); + stringtoSend = stringtoSend.substring(index).trim(); + } + if (!stringtoSend.equals("")) msgs.add(stringtoSend); + } + return msgs; + } + + SelfUser getSelfUser() { + return event.getJDA().getSelfUser(); + } + + public User getAuthor() { + return event.getAuthor(); + } + + public Guild getGuild() { + return event.getGuild(); + } + + public JDA getJDA() { + return event.getJDA(); + } + + public Member getMember() { + return event.getMember(); + } + + public Message getMessage() { + return event.getMessage(); + } + + public TextChannel getTextChannel() { + return event.getChannel(); + } +} \ No newline at end of file diff --git a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java new file mode 100644 index 0000000..8759eab --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java @@ -0,0 +1,122 @@ +package me.polymarsdev.sokobot.listener; + +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.Game; +import me.polymarsdev.sokobot.commands.GameInputCommand; +import me.polymarsdev.sokobot.commands.InfoCommand; +import me.polymarsdev.sokobot.commands.PrefixCommand; +import me.polymarsdev.sokobot.entity.Command; +import me.polymarsdev.sokobot.event.CommandEvent; +import me.polymarsdev.sokobot.util.GameUtil; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import java.util.*; + +public class CommandListener extends ListenerAdapter { + private static final ArrayList commandsNoPrefix = new ArrayList<>( + Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r")); + private static final HashMap commands = new HashMap<>(); + + public CommandListener() { + List botCommands = new ArrayList<>(Arrays.asList(new InfoCommand(), new PrefixCommand())); + botCommands.addAll(Arrays.asList(new GameInputCommand("play"), new GameInputCommand("continue"), + new GameInputCommand("stop"))); + for (String cnp : commandsNoPrefix) botCommands.add(new GameInputCommand(cnp)); + for (Command command : botCommands) commands.put(command.getName().toLowerCase(), command); + System.out.println("[INFO] Loaded " + commands.size() + " commands"); + } + + + @Override + public void onGuildMessageReceived(GuildMessageReceivedEvent event) { + User user = event.getAuthor(); + Message message = event.getMessage(); + TextChannel channel = event.getChannel(); + Guild guild = event.getGuild(); + if (user.getId().equals(event.getJDA().getSelfUser().getId())) { + List embeds = message.getEmbeds(); + if (embeds.size() > 0) { + MessageEmbed embed = embeds.get(0); + if (embed.getTitle() != null && embed.getTitle().length() > 0) { + if (embed.getTitle().startsWith("Sokobot | Level ")) { + message.addReaction("U+2B05").queue(); + message.addReaction("U+27A1").queue(); + message.addReaction("U+2B06").queue(); + message.addReaction("U+2B07").queue(); + message.addReaction("U+1F504").queue(); + MessageEmbed.Footer footerObject = embed.getFooter(); + if (footerObject != null) { + String footer = footerObject.getText(); + if (footer != null) { + long playerId = Long.parseLong(footer.substring(10, footer.length() - 1)); + if (GameUtil.hasGame(playerId)) { + Game game = GameUtil.getGame(playerId); + game.setGameMessage(message); + } + } + } + } + } + } + return; + } + String msgRaw = message.getContentRaw(); + String[] args = msgRaw.split("\\s+"); + if (args.length > 0) { + boolean isMention = msgRaw.equals("<@" + event.getJDA().getSelfUser().getId() + ">") || msgRaw + .equals("<@!" + event.getJDA().getSelfUser().getId() + ">"); + String prefix = Bot.getPrefix(guild); + String arg = args[0].toLowerCase(); + boolean isCommand; + if (arg.startsWith(prefix)) { + if (commandsNoPrefix.contains(arg)) { + isCommand = true; + } else { + String commandName = arg.substring(prefix.length()).toLowerCase(); + isCommand = commands.containsKey(commandName); + if (isCommand) arg = commandName; + } + } else { + isCommand = commandsNoPrefix.contains(arg); + } + if (isCommand) { + if (!hasPermissions(guild, channel)) { + sendInvalidPermissionsMessage(user, channel); + return; + } + Command command = commands.get(arg); + if (isMention) command = commands.get("info"); + if (command == null) return; + command.execute(new CommandEvent(event, Arrays.copyOfRange(msgRaw.split("\\s+"), 1, args.length))); + } + } + } + + private static final Collection requiredPermissions = Arrays + .asList(Permission.MESSAGE_ADD_REACTION, Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_MANAGE, + Permission.MESSAGE_WRITE); + + private boolean hasPermissions(Guild guild, TextChannel channel) { + Member self = guild.getSelfMember(); + if (self.hasPermission(Permission.ADMINISTRATOR)) return true; + return self.hasPermission(channel, requiredPermissions); + } + + private void sendInvalidPermissionsMessage(User user, TextChannel channel) { + if (channel.canTalk()) { + StringBuilder requiredPermissionsDisplay = new StringBuilder(); + for (Permission requiredPermission : requiredPermissions) { + requiredPermissionsDisplay.append("`").append(requiredPermission.getName()).append("`, "); + } + if (requiredPermissionsDisplay.toString().endsWith(", ")) requiredPermissionsDisplay = new StringBuilder( + requiredPermissionsDisplay.substring(0, requiredPermissionsDisplay.length() - 2)); + channel.sendMessage(user.getAsMention() + ", I don't have enough permissions to work properly.\nMake " + + "sure I have the following permissions: " + requiredPermissionsDisplay + + "\nIf you think this is " + + "an error, please contact a server administrator.").queue(); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/polymarsdev/sokobot/listener/GameListener.java b/src/main/java/me/polymarsdev/sokobot/listener/GameListener.java new file mode 100644 index 0000000..c98577d --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/listener/GameListener.java @@ -0,0 +1,68 @@ +package me.polymarsdev.sokobot.listener; + +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.Game; +import me.polymarsdev.sokobot.util.GameUtil; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageReaction; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.guild.GuildLeaveEvent; +import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +public class GameListener extends ListenerAdapter { + + @Override + public void onGuildLeave(GuildLeaveEvent event) { + Guild guild = event.getGuild(); + Bot.removePrefix(guild.getIdLong()); + } + + @Override + public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) { + User user = event.getUser(); + if (user.isBot()) { + return; + } + Guild guild = event.getGuild(); + MessageReaction reaction = event.getReaction(); + TextChannel channel = event.getChannel(); + channel.retrieveMessageById(event.getMessageId()).queue(message -> { + if (message.getAuthor().getId().equals(event.getJDA().getSelfUser().getId())) { + Game game; + if (!GameUtil.hasGame(user.getIdLong())) { + game = new Game(user); + GameUtil.setGame(user.getIdLong(), game); + } else game = GameUtil.getGame(user.getIdLong()); + boolean reactionCommand = true; + String userInput = ""; + switch (event.getReactionEmote().toString()) { + case "RE:U+2b05": + userInput = "left"; + break; + case "RE:U+27a1": + userInput = "right"; + break; + case "RE:U+2b06": + userInput = "up"; + break; + case "RE:U+2b07": + userInput = "down"; + break; + case "RE:U+1f504": + userInput = "r"; + break; + default: + reactionCommand = false; + break; + } + if (reactionCommand) game.run(guild, channel, userInput); + if (guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE)) + reaction.removeReaction(user).queue(); + } + }); + } + +} diff --git a/src/main/java/Box.java b/src/main/java/me/polymarsdev/sokobot/objects/Box.java similarity index 97% rename from src/main/java/Box.java rename to src/main/java/me/polymarsdev/sokobot/objects/Box.java index c39dfaa..6432c0b 100644 --- a/src/main/java/Box.java +++ b/src/main/java/me/polymarsdev/sokobot/objects/Box.java @@ -1,3 +1,5 @@ +package me.polymarsdev.sokobot.objects; + public class Box { int x = 0; diff --git a/src/main/java/Destination.java b/src/main/java/me/polymarsdev/sokobot/objects/Destination.java similarity index 51% rename from src/main/java/Destination.java rename to src/main/java/me/polymarsdev/sokobot/objects/Destination.java index fd9f93d..128282f 100644 --- a/src/main/java/Destination.java +++ b/src/main/java/me/polymarsdev/sokobot/objects/Destination.java @@ -1,24 +1,25 @@ -public class Destination -{ +package me.polymarsdev.sokobot.objects; + +public class Destination { int x = 0; int y = 0; Grid currentGrid; - public Destination(int x, int y, Grid currentGrid) - { + + public Destination(int x, int y, Grid currentGrid) { this.x = x; this.y = y; this.currentGrid = currentGrid; } - public boolean hasBox(Grid currentGrid) - { + + public boolean hasBox(Grid currentGrid) { return currentGrid.isWall(x, y); } - public int getX() - { + + public int getX() { return x; } - public int getY() - { + + public int getY() { return y; } } diff --git a/src/main/java/Grid.java b/src/main/java/me/polymarsdev/sokobot/objects/Grid.java similarity index 97% rename from src/main/java/Grid.java rename to src/main/java/me/polymarsdev/sokobot/objects/Grid.java index d352b12..2f8d32a 100644 --- a/src/main/java/Grid.java +++ b/src/main/java/me/polymarsdev/sokobot/objects/Grid.java @@ -1,3 +1,8 @@ +package me.polymarsdev.sokobot.objects; + +import me.polymarsdev.sokobot.entity.Player; +import me.polymarsdev.sokobot.util.Randomizer; + public class Grid { final int GROUND = 0; diff --git a/src/main/java/Tile.java b/src/main/java/me/polymarsdev/sokobot/objects/Tile.java similarity index 97% rename from src/main/java/Tile.java rename to src/main/java/me/polymarsdev/sokobot/objects/Tile.java index 6c88636..81f2235 100644 --- a/src/main/java/Tile.java +++ b/src/main/java/me/polymarsdev/sokobot/objects/Tile.java @@ -1,3 +1,5 @@ +package me.polymarsdev.sokobot.objects; + public class Tile { final int GROUND = 0; diff --git a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java new file mode 100644 index 0000000..f3cc4ed --- /dev/null +++ b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java @@ -0,0 +1,61 @@ +package me.polymarsdev.sokobot.util; + +import me.polymarsdev.sokobot.Bot; +import me.polymarsdev.sokobot.Game; +import net.dv8tion.jda.api.EmbedBuilder; +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.User; + +import java.util.HashMap; + +public class GameUtil { + + private static final HashMap games = new HashMap<>(); + + public static void setGame(long userId, Game game) { + games.put(userId, game); + } + + public static boolean hasGame(long userId) { + return games.containsKey(userId); + } + + public static Game getGame(long userId) { + return games.get(userId); + } + + public static void removeGame(long userId) { + games.remove(userId); + } + + public static void sendGameEmbed(MessageChannel channel, String level, String game, User user) { + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("Sokobot | Level " + level); + embed.setDescription(game); + embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); + embed.addField("Player", user.getAsMention(), false); + embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); + channel.sendMessage(embed.build()).queue(); + } + + public static void updateGameEmbed(Message message, String level, String game, User user) { + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("Sokobot | Level " + level); + embed.setDescription(game); + embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); + embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); + message.editMessage(embed.build()).queue(); + } + + public static void sendWinEmbed(Guild guild, Message message, String level) { + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("Sokobot | You win!"); + embed.setDescription( + "Type ``" + Bot.getPrefix(guild) + "continue`` to continue to Level " + level + " or ``" + Bot + .getPrefix(guild) + "stop`` to quit "); + embed.setFooter("You can also press any reaction to continue."); + message.editMessage(embed.build()).queue(); + } +} diff --git a/src/main/java/Randomizer.java b/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java similarity index 98% rename from src/main/java/Randomizer.java rename to src/main/java/me/polymarsdev/sokobot/util/Randomizer.java index ad762ad..6ffb385 100644 --- a/src/main/java/Randomizer.java +++ b/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java @@ -1,3 +1,5 @@ +package me.polymarsdev.sokobot.util; + import java.util.*; public class Randomizer{ diff --git a/src/main/resources/token.txt b/src/main/resources/token.txt index 2755fc0..ae0e28a 100644 --- a/src/main/resources/token.txt +++ b/src/main/resources/token.txt @@ -1 +1 @@ -replace this with your Discord bot token (https://discord.com/developers/applications) \ No newline at end of file +NzQwMzAzNzMzODM3MDA0OTEw.XynDlA.YutDr_eKx12ohzNurCm4ZqLMJDY \ No newline at end of file From 7e6d12700af4f3260307cea2c37f7bcd0ae24553 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Fri, 7 Aug 2020 16:23:19 +0200 Subject: [PATCH 04/10] accidently pushed my test-token --- src/main/resources/token.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/token.txt b/src/main/resources/token.txt index ae0e28a..2755fc0 100644 --- a/src/main/resources/token.txt +++ b/src/main/resources/token.txt @@ -1 +1 @@ -NzQwMzAzNzMzODM3MDA0OTEw.XynDlA.YutDr_eKx12ohzNurCm4ZqLMJDY \ No newline at end of file +replace this with your Discord bot token (https://discord.com/developers/applications) \ No newline at end of file From f336fe364bf031ac6fb3a49aecfaab8b01a7528f Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Fri, 7 Aug 2020 16:27:27 +0200 Subject: [PATCH 05/10] finished changes i just started but forgot to finish --- .../sokobot/listener/CommandListener.java | 18 ++++++++++-------- .../me/polymarsdev/sokobot/util/GameUtil.java | 3 +-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java index 8759eab..bf28863 100644 --- a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java +++ b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java @@ -47,14 +47,16 @@ public class CommandListener extends ListenerAdapter { message.addReaction("U+2B06").queue(); message.addReaction("U+2B07").queue(); message.addReaction("U+1F504").queue(); - MessageEmbed.Footer footerObject = embed.getFooter(); - if (footerObject != null) { - String footer = footerObject.getText(); - if (footer != null) { - long playerId = Long.parseLong(footer.substring(10, footer.length() - 1)); - if (GameUtil.hasGame(playerId)) { - Game game = GameUtil.getGame(playerId); - game.setGameMessage(message); + List fields = embed.getFields(); + for (MessageEmbed.Field field : fields) { + if (field.getName() != null && field.getName().equals("Player")) { + if (field.getValue() != null) { + long playerId = Long + .parseLong(field.getValue().substring(2, field.getValue().length() - 1)); + if (GameUtil.hasGame(playerId)) { + Game game = GameUtil.getGame(playerId); + game.setGameMessage(message); + } } } } diff --git a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java index f3cc4ed..fb3a4ca 100644 --- a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java +++ b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java @@ -36,7 +36,6 @@ public class GameUtil { embed.setDescription(game); embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); embed.addField("Player", user.getAsMention(), false); - embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); channel.sendMessage(embed.build()).queue(); } @@ -45,7 +44,7 @@ public class GameUtil { embed.setTitle("Sokobot | Level " + level); embed.setDescription(game); embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); - embed.setFooter("Game of " + user.getAsMention(), user.getAvatarUrl()); + embed.addField("Player", user.getAsMention(), false); message.editMessage(embed.build()).queue(); } From 44c4a944a006fe0725b1bfe01a96052eea7d534c Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Fri, 7 Aug 2020 16:29:36 +0200 Subject: [PATCH 06/10] hotfix console error --- src/main/java/me/polymarsdev/sokobot/Bot.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/me/polymarsdev/sokobot/Bot.java b/src/main/java/me/polymarsdev/sokobot/Bot.java index 37f0c32..71dbcf8 100644 --- a/src/main/java/me/polymarsdev/sokobot/Bot.java +++ b/src/main/java/me/polymarsdev/sokobot/Bot.java @@ -80,7 +80,6 @@ public class Bot { while (s.hasNextLine()) { processCommand(s.nextLine()); } - s.close(); }); consoleThread.setDaemon(true); consoleThread.setName("Console Thread"); From 971a9c33281a6be2423ed0c68b6334a8950c77d9 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Fri, 7 Aug 2020 16:49:18 +0200 Subject: [PATCH 07/10] fixed @mention not responding --- .../sokobot/listener/CommandListener.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java index bf28863..2a83509 100644 --- a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java +++ b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java @@ -73,16 +73,19 @@ public class CommandListener extends ListenerAdapter { String prefix = Bot.getPrefix(guild); String arg = args[0].toLowerCase(); boolean isCommand; - if (arg.startsWith(prefix)) { - if (commandsNoPrefix.contains(arg)) { - isCommand = true; + if (isMention) isCommand = true; + else { + if (arg.startsWith(prefix)) { + if (commandsNoPrefix.contains(arg)) { + isCommand = true; + } else { + String commandName = arg.substring(prefix.length()).toLowerCase(); + isCommand = commands.containsKey(commandName); + if (isCommand) arg = commandName; + } } else { - String commandName = arg.substring(prefix.length()).toLowerCase(); - isCommand = commands.containsKey(commandName); - if (isCommand) arg = commandName; + isCommand = commandsNoPrefix.contains(arg); } - } else { - isCommand = commandsNoPrefix.contains(arg); } if (isCommand) { if (!hasPermissions(guild, channel)) { From 718d2b6c15fe312a1305196194378491a0d444d0 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Sun, 9 Aug 2020 15:09:45 +0200 Subject: [PATCH 08/10] fixed player spawning in a block added mr command to reset the map if map is impossible --- .../java/me/polymarsdev/sokobot/Game.java | 3 + .../me/polymarsdev/sokobot/entity/Player.java | 83 ++++---- .../sokobot/listener/CommandListener.java | 2 +- .../me/polymarsdev/sokobot/objects/Grid.java | 193 ++++++++---------- .../me/polymarsdev/sokobot/util/GameUtil.java | 10 +- .../polymarsdev/sokobot/util/Randomizer.java | 4 - 6 files changed, 140 insertions(+), 155 deletions(-) diff --git a/src/main/java/me/polymarsdev/sokobot/Game.java b/src/main/java/me/polymarsdev/sokobot/Game.java index c06d1ce..8009140 100644 --- a/src/main/java/me/polymarsdev/sokobot/Game.java +++ b/src/main/java/me/polymarsdev/sokobot/Game.java @@ -67,6 +67,9 @@ public class Game { case "d": grid.getPlayer().moveRight(); break; + case "mr": + grid.resetMap(); + break; case "r": grid.reset(); break; diff --git a/src/main/java/me/polymarsdev/sokobot/entity/Player.java b/src/main/java/me/polymarsdev/sokobot/entity/Player.java index fead62e..706ef26 100644 --- a/src/main/java/me/polymarsdev/sokobot/entity/Player.java +++ b/src/main/java/me/polymarsdev/sokobot/entity/Player.java @@ -2,38 +2,42 @@ package me.polymarsdev.sokobot.entity; import me.polymarsdev.sokobot.objects.Grid; -public class Player -{ +public class Player { int x = 0; int y = 0; Grid currentGrid; - public Player(int x, int y, Grid currentGrid) - { + + public Player(int x, int y, Grid currentGrid) { this.x = x; this.y = y; this.currentGrid = currentGrid; } - public int getX() - { + + public int getX() { return x; } - public int getY() - { + + public int getY() { return y; } - public void setPosition(int x, int y) - { - this.x = x; - this.y = y; + + public void resetPosition() { + int setX = 2; + int setY = 2; + while (currentGrid.isBoxRaw(setX, setY)) { + if (setX >= currentGrid.getWidth() - 1) { + setY++; + setX = 1; + } else setX++; + } + this.x = setX; + this.y = setY; } - public boolean moveUp() - { - if (!currentGrid.isWall(x, y - 1)) - { - if (currentGrid.isBox(x, y - 1)) - { - if (currentGrid.getBox(x, y - 1).moveUp()) - { + + public boolean moveUp() { + if (!currentGrid.isWall(x, y - 1)) { + if (currentGrid.isBox(x, y - 1)) { + if (currentGrid.getBox(x, y - 1).moveUp()) { y -= 1; return true; } @@ -44,14 +48,11 @@ public class Player } return false; } - public boolean moveDown() - { - if (!currentGrid.isWall(x, y + 1)) - { - if (currentGrid.isBox(x, y + 1)) - { - if (currentGrid.getBox(x, y + 1).moveDown()) - { + + public boolean moveDown() { + if (!currentGrid.isWall(x, y + 1)) { + if (currentGrid.isBox(x, y + 1)) { + if (currentGrid.getBox(x, y + 1).moveDown()) { y += 1; return true; } @@ -62,14 +63,11 @@ public class Player } return false; } - public boolean moveLeft() - { - if (!currentGrid.isWall(x - 1, y)) - { - if (currentGrid.isBox(x - 1, y)) - { - if (currentGrid.getBox(x - 1, y).moveLeft()) - { + + public boolean moveLeft() { + if (!currentGrid.isWall(x - 1, y)) { + if (currentGrid.isBox(x - 1, y)) { + if (currentGrid.getBox(x - 1, y).moveLeft()) { x -= 1; return true; } @@ -80,14 +78,11 @@ public class Player } return false; } - public boolean moveRight() - { - if (!currentGrid.isWall(x + 1, y)) - { - if (currentGrid.isBox(x + 1, y)) - { - if (currentGrid.getBox(x + 1, y).moveRight()) - { + + public boolean moveRight() { + if (!currentGrid.isWall(x + 1, y)) { + if (currentGrid.isBox(x + 1, y)) { + if (currentGrid.getBox(x + 1, y).moveRight()) { x += 1; return true; } diff --git a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java index 2a83509..5c466c0 100644 --- a/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java +++ b/src/main/java/me/polymarsdev/sokobot/listener/CommandListener.java @@ -17,7 +17,7 @@ import java.util.*; public class CommandListener extends ListenerAdapter { private static final ArrayList commandsNoPrefix = new ArrayList<>( - Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r")); + Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r", "mr")); private static final HashMap commands = new HashMap<>(); public CommandListener() { diff --git a/src/main/java/me/polymarsdev/sokobot/objects/Grid.java b/src/main/java/me/polymarsdev/sokobot/objects/Grid.java index 2f8d32a..64c947a 100644 --- a/src/main/java/me/polymarsdev/sokobot/objects/Grid.java +++ b/src/main/java/me/polymarsdev/sokobot/objects/Grid.java @@ -3,8 +3,7 @@ package me.polymarsdev.sokobot.objects; import me.polymarsdev.sokobot.entity.Player; import me.polymarsdev.sokobot.util.Randomizer; -public class Grid -{ +public class Grid { final int GROUND = 0; final int WALL = 1; final int BOX = 2; @@ -20,15 +19,13 @@ public class Grid int color = 0; Player player; String playerEmote; - Randomizer rand = new Randomizer(); - public Grid(int width, int height, int boxCount, String playerEmote) //create a random grid with specific width, height, and number of boxes + + public Grid(int width, int height, int boxCount, String playerEmote) //create a random grid with specific width, + // height, and number of boxes { this.playerEmote = playerEmote; player = new Player(2, 2, this); - if (boxCount > MAX_BOXES) - { - boxCount = MAX_BOXES; - } + if (boxCount > MAX_BOXES) boxCount = MAX_BOXES; this.boxCount = boxCount; boxes = new Box[boxCount]; destinations = new Destination[boxCount]; @@ -37,127 +34,119 @@ public class Grid grid = new Tile[width][height]; createBoxes(); createDestinations(); + player.resetPosition(); updateGrid(); } - public Player getPlayer() - { + + public Player getPlayer() { return player; } - public void reset() - { - player.setPosition(2, 2); - for (int i = 0; i < boxCount; i++) - { + + public void reset() { + for (int i = 0; i < boxCount; i++) { boxes[i].reset(); } + player.resetPosition(); updateGrid(); } - public void setStatus(int x, int y, int status) - { + + public void resetMap() { + boxes = new Box[boxCount]; + destinations = new Destination[boxCount]; + createBoxes(); + createDestinations(); + player.resetPosition(); + updateGrid(); + } + + public void setStatus(int x, int y, int status) { grid[x][y].setStatus(status); } - public int getStatus(int x, int y) - { + + public int getStatus(int x, int y) { return grid[x][y].getStatus(); } - public boolean isWall(int x, int y) - { + + public boolean isWall(int x, int y) { return grid[x][y].getStatus() == WALL; } - public boolean isBox(int x, int y) - { + + public boolean isBox(int x, int y) { return grid[x][y].getStatus() == BOX; } + public boolean isBoxRaw(int x, int y) //allows you to check if a box is at a position before grid is set up { - for (int i = 0; i < boxCount; i++) - { - if (x == boxes[i].getX() && y == boxes[i].getY()) - { + for (int i = 0; i < boxCount; i++) { + if (x == boxes[i].getX() && y == boxes[i].getY()) { return true; } } return false; } - public boolean isDestination(int x, int y) - { + + public boolean isDestination(int x, int y) { return grid[x][y].getStatus() == DESTINATION; } - public Box getBox(int x, int y) - { - for (int i = 0; i < boxCount; i++) - { - if (x == boxes[i].getX() && y == boxes[i].getY()) - { + + public Box getBox(int x, int y) { + for (int i = 0; i < boxCount; i++) { + if (x == boxes[i].getX() && y == boxes[i].getY()) { return boxes[i]; } } return null; } - public void createBoxes() - { - color = rand.nextInt(6); //runs after each level - for (int i = 0; i < boxCount; i++) - { - int x = rand.nextInt(width - 4) + 2; - int y = rand.nextInt(height - 4) + 2; - for (int j = 0; j < i; j++) - { - 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; - y = rand.nextInt(height - 4) + 2; + + public void createBoxes() { + color = Randomizer.nextInt(6); //runs after each level + for (int i = 0; i < boxCount; i++) { + int x = Randomizer.nextInt(width - 4) + 2; + int y = Randomizer.nextInt(height - 4) + 2; + for (int j = 0; j < i; j++) { + 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 = Randomizer.nextInt(width - 4) + 2; + y = Randomizer.nextInt(height - 4) + 2; } } boxes[i] = new Box(x, y, this); } } - public void createDestinations() - { - for (int i = 0; i < boxCount; i++) - { - int x = rand.nextInt(width - 2) + 1; - int y = rand.nextInt(height - 2) + 1; - for (int j = 0; j < i; j++) - { - while (((x == destinations[j].getX() && y == destinations[j].getY())) || isBoxRaw(x, y)) - { - x = rand.nextInt(width - 2) + 1; - y = rand.nextInt(height - 2) + 1; + + public void createDestinations() { + for (int i = 0; i < boxCount; i++) { + int x = Randomizer.nextInt(width - 2) + 1; + int y = Randomizer.nextInt(height - 2) + 1; + for (int j = 0; j < i; j++) { + while (((x == destinations[j].getX() && y == destinations[j].getY())) || isBoxRaw(x, y)) { + x = Randomizer.nextInt(width - 2) + 1; + y = Randomizer.nextInt(height - 2) + 1; } } destinations[i] = new Destination(x, y, this); } } - public void updateGrid() - { - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { + + public void updateGrid() { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { grid[j][i] = new Tile(GROUND, playerEmote); - if (j == 0 || j == width - 1 || i == 0 || i == height - 1) - { + if (j == 0 || j == width - 1 || i == 0 || i == height - 1) { grid[j][i] = new Tile(WALL, color, playerEmote); } - for (int k = 0; k < boxCount; k++) - { - if (destinations[k].getX() == j && destinations[k].getY() == i) - { + for (int k = 0; k < boxCount; k++) { + if (destinations[k].getX() == j && destinations[k].getY() == i) { grid[j][i] = new Tile(DESTINATION, playerEmote); } } - if (player.getX() == j && player.getY() == i) - { + if (player.getX() == j && player.getY() == i) { grid[j][i] = new Tile(PLAYER, playerEmote); } - for (int k = 0; k < boxCount; k++) - { - if (boxes[k].getX() == j && boxes[k].getY() == i) - { - if (boxes[k].onDestination()) - { + for (int k = 0; k < boxCount; k++) { + if (boxes[k].getX() == j && boxes[k].getY() == i) { + if (boxes[k].onDestination()) { grid[j][i] = new Tile(WALL, color, playerEmote); } else { grid[j][i] = new Tile(BOX, playerEmote); @@ -168,49 +157,45 @@ public class Grid } } } - public boolean hasWon() - { - for (int i = 0; i < boxCount; i++) - { - if (!destinations[i].hasBox(this)) - { + + public boolean hasWon() { + for (int i = 0; i < boxCount; i++) { + if (!destinations[i].hasBox(this)) { return false; } } return true; } - public Tile[][] getGrid() - { + + public Tile[][] getGrid() { return grid; } - public Box[] getBoxes() - { + + public Box[] getBoxes() { return boxes; } - public Destination[] getDestinations() - { + + public Destination[] getDestinations() { return destinations; } - public int getBoxCount() - { + + public int getBoxCount() { return boxCount; } - public int getHeight() - { + + public int getHeight() { return height; } - public int getWidth() - { + + public int getWidth() { return width; } - public String toString() - { + + public String toString() { updateGrid(); String result = ""; - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { result += grid[j][i]; } result += "\n"; diff --git a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java index fb3a4ca..f5c6ef2 100644 --- a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java +++ b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java @@ -34,7 +34,10 @@ public class GameUtil { EmbedBuilder embed = new EmbedBuilder(); embed.setTitle("Sokobot | Level " + level); embed.setDescription(game); - embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); + embed.addField( + "Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " + + "recreate the map", + "", false); embed.addField("Player", user.getAsMention(), false); channel.sendMessage(embed.build()).queue(); } @@ -43,7 +46,10 @@ public class GameUtil { EmbedBuilder embed = new EmbedBuilder(); embed.setTitle("Sokobot | Level " + level); embed.setDescription(game); - embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``) or ``r`` to reset", "", false); + embed.addField( + "Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " + + "recreate the map", + "", false); embed.addField("Player", user.getAsMention(), false); message.editMessage(embed.build()).queue(); } diff --git a/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java b/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java index 6ffb385..a0f6eee 100644 --- a/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java +++ b/src/main/java/me/polymarsdev/sokobot/util/Randomizer.java @@ -6,10 +6,6 @@ public class Randomizer{ public static Random theInstance = null; - public Randomizer(){ - - } - public static Random getInstance(){ if(theInstance == null){ theInstance = new Random(); From 5137d33e951c73e5e8871737446a228a7f755dd8 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Sun, 9 Aug 2020 15:12:00 +0200 Subject: [PATCH 09/10] fixed typo (thanks to @alsoGAMER) --- src/main/java/me/polymarsdev/sokobot/database/Database.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/polymarsdev/sokobot/database/Database.java b/src/main/java/me/polymarsdev/sokobot/database/Database.java index 97162d5..2ce40aa 100644 --- a/src/main/java/me/polymarsdev/sokobot/database/Database.java +++ b/src/main/java/me/polymarsdev/sokobot/database/Database.java @@ -35,7 +35,7 @@ public class Database { con = DriverManager.getConnection( "jdbc:mysql://" + mysql_hostname + ":" + mysql_port + "/" + mysql_database + "?autoReconnect=true", mysql_username, mysql_password); - System.out.println("[INFO] Successfully initialized databse connection."); + System.out.println("[INFO] Successfully initialized database connection."); } else if (dbType == DBType.SQLite) { File sqliteFile = new File(filePath); if (!sqliteFile.exists()) { @@ -44,7 +44,7 @@ public class Database { if (!create) System.out.println("[ERROR] Could not create SQLite file at " + filePath); } con = DriverManager.getConnection("jdbc:sqlite:" + filePath); - System.out.println("[INFO] Successfully initialized databse connection."); + System.out.println("[INFO] Successfully initialized database connection."); } } catch (Exception ex) { System.out.println("[ERROR] Error at creating database connection: " + ex.getMessage()); From 38bc58ac0b453449ca356125bce4784e05c96b37 Mon Sep 17 00:00:00 2001 From: AffluentAvo Date: Mon, 10 Aug 2020 14:52:04 +0200 Subject: [PATCH 10/10] added game timeout after 10 minutes with no action --- src/main/java/me/polymarsdev/sokobot/Bot.java | 2 ++ .../java/me/polymarsdev/sokobot/Game.java | 18 +++++++++-- .../me/polymarsdev/sokobot/util/GameUtil.java | 31 ++++++++++++++----- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/main/java/me/polymarsdev/sokobot/Bot.java b/src/main/java/me/polymarsdev/sokobot/Bot.java index 71dbcf8..e36f658 100644 --- a/src/main/java/me/polymarsdev/sokobot/Bot.java +++ b/src/main/java/me/polymarsdev/sokobot/Bot.java @@ -3,6 +3,7 @@ package me.polymarsdev.sokobot; import me.polymarsdev.sokobot.database.Database; import me.polymarsdev.sokobot.listener.CommandListener; import me.polymarsdev.sokobot.listener.GameListener; +import me.polymarsdev.sokobot.util.GameUtil; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Guild; @@ -75,6 +76,7 @@ public class Bot { builder.setActivity(Activity.playing("@Sokobot for info!")); builder.addEventListeners(new GameListener(), new CommandListener()); shardManager = builder.build(); + GameUtil.runGameTimer(); Thread consoleThread = new Thread(() -> { Scanner s = new Scanner(System.in); while (s.hasNextLine()) { diff --git a/src/main/java/me/polymarsdev/sokobot/Game.java b/src/main/java/me/polymarsdev/sokobot/Game.java index 8009140..e1c4a23 100644 --- a/src/main/java/me/polymarsdev/sokobot/Game.java +++ b/src/main/java/me/polymarsdev/sokobot/Game.java @@ -4,6 +4,8 @@ import me.polymarsdev.sokobot.objects.Grid; import me.polymarsdev.sokobot.util.GameUtil; import net.dv8tion.jda.api.entities.*; +import java.util.concurrent.TimeUnit; + public class Game { long gameMessageID; long channelID; @@ -13,6 +15,7 @@ public class Game { public int level = 1; int width = 9; int height = 6; + public long lastAction; Grid grid = new Grid(width, height, level, playerEmote); public Game(User user) { @@ -36,14 +39,25 @@ public class Game { height = 6; grid = new Grid(width, height, level, playerEmote); gameActive = true; + lastAction = System.currentTimeMillis(); GameUtil.sendGameEmbed(channel, String.valueOf(level), grid.toString(), user); } } + public void stop() { + gameActive = false; + TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID); + if (textChannel != null) { + textChannel.retrieveMessageById(gameMessageID).queue(gameMessage -> gameMessage.delete().queue()); + } + } + public void run(Guild guild, TextChannel channel, String userInput) { + lastAction = System.currentTimeMillis(); if (userInput.equals("stop") && gameActive) { - channel.sendMessage("Thanks for playing, " + user.getAsMention() + "!").queue(); - gameActive = false; + stop(); + channel.sendMessage("Thanks for playing, " + user.getAsMention() + "!") + .queue(msg -> msg.delete().queueAfter(10, TimeUnit.SECONDS)); } if (userInput.equals("play") && !gameActive) { newGame(channel); diff --git a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java index f5c6ef2..5e8988e 100644 --- a/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java +++ b/src/main/java/me/polymarsdev/sokobot/util/GameUtil.java @@ -9,6 +9,8 @@ import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.User; import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; public class GameUtil { @@ -34,10 +36,8 @@ public class GameUtil { EmbedBuilder embed = new EmbedBuilder(); embed.setTitle("Sokobot | Level " + level); embed.setDescription(game); - embed.addField( - "Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " - + "recreate the map", - "", false); + embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " + + "recreate the map", "", false); embed.addField("Player", user.getAsMention(), false); channel.sendMessage(embed.build()).queue(); } @@ -46,10 +46,8 @@ public class GameUtil { EmbedBuilder embed = new EmbedBuilder(); embed.setTitle("Sokobot | Level " + level); embed.setDescription(game); - embed.addField( - "Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " - + "recreate the map", - "", false); + embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to " + + "recreate the map", "", false); embed.addField("Player", user.getAsMention(), false); message.editMessage(embed.build()).queue(); } @@ -63,4 +61,21 @@ public class GameUtil { embed.setFooter("You can also press any reaction to continue."); message.editMessage(embed.build()).queue(); } + + public static void runGameTimer() { + new Timer().scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + long now = System.currentTimeMillis(); + for (long playerId : games.keySet()) { + Game game = games.get(playerId); + long timeDifference = now - game.lastAction; + if (timeDifference > 10 * 60 * 1000) { + game.stop(); + GameUtil.removeGame(playerId); + } + } + } + }, 10 * 60 * 1000, 60 * 1000); + } }