Commit 2451d2c8 authored by Hotaru's avatar Hotaru Committed by Melledy
Browse files

Build region_list and cur_region dynamically

parent f441d066
GpgdCgo4LjIwOS42Ni4xENWsARosaHR0cHM6Ly9vc2V1cm9vYXNlcnZlci55dWFuc2hlbi5jb20vcmVjaGFyZ2U6BGV1cm9COWh0dHBzOi8vYXV0b3BhdGNoaGsueXVhbnNoZW4uY29tL2NsaWVudF9nYW1lX3Jlcy8yLjZfbGl2ZUo8aHR0cHM6Ly9hdXRvcGF0Y2hoay55dWFuc2hlbi5jb20vY2xpZW50X2Rlc2lnbl9kYXRhLzIuNl9saXZlUpIBaHR0cHM6Ly93ZWJzdGF0aWMtc2VhLmhveW92ZXJzZS5jb20veXMvZXZlbnQvaW0tc2VydmljZS9pbmRleC5odG1sP2ltX291dD1mYWxzZSZzaWduX3R5cGU9MiZhdXRoX2FwcGlkPWltX2NjcyZhdXRoa2V5X3Zlcj0xJndpbl9kaXJlY3Rpb249cG9ydHJhaXRiCDIuNl9saXZlcNnsmgOQAdnsmgOaAVx7InJlbW90ZU5hbWUiOiAiZGF0YV92ZXJzaW9ucyIsICJtZDUiOiAiMWE0NDVhZTQ4ZmJkYmUwMzgzMTJiZTg2OGYyODEzZDIiLCAiZmlsZVNpemUiOiA0NDE1faIBW3sicmVtb3RlTmFtZSI6ICJkYXRhX3ZlcnNpb25zIiwgIm1kNSI6ICIxNzU0MmM3YmJhYzQ5YjkxMWRlMjlhOTYyNzU0YmQ2MSIsICJmaWxlU2l6ZSI6IDUxNH2yAYEGCL23mQMa4AV7InJlbW90ZU5hbWUiOiAicmVzX3ZlcnNpb25zX2V4dGVybmFsIiwgIm1kNSI6ICI5ZjA5MmNhMTMwNjdjYWU0MzEzNDkzZWVkM2QzZTlhZSIsICJmaWxlU2l6ZSI6IDUyNjk2Nn0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICI3Yzk0MmU3MDRhODA0YzIxMjJmYzYzZWU5MjhlNzE0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfc3RyZWFtaW5nIiwgIm1kNSI6ICJlMzVmNmQ4NTNkMDRjZTAyMmFjN2MzNmQ0M2Y0MGU3YyIsICJmaWxlU2l6ZSI6IDEyMjAzNn0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jl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
\ No newline at end of file
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
\ No newline at end of file
......@@ -21,7 +21,6 @@ public final class ReloadCommand implements CommandHandler {
Grasscutter.getGameServer().getGachaManager().load();
Grasscutter.getGameServer().getDropManager().load();
Grasscutter.getGameServer().getShopManager().load();
Grasscutter.getDispatchServer().loadQueries();
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done"));
}
......
......@@ -23,6 +23,7 @@ import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent;
import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
import express.Express;
......@@ -41,9 +42,6 @@ import static emu.grasscutter.utils.Language.translate;
import static emu.grasscutter.Configuration.*;
public final class DispatchServer {
public static String query_region_list = "";
public static String query_cur_region = "";
private final Gson gson;
private final String defaultServerName = "os_usa";
......@@ -56,7 +54,6 @@ public final class DispatchServer {
this.regions = new HashMap<>();
this.gson = new GsonBuilder().create();
this.loadQueries();
this.initRegion();
}
......@@ -84,31 +81,11 @@ public final class DispatchServer {
return null;
}
public void loadQueries() {
File file;
file = new File(DATA("query_region_list.txt"));
if (file.exists()) {
query_region_list = new String(FileUtils.read(file));
} else {
Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list.");
}
file = new File(DATA("query_cur_region.txt"));
if (file.exists()) {
query_cur_region = new String(FileUtils.read(file));
} else {
Grasscutter.getLogger().warn("[Dispatch] query_cur_region not found! Using default current region.");
}
}
private void initRegion() {
try {
byte[] decoded = Base64.getDecoder().decode(query_region_list);
QueryRegionListHttpRsp rl = QueryRegionListHttpRsp.parseFrom(decoded);
byte[] decoded2 = Base64.getDecoder().decode(query_cur_region);
QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2);
String dispatchDomain = "http" + (DISPATCH_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":"
+ lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort);
List<RegionSimpleInfo> servers = new ArrayList<>();
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
......@@ -117,24 +94,26 @@ public final class DispatchServer {
.setName("os_usa")
.setTitle(DISPATCH_INFO.defaultName)
.setType("DEV_PUBLIC")
.setDispatchUrl(
"http" + (DISPATCH_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":"
+ lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort)
+ "/query_cur_region/" + defaultServerName)
.setDispatchUrl(dispatchDomain + "/query_cur_region/" + defaultServerName)
.build();
usedNames.add(defaultServerName);
servers.add(server);
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
// todo: we might want to push custom config to client, see regionList below for clues.
RegionInfo serverRegion = RegionInfo.newBuilder()
.setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress))
.setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort))
.setSecretKey(ByteString.copyFrom(FileUtils.read(KEYS_FOLDER + "/dispatchSeed.bin")))
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build();
regions.put(defaultServerName, new RegionData(parsedRegionQuery,
Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
QueryCurrRegionHttpRsp parsedRegionQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build();
regions.put(
defaultServerName,
new RegionData(
parsedRegionQuery,
Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())
)
);
} else if (DISPATCH_INFO.regions.length == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
......@@ -146,35 +125,40 @@ public final class DispatchServer {
Grasscutter.getLogger().error("Region name already in use.");
continue;
}
// todo: we might want to push custom config to client, see regionList below for clues.
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
.setName(regionInfo.Name)
.setTitle(regionInfo.Title)
.setType("DEV_PUBLIC")
.setDispatchUrl(
"http" + (DISPATCH_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":"
+ lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort)
+ "/query_cur_region/" + regionInfo.Name)
.setDispatchUrl(dispatchDomain + "/query_cur_region/" + regionInfo.Name)
.build();
usedNames.add(regionInfo.Name);
servers.add(server);
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
RegionInfo serverRegion = RegionInfo.newBuilder()
.setGateserverIp(regionInfo.Ip)
.setGateserverPort(regionInfo.Port)
.setSecretKey(ByteString
.copyFrom(FileUtils.read(KEYS_FOLDER + "/dispatchSeed.bin")))
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build();
regions.put(regionInfo.Name, new RegionData(parsedRegionQuery,
Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
QueryCurrRegionHttpRsp parsedRegionQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build();
regions.put(
regionInfo.Name,
new RegionData(
parsedRegionQuery,
Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())
)
);
}
byte[] customConfig = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfig, Crypto.DISPATCH_KEY);
QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(rl.getClientSecretKey())
.setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted())
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfig))
.setEnableLoginPc(true)
.build();
......
......@@ -9,6 +9,7 @@ import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp;
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass;
import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.FileUtils;
import java.io.File;
......@@ -30,24 +31,14 @@ public class PacketPlayerLoginRsp extends BasePacket {
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
if (regionCache == null) {
try {
File file = new File(DATA("query_cur_region.txt"));
String query_cur_region = "";
if (file.exists()) {
query_cur_region = new String(FileUtils.read(file));
} else {
Grasscutter.getLogger().warn("query_cur_region not found! Using default current region.");
}
byte[] decodedCurRegion = Base64.getDecoder().decode(query_cur_region);
QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.parseFrom(decodedCurRegion);
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
// todo: we might want to push custom config to client
RegionInfo serverRegion = RegionInfo.newBuilder()
.setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress))
.setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort))
.setSecretKey(ByteString.copyFrom(FileUtils.read(KEYS_FOLDER + "/dispatchSeed.bin")))
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
regionCache = regionQuery.toBuilder().setRegionInfo(serverRegion).build();
regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build();
} catch (Exception e) {
Grasscutter.getLogger().error("Error while initializing region cache!", e);
}
......
......@@ -11,14 +11,18 @@ import static emu.grasscutter.Configuration.*;
public final class Crypto {
private static final SecureRandom secureRandom = new SecureRandom();
public static final long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968");
public static byte[] ENCRYPT_SEED_BUFFER = new byte[0];
public static byte[] DISPATCH_KEY;
public static byte[] DISPATCH_SEED;
public static byte[] ENCRYPT_KEY;
public static long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968");
public static byte[] ENCRYPT_SEED_BUFFER = new byte[0];
public static void loadKeys() {
DISPATCH_KEY = FileUtils.read(KEYS_FOLDER + "/dispatchKey.bin");
DISPATCH_SEED = FileUtils.read(KEYS_FOLDER + "/dispatchSeed.bin");
ENCRYPT_KEY = FileUtils.read(KEYS_FOLDER + "/secretKey.bin");
ENCRYPT_SEED_BUFFER = FileUtils.read(KEYS_FOLDER + "/secretKeyBuffer.bin");
}
......@@ -55,6 +59,6 @@ public final class Crypto {
public static byte[] createSessionKey(int length) {
byte[] bytes = new byte[length];
secureRandom.nextBytes(bytes);
return bytes;
return bytes;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment