From 1d4e0e09d04f2e7427af35af355ac2dbe86a6fbe Mon Sep 17 00:00:00 2001
From: ImmuState <kyoko12@gmx.at>
Date: Wed, 11 May 2022 11:19:25 -0700
Subject: [PATCH] Add gacha details page.

---
 data/gacha_details.html                       | 121 ++++++++++++++++++
 .../grasscutter/game/gacha/GachaBanner.java   |   9 +-
 .../grasscutter/game/gacha/GachaManager.java  |  18 ++-
 .../server/dispatch/DispatchServer.java       |   4 +
 .../dispatch/http/GachaDetailsHandler.java    |  90 +++++++++++++
 src/main/resources/languages/en-US.json       |   9 ++
 src/main/resources/languages/pl-PL.json       |   9 ++
 src/main/resources/languages/zh-CN.json       |   9 ++
 src/main/resources/languages/zh-TW.json       |   9 ++
 9 files changed, 275 insertions(+), 3 deletions(-)
 create mode 100644 data/gacha_details.html
 create mode 100644 src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java

diff --git a/data/gacha_details.html b/data/gacha_details.html
new file mode 100644
index 000000000..16cf7313a
--- /dev/null
+++ b/data/gacha_details.html
@@ -0,0 +1,121 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
+        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
+        <style>
+            body {
+                background-color: #f0f0f0;
+            }
+            p {
+                font-weight:300;
+            }
+            a,a:hover {
+                text-decoration:none !important;
+                color:#626976;
+            }
+            .content {
+                padding:3rem 0;
+            }
+            .container {
+                color:#626976;
+                position: relative;
+            }
+
+            h2 {
+                font-size:20px;
+            }
+            h3 {
+                font-size:16px;
+            }
+        </style>
+        <title>Banner Details</title>
+        <script type="text/javascript" src="/gacha/mappings"></script>
+    </head>
+    <body>
+        <div class="content">
+            <div class="container">
+                <h2 class="mb-5">{{TITLE}}</h2>
+
+                <h3 class="">{{AVAILABLE_FIVE_STARS}}</h3>
+                <hr />
+                <ul id="5-star-list">
+                </ul>
+
+                <h3 class="">{{AVAILABLE_FOUR_STARS}}</h3>
+                <hr />
+                <ul id="4-star-list">
+                </ul>
+
+                <h3 class="">{{AVAILABLE_THREE_STARS}}</h3>
+                <hr />
+                <ul id="3-star-list">
+                </ul>
+            </div>
+        </div>
+        <footer>
+            <div class="copyright">
+                <div class="container">
+                    <div class="row">
+                        <div class="col-md-6">
+                            <span>
+                                Template by BecodReyes. All rights reserved.
+                            </span>
+                        </div>
+                        <div class="col-md-6">
+                            <ul style="float:right">
+                                <li class="list-inline-item">
+                                    <a href="https://github.com/Grasscutters/Grasscutter">Github</a>
+                                </li>
+                                <li class="list-inline-item">·</li>
+                                <li class="list-inline-item">
+                                    <a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </footer>
+
+        <script>
+            fiveStarItems = {{FIVE_STARS}};
+            fourStarItems = {{FOUR_STARS}};
+            threeStarItems = {{THREE_STARS}};
+
+            var lang = new window.URLSearchParams(window.location.search).get("lang");
+            function getNameForId(itemId) {
+                if (mappings[lang] != null && mappings[lang][itemId] != null) {
+                    return mappings[lang][itemId][0];
+                }
+                else if (mappings["en-us"] != null && mappings["en-us"][itemId] != null) {
+                    return mappings["en-us"][itemId][0];
+                }
+                
+                return itemId.toString();
+            }
+
+            fiveStarList = document.getElementById("5-star-list");
+            fourStarList = document.getElementById("4-star-list");
+            threeStarList = document.getElementById("3-star-list");
+
+            fiveStarItems.forEach(element => {
+                var entry = document.createElement("li");
+                entry.innerHTML = getNameForId(element);
+                fiveStarList.appendChild(entry);
+            });
+            fourStarItems.forEach(element => {
+                var entry = document.createElement("li");
+                entry.innerHTML = getNameForId(element);
+                fourStarList.appendChild(entry);
+            });
+            threeStarItems.forEach(element => {
+                var entry = document.createElement("li");
+                entry.innerHTML = getNameForId(element);
+                threeStarList.appendChild(entry);
+            });
+        </script>
+    </body>
+</html>
diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
index dce433fcf..7a2646a4f 100644
--- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
+++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
@@ -102,6 +102,11 @@ public class GachaBanner {
 						+ lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":"
 						+ lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort)
 						+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
