diff --git a/pom.xml b/pom.xml index c443c02..66896e4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ maunz-discord Maunz A multi-purpose Discord bot with a focus on Source server tracking - r56 + r57 https://github.com/Vauff/Maunz-Discord Maunz-Discord diff --git a/src/com/vauff/maunzdiscord/commands/Map.java b/src/com/vauff/maunzdiscord/commands/Map.java index 817ef11..56d4d61 100644 --- a/src/com/vauff/maunzdiscord/commands/Map.java +++ b/src/com/vauff/maunzdiscord/commands/Map.java @@ -121,7 +121,7 @@ private void runCmd(DeferrableInteractionEvent event, User user, Document doc, b return; } - String url = MapImages.getMapImageURL(doc.getString("lastMap"), serverDoc.getInteger("appId")); + String url = MapImages.getMapImageURL(doc.getString("lastMap"), serverDoc.getString("appId")); String ipPort = serverDoc.getString("ip") + ":" + serverDoc.getInteger("port"); EmbedCreateSpec embed = EmbedCreateSpec.builder() @@ -195,7 +195,7 @@ private void runCmd(DeferrableInteractionEvent event, User user, Document doc, b if (!formattedMap.equals("")) { - String url = MapImages.getMapImageURL(formattedMap, serverDoc.getInteger("appId")); + String url = MapImages.getMapImageURL(formattedMap, serverDoc.getString("appId")); String lastPlayed = ""; String firstPlayed = ""; diff --git a/src/com/vauff/maunzdiscord/commands/Players.java b/src/com/vauff/maunzdiscord/commands/Players.java index 36c0b77..5dfea82 100644 --- a/src/com/vauff/maunzdiscord/commands/Players.java +++ b/src/com/vauff/maunzdiscord/commands/Players.java @@ -105,6 +105,12 @@ private void runCmd(DeferrableInteractionEvent event, User user, Document doc) int numberOfPlayers = serverDoc.getList("players", String.class).size(); + if (numberOfPlayers < 2 && serverDoc.getString("appId").equals("730_cs2")) + { + Util.editReply(event, "Players Online: **" + serverDoc.getString("playerCount") + "**\n\n**Note:** Counter-Strike 2 servers do not currently support querying player information"); + return; + } + if (numberOfPlayers == 0) { Util.editReply(event, "There are currently no players online!"); diff --git a/src/com/vauff/maunzdiscord/commands/Servers.java b/src/com/vauff/maunzdiscord/commands/Servers.java index da7518b..c9a5a64 100644 --- a/src/com/vauff/maunzdiscord/commands/Servers.java +++ b/src/com/vauff/maunzdiscord/commands/Servers.java @@ -289,7 +289,7 @@ private ObjectId getOrCreateServer(String ip, int port) return id; } - Document server = new Document("enabled", true).append("ip", ip).append("port", port).append("appId", 0).append("name", "N/A").append("map", "N/A").append("timestamp", 0L).append("playerCount", "0/0").append("players", new ArrayList()) + Document server = new Document("enabled", true).append("ip", ip).append("port", port).append("appId", "0").append("name", "N/A").append("map", "N/A").append("timestamp", 0L).append("playerCount", "0/0").append("players", new ArrayList()) .append("downtimeTimer", 0).append("failedConnectionsThreshold", 3).append("mapDatabase", new ArrayList()); return Main.mongoDatabase.getCollection("servers").insertOne(server).getInsertedId().asObjectId().getValue(); diff --git a/src/com/vauff/maunzdiscord/core/Main.java b/src/com/vauff/maunzdiscord/core/Main.java index a62aa53..4f25030 100644 --- a/src/com/vauff/maunzdiscord/core/Main.java +++ b/src/com/vauff/maunzdiscord/core/Main.java @@ -33,7 +33,7 @@ public class Main { public static GatewayDiscordClient gateway; public static MongoDatabase mongoDatabase; - public static String version = "r56"; + public static String version = "r57"; public static Config cfg; /** diff --git a/src/com/vauff/maunzdiscord/servertracking/MapImageTimer.java b/src/com/vauff/maunzdiscord/servertracking/MapImageTimer.java index 805baa3..dea0ede 100644 --- a/src/com/vauff/maunzdiscord/servertracking/MapImageTimer.java +++ b/src/com/vauff/maunzdiscord/servertracking/MapImageTimer.java @@ -64,16 +64,15 @@ public class MapImageTimer Logger.log.debug("Map image API updated, rebuilding image list and clearing caches"); - for (String key : response.keySet()) + for (String appId : response.keySet()) { - if (!NumberUtils.isCreatable(key)) + if (appId.equals("lastUpdated")) continue; ArrayList maps = new ArrayList<>(); - int appId = Integer.parseInt(key); - for (int i = 0; i < response.getJSONArray(key).length(); i++) - maps.add(response.getJSONArray(key).getString(i)); + for (int i = 0; i < response.getJSONArray(appId).length(); i++) + maps.add(response.getJSONArray(appId).getString(i)); MapImages.mapImages.put(appId, maps); MapImages.mapImageLookupCache.put(appId, new HashMap<>()); diff --git a/src/com/vauff/maunzdiscord/servertracking/MapImages.java b/src/com/vauff/maunzdiscord/servertracking/MapImages.java index aa86f4c..52cd523 100644 --- a/src/com/vauff/maunzdiscord/servertracking/MapImages.java +++ b/src/com/vauff/maunzdiscord/servertracking/MapImages.java @@ -13,12 +13,12 @@ public class MapImages /** * Holds the latest image lists pulled from vauff.com */ - public static HashMap> mapImages = new HashMap<>(); + public static HashMap> mapImages = new HashMap<>(); /** * Cached lookup results from Util#getMapImageURL */ - public static HashMap> mapImageLookupCache = new HashMap<>(); + public static HashMap> mapImageLookupCache = new HashMap<>(); /** * Cached colour results from Util#averageColourFromURL @@ -38,7 +38,7 @@ public class MapImages * @param appId App ID of the game * @return Image URL for the map, or "" on failure */ - public static String getMapImageURL(String map, int appId) + public static String getMapImageURL(String map, String appId) { // Force lower case since GameTracker does, and for an accurate levenshtein distance String mapLower = map.toLowerCase(); @@ -88,7 +88,7 @@ public static Color getMapImageColour(String url) throws Exception * @param appId App ID of the game * @return Image URL for the map, or "" on failure */ - private static String getVauffMapImageURL(String map, int appId) + private static String getVauffMapImageURL(String map, String appId) { // As we accomodate for mapCharacterLimit, all image map names at vauff.com should never exceed 31 characters String trimmedMap = StringUtils.substring(map, 0, 31); @@ -124,94 +124,96 @@ private static String getVauffMapImageURL(String map, int appId) * @param appId App ID of the game * @return GameTracker's directory name for the given game */ - private static String appIdToGameTrackerName(int appId) + private static String appIdToGameTrackerName(String appId) { return switch (appId) { - case 10 -> "cs"; - case 20 -> "tfc"; - case 30 -> "dod"; - case 70 -> "hl"; - case 80 -> "czero"; - case 240 -> "css"; - case 300 -> "dods"; - case 320 -> "hl2dm"; - case 440 -> "tf2"; - case 500 -> "l4d"; - case 550 -> "left4dead2"; - case 570 -> "dota2"; - case 630 -> "alienswarm"; - case 730 -> "csgo"; - case 1200 -> "ror"; - case 1250 -> "killingfloor"; - case 1280 -> "rordh"; - case 2200 -> "q3"; - case 2210 -> "q4"; - case 2310 -> "qw"; - case 2320 -> "q2"; - case 2620 -> "cod"; - case 2630 -> "cod2"; - case 2640 -> "uo"; - case 4000 -> "garrysmod"; - case 4920 -> "ns2"; - case 6020 -> "swjk"; - case 6060 -> "swbf2"; - case 7940 -> "cod4"; - case 9010 -> "wolf"; - case 9050 -> "doom3"; - case 9460 -> "ffow"; - case 10000 -> "etqw"; - case 10090 -> "codww"; - case 13140 -> "aa3"; - case 13210 -> "ut3"; - case 13230 -> "ut2k4"; - case 13240 -> "ut"; - case 17300 -> "crysis"; - case 17330 -> "warhead"; - case 17500 -> "hl2zp"; - case 17700 -> "ins"; - case 21090 -> "fear"; - case 22350 -> "brink"; - case 24960 -> "bc2"; - case 33900 -> "arma2"; - case 35450 -> "ro2"; - case 42700 -> "blackops"; - case 47790 -> "moh"; - case 55100 -> "homefront"; - case 63200 -> "mnc"; - case 63380 -> "sniperelite2"; - case 65780 -> "arma"; - case 96300 -> "ravaged"; - case 107410 -> "arma3"; - case 108800 -> "crysis2"; - case 115300 -> "mw3"; - case 203290 -> "aapg"; - case 211820 -> "starbound"; - case 214630 -> "blackopsmac"; - case 221100 -> "dayz"; - case 222880 -> "insurgency2014"; - case 224580 -> "dayzmod"; - case 232090 -> "kf2"; - case 238430 -> "contagion"; - case 244850 -> "spaceengi"; - case 251570 -> "7daystodie"; - case 252490 -> "rust"; - case 253530 -> "ff"; - case 259080 -> "jc2"; - case 282440 -> "ql"; - case 290080 -> "lifyo"; - case 311210 -> "codbo3"; - case 346110 -> "arkse"; - case 393420 -> "hurtworld"; - case 440900 -> "conan"; - case 489940 -> "battalion1944"; - case 529180 -> "dnl"; - case 581320 -> "ins_sandstorm"; - case 659280 -> "urbanterror"; - case 1238820 -> "bf3"; - case 1238860 -> "bf4"; - case 1238880 -> "bfhl"; - case 1873030 -> "et"; + case "10" -> "cs"; + case "20" -> "tfc"; + case "30" -> "dod"; + case "70" -> "hl"; + case "80" -> "czero"; + case "240" -> "css"; + case "300" -> "dods"; + case "320" -> "hl2dm"; + case "440" -> "tf2"; + case "500" -> "l4d"; + case "550" -> "left4dead2"; + case "570" -> "dota2"; + case "630" -> "alienswarm"; + case "730_csgo" -> "csgo"; + // TODO: Update this if GameTracker ever gets CS2 support + case "730_cs2" -> "csgo"; + case "1200" -> "ror"; + case "1250" -> "killingfloor"; + case "1280" -> "rordh"; + case "2200" -> "q3"; + case "2210" -> "q4"; + case "2310" -> "qw"; + case "2320" -> "q2"; + case "2620" -> "cod"; + case "2630" -> "cod2"; + case "2640" -> "uo"; + case "4000" -> "garrysmod"; + case "4920" -> "ns2"; + case "6020" -> "swjk"; + case "6060" -> "swbf2"; + case "7940" -> "cod4"; + case "9010" -> "wolf"; + case "9050" -> "doom3"; + case "9460" -> "ffow"; + case "10000" -> "etqw"; + case "10090" -> "codww"; + case "13140" -> "aa3"; + case "13210" -> "ut3"; + case "13230" -> "ut2k4"; + case "13240" -> "ut"; + case "17300" -> "crysis"; + case "17330" -> "warhead"; + case "17500" -> "hl2zp"; + case "17700" -> "ins"; + case "21090" -> "fear"; + case "22350" -> "brink"; + case "24960" -> "bc2"; + case "33900" -> "arma2"; + case "35450" -> "ro2"; + case "42700" -> "blackops"; + case "47790" -> "moh"; + case "55100" -> "homefront"; + case "63200" -> "mnc"; + case "63380" -> "sniperelite2"; + case "65780" -> "arma"; + case "96300" -> "ravaged"; + case "107410" -> "arma3"; + case "108800" -> "crysis2"; + case "115300" -> "mw3"; + case "203290" -> "aapg"; + case "211820" -> "starbound"; + case "214630" -> "blackopsmac"; + case "221100" -> "dayz"; + case "222880" -> "insurgency2014"; + case "224580" -> "dayzmod"; + case "232090" -> "kf2"; + case "238430" -> "contagion"; + case "244850" -> "spaceengi"; + case "251570" -> "7daystodie"; + case "252490" -> "rust"; + case "253530" -> "ff"; + case "259080" -> "jc2"; + case "282440" -> "ql"; + case "290080" -> "lifyo"; + case "311210" -> "codbo3"; + case "346110" -> "arkse"; + case "393420" -> "hurtworld"; + case "440900" -> "conan"; + case "489940" -> "battalion1944"; + case "529180" -> "dnl"; + case "581320" -> "ins_sandstorm"; + case "659280" -> "urbanterror"; + case "1238820" -> "bf3"; + case "1238860" -> "bf4"; + case "1238880" -> "bfhl"; + case "1873030" -> "et"; default -> ""; }; } diff --git a/src/com/vauff/maunzdiscord/servertracking/ServerRequestThread.java b/src/com/vauff/maunzdiscord/servertracking/ServerRequestThread.java index aa1a730..73bd526 100644 --- a/src/com/vauff/maunzdiscord/servertracking/ServerRequestThread.java +++ b/src/com/vauff/maunzdiscord/servertracking/ServerRequestThread.java @@ -77,10 +77,11 @@ public void run() serverInfoSuccess = true; } + // TODO: Remove this eventually when most CS:GO servers die, issue is not present in CS2 and isn't worth keeping for a few servers // CS:GO servers by default use host_info_show 1 which uses a game-specific A2S_INFO implementation, only host_info_show 2 uses SteamWorks // Unfortunately this implementation is incapable of providing a correct player count during a map change (returns 0), so we work around this by double-checking "empty" CS:GO servers are actually empty a little while after // See https://github.com/perilouswithadollarsign/cstrike15_src/blob/master/engine/baseserver.cpp#L1261, GetNumPlayers() uses m_pUserInfoTable which is emptied during a map change - if (doc.getInteger("appId") == 730 && server.getServerInfo().containsKey("numberOfPlayers") && !Objects.isNull(server.getServerInfo().get("numberOfPlayers")) && ((Byte) server.getServerInfo().get("numberOfPlayers")).intValue() == 0 && !retriedForCsgoPlayerCount) + if (doc.getString("appId").equals("730_csgo") && server.getServerInfo().containsKey("numberOfPlayers") && !Objects.isNull(server.getServerInfo().get("numberOfPlayers")) && ((Byte) server.getServerInfo().get("numberOfPlayers")).intValue() == 0 && !retriedForCsgoPlayerCount) { Thread.sleep(5000); serverInfoSuccess = false; @@ -131,7 +132,7 @@ public void run() HashMap serverInfo = server.getServerInfo(); long timestamp = 0; - int appId = 0; + String appId = "0"; String map = ""; String name = "N/A"; int currentPlayers = 0; @@ -149,11 +150,31 @@ public void run() // 24-bit app id within 64-bit game id, may not be available if (serverInfo.containsKey("gameId") && !Objects.isNull(serverInfo.get("gameId"))) - appId = (int) (((long) serverInfo.get("gameId")) & (1L << 24) - 1L); - + appId = String.valueOf((int) (((long) serverInfo.get("gameId")) & (1L << 24) - 1L)); // 16-bit app id, possibly truncated but (theoretically) always available else if (serverInfo.containsKey("appId") && !Objects.isNull(serverInfo.get("appId"))) - appId = (short) serverInfo.get("appId"); + appId = String.valueOf((short) serverInfo.get("appId")); + + // Special handling for CS:GO/CS2, thanks for two games on the same app id, Valve! + if (appId.equals("730")) + { + if (serverInfo.containsKey("gameVersion") && !Objects.isNull(serverInfo.get("gameVersion"))) + { + String version = serverInfo.get("gameVersion").toString(); + + // Strip periods, take first three numbers only e.g. "1.38.8.1" > 138 + int majorVersion = Integer.parseInt(version.replace(".", "").substring(0, 3)); + + if (majorVersion >= 139) + appId = "730_cs2"; + else if (majorVersion <= 138) + appId = "730_csgo"; + } + else + { + appId = "0"; + } + } if (serverInfo.containsKey("serverName") && !Objects.isNull(serverInfo.get("serverName"))) name = serverInfo.get("serverName").toString(); @@ -201,7 +222,7 @@ else if (serverInfo.containsKey("appId") && !Objects.isNull(serverInfo.get("appI } } - if (appId != 0 && appId != doc.getInteger("appId")) + if (!appId.equals("0") && !appId.equals(doc.getString("appId"))) Main.mongoDatabase.getCollection("servers").updateOne(eq("_id", id), new Document("$set", new Document("appId", appId))); if (!playerCount.equals("") && !playerCount.equals(doc.getString("playerCount"))) diff --git a/src/com/vauff/maunzdiscord/servertracking/ServiceProcessThread.java b/src/com/vauff/maunzdiscord/servertracking/ServiceProcessThread.java index 6473151..f501c1b 100644 --- a/src/com/vauff/maunzdiscord/servertracking/ServiceProcessThread.java +++ b/src/com/vauff/maunzdiscord/servertracking/ServiceProcessThread.java @@ -96,7 +96,7 @@ public void run() if (!map.equals("") && !doc.getString("lastMap").equalsIgnoreCase(map)) { - String url = MapImages.getMapImageURL(map, serverDoc.getInteger("appId")); + String url = MapImages.getMapImageURL(map, serverDoc.getString("appId")); String ipPort = serverDoc.getString("ip") + ":" + serverDoc.getInteger("port"); EmbedCreateSpec embed = EmbedCreateSpec.builder()