Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create p2ce addons, other misc #6

Merged
merged 6 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.25 FATAL_ERROR)

# Create project
project(sdk_launcher
DESCRIPTION "A minimal SDK launcher for Strata Source engine games"
VERSION "0.4.2"
DESCRIPTION "An SDK launcher for Strata Source engine games"
VERSION "0.5.0"
HOMEPAGE_URL "https://github.com/StrataSource/sdk-launcher")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down Expand Up @@ -142,6 +142,8 @@ add_executable(${PROJECT_TARGET_NAME} WIN32
"${CMAKE_CURRENT_SOURCE_DIR}/src/Main.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/NewModDialog.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/NewModDialog.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/NewP2CEAddonDialog.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/NewP2CEAddonDialog.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h")

Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SDK Launcher

A minimal SDK launcher for Strata Source engine games.
An SDK launcher for Strata Source engine games.

To install, download the latest release for the given game and extract
the executable to `bin/win64` or `bin/linux64` (depending on your OS).
Expand Down Expand Up @@ -32,6 +32,8 @@ Here is an example config file that may be loaded into the SDK launcher.
// Optional, holds the download URL of the mod template for the game (must point to a zip file)
// For reference, this is the P2CE template mod download URL:
"mod_template_url": "https://github.com/StrataSource/p2ce-mod-template/archive/refs/heads/main.zip",
// Optional, the default is false (enables P2CE-style addons)
"supports_p2ce_addons": false,
// Sections hold titled groups of buttons
"sections": [
{
Expand All @@ -53,12 +55,13 @@ Here is an example config file that may be loaded into the SDK launcher.
// Arguments are optional for command-type actions, and will be passed
// to the command when ran. ${GAME} expands to the game directory name.
"arguments": ["-game", "${GAME}", "-dev"],
// The icon override (optional) allows the config creator to change the
// icon for any button. ${GAME_ICON} is a special keyword for steam-type
// configs which loads the game icon from Steam. ${STRATA_ICON} is a
// special keyword that picks the appropriate Strata icon based on the
// theme of the application. Filesystem paths are supported here.
"icon_override": "${GAME_ICON}",
// Optional, the icon override allows the config creator to change the
// default icon for any button. ${STRATA_ICON} is a special keyword that
// picks the appropriate Strata icon based on the theme of the application.
// ${SDKLAUNCHER_ICON} places the icon of the SDK Launcher application,
// and will fall back to the Strata icon if there is no custom icon for
// the current compiled game. Filesystem paths are supported here.
"icon_override": "${STRATA_ICON}",
// Optional, allows buttons to only appear on a given OS. Allowed values
// are "windows", "linux", or both in the same string like "windows,linux"
// (although that's equivalent to not adding this value in the first place).
Expand Down
8 changes: 7 additions & 1 deletion res/config/momentum.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
"action": "${ROOT}/bin/${PLATFORM}/chaos",
"arguments": ["-game", "${GAME}", "-dev", "-console", "-tools"],
"icon_override": "${GAME_ICON}"
},
{
"name": "Momentum Mod - Safe Mode (No Custom Content)",
"type": "command",
"action": "${ROOT}/bin/${PLATFORM}/chaos",
"arguments": ["-game", "${GAME}", "-nousermount", "-nocustommount"],
"icon_override": "${GAME_ICON}"
}
]
},
Expand All @@ -27,7 +34,6 @@
"name": "Hammer Editor",
"type": "command",
"action": "${ROOT}/bin/win64/hammer",
"arguments": ["-game", "${GAME}"],
"os": "windows"
},
{
Expand Down
11 changes: 9 additions & 2 deletions res/config/p2ce.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"game_default": "p2ce",
"window_height": 575,
"window_height": 600,
"mod_template_url": "https://github.com/StrataSource/p2ce-mod-template/archive/refs/heads/main.zip",
"supports_p2ce_addons": true,
"sections": [
{
"name": "Game",
Expand All @@ -19,6 +20,13 @@
"action": "${ROOT}/bin/${PLATFORM}/chaos",
"arguments": ["-game", "${GAME}", "-dev", "-console", "-tools"],
"icon_override": "${GAME_ICON}"
},
{
"name": "Portal 2: CE - Safe Mode",
"type": "command",
"action": "${ROOT}/bin/${PLATFORM}/chaos",
"arguments": ["-game", "${GAME}", "-nousermount", "-nocustommount"],
"icon_override": "${GAME_ICON}"
}
]
},
Expand All @@ -29,7 +37,6 @@
"name": "Hammer Editor",
"type": "command",
"action": "${ROOT}/bin/win64/hammer",
"arguments": ["-game", "${GAME}"],
"os": "windows"
},
{
Expand Down
8 changes: 7 additions & 1 deletion res/config/revolution.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
"action": "${ROOT}/bin/${PLATFORM}/revolution",
"arguments": ["-game", "${GAME}", "-dev", "-console", "-tools"],
"icon_override": "${GAME_ICON}"
},
{
"name": "Portal: Revolution - Safe Mode (No Custom Content)",
"type": "command",
"action": "${ROOT}/bin/${PLATFORM}/chaos",
"arguments": ["-game", "${GAME}", "-nousermount", "-nocustommount"],
"icon_override": "${GAME_ICON}"
}
]
},
Expand All @@ -27,7 +34,6 @@
"name": "Hammer Editor",
"type": "command",
"action": "${ROOT}/bin/win64/hammer",
"arguments": ["-game", "${GAME}"],
"os": "windows"
},
{
Expand Down
Binary file added res/icons/p2ce_sdk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions res/res.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<file>config/p2ce.json</file>
<file>config/revolution.json</file>

<file>icons/p2ce_sdk.png</file>
<file>icons/strata_dark.png</file>
<file>icons/strata_light.png</file>
</qresource>
Expand Down
4 changes: 4 additions & 0 deletions src/GameConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ std::optional<GameConfig> GameConfig::parse(const QString& path) {
gameConfig.modTemplateURL = configObject["mod_template_url"].toString();
}

if (configObject.contains("supports_p2ce_addons") && configObject["supports_p2ce_addons"].isBool()) {
gameConfig.p2ceAddonsSupported = configObject["supports_p2ce_addons"].toBool();
}

if (!configObject.contains("sections") || !configObject["sections"].isArray()) {
return std::nullopt;
}
Expand Down
3 changes: 3 additions & 0 deletions src/GameConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class GameConfig {

[[nodiscard]] const QString& getModTemplateURL() const { return this->modTemplateURL; }

[[nodiscard]] bool supportsP2CEAddons() const { return this->p2ceAddonsSupported; }

[[nodiscard]] const QList<Section>& getSections() const { return this->sections; }

void setVariable(const QString& variable, const QString& replacement);
Expand All @@ -64,6 +66,7 @@ class GameConfig {
bool usesLegacyBinDir = false;
int windowHeight = DEFAULT_WINDOW_HEIGHT;
QString modTemplateURL;
bool p2ceAddonsSupported = false;
QList<Section> sections;

GameConfig() = default;
Expand Down
6 changes: 4 additions & 2 deletions src/NewModDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <miniz.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDesktopServices>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
Expand Down Expand Up @@ -228,7 +229,7 @@ NewModDialog::NewModDialog(QString gameRoot_, QString downloadURL_, QWidget* par

this->parentFolder = new QComboBox{this};
if (knowsSourcemodsDirLocation) {
this->parentFolder->addItem("Steam's SourceMods Folder");
this->parentFolder->addItem(tr("Steam's SourceMods Folder"));
}
this->parentFolder->addItem(tr("Game Folder"));
this->parentFolder->addItem(tr("Custom Location"));
Expand All @@ -240,7 +241,7 @@ NewModDialog::NewModDialog(QString gameRoot_, QString downloadURL_, QWidget* par
layout->addRow(parentFolderCustomLabel, this->parentFolderCustom);

this->modID = new QLineEdit{this};
this->modID->setPlaceholderText("For example: p2ce, revolution, portal2");
this->modID->setPlaceholderText(tr("For example: p2ce, revolution, portal2"));
layout->addRow(tr("Mod ID"), this->modID);

this->addShortcutOnDesktop = new QCheckBox{this};
Expand Down Expand Up @@ -335,6 +336,7 @@ NewModDialog::NewModDialog(QString gameRoot_, QString downloadURL_, QWidget* par
// If installing to sourcemods, tell user they will need to restart steam
if (this->parentFolder->count() == 3 && this->parentFolder->currentIndex() == 0) {
QMessageBox::information(this, tr("Info"), tr("Your mod has been installed to Steam's SourceMods folder, which means it will show up in your Steam library! This requires you to restart Steam once."));
QDesktopServices::openUrl({QString("file:///") + modInstallDir});
}

this->accept();
Expand Down
137 changes: 137 additions & 0 deletions src/NewP2CEAddonDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include "NewP2CEAddonDialog.h"

#include <filesystem>
#include <utility>

#include <QCheckBox>
#include <QComboBox>
#include <QDesktopServices>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QStandardPaths>
#include <QPlainTextEdit>

NewP2CEAddonDialog::NewP2CEAddonDialog(QString gameRoot_, QWidget* parent)
: QDialog(parent)
, gameRoot(std::move(gameRoot_)) {
// Window setup
this->setModal(true);
this->setWindowTitle(tr("New Addon"));
this->setMinimumWidth(350);

// Create UI elements
auto* layout = new QFormLayout{this};

this->addonID = new QLineEdit{this};
this->addonID->setPlaceholderText(tr("For example: p2ce, revolution, portal2"));
layout->addRow(tr("Addon ID"), this->addonID);

this->addonName = new QLineEdit{this};
this->addonName->setPlaceholderText(tr("My Cool Addon"));
layout->addRow(tr("Addon Name"), this->addonName);

this->addonDesc = new QPlainTextEdit{this};
this->addonDesc->setPlaceholderText(tr("A description of the addon."));
layout->addRow(tr("Addon Description"), this->addonDesc);

this->addonType = new QComboBox{this};
this->addonType->addItem("Campaign");
this->addonType->addItem("Map");
this->addonType->addItem("Asset Pack");
this->addonType->addItem("Other");
layout->addRow(tr("Addon Type"), this->addonType);

this->addShortcutOnDesktop = new QCheckBox{this};
this->addShortcutOnDesktop->setCheckState(Qt::Unchecked);
layout->addRow(tr("Create Desktop Shortcut"), this->addShortcutOnDesktop);

auto* buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this};
layout->addWidget(buttonBox);

// Connect ok/cancel buttons to download stuff
QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, [this] {
// Validate mod ID
if (this->addonID->text().trimmed().isEmpty()) {
QMessageBox::critical(this, tr("Incorrect Input"), tr("Addon ID must not be empty."));
return;
}

const auto addonInstallDir = this->getAddonInstallDir();

// Validate install location
if (std::filesystem::exists(addonInstallDir.toLocal8Bit().constData())) {
QMessageBox::critical(this, tr("Incorrect Input"), tr("A folder with the name of the addon ID already exists at this install location."));
return;
}

// Create directory structure
QDir dir{this->getAddonInstallDir()};
dir.mkpath("maps");
dir.mkpath("materials");
dir.mkpath("models");
dir.mkpath("particles");
dir.mkpath("resource");
dir.mkpath("scenes");
dir.mkpath("scripts");
dir.mkpath("sound");

// Create addon.kv3
QFile addonKV3{this->getAddonInstallDir() + QDir::separator() + "addon.kv3"};
addonKV3.open(QIODevice::WriteOnly | QIODevice::Text);

static constexpr auto p2ceAddonKV3ContentsBase = R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you exclude this line? It is optional

{
mod = "%1"
description = "%2"
type = "%3"
id = 0
thumbnail = ""
authors = [ ]
dependencies = [ ]
tags = [ ]
ignore = [ ]
metadata =
{
}
assets =
{
logo = ""
cover = ""
}
})";
{
QTextStream out{&addonKV3};
out << QString{p2ceAddonKV3ContentsBase}.arg(this->addonName->text(), this->addonDesc->toPlainText(), this->addonType->currentText()).toLocal8Bit();
}
addonKV3.close();

// Create desktop shortcut
if (this->addShortcutOnDesktop->isChecked()) {
const auto shortcutPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + QDir::separator() + this->addonID->text().trimmed();
#ifdef _WIN32
QFile::link(addonInstallDir, shortcutPath + ".lnk");
#else
QFile::link(addonInstallDir, shortcutPath);
#endif
}
QDesktopServices::openUrl({QString("file:///") + addonInstallDir});

this->accept();
});
QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &NewP2CEAddonDialog::reject);
}

QString NewP2CEAddonDialog::getAddonInstallDir() const {
return this->gameRoot + QDir::separator() + "addons" + QDir::separator() + this->addonID->text().trimmed();
}

void NewP2CEAddonDialog::open(QString gameRoot, QWidget* parent) {
auto* dialog = new NewP2CEAddonDialog{std::move(gameRoot), parent};
dialog->exec();
dialog->deleteLater();
}
28 changes: 28 additions & 0 deletions src/NewP2CEAddonDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <QDialog>

class QCheckBox;
class QComboBox;
class QLineEdit;
class QPlainTextEdit;

class NewP2CEAddonDialog : public QDialog {
Q_OBJECT;

public:
NewP2CEAddonDialog(QString gameRoot_, QWidget* parent = nullptr);

[[nodiscard]] QString getAddonInstallDir() const;

static void open(QString gameRoot, QWidget* parent = nullptr);

private:
QString gameRoot;

QLineEdit* addonID;
QLineEdit* addonName;
QPlainTextEdit* addonDesc;
QComboBox* addonType;
QCheckBox* addShortcutOnDesktop;
};
Loading
Loading