Skip to content

Commit

Permalink
Merge pull request #16 from YuxuanZuo/develop
Browse files Browse the repository at this point in the history
Release v0.3.0
  • Loading branch information
YuxuanZuo authored Nov 23, 2023
2 parents fba2e09 + ad95c32 commit 87277a2
Show file tree
Hide file tree
Showing 41 changed files with 466 additions and 2,370 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/deploy_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
build_number=$(grep -Pom1 '@@release\.build_number=\K.*(?=@@)' <<< $release_body)
version_number=$(grep -Pom1 '@@release\.version_number=\K.*(?=@@)' <<< $release_body)
asset_name='${{ github.event.release.assets[0].name }}'
release_published_at='${{ github.event.release.published_at }}'
cd ~/deploy
git config --local user.name "github-actions[bot]"
Expand All @@ -49,12 +50,14 @@ jobs:
jq -n \
--arg build_number "$build_number" \
--arg version "$version_number" \
--arg release_time "$release_published_at" \
--arg download_url "https://multiyggdrasil.zuoyx.xyz/artifact/$build_number/$asset_name" \
--arg sha256 "$sha256" \
'
{
"build_number": $build_number|tonumber,
"version": $version,
"release_time": $release_time,
"download_url": $download_url,
"checksums": {
"sha256": $sha256
Expand Down
15 changes: 9 additions & 6 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# MultiYggdrasil
[![latest release](https://img.shields.io/github/v/tag/YuxuanZuo/MultiYggdrasil?color=yellow&include_prereleases&label=version&sort=semver&style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/releases)
[![ci status](https://img.shields.io/github/workflow/status/YuxuanZuo/MultiYggdrasil/CI?style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/actions?query=workflow%3ACI)
[![ci status](https://img.shields.io/github/actions/workflow/status/YuxuanZuo/MultiYggdrasil/ci.yml?branch=develop)](https://github.com/YuxuanZuo/MultiYggdrasil/actions?query=workflow%3ACI)
[![license agpl-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue.svg?style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/blob/develop/LICENSE)

A fork of authlib-injector with support for coexist with the Mojang authentication server.
Expand All @@ -27,6 +27,9 @@ Configure Minecraft server with the following JVM parameter:
```
-javaagent:{/path/to/MultiYggdrasil.jar}={Authentication Server URL}
```
Note: Unless the custom authentication server supports Mojang authentication server coexistence, this feature will not
be enabled by default. You need to enable it by adding specific JVM parameters. Please refer to the [Options](README.en.md#options)
section for more details.

## Options
```
Expand Down Expand Up @@ -74,6 +77,7 @@ Configure Minecraft server with the following JVM parameter:
-Dauthlibinjector.disableHttpd
Disable local HTTP server.
Features (see below) depending on local HTTP server will be unavailable:
- Mojang Yggdrasil server
- Mojang namespace
- Legacy skin API polyfill
Expand Down Expand Up @@ -121,9 +125,9 @@ Configure Minecraft server with the following JVM parameter:
In order to distinguish the username of players from the custom authentication server from that of the Mojang server,
the player who from the custom authentication server will add a namespace suffix to their username.
For example:
Notch.custom
Notch.cust
If the option "-Dmultiyggdrasil.namespace" is not set and the field "namespace" is not sent by authentication
server, the server will issue a default namespace called "custom". If any fields were sent, the server will use the
server, the server will issue a default namespace called "cust". If any fields were sent, the server will use the
namespace that you defined earlier.
Some features that conflict with Mojang Yggdrasil server will no longer available anymore:
Expand All @@ -134,7 +138,7 @@ Configure Minecraft server with the following JVM parameter:
(The default is to give priority to verification of the genuine player).
-Dmultiyggdrasil.namespace={namespace string}
Set the namespace used by the feature "Mojang authentication server". Allowed characters are a-z0-9._- .
Set the namespace used by the feature "Mojang authentication server". Allowed characters are a-z0-9_- .
-Dmultiyggdrasil.noNamespaceSuffix
Do not add namespace suffix to the username.
Expand All @@ -157,5 +161,4 @@ This work is licensed under the [GNU Affero General Public License v3.0](https:/
* [authlib-injector](https://github.com/yushijinhun/authlib-injector) by [Haowei Wen](https://github.com/yushijinhun)
This is the base of this project, which makes our ideas possible.
* [Gson](https://github.com/google/gson) by Google Inc.
* [ASM](https://asm.ow2.io) by INRIA, France Telecom
* [NanoHttpd](https://github.com/NanoHttpd/nanohttpd)
* [ASM](https://asm.ow2.io) by INRIA, France Telecom
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# MultiYggdrasil
[![latest release](https://img.shields.io/github/v/tag/YuxuanZuo/MultiYggdrasil?color=yellow&include_prereleases&label=version&sort=semver&style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/releases)
[![ci status](https://img.shields.io/github/workflow/status/YuxuanZuo/MultiYggdrasil/CI?style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/actions?query=workflow%3ACI)
[![ci status](https://img.shields.io/github/actions/workflow/status/YuxuanZuo/MultiYggdrasil/ci.yml?branch=develop)](https://github.com/YuxuanZuo/MultiYggdrasil/actions?query=workflow%3ACI)
[![license agpl-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue.svg?style=flat-square)](https://github.com/YuxuanZuo/MultiYggdrasil/blob/develop/LICENSE)

一个 [authlib-injector](https://github.com/yushijinhun/authlib-injector) 的分支, 添加了与 Mojang 验证服务器共存的支持.
Expand All @@ -27,6 +27,8 @@ gradle
```
-javaagent:{MultiYggdrasil.jar 的路径}={验证服务器 URL (API 地址)}
```
注意: 除非验证服务器支持, 否则默认情况下不会启用 Mojang 验证服务器共存功能, 您需要通过添加特定 JVM 参数来启用这项功能.
详情请参阅[参数](README.md#参数)小节.

## 参数
```
Expand Down Expand Up @@ -82,6 +84,7 @@ gradle
-Dauthlibinjector.disableHttpd
禁用内建的 HTTP 服务器.
以下依赖内建 HTTP 服务器的功能将不可用:
- Mojang 验证服务器
- Mojang 命名空间
- 旧式皮肤 API polyfill
Expand Down Expand Up @@ -124,8 +127,8 @@ gradle
为了将自定义验证服务器角色与正版角色的用户名区别开, 前者的用户名将被添加命名空间后缀.
例如:
Notch.custom
若未设置 -Dmultiyggdrasil.namespace 参数且验证服务器未设置 namespace 字段, 将使用默认命名空间 custom, 否则使用定义的命名空间.
Notch.cust
若未设置 -Dmultiyggdrasil.namespace 参数且验证服务器未设置 namespace 字段, 将使用默认命名空间 cust, 否则使用定义的命名空间.
以下与 Mojang 验证服务器冲突的功能将不可用:
- Mojang 命名空间
Expand All @@ -134,7 +137,7 @@ gradle
在登录游戏服务器时优先验证来自自定义验证服务器的角色(默认为优先验证正版角色).
-Dmultiyggdrasil.namespace={命名空间字符串}
设置 Mojang 验证服务器 功能使用的命名空间, 允许的字符为 a-z0-9._- .
设置 Mojang 验证服务器 功能使用的命名空间, 允许的字符为 a-z0-9_- .
-Dmultiyggdrasil.noNamespaceSuffix
不要在用户名中添加命名空间后缀.
Expand All @@ -155,5 +158,4 @@ gradle
* [authlib-injector](https://github.com/yushijinhun/authlib-injector) by [Haowei Wen](https://github.com/yushijinhun)
这是本项目的基础, 它使得我们的想法成为可能.
* [Gson](https://github.com/google/gson) by Google Inc.
* [ASM](https://asm.ow2.io) by INRIA, France Telecom
* [NanoHttpd](https://github.com/NanoHttpd/nanohttpd)
* [ASM](https://asm.ow2.io) by INRIA, France Telecom
17 changes: 8 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'com.palantir.git-version' version '0.15.0'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'com.palantir.git-version' version '3.0.0'
id 'java'
}

Expand All @@ -9,16 +9,18 @@ repositories {
}

dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'org.ow2.asm:asm:9.3'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'org.ow2.asm:asm:9.6'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
}

test {
useJUnitPlatform()
}

sourceCompatibility = 17
compileJava {
options.release = 17
}

def buildNumber = System.getenv('AI_BUILD_NUMBER')
def gitInfo = versionDetails()
Expand Down Expand Up @@ -55,9 +57,6 @@ shadowJar {
exclude 'META-INF/maven/**'
exclude 'module-info.class'
exclude '**/module-info.class'

relocate 'com.google.gson', 'xyz.zuoyx.multiyggdrasil.internal.com.google.gson'
relocate 'org.objectweb.asm', 'xyz.zuoyx.multiyggdrasil.internal.org.objectweb.asm'
}

defaultTasks 'clean', 'shadowJar'
12 changes: 7 additions & 5 deletions src/main/java/xyz/zuoyx/multiyggdrasil/MultiYggdrasil.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.io.UncheckedIOException;
import java.lang.instrument.Instrumentation;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
Expand Down Expand Up @@ -143,7 +145,7 @@ private static APIMetadata fetchAPIMetadata(String apiUrl) {
} else {

try {
HttpURLConnection connection = (HttpURLConnection) new URL(apiUrl).openConnection();
HttpURLConnection connection = (HttpURLConnection) new URI(apiUrl).toURL().openConnection();

String ali = connection.getHeaderField("x-authlib-injector-api-location");
if (ali != null) {
Expand All @@ -156,7 +158,7 @@ private static APIMetadata fetchAPIMetadata(String apiUrl) {
try (InputStream in = connection.getInputStream()) {
while (in.read() != -1)
;
} catch (IOException e) {
} catch (IOException ignored) {
}

log(INFO, "Redirect to: " + absoluteAli);
Expand All @@ -169,7 +171,7 @@ private static APIMetadata fetchAPIMetadata(String apiUrl) {
try (InputStream in = connection.getInputStream()) {
metadataResponse = asString(asBytes(in));
}
} catch (IOException e) {
} catch (URISyntaxException | IOException e) {
log(ERROR, "Failed to fetch metadata: " + e);
throw new InitializationException(e);
}
Expand Down Expand Up @@ -211,8 +213,8 @@ private static void warnIfHttp(String url) {
}

private static String addHttpsIfMissing(String url) {
String lowercased = url.toLowerCase();
if (!lowercased.startsWith("http://") && !lowercased.startsWith("https://")) {
String lowerCase = url.toLowerCase();
if (!lowerCase.startsWith("http://") && !lowerCase.startsWith("https://")) {
url = "https://" + url;
}
return url;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
package xyz.zuoyx.multiyggdrasil.httpd;

import static xyz.zuoyx.multiyggdrasil.util.IOUtils.CONTENT_TYPE_JSON;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.CONTENT_TYPE_TEXT;
import java.util.Optional;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.IHTTPSession;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Response;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Status;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.sendResponse;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import xyz.zuoyx.multiyggdrasil.util.UnsupportedURLException;

/**
* Disables Mojang's anti-features.
Expand All @@ -38,17 +37,17 @@ public boolean canHandle(String domain) {
}

@Override
public Optional<Response> handle(String domain, String path, IHTTPSession session) {
if (domain.equals("api.minecraftservices.com") && path.equals("/privileges") && session.getMethod().equals("GET")) {
return Optional.of(Response.newFixedLength(Status.OK, CONTENT_TYPE_JSON, RESPONSE_PRIVILEGES));
} else if (domain.equals("api.minecraftservices.com") && path.equals("/player/attributes") && session.getMethod().equals("GET")) {
return Optional.of(Response.newFixedLength(Status.OK, CONTENT_TYPE_JSON, RESPONSE_PLAYER_ATTRIBUTES));
} else if (domain.equals("api.minecraftservices.com") && path.equals("/privacy/blocklist") && session.getMethod().equals("GET")) {
return Optional.of(Response.newFixedLength(Status.OK, CONTENT_TYPE_JSON, RESPONSE_PRIVACY_BLOCKLIST));
} else if (domain.equals("sessionserver.mojang.com") && path.equals("/blockedservers") && session.getMethod().equals("GET")) {
return Optional.of(Response.newFixedLength(Status.NOT_FOUND, CONTENT_TYPE_TEXT, ""));
public void handle(String domain, String path, HttpExchange exchange) throws UnsupportedURLException, IOException {
if (domain.equals("api.minecraftservices.com") && path.equals("/privileges") && exchange.getRequestMethod().equals("GET")) {
sendResponse(exchange, 200, CONTENT_TYPE_JSON, RESPONSE_PRIVILEGES.getBytes());
} else if (domain.equals("api.minecraftservices.com") && path.equals("/player/attributes") && exchange.getRequestMethod().equals("GET")) {
sendResponse(exchange, 200, CONTENT_TYPE_JSON, RESPONSE_PLAYER_ATTRIBUTES.getBytes());
} else if (domain.equals("api.minecraftservices.com") && path.equals("/privacy/blocklist") && exchange.getRequestMethod().equals("GET")) {
sendResponse(exchange, 200, CONTENT_TYPE_JSON, RESPONSE_PRIVACY_BLOCKLIST.getBytes());
} else if (domain.equals("sessionserver.mojang.com") && path.equals("/blockedservers") && exchange.getRequestMethod().equals("GET")) {
sendResponse(exchange, 404, null, null);
} else {
return Optional.empty();
throw new UnsupportedURLException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
package xyz.zuoyx.multiyggdrasil.httpd;

import static xyz.zuoyx.multiyggdrasil.util.IOUtils.CONTENT_TYPE_JSON;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.sendResponse;
import static xyz.zuoyx.multiyggdrasil.util.JsonUtils.toJsonString;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.google.gson.JsonObject;
import xyz.zuoyx.multiyggdrasil.MultiYggdrasil;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.IHTTPSession;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Response;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Status;
import xyz.zuoyx.multiyggdrasil.transform.PerformanceMetrics;

/**
* MultiYggdrasil's debug API
*/
public class DebugApiEndpoint {

public Response serve(IHTTPSession session) {
if (session.getUri().equals("/debug/metrics") && session.getMethod().equals("GET")) {
public void serve(HttpExchange exchange) throws IOException {
if (exchange.getRequestURI().getPath().equals("/debug/metrics") && exchange.getRequestMethod().equals("GET")) {
PerformanceMetrics metrics = MultiYggdrasil.getClassTransformer().performanceMetrics;
JsonObject response = new JsonObject();
response.addProperty("totalTime", metrics.getTotalTime());
Expand All @@ -40,9 +40,9 @@ public Response serve(IHTTPSession session) {
response.addProperty("analysisTime", metrics.getAnalysisTime());
response.addProperty("classesScanned", metrics.getClassesScanned());
response.addProperty("classesSkipped", metrics.getClassesSkipped());
return Response.newFixedLength(Status.OK, CONTENT_TYPE_JSON, toJsonString(response));
sendResponse(exchange, 200, CONTENT_TYPE_JSON, toJsonString(response).getBytes());
} else {
return Response.newFixedLength(Status.NOT_FOUND, null, null);
sendResponse(exchange, 404, null, null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,28 @@
package xyz.zuoyx.multiyggdrasil.httpd;

import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.CONTENT_TYPE_IMAGE;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.asString;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.http;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.newUncheckedIOException;
import static xyz.zuoyx.multiyggdrasil.util.IOUtils.sendResponse;
import static xyz.zuoyx.multiyggdrasil.util.JsonUtils.parseJson;
import static xyz.zuoyx.multiyggdrasil.util.Logging.log;
import static xyz.zuoyx.multiyggdrasil.util.Logging.Level.DEBUG;
import static xyz.zuoyx.multiyggdrasil.util.Logging.Level.INFO;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.util.Base64;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.IHTTPSession;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Response;
import xyz.zuoyx.multiyggdrasil.internal.fi.iki.elonen.Status;
import com.sun.net.httpserver.HttpExchange;
import xyz.zuoyx.multiyggdrasil.util.JsonUtils;
import xyz.zuoyx.multiyggdrasil.util.UnsupportedURLException;
import xyz.zuoyx.multiyggdrasil.yggdrasil.YggdrasilClient;

public class LegacySkinAPIFilter implements URLFilter {
Expand All @@ -58,12 +57,12 @@ public boolean canHandle(String domain) {
}

@Override
public Optional<Response> handle(String domain, String path, IHTTPSession session) {
public void handle(String domain, String path, HttpExchange exchange) throws UnsupportedURLException, IOException {
if (!domain.equals("skins.minecraft.net"))
return empty();
throw new UnsupportedURLException();
Matcher matcher = PATH_SKINS.matcher(path);
if (!matcher.find())
return empty();
throw new UnsupportedURLException();
String username = matcher.group("username");

// Minecraft does not encode non-ASCII characters in URLs
Expand All @@ -87,15 +86,17 @@ public Optional<Response> handle(String domain, String path, IHTTPSession sessio
byte[] data;
try {
data = http("GET", url);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid URL [" + url + "]");
} catch (IOException e) {
throw newUncheckedIOException("Failed to retrieve skin from " + url, e);
}
log(INFO, "Retrieved skin for " + username + " from " + url + ", " + data.length + " bytes");
return of(Response.newFixedLength(Status.OK, "image/png", new ByteArrayInputStream(data), data.length));
sendResponse(exchange, 200, CONTENT_TYPE_IMAGE, data);

} else {
log(INFO, "No skin is found for " + username);
return of(Response.newFixedLength(Status.NOT_FOUND, null, null));
sendResponse(exchange, 404, null, null);
}
}

Expand Down
Loading

0 comments on commit 87277a2

Please sign in to comment.