+		String details = "http" + (DISPATCH_INFO.encryption.useInRouting ? "s" : "") + "://"
+						+ lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":"
+						+ lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort)
+						+ "/gacha/details?s=" + sessionKey + "&gachaType=" + gachaType;
+
 		// Grasscutter.getLogger().info("record = " + record);
 		GachaInfo.Builder info = GachaInfo.newBuilder()
 				.setGachaType(this.getGachaType())
@@ -112,8 +117,8 @@ public class GachaBanner {
 	            .setCostItemNum(1)
 	            .setGachaPrefabPath(this.getPrefabPath())
 	            .setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
-	            .setGachaProbUrl(record)
-	            .setGachaProbUrlOversea(record)
+	            .setGachaProbUrl(details)
+	            .setGachaProbUrlOversea(details)
 	            .setGachaRecordUrl(record)
 	            .setGachaRecordUrlOversea(record)
 	            .setTenCostItemId(this.getCostItem())
diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
index 03edca09a..f0baf65a9 100644
--- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
+++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
@@ -65,7 +65,23 @@ public class GachaManager {
 	public Int2ObjectMap<GachaBanner> getGachaBanners() {
 		return gachaBanners;
 	}
-	
+
+	public int[] getYellowAvatars() {
+		return this.yellowAvatars;
+	}
+	public int[] getYellowWeapons() {
+		return this.yellowWeapons;
+	}
+	public int[] getPurpleAvatars() {
+		return this.purpleAvatars;
+	}
+	public int[] getPurpleWeapons() {
+		return this.purpleWeapons;
+	}
+	public int[] getBlueWeapons() {
+		return this.blueWeapons;
+	}
+
 	public int randomRange(int min, int max) {
 		return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
 	}
diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
index 4e09f8881..c78aff7c6 100644
--- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
+++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
@@ -16,6 +16,7 @@ import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
 import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
 import emu.grasscutter.server.dispatch.authentication.AuthenticationHandler;
 import emu.grasscutter.server.dispatch.authentication.DefaultAuthenticationHandler;
+import emu.grasscutter.server.dispatch.http.GachaDetailsHandler;
 import emu.grasscutter.server.dispatch.http.GachaRecordHandler;
 import emu.grasscutter.server.dispatch.json.*;
 import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
@@ -455,6 +456,9 @@ public final class DispatchServer {
 
 		httpServer.raw().config.addSinglePageRoot("/gacha/mappings", gachaMappingsPath, Location.EXTERNAL);
 
+		// gacha details
+		httpServer.get("/gacha/details", new GachaDetailsHandler());
+
 		// static file support for plugins
 		httpServer.raw().config.precompressStaticFiles = false; // If this isn't set to false, files such as images may appear corrupted when serving static files
 
diff --git a/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java b/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java
new file mode 100644
index 000000000..d46cead40
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java
@@ -0,0 +1,90 @@
+package emu.grasscutter.server.dispatch.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.database.DatabaseHelper;
+import emu.grasscutter.game.Account;
+import emu.grasscutter.game.gacha.GachaBanner;
+import emu.grasscutter.game.gacha.GachaManager;
+import emu.grasscutter.game.gacha.GachaBanner.BannerType;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.utils.FileUtils;
+import emu.grasscutter.utils.Utils;
+import express.http.HttpContextHandler;
+import express.http.Request;
+import express.http.Response;
+
+import static emu.grasscutter.utils.Language.translate;
+
+import static emu.grasscutter.Configuration.*;
+
+public final class GachaDetailsHandler implements HttpContextHandler {
+	private final String render_template;
+
+	public GachaDetailsHandler() {
+		File template = new File(Utils.toFilePath(DATA("/gacha_details.html")));
+		this.render_template = template.exists() ? new String(FileUtils.read(template)) : null;
+	}
+
+	@Override
+	public void handle(Request req, Response res) throws IOException {
+		String response = this.render_template;
+
+		// Get player info (for langauge).
+		String sessionKey = req.query("s");
+		Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
+		Player player = Grasscutter.getGameServer().getPlayerByUid(account.getPlayerUid());
+
+		// If the template was not loaded, return an error.
+		if (this.render_template == null) {
+			res.send(translate(player, "gacha.details.template_missing"));
+			return;
+		}
+
+		// Add translated title etc. to the page.
+		response = response.replace("{{TITLE}}", translate(player, "gacha.details.title"));
+		response = response.replace("{{AVAILABLE_FIVE_STARS}}", translate(player, "gacha.details.available_five_stars"));
+		response = response.replace("{{AVAILABLE_FOUR_STARS}}", translate(player, "gacha.details.available_four_stars"));
+		response = response.replace("{{AVAILABLE_THREE_STARS}}", translate(player, "gacha.details.available_three_stars"));
+		
+		// Get the banner info for the banner we want.
+		int gachaType = Integer.parseInt(req.query("gachaType"));
+		GachaManager manager = Grasscutter.getGameServer().getGachaManager();
+	 	GachaBanner banner = manager.getGachaBanners().get(gachaType);
+
+		// Add 5-star items.
+		Set<String> fiveStarItems = new LinkedHashSet<>();
+
+		Arrays.stream(banner.getRateUpItems1()).forEach(i -> fiveStarItems.add(Integer.toString(i)));
+		if (banner.getBannerType() == BannerType.STANDARD || banner.getBannerType() == BannerType.EVENT) {
+			Arrays.stream(manager.getYellowAvatars()).forEach(i -> fiveStarItems.add(Integer.toString(i)));
+		}
+		if (banner.getBannerType() == BannerType.STANDARD || banner.getBannerType() == BannerType.WEAPON) {
+			Arrays.stream(manager.getYellowWeapons()).forEach(i -> fiveStarItems.add(Integer.toString(i)));
+		}
+
+		response = response.replace("{{FIVE_STARS}}", "[" + String.join(",", fiveStarItems) + "]");
+		
+		// Add 4-star items.
+		Set<String> fourStarItems = new LinkedHashSet<>();
+
+		Arrays.stream(banner.getRateUpItems2()).forEach(i -> fourStarItems.add(Integer.toString(i)));
+		Arrays.stream(manager.getPurpleAvatars()).forEach(i -> fourStarItems.add(Integer.toString(i)));
+		Arrays.stream(manager.getPurpleWeapons()).forEach(i -> fourStarItems.add(Integer.toString(i)));
+
+		response = response.replace("{{FOUR_STARS}}", "[" + String.join(",", fourStarItems) + "]");
+
+		// Add 3-star items.
+		Set<String> threeStarItems = new LinkedHashSet<>();
+		Arrays.stream(manager.getBlueWeapons()).forEach(i -> threeStarItems.add(Integer.toString(i)));
+		response = response.replace("{{THREE_STARS}}", "[" + String.join(",", threeStarItems) + "]");
+
+		// Done.
+		res.send(response);
+	}
+}
diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json
index 81e97eed7..42b52d7f5 100644
--- a/src/main/resources/languages/en-US.json
+++ b/src/main/resources/languages/en-US.json
@@ -352,5 +352,14 @@
     "resetshop": {
       "description": "reset shop"
     }
+  },
+  "gacha": {
+    "details": {
+      "title": "Banner Details",
+      "available_five_stars": "Available 5-star Items",
+      "available_four_stars": "Available 4-star Items",
+      "available_three_stars": "Available 3-star Items",
+      "template_missing": "data/gacha_details.html is missing."
+    }
   }
 }
diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json
index aa06723d8..e5eff2d84 100644
--- a/src/main/resources/languages/pl-PL.json
+++ b/src/main/resources/languages/pl-PL.json
@@ -302,5 +302,14 @@
     "resetshop": {
       "description": "zresetuj sklep"
     }
+  },
+  "gacha": {
+    "details": {
+      "title": "Banner Details",
+      "available_five_stars": "Available 5-star Items",
+      "available_four_stars": "Available 4-star Items",
+      "available_three_stars": "Available 3-star Items",
+      "template_missing": "data/gacha_details.html is missing."
+    }
   }
 }
\ No newline at end of file
diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json
index 84d4a8c94..ac46370d5 100644
--- a/src/main/resources/languages/zh-CN.json
+++ b/src/main/resources/languages/zh-CN.json
@@ -349,5 +349,14 @@
     "resetshop": {
       "description": "重置商店刷新时间"
     }
+  },
+  "gacha": {
+    "details": {
+      "title": "Banner Details",
+      "available_five_stars": "Available 5-star Items",
+      "available_four_stars": "Available 4-star Items",
+      "available_three_stars": "Available 3-star Items",
+      "template_missing": "data/gacha_details.html is missing."
+    }
   }
 }
diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json
index 8e7a75949..f6ae52c9d 100644
--- a/src/main/resources/languages/zh-TW.json
+++ b/src/main/resources/languages/zh-TW.json
@@ -302,5 +302,14 @@
     "resetshop": {
       "description": "重置商店時間"
     }
+  },
+  "gacha": {
+    "details": {
+      "title": "Banner Details",
+      "available_five_stars": "Available 5-star Items",
+      "available_four_stars": "Available 4-star Items",
+      "available_three_stars": "Available 3-star Items",
+      "template_missing": "data/gacha_details.html is missing."
+    }
   }
 }