mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-03-13 10:17:17 +08:00
Merge remote-tracking branch 'upstream/development' into development
This commit is contained in:
commit
9eca5123a5
@ -60,10 +60,14 @@ There is a dummy user named "Server" in every player's friends list that you can
|
|||||||
|
|
||||||
`!resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes.
|
`!resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes.
|
||||||
|
|
||||||
`!sethp [hp]`
|
`!setstats [stats] [amount]` - Changes the current character's specified stat.
|
||||||
|
|
||||||
`!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory
|
`!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory
|
||||||
|
|
||||||
|
`!pos` - Gets your current coordinate.
|
||||||
|
|
||||||
|
`!weather [weather id]` - Changes the current weather.
|
||||||
|
|
||||||
*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).*
|
*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).*
|
||||||
|
|
||||||
### Bonus
|
### Bonus
|
||||||
|
9
proxy.py
9
proxy.py
@ -16,12 +16,14 @@
|
|||||||
# - mitmdump from mitmproxy
|
# - mitmdump from mitmproxy
|
||||||
#
|
#
|
||||||
# @author MlgmXyysd
|
# @author MlgmXyysd
|
||||||
# @version 1.0
|
# @version 1.1
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
|
||||||
from mitmproxy import http
|
from mitmproxy import http
|
||||||
|
from proxy_config import USE_SSL
|
||||||
from proxy_config import REMOTE_HOST
|
from proxy_config import REMOTE_HOST
|
||||||
|
from proxy_config import REMOTE_PORT
|
||||||
|
|
||||||
class MlgmXyysd_Genshin_Impact_Proxy:
|
class MlgmXyysd_Genshin_Impact_Proxy:
|
||||||
|
|
||||||
@ -60,7 +62,12 @@ class MlgmXyysd_Genshin_Impact_Proxy:
|
|||||||
|
|
||||||
def request(self, flow: http.HTTPFlow) -> None:
|
def request(self, flow: http.HTTPFlow) -> None:
|
||||||
if flow.request.host in self.LIST_DOMAINS:
|
if flow.request.host in self.LIST_DOMAINS:
|
||||||
|
if USE_SSL:
|
||||||
|
flow.request.scheme = "https"
|
||||||
|
else:
|
||||||
|
flow.request.scheme = "http"
|
||||||
flow.request.host = REMOTE_HOST
|
flow.request.host = REMOTE_HOST
|
||||||
|
flow.request.port = REMOTE_PORT
|
||||||
|
|
||||||
addons = [
|
addons = [
|
||||||
MlgmXyysd_Genshin_Impact_Proxy()
|
MlgmXyysd_Genshin_Impact_Proxy()
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
# This can also be replaced with another IP address.
|
# This can also be replaced with another IP address.
|
||||||
REMOTE_HOST = "localhost"
|
USE_SSL = True
|
||||||
|
REMOTE_HOST = "127.0.0.1"
|
||||||
|
REMOTE_PORT = 443
|
@ -24,11 +24,9 @@ public final class Config {
|
|||||||
public DispatchServerOptions getDispatchOptions() { return DispatchServer; }
|
public DispatchServerOptions getDispatchOptions() { return DispatchServer; }
|
||||||
|
|
||||||
public static class DispatchServerOptions {
|
public static class DispatchServerOptions {
|
||||||
public String Ip = "127.0.0.1";
|
public String Ip = "0.0.0.0";
|
||||||
public String PublicIp = "";
|
public String PublicIp = "127.0.0.1";
|
||||||
public int Port = 443;
|
public int Port = 443;
|
||||||
public int OverseaLogPort = 8888;
|
|
||||||
public int UploadLogPort = 80;
|
|
||||||
public String KeystorePath = "./keystore.p12";
|
public String KeystorePath = "./keystore.p12";
|
||||||
public String KeystorePassword = "";
|
public String KeystorePassword = "";
|
||||||
public Boolean UseSSL = true;
|
public Boolean UseSSL = true;
|
||||||
@ -51,8 +49,8 @@ public final class Config {
|
|||||||
|
|
||||||
public static class GameServerOptions {
|
public static class GameServerOptions {
|
||||||
public String Name = "Test";
|
public String Name = "Test";
|
||||||
public String Ip = "127.0.0.1";
|
public String Ip = "0.0.0.0";
|
||||||
public String PublicIp = "";
|
public String PublicIp = "127.0.0.1";
|
||||||
public int Port = 22102;
|
public int Port = 22102;
|
||||||
|
|
||||||
public String DispatchServerDatabaseUrl = "mongodb://localhost:27017";
|
public String DispatchServerDatabaseUrl = "mongodb://localhost:27017";
|
||||||
|
@ -95,18 +95,19 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void item(GenshinPlayer player, ItemData itemData, int amount) {
|
private void item(GenshinPlayer player, ItemData itemData, int amount) {
|
||||||
GenshinItem genshinItem = new GenshinItem(itemData);
|
|
||||||
if (itemData.isEquip()) {
|
if (itemData.isEquip()) {
|
||||||
List<GenshinItem> items = new LinkedList<>();
|
List<GenshinItem> items = new LinkedList<>();
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
items.add(genshinItem);
|
items.add(new GenshinItem(itemData));
|
||||||
}
|
}
|
||||||
player.getInventory().addItems(items);
|
player.getInventory().addItems(items);
|
||||||
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
|
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
|
||||||
} else {
|
} else {
|
||||||
|
GenshinItem genshinItem = new GenshinItem(itemData);
|
||||||
genshinItem.setCount(amount);
|
genshinItem.setCount(amount);
|
||||||
player.getInventory().addItem(genshinItem);
|
player.getInventory().addItem(genshinItem);
|
||||||
player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop));
|
player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Command(label = "list", description = "List online players")
|
||||||
|
public class ListCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
Map<Integer, GenshinPlayer> playersMap = Grasscutter.getGameServer().getPlayers();
|
||||||
|
|
||||||
|
CommandHandler.sendMessage(sender, String.format("There are %s player(s) online:", playersMap.size()));
|
||||||
|
|
||||||
|
if (playersMap.size() != 0) {
|
||||||
|
StringBuilder playerSet = new StringBuilder();
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, GenshinPlayer> entry : playersMap.entrySet()) {
|
||||||
|
playerSet.append(entry.getValue().getNickname());
|
||||||
|
playerSet.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String players = playerSet.toString();
|
||||||
|
|
||||||
|
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,10 @@
|
|||||||
package emu.grasscutter.server.dispatch;
|
package emu.grasscutter.server.dispatch;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import com.sun.net.httpserver.HttpsConfigurator;
|
import com.sun.net.httpserver.HttpsConfigurator;
|
||||||
import com.sun.net.httpserver.HttpsServer;
|
import com.sun.net.httpserver.HttpsServer;
|
||||||
|
|
||||||
@ -31,29 +16,31 @@ import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegio
|
|||||||
import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp;
|
import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp;
|
||||||
import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
|
import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
|
||||||
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
|
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
|
||||||
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson;
|
import emu.grasscutter.server.dispatch.json.*;
|
||||||
import emu.grasscutter.server.dispatch.json.ComboTokenResJson;
|
|
||||||
import emu.grasscutter.server.dispatch.json.LoginAccountRequestJson;
|
|
||||||
import emu.grasscutter.server.dispatch.json.LoginResultJson;
|
|
||||||
import emu.grasscutter.server.dispatch.json.LoginTokenRequestJson;
|
|
||||||
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
|
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public final class DispatchServer {
|
public final class DispatchServer {
|
||||||
|
public static String query_region_list = "";
|
||||||
|
public static String query_cur_region = "";
|
||||||
|
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
private final String defaultServerName = "os_usa";
|
private final String defaultServerName = "os_usa";
|
||||||
//private QueryCurrRegionHttpRsp currRegion;
|
|
||||||
|
|
||||||
public String regionListBase64;
|
public String regionListBase64;
|
||||||
public HashMap<String, RegionData> regions;
|
public HashMap<String, RegionData> regions;
|
||||||
|
|
||||||
public static String query_region_list = "";
|
|
||||||
public static String query_cur_region = "";
|
|
||||||
|
|
||||||
public DispatchServer() {
|
public DispatchServer() {
|
||||||
this.regions = new HashMap<String, RegionData>();
|
this.regions = new HashMap<String, RegionData>();
|
||||||
this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port);
|
this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port);
|
||||||
@ -77,7 +64,7 @@ public final class DispatchServer {
|
|||||||
return regions.get(defaultServerName).parsedRegionQuery;
|
return regions.get(defaultServerName).parsedRegionQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
Grasscutter.getLogger().error("Ignore the error below");
|
Grasscutter.getLogger().warn("[Dispatch] Unsupported run mode for getCurrRegion()");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,14 +75,14 @@ public final class DispatchServer {
|
|||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
query_region_list = new String(FileUtils.read(file));
|
query_region_list = new String(FileUtils.read(file));
|
||||||
} else {
|
} else {
|
||||||
Grasscutter.getLogger().warn("query_region_list not found! Using default region list.");
|
Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list.");
|
||||||
}
|
}
|
||||||
|
|
||||||
file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt");
|
file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt");
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
query_cur_region = new String(FileUtils.read(file));
|
query_cur_region = new String(FileUtils.read(file));
|
||||||
} else {
|
} else {
|
||||||
Grasscutter.getLogger().warn("query_cur_region not found! Using default current region.");
|
Grasscutter.getLogger().warn("[Dispatch] query_cur_region not found! Using default current region.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +117,7 @@ public final class DispatchServer {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) {
|
if(Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) {
|
||||||
Grasscutter.getLogger().error("Dispatch server has no game servers available. Exiting due to unplayable state.");
|
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +161,7 @@ public final class DispatchServer {
|
|||||||
|
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
HttpServer server;
|
HttpServer server;
|
||||||
if(Grasscutter.getConfig().getDispatchOptions().UseSSL) {
|
if (Grasscutter.getConfig().getDispatchOptions().UseSSL) {
|
||||||
HttpsServer httpsServer;
|
HttpsServer httpsServer;
|
||||||
httpsServer = HttpsServer.create(getAddress(), 0);
|
httpsServer = HttpsServer.create(getAddress(), 0);
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
@ -190,58 +177,36 @@ public final class DispatchServer {
|
|||||||
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
|
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
|
||||||
server = httpsServer;
|
server = httpsServer;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger().error("No SSL cert found!");
|
Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server.");
|
||||||
return;
|
Grasscutter.getConfig().getDispatchOptions().UseSSL = false;
|
||||||
|
server = HttpServer.create(getAddress(), 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
server = HttpServer.create(getAddress(), 0);
|
server = HttpServer.create(getAddress(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.createContext("/", t -> {
|
server.createContext("/", t -> responseHTML(t, "Hello"));
|
||||||
//Create a response form the request query parameters
|
|
||||||
String response = "Hello";
|
|
||||||
//Set the response header status and length
|
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
//Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Dispatch
|
// Dispatch
|
||||||
server.createContext("/query_region_list", t -> {
|
server.createContext("/query_region_list", t -> {
|
||||||
// Log
|
// Log
|
||||||
Grasscutter.getLogger().info("Client request: query_region_list");
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress()));
|
||||||
// Create a response form the request query parameters
|
|
||||||
String response = regionListBase64;
|
responseHTML(t, regionListBase64);
|
||||||
// Set the response header status and length
|
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
// Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (String regionName : regions.keySet()) {
|
for (String regionName : regions.keySet()) {
|
||||||
server.createContext("/query_cur_region_" + regionName, t -> {
|
server.createContext("/query_cur_region_" + regionName, t -> {
|
||||||
String regionCurrentBase64 = regions.get(regionName).Base64;
|
String regionCurrentBase64 = regions.get(regionName).Base64;
|
||||||
// Log
|
// Log
|
||||||
Grasscutter.getLogger().info("Client request: query_cur_region_" + regionName);
|
Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region_%s", t.getRemoteAddress(), regionName));
|
||||||
// Create a response form the request query parameters
|
// Create a response form the request query parameters
|
||||||
URI uri = t.getRequestURI();
|
URI uri = t.getRequestURI();
|
||||||
String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
|
String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
|
||||||
if (uri.getQuery() != null && uri.getQuery().length() > 0) {
|
if (uri.getQuery() != null && uri.getQuery().length() > 0) {
|
||||||
response = regionCurrentBase64;
|
response = regionCurrentBase64;
|
||||||
}
|
}
|
||||||
// Set the response header status and length
|
responseHTML(t, response);
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
// Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,15 +217,16 @@ public final class DispatchServer {
|
|||||||
try {
|
try {
|
||||||
String body = Utils.toString(t.getRequestBody());
|
String body = Utils.toString(t.getRequestBody());
|
||||||
requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class);
|
requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) { }
|
||||||
|
|
||||||
}
|
|
||||||
// Create response json
|
// Create response json
|
||||||
if (requestData == null) {
|
if (requestData == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LoginResultJson responseData = new LoginResultJson();
|
LoginResultJson responseData = new LoginResultJson();
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress()));
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
||||||
|
|
||||||
@ -271,13 +237,24 @@ public final class DispatchServer {
|
|||||||
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
||||||
account = DatabaseHelper.createAccountWithId(requestData.account, 0);
|
account = DatabaseHelper.createAccountWithId(requestData.account, 0);
|
||||||
|
|
||||||
|
if (account != null) {
|
||||||
responseData.message = "OK";
|
responseData.message = "OK";
|
||||||
responseData.data.account.uid = account.getId();
|
responseData.data.account.uid = account.getId();
|
||||||
responseData.data.account.token = account.generateSessionKey();
|
responseData.data.account.token = account.generateSessionKey();
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid));
|
||||||
|
} else {
|
||||||
|
responseData.retcode = -201;
|
||||||
|
responseData.message = "Username not found, create failed.";
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
responseData.retcode = -201;
|
responseData.retcode = -201;
|
||||||
responseData.message = "Username not found.";
|
responseData.message = "Username not found.";
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Account was found, log the player in
|
// Account was found, log the player in
|
||||||
@ -285,17 +262,11 @@ public final class DispatchServer {
|
|||||||
responseData.data.account.uid = account.getId();
|
responseData.data.account.uid = account.getId();
|
||||||
responseData.data.account.token = account.generateSessionKey();
|
responseData.data.account.token = account.generateSessionKey();
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a response
|
responseJSON(t, responseData);
|
||||||
String response = getGsonFactory().toJson(responseData);
|
|
||||||
// Set the response header status and length
|
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
// Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
});
|
||||||
// Login via token
|
// Login via token
|
||||||
server.createContext("/hk4e_global/mdk/shield/api/verify", t -> {
|
server.createContext("/hk4e_global/mdk/shield/api/verify", t -> {
|
||||||
@ -304,14 +275,14 @@ public final class DispatchServer {
|
|||||||
try {
|
try {
|
||||||
String body = Utils.toString(t.getRequestBody());
|
String body = Utils.toString(t.getRequestBody());
|
||||||
requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class);
|
requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) { }
|
||||||
|
|
||||||
}
|
|
||||||
// Create response json
|
// Create response json
|
||||||
if (requestData == null) {
|
if (requestData == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LoginResultJson responseData = new LoginResultJson();
|
LoginResultJson responseData = new LoginResultJson();
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress()));
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
Account account = DatabaseHelper.getAccountById(requestData.uid);
|
Account account = DatabaseHelper.getAccountById(requestData.uid);
|
||||||
@ -320,22 +291,18 @@ public final class DispatchServer {
|
|||||||
if (account == null || !account.getSessionKey().equals(requestData.token)) {
|
if (account == null || !account.getSessionKey().equals(requestData.token)) {
|
||||||
responseData.retcode = -111;
|
responseData.retcode = -111;
|
||||||
responseData.message = "Game account cache information error";
|
responseData.message = "Game account cache information error";
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress()));
|
||||||
} else {
|
} else {
|
||||||
responseData.message = "OK";
|
responseData.message = "OK";
|
||||||
responseData.data.account.uid = requestData.uid;
|
responseData.data.account.uid = requestData.uid;
|
||||||
responseData.data.account.token = requestData.token;
|
responseData.data.account.token = requestData.token;
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a response
|
responseJSON(t, responseData);
|
||||||
String response = getGsonFactory().toJson(responseData);
|
|
||||||
// Set the response header status and length
|
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
// Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
});
|
||||||
// Exchange for combo token
|
// Exchange for combo token
|
||||||
server.createContext("/hk4e_global/combo/granter/login/v2/login", t -> {
|
server.createContext("/hk4e_global/combo/granter/login/v2/login", t -> {
|
||||||
@ -344,9 +311,8 @@ public final class DispatchServer {
|
|||||||
try {
|
try {
|
||||||
String body = Utils.toString(t.getRequestBody());
|
String body = Utils.toString(t.getRequestBody());
|
||||||
requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class);
|
requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) { }
|
||||||
|
|
||||||
}
|
|
||||||
// Create response json
|
// Create response json
|
||||||
if (requestData == null || requestData.data == null) {
|
if (requestData == null || requestData.data == null) {
|
||||||
return;
|
return;
|
||||||
@ -361,22 +327,18 @@ public final class DispatchServer {
|
|||||||
if (account == null || !account.getSessionKey().equals(loginData.token)) {
|
if (account == null || !account.getSessionKey().equals(loginData.token)) {
|
||||||
responseData.retcode = -201;
|
responseData.retcode = -201;
|
||||||
responseData.message = "Wrong session key.";
|
responseData.message = "Wrong session key.";
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress()));
|
||||||
} else {
|
} else {
|
||||||
responseData.message = "OK";
|
responseData.message = "OK";
|
||||||
responseData.data.open_id = loginData.uid;
|
responseData.data.open_id = loginData.uid;
|
||||||
responseData.data.combo_id = "157795300";
|
responseData.data.combo_id = "157795300";
|
||||||
responseData.data.combo_token = account.generateLoginToken();
|
responseData.data.combo_token = account.generateLoginToken();
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a response
|
responseJSON(t, responseData);
|
||||||
String response = getGsonFactory().toJson(responseData);
|
|
||||||
// Set the response header status and length
|
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json"));
|
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
|
||||||
// Write the response string
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(response.getBytes());
|
|
||||||
os.close();
|
|
||||||
});
|
});
|
||||||
// Agreement and Protocol
|
// Agreement and Protocol
|
||||||
server.createContext( // hk4e-sdk-os.hoyoverse.com
|
server.createContext( // hk4e-sdk-os.hoyoverse.com
|
||||||
@ -444,57 +406,67 @@ public final class DispatchServer {
|
|||||||
"/perf/config/verify",
|
"/perf/config/verify",
|
||||||
new DispatchHttpJsonHandler("{\"code\":0}")
|
new DispatchHttpJsonHandler("{\"code\":0}")
|
||||||
);
|
);
|
||||||
// Start server
|
|
||||||
server.start();
|
|
||||||
Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort());
|
|
||||||
|
|
||||||
// Logging servers
|
// Logging servers
|
||||||
HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().OverseaLogPort), 0);
|
server.createContext( // overseauspider.yuanshen.com
|
||||||
overseaLogServer.createContext( // overseauspider.yuanshen.com
|
|
||||||
"/log",
|
"/log",
|
||||||
new DispatchHttpJsonHandler("{\"code\":0}")
|
new DispatchHttpJsonHandler("{\"code\":0}")
|
||||||
);
|
);
|
||||||
overseaLogServer.start();
|
|
||||||
Grasscutter.getLogger().info("Log server (overseauspider) started on port " + 8888);
|
|
||||||
|
|
||||||
HttpServer uploadLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().UploadLogPort), 0);
|
server.createContext( // log-upload-os.mihoyo.com
|
||||||
uploadLogServer.createContext( // log-upload-os.mihoyo.com
|
|
||||||
"/crash/dataUpload",
|
"/crash/dataUpload",
|
||||||
new DispatchHttpJsonHandler("{\"code\":0}")
|
new DispatchHttpJsonHandler("{\"code\":0}")
|
||||||
);
|
);
|
||||||
uploadLogServer.createContext("/gacha", t -> {
|
server.createContext("/gacha", t -> responseHTML(t, "<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"));
|
||||||
//Create a response form the request query parameters
|
|
||||||
String response = "<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>";
|
// Start server
|
||||||
//Set the response header status and length
|
server.start();
|
||||||
|
Grasscutter.getLogger().info("[Dispatch] Dispatch server started on port " + getAddress().getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void responseJSON(HttpExchange t, Object data) throws IOException {
|
||||||
|
// Create a response
|
||||||
|
String response = getGsonFactory().toJson(data);
|
||||||
|
// Set the response header status and length
|
||||||
|
t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json"));
|
||||||
|
t.sendResponseHeaders(200, response.getBytes().length);
|
||||||
|
// Write the response string
|
||||||
|
OutputStream os = t.getResponseBody();
|
||||||
|
os.write(response.getBytes());
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void responseHTML(HttpExchange t, String response) throws IOException {
|
||||||
|
// Set the response header status and length
|
||||||
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
|
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
|
||||||
t.sendResponseHeaders(200, response.getBytes().length);
|
t.sendResponseHeaders(200, response.getBytes().length);
|
||||||
//Write the response string
|
//Write the response string
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
os.close();
|
os.close();
|
||||||
});
|
|
||||||
uploadLogServer.start();
|
|
||||||
Grasscutter.getLogger().info("Log server (log-upload-os) started on port " + Grasscutter.getConfig().getDispatchOptions().UploadLogPort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> parseQueryString(String qs) {
|
private Map<String, String> parseQueryString(String qs) {
|
||||||
Map<String, String> result = new HashMap<>();
|
Map<String, String> result = new HashMap<>();
|
||||||
if (qs == null)
|
if (qs == null) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int last = 0, next, l = qs.length();
|
int last = 0, next, l = qs.length();
|
||||||
while (last < l) {
|
while (last < l) {
|
||||||
next = qs.indexOf('&', last);
|
next = qs.indexOf('&', last);
|
||||||
if (next == -1)
|
if (next == -1) {
|
||||||
next = l;
|
next = l;
|
||||||
|
}
|
||||||
|
|
||||||
if (next > last) {
|
if (next > last) {
|
||||||
int eqPos = qs.indexOf('=', last);
|
int eqPos = qs.indexOf('=', last);
|
||||||
try {
|
try {
|
||||||
if (eqPos < 0 || eqPos > next)
|
if (eqPos < 0 || eqPos > next) {
|
||||||
result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
|
result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
|
||||||
else
|
} else {
|
||||||
result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
|
result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
|
||||||
|
}
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
|
throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ public class ComboTokenReqJson {
|
|||||||
public String device;
|
public String device;
|
||||||
public String sign;
|
public String sign;
|
||||||
|
|
||||||
public class LoginTokenData {
|
public static class LoginTokenData {
|
||||||
public String uid;
|
public String uid;
|
||||||
public String token;
|
public String token;
|
||||||
public boolean guest;
|
public boolean guest;
|
||||||
|
@ -5,7 +5,7 @@ public class ComboTokenResJson {
|
|||||||
public int retcode;
|
public int retcode;
|
||||||
public LoginData data = new LoginData();
|
public LoginData data = new LoginData();
|
||||||
|
|
||||||
public class LoginData {
|
public static class LoginData {
|
||||||
public int account_type = 1;
|
public int account_type = 1;
|
||||||
public boolean heartbeat;
|
public boolean heartbeat;
|
||||||
public String combo_id;
|
public String combo_id;
|
||||||
|
@ -5,7 +5,7 @@ public class LoginResultJson {
|
|||||||
public int retcode;
|
public int retcode;
|
||||||
public VerifyData data = new VerifyData();
|
public VerifyData data = new VerifyData();
|
||||||
|
|
||||||
public class VerifyData {
|
public static class VerifyData {
|
||||||
public VerifyAccountData account = new VerifyAccountData();
|
public VerifyAccountData account = new VerifyAccountData();
|
||||||
public boolean device_grant_required = false;
|
public boolean device_grant_required = false;
|
||||||
public String realname_operation = "NONE";
|
public String realname_operation = "NONE";
|
||||||
@ -13,7 +13,7 @@ public class LoginResultJson {
|
|||||||
public boolean safe_mobile_required = false;
|
public boolean safe_mobile_required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VerifyAccountData {
|
public static class VerifyAccountData {
|
||||||
public String uid;
|
public String uid;
|
||||||
public String name = "";
|
public String name = "";
|
||||||
public String email;
|
public String email;
|
||||||
|
@ -12,7 +12,7 @@ public class PacketSceneUnlockInfoNotify extends GenshinPacket {
|
|||||||
|
|
||||||
SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder()
|
SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder()
|
||||||
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1))
|
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1))
|
||||||
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3))
|
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3).addSceneTagIdList(102).addSceneTagIdList(113).addSceneTagIdList(117))
|
||||||
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4).addSceneTagIdList(106).addSceneTagIdList(109))
|
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4).addSceneTagIdList(106).addSceneTagIdList(109))
|
||||||
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5))
|
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5))
|
||||||
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6))
|
.addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user