Compare commits

..

2 Commits

Author SHA1 Message Date
56c7d0d753 Merge branch 'dev/3.0.0' into netease/dev
Some checks failed
Java CI with Gradle / build (push) Failing after 6m42s
# Conflicts:
#	api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java
#	proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
2025-07-24 09:19:06 +08:00
7cd3011e45 feat: 魔改网易登录接口
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
2025-01-28 19:20:32 +08:00
9 changed files with 167 additions and 259 deletions

View File

@@ -5,7 +5,7 @@ plugins {
}
java {
withJavadocJar()
// withJavadocJar()
withSourcesJar()
sourceSets["main"].java {

View File

@@ -203,4 +203,8 @@ public interface ProxyConfig {
default boolean isKickOnTabCompleteRateLimit() {
return getKickAfterRateLimitedTabCompletes() > 0;
}
String getNeteaseAuthUrl();
String getNeteaseGameId();
}

View File

@@ -225,4 +225,116 @@ public final class GameProfile {
+ '}';
}
}
/**
* netease auth response.
*/
public static class Response {
private Integer code = 0;
private String message;
private String details;
private ResponseEntity entity;
/**
* default constructor.
*
* @param code -
* @param message -
* @param details -
* @param entity the game profile
*/
public Response(Integer code, String message, String details, ResponseEntity entity) {
this.code = code;
this.message = message;
this.details = details;
this.entity = entity;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public ResponseEntity getEntity() {
return entity;
}
public void setEntity(ResponseEntity entity) {
this.entity = entity;
}
}
/**
* netease auth response entity.
*/
public static class ResponseEntity {
private String id;
private String name;
private List<Property> properties;
/**
* default constructor.
*
* @param id -
* @param name -
* @param properties -
*/
public ResponseEntity(String id, String name, List<Property> properties) {
this.id = id;
this.name = name;
this.properties = properties;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "ResponseEntity{"
+ "id='" + id + '\''
+ ", name='" + name + '\''
+ ", properties=" + properties
+ '}';
}
}
}

View File

@@ -52,7 +52,6 @@ import com.velocitypowered.proxy.connection.util.ServerListPingHandler;
import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.netease.NeteaseDataManager;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
@@ -287,14 +286,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
new GlistCommand(this).register();
new SendCommand(this).register();
try {
NeteaseDataManager.init();
} catch (Exception e) {
logger.error("Failed to initialize NeteaseDataManager, please check your config.", e);
LogManager.shutdown();
System.exit(1);
}
this.doStartupConfigLoad();
for (ServerInfo cliServer : options.getServers()) {

View File

@@ -94,6 +94,11 @@ public class VelocityConfiguration implements ProxyConfig {
@Expose
private boolean forceKeyAuthentication = true; // Added in 1.19
@Expose
private String authUrl = "http://192.168.46.50:9999/check";
@Expose
private String gameId = "77140593557373952";
private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced,
Query query, Metrics metrics) {
this.servers = servers;
@@ -109,7 +114,7 @@ public class VelocityConfiguration implements ProxyConfig {
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
boolean samplePlayersInPing, boolean enablePlayerAddressLogging, Servers servers,
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics,
boolean forceKeyAuthentication) {
boolean forceKeyAuthentication, String authUrl, String gameId) {
this.bind = bind;
this.motd = motd;
this.showMaxPlayers = showMaxPlayers;
@@ -128,6 +133,8 @@ public class VelocityConfiguration implements ProxyConfig {
this.query = query;
this.metrics = metrics;
this.forceKeyAuthentication = forceKeyAuthentication;
this.authUrl = authUrl;
this.gameId = gameId;
}
/**
@@ -449,6 +456,16 @@ public class VelocityConfiguration implements ProxyConfig {
return advanced.isEnableReusePort();
}
@Override
public String getNeteaseAuthUrl() {
return authUrl;
}
@Override
public String getNeteaseGameId() {
return gameId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@@ -466,6 +483,8 @@ public class VelocityConfiguration implements ProxyConfig {
.add("favicon", favicon)
.add("enablePlayerAddressLogging", enablePlayerAddressLogging)
.add("forceKeyAuthentication", forceKeyAuthentication)
.add("authUrl", authUrl)
.add("gameId", gameId)
.toString();
}
@@ -565,6 +584,8 @@ public class VelocityConfiguration implements ProxyConfig {
|| forwardingMode == PlayerInfoForwarding.BUNGEEGUARD)) {
throw new RuntimeException("The forwarding-secret file must not be empty.");
}
final String authUrl = config.getOrElse("auth-url", "http://192.168.46.50:9999/check");
final String gameId = config.getOrElse("game-id", "77140593557373952");
return new VelocityConfiguration(
bind,
@@ -584,7 +605,9 @@ public class VelocityConfiguration implements ProxyConfig {
new Advanced(advancedConfig),
new Query(queryConfig),
new Metrics(metricsConfig),
forceKeyAuthentication
forceKeyAuthentication,
authUrl,
gameId
);
}
}

View File

@@ -34,8 +34,6 @@ import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.netease.AuthResponse;
import com.velocitypowered.proxy.netease.NeteaseDataManager;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket;
@@ -208,12 +206,14 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
JsonObject data = new JsonObject();
data.addProperty("username", login.getUsername());
data.addProperty("serverId", serverId);
data.addProperty("gameID", NeteaseDataManager.getConfig().getGameId());
data.addProperty("gameID", server.getConfiguration().getNeteaseGameId());
HttpRequest httpRequest = HttpRequest.newBuilder()
.headers("Content-Type", "application/json")
.uri(URI.create(NeteaseDataManager.getConfig().getAuthUrl()))
.POST(HttpRequest.BodyPublishers.ofString(data.toString()))
.headers(
"User-Agent", server.getVersion().getName() + "/" + server.getVersion().getVersion(),
"Content-Type", "application/json"
).POST(HttpRequest.BodyPublishers.ofString(data.toString()))
.uri(URI.create(server.getConfiguration().getNeteaseAuthUrl()))
.build();
final HttpClient httpClient = server.createHttpClient();
@@ -244,12 +244,13 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
try {
if (response.statusCode() == 200) {
final AuthResponse authResponse = GENERAL_GSON.fromJson(response.body(), AuthResponse.class);
final GameProfile.Response authResponse = GENERAL_GSON.fromJson(response.body(),
GameProfile.Response.class);
if (authResponse.getCode() != 0) {
inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down"));
logger.error("Error authenticating {} with netease", login.getUsername());
} else {
AuthResponse.ResponseEntity entity = authResponse.getEntity();
GameProfile.ResponseEntity entity = authResponse.getEntity();
if (entity.getName() == null || entity.getName().isEmpty()) {
entity.setName(login.getUsername());
}
@@ -258,8 +259,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
}
if (entity.getId() == null) {
inbound.disconnect(
Component.translatable("velocity.error.online-mode-only", NamedTextColor.RED)
);
Component.translatable("velocity.error.online-mode-only", NamedTextColor.RED));
} else {
GameProfile profile = new GameProfile(entity.getId(), entity.getName(), entity.getProperties());
// Not so fast, now we verify the public key for 1.19.1+

View File

@@ -1,133 +0,0 @@
/*
* Copyright (C) 2025 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.netease;
import com.velocitypowered.api.util.GameProfile;
import java.util.List;
/**
* netease auth response.
*/
public class AuthResponse {
private Integer code = 0;
private String message;
private String details;
private ResponseEntity entity;
/**
* default constructor.
*
* @param code -
* @param message -
* @param details -
* @param entity the game profile
*/
public AuthResponse(Integer code, String message, String details, ResponseEntity entity) {
this.code = code;
this.message = message;
this.details = details;
this.entity = entity;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public ResponseEntity getEntity() {
return entity;
}
public void setEntity(ResponseEntity entity) {
this.entity = entity;
}
/**
* netease auth response entity.
*/
public static class ResponseEntity {
private String id;
private String name;
private List<GameProfile.Property> properties;
/**
* default constructor.
*
* @param id -
* @param name -
* @param properties -
*/
public ResponseEntity(String id, String name, List<GameProfile.Property> properties) {
this.id = id;
this.name = name;
this.properties = properties;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<GameProfile.Property> getProperties() {
return properties;
}
public void setProperties(List<GameProfile.Property> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "ResponseEntity{"
+ "id='" + id + '\''
+ ", name='" + name + '\''
+ ", properties=" + properties
+ '}';
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright (C) 2025 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.netease;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import org.jetbrains.annotations.NotNull;
/**
* netease data manager.
*/
public class NeteaseDataManager {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
private static Config config;
/**
* init netease data manager.
*
* @throws IOException -
*/
public static void init() throws IOException {
File file = new File("netease.json");
if (file.exists()) {
String s = Files.readString(file.toPath(), StandardCharsets.UTF_8);
config = GSON.fromJson(s, Config.class);
} else {
config = new Config();
Files.writeString(file.toPath(), GSON.toJson(config), StandardCharsets.UTF_8);
}
}
public static Config getConfig() {
return config;
}
/**
* netease config.
*/
public static class Config {
/**
* 认证链接
*
* <p>正式环境http://x19authserver.nie.netease.com/check
*
* <p>测试环境http://x19authexpr.nie.netease.com/check
*
* <p>1.20版本请使用以下接口:
*
* <p>正式环境https://x19apigatewayobt.nie.netease.com/pcauth/check
*
* <p>测试环境https://x19apigatewayexpr.nie.netease.com/pcauth/check
*
* <p>另有外网测试认证接口:
*
* <p>http://x19authtest.nie.netease.com/check
*
* <p>对应接入test版bc认证通常情况下不使用需要启用时会另行沟通
*/
@SuppressWarnings("JavadocLinkAsPlainText")
@NotNull
private String authUrl = "http://192.168.0.100:9999/check";
/**
* 网络服游戏 id在开发者平台中可以查看.
*/
@NotNull
private String gameId = "12345678901234567";
public @NotNull String getAuthUrl() {
return authUrl;
}
public void setAuthUrl(@NotNull String authUrl) {
this.authUrl = authUrl;
}
public @NotNull String getGameId() {
return gameId;
}
public void setGameId(@NotNull String gameId) {
this.gameId = gameId;
}
}
}

View File

@@ -74,6 +74,20 @@ sample-players-in-ping = false
# If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs
enable-player-address-logging = true
# 认证链接
# 正式环境http://x19authserver.nie.netease.com/check
# 测试环境http://x19authexpr.nie.netease.com/check
# 1.20版本请使用以下接口:
# 正式环境https://x19apigatewayobt.nie.netease.com/pcauth/check
# 测试环境https://x19apigatewayexpr.nie.netease.com/pcauth/check
# 另有外网测试认证接口对应接入test版bc认证通常情况下不使用需要启用时会另行沟通。
# http://x19authtest.nie.netease.com/check
auth-url = "http://192.168.46.50:9999/check"
# 网络服游戏 id
# 在开发者平台中可以查看
game-id = "77140593557373952"
[servers]
# Configure your servers here. Each key represents the server's name, and the value
# represents the IP address of the server to connect to.