Skip to content

Commit

Permalink
Merge branch 'PolyMC:stable' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
fn2006 authored May 9, 2022
2 parents cf9e077 + da25f3b commit b762dc0
Show file tree
Hide file tree
Showing 23 changed files with 601 additions and 400 deletions.
36 changes: 17 additions & 19 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:

- os: ubuntu-20.04

- os: ubuntu-20.04
appimage: true

- os: windows-2022
name: "Windows-i686"
msystem: mingw32
Expand Down Expand Up @@ -66,30 +69,25 @@ jobs:
ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Install OpenJDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'

- name: Install Qt (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install qt@5
brew install qt@5 ninja
- name: Update Qt (AppImage)
if: runner.os == 'Linux' && matrix.appimage == true
run: |
sudo add-apt-repository ppa:savoury1/qt-5-15
- name: Install Qt (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get -y update
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
- name: Install Ninja
if: runner.os != 'Windows'
uses: urkle/action-get-ninja@v1
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build
- name: Prepare AppImage (Linux)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage == true
run: |
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
Expand Down Expand Up @@ -167,15 +165,15 @@ jobs:
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
- name: Package (Linux)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage != true
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
cd ${{ env.INSTALL_DIR }}
tar --owner root --group root -czf ../PolyMC.tar.gz *
- name: Package (Linux, portable)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage != true
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
Expand All @@ -184,7 +182,7 @@ jobs:
tar -czf ../PolyMC-portable.tar.gz *
- name: Package AppImage (Linux)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage == true
shell: bash
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
Expand Down Expand Up @@ -234,21 +232,21 @@ jobs:
path: ${{ env.INSTALL_PORTABLE_DIR }}/**

- name: Upload binary tarball (Linux)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage != true
uses: actions/upload-artifact@v3
with:
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PolyMC.tar.gz

- name: Upload binary tarball (Linux, portable)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage != true
uses: actions/upload-artifact@v3
with:
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PolyMC-portable.tar.gz

- name: Upload AppImage (Linux)
if: runner.os == 'Linux'
if: runner.os == 'Linux' && matrix.appimage == true
uses: actions/upload-artifact@v3
with:
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ CMakeLists.txt.user.*
/.project
/.settings
/.idea
/.vscode
.clang-format
cmake-build-*/
Debug

Expand Down
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 1)
set(Launcher_VERSION_MINOR 2)
set(Launcher_VERSION_HOTFIX 1)
set(Launcher_VERSION_HOTFIX 2)

# Build number
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
Expand All @@ -89,6 +89,10 @@ set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can g
# MSA Client ID
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")

# CurseForge API Key
# CHANGE THIS IF YOU FORK THIS PROJECT!
set(Launcher_CURSEFORGE_API_KEY "$2a$10$iR1RdPDG95FWdILZbHuoMOlV4vL4eckBx7QPZR6SVZmliEb9ZQplu" CACHE STRING "CurseForge API Key")

# Bug tracker URL
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")

Expand Down
1 change: 1 addition & 0 deletions buildconfig/BuildConfig.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Config::Config()
HELP_URL = "@Launcher_HELP_URL@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
CURSEFORGE_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
META_URL = "@Launcher_META_URL@";

BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
Expand Down
12 changes: 7 additions & 5 deletions buildconfig/BuildConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@
/**
* \brief The Config class holds all the build-time information passed from the build system.
*/
class Config
{
public:
class Config {
public:
Config();
QString LAUNCHER_NAME;
QString LAUNCHER_DISPLAYNAME;
Expand Down Expand Up @@ -74,7 +73,6 @@ class Config
/// URL for the updater's channel
QString UPDATER_BASE;


/// User-Agent to use.
QString USER_AGENT;

Expand Down Expand Up @@ -116,6 +114,11 @@ class Config
*/
QString MSA_CLIENT_ID;

/**
* Client API key for CurseForge
*/
QString CURSEFORGE_API_KEY;

/**
* Metadata repository URL prefix
*/
Expand Down Expand Up @@ -154,4 +157,3 @@ class Config
};

extern const Config BuildConfig;

2 changes: 2 additions & 0 deletions launcher/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ set(MINECRAFT_SOURCES
minecraft/auth/steps/MigrationEligibilityStep.h
minecraft/auth/steps/MinecraftProfileStep.cpp
minecraft/auth/steps/MinecraftProfileStep.h
minecraft/auth/steps/MinecraftProfileStepMojang.cpp
minecraft/auth/steps/MinecraftProfileStepMojang.h
minecraft/auth/steps/MSAStep.cpp
minecraft/auth/steps/MSAStep.h
minecraft/auth/steps/XboxAuthorizationStep.cpp
Expand Down
175 changes: 175 additions & 0 deletions launcher/minecraft/auth/Parsers.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Parsers.h"
#include "Json.h"

#include <QJsonDocument>
#include <QJsonArray>
Expand Down Expand Up @@ -212,6 +213,180 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
return true;
}

namespace {
// these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee)
// they are needed because the session server doesn't return skin urls for default skins
static const QString SKIN_URL_STEVE = "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b";
static const QString SKIN_URL_ALEX = "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032";

bool isDefaultModelSteve(QString uuid) {
// need to calculate *Java* hashCode of UUID
// if number is even, skin/model is steve, otherwise it is alex

// just in case dashes are in the id
uuid.remove('-');

if (uuid.size() != 32) {
return true;
}

// qulonglong is guaranteed to be 64 bits
// we need to use unsigned numbers to guarantee truncation below
qulonglong most = uuid.left(16).toULongLong(nullptr, 16);
qulonglong least = uuid.right(16).toULongLong(nullptr, 16);
qulonglong xored = most ^ least;
return ((static_cast<quint32>(xored >> 32)) ^ static_cast<quint32>(xored)) % 2 == 0;
}
}

/**
Uses session server for skin/cape lookup instead of profile,
because locked Mojang accounts cannot access profile endpoint
(https://api.minecraftservices.com/minecraft/profile/)
ref: https://wiki.vg/Mojang_API#UUID_to_Profile_and_Skin.2FCape
{
"id": "<profile identifier>",
"name": "<player name>",
"properties": [
{
"name": "textures",
"value": "<base64 string>"
}
]
}
decoded base64 "value":
{
"timestamp": <java time in ms>,
"profileId": "<profile uuid>",
"profileName": "<player name>",
"textures": {
"SKIN": {
"url": "<player skin URL>"
},
"CAPE": {
"url": "<player cape URL>"
}
}
}
*/

bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) {
qDebug() << "Parsing Minecraft profile...";
#ifndef NDEBUG
qDebug() << data;
#endif

QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if(jsonError.error) {
qWarning() << "Failed to parse response as JSON: " << jsonError.errorString();
return false;
}

auto obj = Json::requireObject(doc, "mojang minecraft profile");
if(!getString(obj.value("id"), output.id)) {
qWarning() << "Minecraft profile id is not a string";
return false;
}

if(!getString(obj.value("name"), output.name)) {
qWarning() << "Minecraft profile name is not a string";
return false;
}

auto propsArray = obj.value("properties").toArray();
QByteArray texturePayload;
for( auto p : propsArray) {
auto pObj = p.toObject();
auto name = pObj.value("name");
if (!name.isString() || name.toString() != "textures") {
continue;
}

auto value = pObj.value("value");
if (value.isString()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
texturePayload = QByteArray::fromBase64(value.toString().toUtf8(), QByteArray::AbortOnBase64DecodingErrors);
#else
texturePayload = QByteArray::fromBase64(value.toString().toUtf8());
#endif
}

if (!texturePayload.isEmpty()) {
break;
}
}

if (texturePayload.isNull()) {
qWarning() << "No texture payload data";
return false;
}

doc = QJsonDocument::fromJson(texturePayload, &jsonError);
if(jsonError.error) {
qWarning() << "Failed to parse response as JSON: " << jsonError.errorString();
return false;
}

obj = Json::requireObject(doc, "session texture payload");
auto textures = obj.value("textures");
if (!textures.isObject()) {
qWarning() << "No textures array in response";
return false;
}

Skin skinOut;
// fill in default skin info ourselves, as this endpoint doesn't provide it
bool steve = isDefaultModelSteve(output.id);
skinOut.variant = steve ? "classic" : "slim";
skinOut.url = steve ? SKIN_URL_STEVE : SKIN_URL_ALEX;
// sadly we can't figure this out, but I don't think it really matters...
skinOut.id = "00000000-0000-0000-0000-000000000000";
Cape capeOut;
auto tObj = textures.toObject();
for (auto idx = tObj.constBegin(); idx != tObj.constEnd(); ++idx) {
if (idx->isObject()) {
if (idx.key() == "SKIN") {
auto skin = idx->toObject();
if (!getString(skin.value("url"), skinOut.url)) {
qWarning() << "Skin url is not a string";
return false;
}

auto maybeMeta = skin.find("metadata");
if (maybeMeta != skin.end() && maybeMeta->isObject()) {
auto meta = maybeMeta->toObject();
// might not be present
getString(meta.value("model"), skinOut.variant);
}
}
else if (idx.key() == "CAPE") {
auto cape = idx->toObject();
if (!getString(cape.value("url"), capeOut.url)) {
qWarning() << "Cape url is not a string";
return false;
}

// we don't know the cape ID as it is not returned from the session server
// so just fake it - changing capes is probably locked anyway :(
capeOut.alias = "cape";
}
}
}

output.skin = skinOut;
if (capeOut.alias == "cape") {
output.capes = QMap<QString, Cape>({{capeOut.alias, capeOut}});
output.currentCape = capeOut.alias;
}

output.validity = Katabasis::Validity::Certain;
return true;
}

bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) {
qDebug() << "Parsing Minecraft entitlements...";
#ifndef NDEBUG
Expand Down
1 change: 1 addition & 0 deletions launcher/minecraft/auth/Parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Parsers
bool parseMojangResponse(QByteArray &data, Katabasis::Token &output);

bool parseMinecraftProfile(QByteArray &data, MinecraftProfile &output);
bool parseMinecraftProfileMojang(QByteArray &data, MinecraftProfile &output);
bool parseMinecraftEntitlements(QByteArray &data, MinecraftEntitlement &output);
bool parseRolloutResponse(QByteArray &data, bool& result);
}
Loading

0 comments on commit b762dc0

Please sign in to comment.