diff options
| author | 2025-08-25 22:28:24 -0500 | |
|---|---|---|
| committer | 2025-08-25 22:28:24 -0500 | |
| commit | 954e102e836e19ea425253f1990fff8d76dd98b4 (patch) | |
| tree | 2dc68f8a09efc5b11c88ee4154b6487db0baadbb | |
initial commit
26 files changed, 1391 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e6cdcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +.gradle +build/ +work/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..b0da77f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("java") +} + +group = "dev.figboot.launcher" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +}
\ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 0000000..b65c6b9 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `java-library` + id("io.freefair.lombok") version "8.14.2" +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + +group = "dev.figboot.launcher" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.slf4j:slf4j-api:1.7.25") + + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} diff --git a/core/src/main/java/dev/figboot/launcher/core/Launcher.java b/core/src/main/java/dev/figboot/launcher/core/Launcher.java new file mode 100644 index 0000000..a95fed4 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/Launcher.java @@ -0,0 +1,24 @@ +package dev.figboot.launcher.core; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +public class Launcher { + private static final Logger LOGGER = LoggerFactory.getLogger(Launcher.class); + + protected final LauncherInterfaceAccess iface; + protected final ScheduledExecutorService service; + + public Launcher(LauncherInterfaceAccess iface) { + this.iface = iface; + service = new ScheduledThreadPoolExecutor(4); + } + + public void loadVersions() throws IOException { + + } +} diff --git a/core/src/main/java/dev/figboot/launcher/core/LauncherInterfaceAccess.java b/core/src/main/java/dev/figboot/launcher/core/LauncherInterfaceAccess.java new file mode 100644 index 0000000..b1292ae --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/LauncherInterfaceAccess.java @@ -0,0 +1,7 @@ +package dev.figboot.launcher.core; + +import dev.figboot.launcher.core.task.Task; + +public interface LauncherInterfaceAccess { + Task createTask(String description); +} diff --git a/core/src/main/java/dev/figboot/launcher/core/config/LauncherConfiguration.java b/core/src/main/java/dev/figboot/launcher/core/config/LauncherConfiguration.java new file mode 100644 index 0000000..99bcca4 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/config/LauncherConfiguration.java @@ -0,0 +1,8 @@ +package dev.figboot.launcher.core.config; + +import java.nio.file.Path; + +public interface LauncherConfiguration { + Path getConfigPath(); + Path getInstancesPath(); +} diff --git a/core/src/main/java/dev/figboot/launcher/core/config/SimpleLauncherConfiguration.java b/core/src/main/java/dev/figboot/launcher/core/config/SimpleLauncherConfiguration.java new file mode 100644 index 0000000..15ab1be --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/config/SimpleLauncherConfiguration.java @@ -0,0 +1,24 @@ +package dev.figboot.launcher.core.config; + +import dev.figboot.launcher.core.system.SystemInfo; + +import java.nio.file.Path; + +public class SimpleLauncherConfiguration implements LauncherConfiguration { + //private final Path configPath, instancePath; + + public SimpleLauncherConfiguration() { + + //configPath = FileSystems.getDefault(). + } + + @Override + public Path getConfigPath() { + return null; + } + + @Override + public Path getInstancesPath() { + return null; + } +} diff --git a/core/src/main/java/dev/figboot/launcher/core/system/CurrentSystemInfo.java b/core/src/main/java/dev/figboot/launcher/core/system/CurrentSystemInfo.java new file mode 100644 index 0000000..0c67506 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/system/CurrentSystemInfo.java @@ -0,0 +1,147 @@ +package dev.figboot.launcher.core.system; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.EnumMap; +import java.util.Map; +import java.util.regex.Pattern; + +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +class CurrentSystemInfo implements SystemInfo { + private static final Logger LOGGER = LoggerFactory.getLogger(CurrentSystemInfo.class); + + private static CurrentSystemInfo info = null; + + static synchronized CurrentSystemInfo get() { + if (info != null) { + return info; + } + + return (info = gather()); + } + + private static String logSystemProperty(String key, String value, boolean custom) { + LOGGER.info("System property: \"{}\" = {}{}", key, value == null ? "(unset)" : value, custom ? " (*)" : ""); + return value; + } + + private static String getSystemProperty(String key) { + String customVal = System.getProperty("dev.figboot.launcher.core.SystemInfo." + key); + if (customVal != null) + return logSystemProperty(key, customVal, true); + return logSystemProperty(key, System.getProperty(key), false); + } + + private static boolean isBitMismatchCondition(OperatingSystem os) { + if (System.getProperty("dev.figboot.launcher.core.SystemInfo.acknowledgeBitMismatch") != null) return false; + if (os != OperatingSystem.WINDOWS) return false; // seems like only windows users typically have this issue + + String realArch = System.getenv("PROCESSOR_ARCHITEW6432"); + return realArch != null && realArch.equalsIgnoreCase("amd64"); + } + + private static String getRuntimePlatform(OperatingSystem os, String arch) { + for (RuntimePlatformRule rule : JRE_PLAT_RULES) { + if (rule.matches(os, arch)) { + LOGGER.info("Java runtime platform detected as '{}'.", rule.platform); + return rule.platform; + } + } + + LOGGER.info("Java runtime platform not detected. Please file a bug report if you believe your platform should be detected."); + return "gamecore"; + } + + private static CurrentSystemInfo gather() { + String osName = getSystemProperty("os.name"); + String osArch = getSystemProperty("os.arch"); + String osVersion = getSystemProperty("os.version"); + + String fileSep = getSystemProperty("file.separator"); + String pathSep = getSystemProperty("path.separator"); + + OperatingSystem detectedOS = OperatingSystem.UNKNOWN; + int archBits; + + for (Map.Entry<OperatingSystem, Pattern> ent : OS_PATTERNS.entrySet()) { + if (ent.getValue().matcher(osName).find()) { // NOTE: not necessarily a complete match + LOGGER.info("Detected operating system: {}", ent.getKey()); + detectedOS = ent.getKey(); + } + } + + if (detectedOS == OperatingSystem.UNKNOWN) { + LOGGER.warn("Unknown operating system \"{}\"! The launcher may not function correctly.", osName); + LOGGER.warn("Try specifying \"windows\" or \"macos\" or \"linux\" in the \"dev.figboot.launcher.core.SystemInfo.os.name\" system property, " + + "and consider filing a bug report with your OS details if you believe your OS should be detected automatically."); + } + + if (osArch.contains("64")) { // seems to work well enough. + archBits = 64; + } else { + archBits = 32; + + if (isBitMismatchCondition(detectedOS)) { + /* NOTE: it's not safe to just pretend we're on 64-bit windows, since WOW64 mode changes various things + * about the application's environment. I don't intend to do more than just send a warning in the log. */ + LOGGER.warn("It appears that you're using a 32-bit version of java on 64-bit Windows. You may have issues " + + "launching the game when using Mojang-provided java runtimes. To suppress this warning, define the " + + "\"dev.figboot.launcher.core.SystemInfo.acknowledgeBitMismatch\" system property."); + } + } + + return new CurrentSystemInfo(detectedOS, getRuntimePlatform(detectedOS, osArch), archBits, osArch, osVersion, fileSep, pathSep); + } + + @Getter private final OperatingSystem operatingSystem; + @Getter private final String runtimePlatform; + @Getter private final int archBits; + + @Getter private final String archName; + @Getter private final String osVersion; + + @Getter private final String fileSeparator; + @Getter private final String pathSeparator; + + private static final Pattern ARCH_X8664 = Pattern.compile("^((x(86_)?|amd|ia)64)$", Pattern.CASE_INSENSITIVE); + private static final Pattern ARCH_X86 = Pattern.compile("^(x86(_?32)?|i[3-6]86|ia32|x32)$", Pattern.CASE_INSENSITIVE); + private static final Pattern ARCH_ARM64 = Pattern.compile("^aarch64$", Pattern.CASE_INSENSITIVE); + private static final Pattern ARCH_PPC = Pattern.compile("^ppc$", Pattern.CASE_INSENSITIVE); + + private static final RuntimePlatformRule[] JRE_PLAT_RULES = { + new RuntimePlatformRule(OperatingSystem.WINDOWS, ARCH_X8664, "windows-x64"), + new RuntimePlatformRule(OperatingSystem.WINDOWS, ARCH_X86, "windows-x86"), + new RuntimePlatformRule(OperatingSystem.WINDOWS, ARCH_ARM64, "windows-arm64"), + new RuntimePlatformRule(OperatingSystem.MACOS, ARCH_PPC, "mac-os"), + new RuntimePlatformRule(OperatingSystem.MACOS, ARCH_ARM64, "mac-os-arm64"), + new RuntimePlatformRule(OperatingSystem.LINUX, ARCH_X8664, "linux"), + new RuntimePlatformRule(OperatingSystem.LINUX, ARCH_X86, "linux-i386") + }; + + private static final Map<OperatingSystem, Pattern> OS_PATTERNS; + + static { + OS_PATTERNS = new EnumMap<>(OperatingSystem.class); + + OS_PATTERNS.put(OperatingSystem.WINDOWS, Pattern.compile("^windows", Pattern.CASE_INSENSITIVE)); + OS_PATTERNS.put(OperatingSystem.MACOS, Pattern.compile("^(darwin$|mac)", Pattern.CASE_INSENSITIVE)); + + // are BSDs and other UNIXs "close enough" to being Linux? + OS_PATTERNS.put(OperatingSystem.LINUX, Pattern.compile("^linux", Pattern.CASE_INSENSITIVE)); + } + + @RequiredArgsConstructor + private static class RuntimePlatformRule { + private final OperatingSystem os; + private final Pattern archPattern; + private final String platform; + + boolean matches(OperatingSystem os, String arch) { + return this.os == os && archPattern.matcher(arch).find(); + } + } +} diff --git a/core/src/main/java/dev/figboot/launcher/core/system/SystemInfo.java b/core/src/main/java/dev/figboot/launcher/core/system/SystemInfo.java new file mode 100644 index 0000000..2ed8237 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/system/SystemInfo.java @@ -0,0 +1,38 @@ +package dev.figboot.launcher.core.system; + +import lombok.RequiredArgsConstructor; + +public interface SystemInfo { + static SystemInfo current() { + return CurrentSystemInfo.get(); + } + + OperatingSystem getOperatingSystem(); + String getRuntimePlatform(); + int getArchBits(); + + String getArchName(); + String getOsVersion(); + + String getFileSeparator(); + String getPathSeparator(); + + @RequiredArgsConstructor + enum OperatingSystem { + WINDOWS, + MACOS, + LINUX, + UNKNOWN; + + public OperatingSystem getByVersionName(String name) { + switch (name) { + case "windows": return WINDOWS; + case "osx": return MACOS; + case "linux": return LINUX; + } + + // don't return UNKNOWN here, it could mask a launcher bug + throw new IllegalArgumentException("unknown OS family name: '" + name + "'"); + } + } +} diff --git a/core/src/main/java/dev/figboot/launcher/core/task/Task.java b/core/src/main/java/dev/figboot/launcher/core/task/Task.java new file mode 100644 index 0000000..9fb603a --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/task/Task.java @@ -0,0 +1,5 @@ +package dev.figboot.launcher.core.task; + +public interface Task { + void complete(); +} diff --git a/core/src/main/java/dev/figboot/launcher/core/version/CompleteVersion.java b/core/src/main/java/dev/figboot/launcher/core/version/CompleteVersion.java new file mode 100644 index 0000000..b69b857 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/version/CompleteVersion.java @@ -0,0 +1,4 @@ +package dev.figboot.launcher.core.version; + +public interface CompleteVersion extends VersionInfo { +} diff --git a/core/src/main/java/dev/figboot/launcher/core/version/VersionInfo.java b/core/src/main/java/dev/figboot/launcher/core/version/VersionInfo.java new file mode 100644 index 0000000..4ffb588 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/version/VersionInfo.java @@ -0,0 +1,11 @@ +package dev.figboot.launcher.core.version; + +import java.util.Date; + +public interface VersionInfo { + String getId(); + Object getType(); + Date getTime(); + Date getUpdateTime(); + int getComplianceLevel(); +} diff --git a/core/src/main/java/dev/figboot/launcher/core/version/VersionManifest.java b/core/src/main/java/dev/figboot/launcher/core/version/VersionManifest.java new file mode 100644 index 0000000..2bc4ac6 --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/version/VersionManifest.java @@ -0,0 +1,5 @@ +package dev.figboot.launcher.core.version; + +public interface VersionManifest extends VersionStorage<VersionInfo> { + +} diff --git a/core/src/main/java/dev/figboot/launcher/core/version/VersionStorage.java b/core/src/main/java/dev/figboot/launcher/core/version/VersionStorage.java new file mode 100644 index 0000000..7381d5a --- /dev/null +++ b/core/src/main/java/dev/figboot/launcher/core/version/VersionStorage.java @@ -0,0 +1,10 @@ +package dev.figboot.launcher.core.version; + +public interface VersionStorage<V extends VersionInfo> { + /** + * Gets an (already loaded) version from this storage's cache. + * @param id the version id (string) + * @return the version info or null + */ + V getVersion(String id); +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..249e583 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..51340db --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Aug 17 14:44:52 CDT 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/gui/build.gradle.kts b/gui/build.gradle.kts new file mode 100644 index 0000000..16b7bf3 --- /dev/null +++ b/gui/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + application +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + +group = "dev.figboot.launcher" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} + +application { + mainClass = "dev.figboot.launcher.gui.GUIMain" + applicationDefaultJvmArgs = listOf("-Dswing.aatext=true", "-Dawt.useSystemAAFontSettings=on") +} diff --git a/gui/src/main/java/dev/figboot/launcher/gui/GUIMain.java b/gui/src/main/java/dev/figboot/launcher/gui/GUIMain.java new file mode 100644 index 0000000..dc5b2ed --- /dev/null +++ b/gui/src/main/java/dev/figboot/launcher/gui/GUIMain.java @@ -0,0 +1,106 @@ +package dev.figboot.launcher.gui; + +import javax.swing.*; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; +import java.awt.*; + +public class GUIMain { + public static void main(String[] args) { + JFrame mainFrame = new JFrame("amogus"); + mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + mainFrame.setMinimumSize(new Dimension(480, 360)); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + mainFrame.setContentPane(mainPanel); + + JPanel statusPanel = new JPanel(); + statusPanel.setBorder(new CompoundBorder(new MatteBorder(1, 0, 0, 0, Color.GRAY), new EmptyBorder(0, 4, 0, 4))); + //statusPanel.setBorder(new LineBorder(Color.BLACK, 1)); + + JProgressBar statusProgress = new JProgressBar(0, 100); + statusProgress.setValue(33); + statusPanel.add(statusProgress); + + JLabel statusLabel = new JLabel("Loading online versions (+2 tasks)"); + statusLabel.setOpaque(false); + statusPanel.add(statusLabel); + + mainPanel.add(statusPanel, BorderLayout.PAGE_END); + + JPanel launchPanel = new JPanel(); + launchPanel.setLayout(new BorderLayout()); + //launchPanel.setBorder(new LineBorder(Color.GREEN, 1)); + mainPanel.add(launchPanel, BorderLayout.CENTER); + + JPanel navPanel = new JPanel(); + navPanel.setLayout(new BorderLayout()); + //navPanel.setBorder(new LineBorder(Color.CYAN, 1)); + launchPanel.add(navPanel, BorderLayout.LINE_END); + + JPanel navItemPanel = new JPanel(); + navItemPanel.setLayout(new GridLayout(0, 1)); + navPanel.add(navItemPanel, BorderLayout.PAGE_START); + + Font font = new Font(null, Font.BOLD, 16); + ButtonGroup navItems = new ButtonGroup(); + for (String name : new String[]{"News", "Instances", "Accounts", "Explore", "Add-ons", "Settings"}) { + JToggleButton btnNavItem = new JToggleButton(name); + navItems.add(btnNavItem); + btnNavItem.setSelected(name.equals("Instances")); + btnNavItem.setFont(font); + btnNavItem.setPreferredSize(new Dimension(175, 40)); + navItemPanel.add(btnNavItem); + } + + JPanel playPanel = new JPanel(); + playPanel.setLayout(new GridLayout(1, 0)); + //playPanel.setBorder(new LineBorder(Color.RED, 1)); + playPanel.setBorder(new EmptyBorder(2, 0, 2, 0)); + launchPanel.add(playPanel, BorderLayout.PAGE_END); + + JPanel accountSelectPanelOuter = new JPanel(); + accountSelectPanelOuter.setLayout(new BoxLayout(accountSelectPanelOuter, BoxLayout.PAGE_AXIS)); + accountSelectPanelOuter.setBorder(new EmptyBorder(4, 4, 4, 4)); + JPanel accountSelectPanel = new JPanel(); + accountSelectPanel.setLayout(new BorderLayout()); + accountSelectPanel.setPreferredSize(new Dimension(200, 30)); + accountSelectPanel.setMaximumSize(new Dimension(200, 30)); + + JComboBox<String> accountSelector = new JComboBox<>(new String[]{"figboot", "bootfig", "figroot", "FacePalmOS", "EXR0"}); + accountSelectPanel.add(accountSelector, BorderLayout.CENTER); + + accountSelectPanel.add(new JButton("+"), BorderLayout.LINE_END); + + accountSelectPanelOuter.add(Box.createGlue()); + accountSelectPanelOuter.add(accountSelectPanel); + accountSelectPanelOuter.add(Box.createGlue()); + playPanel.add(accountSelectPanelOuter); + + JButton playButton = new JButton("<html><center><p>Play<br><span style=\"font-weight: normal; font-size: 0.8em\">Latest release</span></p></center></html>"); + playButton.setFont(font); + playButton.setPreferredSize(new Dimension(0, 60)); + playPanel.add(playButton); + + playPanel.add(new JPanel()); + + JPanel actionPanel = new JPanel(); + CardLayout cardLayout = new CardLayout(); + actionPanel.setLayout(cardLayout); + cardLayout.show(actionPanel, "Instances"); + + InstanceListPanel instances = new InstanceListPanel(); + instances.setOpaque(false); + + JScrollPane instanceListScroll = new JScrollPane(instances, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + //instanceListScroll.setBorder(new LineBorder(Color.ORANGE, 1)); + instanceListScroll.getViewport().setBackground(Color.LIGHT_GRAY); + launchPanel.add(instanceListScroll, BorderLayout.CENTER); + + mainFrame.setSize(1067, 600); + mainFrame.invalidate(); + mainFrame.setVisible(true); + } +} diff --git a/gui/src/main/java/dev/figboot/launcher/gui/InstanceListPanel.java b/gui/src/main/java/dev/figboot/launcher/gui/InstanceListPanel.java new file mode 100644 index 0000000..8ed8c5c --- /dev/null +++ b/gui/src/main/java/dev/figboot/launcher/gui/InstanceListPanel.java @@ -0,0 +1,212 @@ +package dev.figboot.launcher.gui; + +import javax.swing.*; +import javax.swing.border.CompoundBorder; +import javax.swing.border.LineBorder; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +public class InstanceListPanel extends JPanel implements Scrollable { + private Font titleFont, versionFont; + + public InstanceListPanel() { + setLayout(new WrapLayout(FlowLayout.LEADING, 5, 5)); + + updateFont(); + + add(new Instance("Latest release", "Release 1.21.8", true)); + add(new Instance("Latest snapshot", "Snapshot 25w19a", false)); + add(new Instance("SevTech Ages", "Release forge_1.7.10-1059.10.aaaaaaa", false)); + add(new Instance("Hypixel Modpack", "Release forge_1.8.9-1750.19.4", false)); + add(new Instance("gangsta shi", "Release fabric-1.16.5+0.19.0", false)); + } + + private void updateFont() { + titleFont = new Font(null, Font.BOLD, getFont().getSize() + 2); + versionFont = new Font(null, Font.PLAIN, getFont().getSize()); + } + + private void handleSelect(Instance selected) { + for (int i = 0, max = getComponentCount(); i < max; ++i) { + Component comp = getComponent(i); + if (comp instanceof Instance) { + ((Instance)comp).setSelected(comp == selected); + } + } + } + + private static final Color SELECTED_COLOR = new Color(0x00B0FF); + private static final Color ARMED_COLOR = SELECTED_COLOR.darker(); + private static final Color ROLLOVER_COLOR = new Color(0x909090); + + private class Instance extends JPanel implements MouseListener { + private boolean selected; + private boolean rollover = false; + private boolean armed = false; + + Instance(String text, String version, boolean selected) { + setBorder(new CompoundBorder(new LineBorder(Color.BLACK, 1), new InstBorder())); + + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + JLabel iconLabel = new JLabel(new Icon() { + @Override + public void paintIcon(Component c, Graphics _g, int x, int y) { + Graphics2D g = (Graphics2D)_g; + + Object oldAAHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + Color oldColor = g.getColor(); + Stroke oldStroke = g.getStroke(); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(Color.RED); + g.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + g.drawLine(128 / 4, 128 / 4, 3 * 128 / 4, 3 * 128 / 4); + g.drawLine(128 / 4, 3 * 128 / 4, 3 * 128 / 4, 128 / 4); + + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1)); + g.drawRect(0, 0, 127, 127); + + g.setStroke(oldStroke); + g.setColor(oldColor); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAAHint); + } + + @Override + public int getIconWidth() { + return 128; + } + + @Override + public int getIconHeight() { + return 128; + } + }); + iconLabel.setAlignmentX(CENTER_ALIGNMENT); + + JLabel titleLabel = new JLabel(text); + titleLabel.setFont(titleFont); + titleLabel.setAlignmentX(CENTER_ALIGNMENT); + + JLabel versionLabel = new JLabel(version); + versionLabel.setFont(versionFont); + versionLabel.setAlignmentX(CENTER_ALIGNMENT); + + add(titleLabel); + add(iconLabel); + add(Box.createGlue()); + add(versionLabel); + add(Box.createGlue()); + + JPanel actionsPanel = new JPanel(); + actionsPanel.setAlignmentX(CENTER_ALIGNMENT); + actionsPanel.setLayout(new BoxLayout(actionsPanel, BoxLayout.LINE_AXIS)); + actionsPanel.add(new JButton("Play")); + actionsPanel.add(new JButton("Edit")); + + add(actionsPanel); + add(Box.createVerticalStrut(2)); + + this.selected = selected; + + Dimension prefSz = getPreferredSize(); + setPreferredSize(new Dimension(215, prefSz.height)); + + addMouseListener(this); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + InstanceListPanel.this.handleSelect(this); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + armed = true; + repaint(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + armed = false; + repaint(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + rollover = true; + repaint(); + } + + @Override + public void mouseExited(MouseEvent e) { + rollover = false; + repaint(); + } + + public void setSelected(boolean sel) { + if (this.selected == sel) return; + + this.selected = sel; + repaint(); + } + + class InstBorder extends LineBorder { + InstBorder() { + super(null, 3, false); + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (armed) { + lineColor = ARMED_COLOR; + super.paintBorder(c, g, x, y, width, height); + } else if (selected) { + lineColor = SELECTED_COLOR; + super.paintBorder(c, g, x, y, width, height); + } else if (rollover) { + lineColor = ROLLOVER_COLOR; + super.paintBorder(c, g, x, y, width, height); + } + } + + @Override + public boolean isBorderOpaque() { + return selected || rollover || armed; + } + } + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 20; // TODO + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 40; // TODO + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return false; + } +} diff --git a/gui/src/main/java/dev/figboot/launcher/gui/WrapLayout.java b/gui/src/main/java/dev/figboot/launcher/gui/WrapLayout.java new file mode 100644 index 0000000..f0b3095 --- /dev/null +++ b/gui/src/main/java/dev/figboot/launcher/gui/WrapLayout.java @@ -0,0 +1,204 @@ +/* +MIT License + +Copyright (c) 2023 Rob Camick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +package dev.figboot.launcher.gui; + +import java.awt.*; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** + * FlowLayout subclass that fully supports wrapping of components. + */ +public class WrapLayout extends FlowLayout { + private Dimension preferredLayoutSize; + + /** + * Constructs a new <code>WrapLayout</code> with a left + * alignment and a default 5-unit horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new <code>FlowLayout</code> with the specified + * alignment and a default 5-unit horizontal and vertical gap. + * The value of the alignment argument must be one of + * <code>WrapLayout</code>, <code>WrapLayout</code>, + * or <code>WrapLayout</code>. + * + * @param align the alignment value + */ + public WrapLayout(int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment + * and the indicated horizontal and vertical gaps. + * <p> + * The value of the alignment argument must be one of + * <code>WrapLayout</code>, <code>WrapLayout</code>, + * or <code>WrapLayout</code>. + * + * @param align the alignment value + * @param hgap the horizontal gap between components + * @param vgap the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the + * <i>visible</i> components in the specified target container. + * + * @param target the component which needs to be laid out + * @return the preferred dimensions to lay out the + * subcomponents of the specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the <i>visible</i> + * components contained in the specified target container. + * + * @param target the component which needs to be laid out + * @return the minimum dimensions to lay out the + * subcomponents of the specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target + * container. + * + * @param target target to get layout size for + * @param preferred should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + Container container = target; + + while (container.getSize().width == 0 && container.getParent() != null) { + container = container.getParent(); + } + + targetWidth = container.getSize().width; + + if (targetWidth == 0) + targetWidth = Integer.MAX_VALUE; + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); + + // Can't add the component to current row. Start a new row. + + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the first + + if (rowWidth != 0) { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do this. + + Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); + + if (scrollPane != null && target.isValid()) { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row + * to update the preferred size for the container. + * + * @param dim update the width and height when appropriate + * @param rowWidth the width of the row to add + * @param rowHeight the height of the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } +} diff --git a/gui/src/main/resources/icons/play.svg b/gui/src/main/resources/icons/play.svg new file mode 100644 index 0000000..799ea9c --- /dev/null +++ b/gui/src/main/resources/icons/play.svg @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="48" + height="48" + viewBox="0 0 48 48" + version="1.1" + id="svg1" + inkscape:export-filename="play_24.png" + inkscape:export-xdpi="48" + inkscape:export-ydpi="48" + inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" + sodipodi:docname="drawing.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview1" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + inkscape:zoom="7.2773073" + inkscape:cx="-6.3210193" + inkscape:cy="40.743092" + inkscape:window-width="1916" + inkscape:window-height="1026" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="layer1" /> + <defs + id="defs1"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 24 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="48 : 24 : 1" + inkscape:persp3d-origin="24 : 16 : 1" + id="perspective1" /> + <filter + inkscape:label="Button" + inkscape:menu="Bevels" + inkscape:menu-tooltip="Soft bevel, slightly depressed middle" + style="color-interpolation-filters:sRGB;" + id="filter21" + x="-0.4555" + y="-0.4555" + width="1.911" + height="1.911"> + <feGaussianBlur + stdDeviation="2.3" + in="SourceAlpha" + result="result0" + id="feGaussianBlur18" /> + <feMorphology + in="SourceAlpha" + radius="6.5" + result="result1" + id="feMorphology18" /> + <feGaussianBlur + stdDeviation="7" + in="result1" + id="feGaussianBlur19" /> + <feColorMatrix + values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.3 0" + result="result91" + id="feColorMatrix19" /> + <feComposite + in="result0" + operator="out" + result="result2" + in2="result91" + id="feComposite19" /> + <feDiffuseLighting + surfaceScale="10" + id="feDiffuseLighting19"> + <feDistantLight + azimuth="225" + elevation="45" + id="feDistantLight19" /> + </feDiffuseLighting> + <feBlend + in2="SourceGraphic" + mode="multiply" + id="feBlend19" /> + <feComposite + in2="SourceAlpha" + operator="in" + result="result3" + id="feComposite20" /> + <feGaussianBlur + stdDeviation="1" + in="result3" + result="result4" + id="feGaussianBlur20" /> + <feComposite + in2="result3" + operator="atop" + id="feComposite21" /> + </filter> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <circle + style="fill:#00ff00;filter:url(#filter21)" + id="path1" + r="20" + cy="24" + cx="24" /> + <path + sodipodi:type="star" + style="fill:#ffffff;stroke-width:1;stroke-dasharray:none;fill-opacity:1" + id="path7" + inkscape:flatsided="true" + sodipodi:sides="3" + sodipodi:cx="19.031765" + sodipodi:cy="16.146082" + sodipodi:r1="9.0851498" + sodipodi:r2="4.5425749" + sodipodi:arg1="2.0943951" + sodipodi:arg2="3.1415927" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 14.48919,24.014052 0,-15.7359406 13.627725,7.8679706 z" + inkscape:transform-center-x="-2.2712875" + transform="translate(2.6969476,7.8539181)" /> + </g> +</svg> diff --git a/gui/src/main/resources/icons/play_24.png b/gui/src/main/resources/icons/play_24.png Binary files differnew file mode 100644 index 0000000..11bd88e --- /dev/null +++ b/gui/src/main/resources/icons/play_24.png diff --git a/guiconcept.png b/guiconcept.png Binary files differnew file mode 100644 index 0000000..b0284cc --- /dev/null +++ b/guiconcept.png diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..f4624c2 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,3 @@ +rootProject.name = "l4s2" +include("core") +include("gui")
\ No newline at end of file |
