Compare commits

...
This repository has been archived on 2022-10-09. You can view files and clone it, but cannot push or open issues or pull requests.

15 Commits

Author SHA1 Message Date
5472576606
Fixed a compilation issue under Windows and MacOS, added safeguards 2022-01-15 19:45:53 +03:00
4a6aa5dbf7
Set source code encoding in build.gradle 2022-01-14 13:28:02 +03:00
c3bbc8661d
Added GLFW error handling and fixed a bug
- GLFW errors are now detected
- Added IntConstantsMap
- Removed window maximization on init
2022-01-14 00:28:52 +03:00
eadc85bfda
Fixed GitHub issue forms 2022-01-12 21:23:53 +03:00
ca8ac6e7b6
Added issue forms for GitHub 2022-01-12 17:23:46 +03:00
0638794a80
Added ColorScheme to manage GUI colors; changed BG color slightly 2022-01-12 12:09:55 +03:00
e6e55a6c40
Fixed 'HEAD' in detected Git branch on Jenkins 2022-01-10 23:59:09 +03:00
57a86b544e
Fixed dependency detection for external tools 2022-01-10 21:19:31 +03:00
b18eac44b8
Updated all dependencies
- Updated LWJGL to 3.3.0
  - Targets windows-arm64 and macos-arm64 are now available
- Updated Guava to 31.0.1
- Updated Log4j to 2.17.1
2022-01-10 19:54:14 +03:00
c3c8a6e5e0
Updated Gradle wrapper to 7.3.3 2022-01-09 21:09:02 +03:00
fe01e1b81c
Fixed first-time packaging issue in build script 2022-01-09 21:02:56 +03:00
162b2249b1
Rewrote build script, improved packaging
- Renamed :packageWindows to :packageNsis
- ImageMagick now required for :packageNsis
- Packaging output is now in build/packages
- Rewrote build script
  - Packaging logic and LWJGL dependencies now in separate files
  - Packaging logic now implemented with Gradle, not Bash scripts
    - Cross-platform?
- Added :packageZip
  - Universal ZIP with start scripts
- Changed version detection logic
  - Tags of ancestor commits are now considered
  - Only tags with format vMAJOR.MINOR.PATCH[-SUFFIX] are considered
  - If the tag's commit isn't HEAD, PATCH is incremented
  - When version detection fails, dummy version is 999.0.0-<buildID or date>
- LWJGL target platforms can be overridden with forceTargets project property
2022-01-07 22:23:24 +03:00
dececb4589
Fixed window icons error on Wayland 2021-12-26 20:36:32 +03:00
ec17eb7065
Added window icons
- Added window icons for various sizes
  - Broken on Wayland
  - Due to a bug in texture loading, textures a flipped
    - Included non-flipped versions
- Added TextureSettings.allocateExactBuffer
  - When true, texture size is not rounded to a power of 2
2021-12-26 14:30:50 +03:00
e4d0570200
Added automatic version detection to Gradle and game 2021-12-25 20:24:45 +03:00
72 changed files with 2187 additions and 873 deletions

2
.gitattributes vendored
View File

@ -6,4 +6,4 @@
* text=auto eol=lf * text=auto eol=lf
*.bat text eol=crlf *.bat text eol=crlf
*.nsi text eol=crlf

49
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: Bug Report
description: Let us know about a problem
labels: [bug]
body:
- type: markdown
attributes:
value: |
Use this template to report identified problems. If the game suddenly crashed, and you need help identifying the cause of a crashreport, please use the Investigate Crashreport template instead.
**Do not forget to give your issue a descriptive title.**
- type: textarea
attributes:
label: Steps to reproduce
description: |
What did you do just before the problem appeared, or where can it be observed?
If the issue occurs unreliably, please estimate the probability of the crash.
placeholder: |
1. Look at a cow
2. Wait for the cow to look back
3. The cow turns away after a minute
The issue occurs approximately once every 10 attempts.
Player inventory must be empty. Does not occur in multiplayer. Occurred inside a virtual machine.
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: |
What should happen?
placeholder: |
The cow should stare back indefinitely, because obviously it should, and because comedy.
validations:
required: true
- type: textarea
attributes:
label: Other information
description: What else can you tell us about this bug?
validations:
required: false
- type: textarea
attributes:
label: Crashreport
description: If you have a relevant crashreport, paste the contents of the crashreport file here
render: text
validations:
required: false

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Official Progressia Discord
url: https://discord.gg/TWuXbVmX23
about: Ask developers or community members for help

41
.github/ISSUE_TEMPLATE/crashreport.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Investigate Crashreport
description: Submit a crashreport to determine the problem
labels: [bug, crashreport]
body:
- type: markdown
attributes:
value: |
Use this template to submit crashreports for initial analisys. If you don't have a crashreport, or you have a good understanding of the underlying cause, please use the Bug Report template instead.
Crashreports can be found in `WORKING-DIRECTORY/crashreports` (take notice of the timestamp). `WORKING-DIRECTORY` is `C:\Users\<windows-user>\AppData\Roaming\Progressia` on Windows when using the installer.
**Do not forget to give your issue a descriptive title.**
- type: textarea
attributes:
label: Steps to reproduce
description: |
What did you do just before the crash? How could someone else reproduce it? What other context might be relevant?
If the issue occurs unreliably, please estimate the probability of the crash.
placeholder: |
1. Enter a new world
2. Turn around
3. Wait 5 minutes
4. Game crashes half of the time
Player inventory must be empty. Does not crash in multiplayer. Occurred inside a virtual machine.
validations:
required: true
- type: textarea
attributes:
label: Other information
description: What else can you tell us about this crashreport?
validations:
required: false
- type: textarea
attributes:
label: Crashreport
description: Paste the contents of the crashreport file here
render: text
validations:
required: true

View File

@ -1,23 +1,29 @@
/* /*
* build.gradle for Progressia * Build logic for Progressia
* build.gradle
*
* Please refer to
*
* docs/building/BuildScriptReference.md
*
* for user reference.
*/ */
plugins { plugins {
// Apply the java-library plugin to add support for Java Library id 'java'
id 'java-library'
// GrGit
/* // A JGit wrapper for Groovy used to access git commit info
* Uncomment the following line to enable the Eclipse plugin. id 'org.ajoberstar.grgit' version '4.1.1'
* This is only necessary if you don't use Buildship plugin from the IDE
*/
//id 'eclipse'
} }
/*
* Configure Java version
*/
java { java {
/*
* We're Java 8 for now.
* Why? As of 2020, most users have Oracle Java, which only supports Java 8.
*/
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
@ -34,6 +40,18 @@ compileJava {
} }
} }
/*
* Set encoding
*/
compileJava {
options.encoding = 'utf8'
}
/* /*
* Dependencies * Dependencies
*/ */
@ -42,7 +60,7 @@ repositories {
mavenCentral() mavenCentral()
/* /*
* Specify Windcorp Maven repository * Windcorp Maven repository
* Currently used by: * Currently used by:
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1 * - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
*/ */
@ -52,7 +70,7 @@ repositories {
dependencies { dependencies {
// Google Guava // Google Guava
// A generic utilities library // A generic utilities library
implementation 'com.google.guava:guava:30.0-jre' implementation 'com.google.guava:guava:31.0.1-jre'
// Trove4j // Trove4j
// Provides optimized Collections for primitive types // Provides optimized Collections for primitive types
@ -65,238 +83,311 @@ dependencies {
// Log4j // Log4j
// A logging library // A logging library
implementation 'org.apache.logging.log4j:log4j-api:2.17.0' implementation 'org.apache.logging.log4j:log4j-api:2.17.1'
implementation 'org.apache.logging.log4j:log4j-core:2.17.0' implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
// JUnit // JUnit
// A unit-testing library // A unit-testing library
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
// See LWJGL dependencies below // Also see LWJGL dependencies in build_logic/lwjgl.gradle
} }
/*
* Progressia uses the following LWJGL libraries:
* - Core libraries
* - OpenGL
* - OpenAL
* - GLFW
* - STB
*/
/* /*
* LWJGL * Version resolution
* (auto-generated script)
* ((here be dragons))
*/ */
import org.gradle.internal.os.OperatingSystem import org.ajoberstar.grgit.*
project.ext.lwjglVersion = "3.2.3" // Pattern: vMAJOR.MINOR.PATCH[-SUFFIX]
project.ext.tagFormat = /^v(\d+)\.(\d+)\.(\d+)(-[\w\-]*)?$/
switch (OperatingSystem.current()) { String version_parseVersion(String tag, boolean increment) {
case OperatingSystem.LINUX: try {
def osArch = System.getProperty("os.arch")
project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64") def data = (tag =~ tagFormat)[0]
? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}"
: "natives-linux" def major = data[1]
break def minor = data[2]
case OperatingSystem.MAC_OS: def patch = data[3] as int
project.ext.lwjglNatives = "natives-macos" def suffix = data[4] ?: ''
break
case OperatingSystem.WINDOWS: if (increment) {
project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86" def oldVersion = "$major.$minor.$patch$suffix"
break patch++
} def newVersion = "$major.$minor.$patch$suffix"
dependencies { logger.info "Version parsed from Git: $oldVersion, incremented to $newVersion"
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") return newVersion
} else {
implementation "org.lwjgl:lwjgl" def newVersion = "$major.$minor.$patch$suffix"
implementation "org.lwjgl:lwjgl-glfw" logger.info "Version parsed from Git: $newVersion"
implementation "org.lwjgl:lwjgl-openal" return newVersion
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
}
// LWJGL END
/*
* Tasks
*/
/*
* Additional native libraries specification
*/
project.ext.platforms = new HashSet<>()
task addNativeDependencies {
doFirst {
def archs = project.ext.platforms
switch (archs.size()) {
case 0:
println "Adding LWJGL native dependencies for local platform only:\n\t$lwjglNatives"
archs.add project.ext.lwjglNatives
break
case 1:
println "Adding LWJGL native dependencies for platform\n\t" + archs.get(0)
break
default:
println "Adding LWJGL native dependencies for platforms:\n\t" + archs.join("\n\t")
} }
} catch (any) {
logger.warn "Could not parse version from tag \"$tag\""
return tag
}
}
if (project.ext.lwjglNatives.isEmpty()) println "WTF" Tag version_findRelevantTag(Grgit git) {
def tags = git.tag.list()
dependencies {
archs.each { arch -> def commits = [ git.head() ]
runtimeOnly "org.lwjgl:lwjgl::$arch" def visited = new HashSet<>()
runtimeOnly "org.lwjgl:lwjgl-glfw::$arch"
runtimeOnly "org.lwjgl:lwjgl-openal::$arch" while (true) {
runtimeOnly "org.lwjgl:lwjgl-opengl::$arch" if (commits.isEmpty()) return null
runtimeOnly "org.lwjgl:lwjgl-stb::$arch"
def nextCommits = new HashSet<>()
def formatSpecificationPrinted = false
for (def commit : commits) {
def tag = tags.findAll { it.commit == commit } ?.max { it.dateTime }
if (tag != null) {
if (tag.name ==~ tagFormat) {
return tag
} else {
if (!formatSpecificationPrinted) {
formatSpecificationPrinted = true
logger.info 'Expecting tag format: vMAJOR.MINOR.PATCH[-SUFFIX]'
}
logger.info 'Ignoring tag due to invalid format: {}', tag.name
}
} }
nextCommits.addAll commit.parentIds.collect(git.resolve.&toCommit)
} }
visited.addAll commits
nextCommits.removeAll visited
commits = nextCommits
} }
} }
compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved task resolveVersion {
description 'Resolves version information from Git repository or project properties.'
task requestLinuxDependencies {
description 'Adds linux, linux-arm64 and linux-arm32 native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll([
'natives-linux',
'natives-linux-arm64',
'natives-linux-arm32'
])
}
}
task requestWindowsDependencies {
description 'Adds windows and windows-x86 native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll([
'natives-windows',
'natives-windows-x86'
])
}
}
task requestMacOSDependencies {
description 'Adds macos native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll(['natives-macos'])
}
}
def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') }
task requestCrossPlatformDependencies {
description 'Adds native libraries for all available platforms to built artifacts.'
dependsOn dependencySpecificationTasks
}
addNativeDependencies.mustRunAfter dependencySpecificationTasks
/*
* Determines if the provided dependency should be packaged
*/
def isDependencyRequested(String dep) {
if (dep.endsWith(".jar")) {
dep = dep.substring(0, dep.length() - ".jar".length())
}
return !dep.contains("natives-") ||
project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length()))
}
/*
* Manifest specification
*/
task specifyLocalManifest {
dependsOn addNativeDependencies // Make sure all native dependencies are specified
doFirst { doFirst {
def classPath = []
project.ext.commit = System.env.GIT_COMMIT
configurations.runtimeClasspath.each { project.ext.branch = System.env.GIT_BRANCH
if (isDependencyRequested(it.getName())) {
classPath.add("lib/" + it.getName()) try {
def git = Grgit.open(dir: project.projectDir)
project.ext.commit = commit ?: git.head().id
project.ext.branch = branch ?: git.branch.current().name
if (project.version != 'unspecified') {
// Leave version as-is
} else { } else {
println "\tRemoving from JAR classpath (not requested): " + it.getName()
// Resolve version from Git
def tag = version_findRelevantTag(git)
if (tag == null) {
String suffix
if (project.hasProperty('buildId')) {
suffix = project.buildId;
} else {
suffix = java.time.ZonedDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern('yyyy_MM_dd'))
}
project.version = "999.0.0-$suffix"
logger.warn 'Git repository does not contain an applicable tag, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
} else {
project.version = version_parseVersion(tag.name, tag.commit != git.head())
}
} }
} catch (org.eclipse.jgit.errors.RepositoryNotFoundException e) {
if (project.version == 'unspecified') project.version = 'dev'
project.ext.commit = commit ?: '-'
project.ext.branch = branch ?: '-'
logger.warn 'No Git repository found in project root, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
} }
if (classPath.size() == configurations.runtimeClasspath.size()) { if (branch.contains '/') {
println "Nothing removed from JAR classpath" // Strip remote - no one wants that
project.ext.branch = branch.takeAfter '/'
} }
jar { if (!project.hasProperty('buildId')) {
manifest { project.ext.buildId = '-'
attributes(
"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain",
"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' ')
)
}
} }
} }
doLast {
}
} }
jar.dependsOn specifyLocalManifest
/* /*
* Library export * Apply LWJGL logic
*/
apply from: 'build_logic/lwjgl.gradle'
/*
* Copy libraries into build/libs/lib directory, next to Progressia.jar
*/ */
task exportLibs(type: Sync) { task exportLibs(type: Sync) {
mustRunAfter addNativeDependencies description 'Copies runtime libraries into a subdirectory next to the output JAR.'
jar.dependsOn exportLibs
dependsOn lwjgl_addNativesToRuntimeOnly
into libsDirectory.get().getAsFile().getPath() + "/lib" // from defined in configureManifest
exclude { !isDependencyRequested(it.getName()) } into 'build/libs/lib'
from configurations.runtimeClasspath
} }
jar.dependsOn(exportLibs)
/* /*
* Packaging * Configure JAR manifest
*/ */
task packageDebian(type: Exec) { task configureManifest {
description 'Builds the project and creates a Debain package.' description 'Populates JAR manifest with Main-Class, Class-Path and version metadata.'
group 'Progressia'
dependsOn build jar.dependsOn configureManifest
dependsOn requestLinuxDependencies dependsOn resolveVersion
dependsOn lwjgl_addNativesToRuntimeOnly
commandLine './buildPackages.sh', 'debian' doFirst {
def classPath = project.lwjgl.replaceNativesIn(configurations.runtimeClasspath)
doLast {
println "Debian package available in build_packages/" exportLibs.from classPath
jar.manifest.attributes(
'Main-Class': 'ru.windcorp.progressia.client.ProgressiaClientMain',
'Class-Path': classPath.collect { "lib/${java.net.URLEncoder.encode it.name}" } .join(' '),
'Specification-Title': 'Progressia',
'Implementation-Title': 'Progressia',
'Implementation-Version': project.version,
'Implementation-Version-Git-Commit': project.commit,
'Implementation-Version-Git-Branch': project.branch,
'Implementation-Version-BuildId': project.buildId,
)
} }
} }
task packageWindows(type: Exec) {
description 'Builds the project and creates a Windows installer.'
group 'Progressia'
dependsOn build
dependsOn requestWindowsDependencies
commandLine './buildPackages.sh', 'windows' /*
* Packaging working directory configuration
*/
doLast { import java.nio.file.*
println "Windows installer available in build_packages/" import java.nio.file.attribute.*
task deletePackagingDirs(type: Delete) {
description 'Deletes build/tmp/packaging directory.'
followSymlinks = false
delete 'build/tmp/packaging'
}
task linkBuildOutputForPackaging {
description 'Symlinks the contents of build/libs into packaging working directory.'
dependsOn build
mustRunAfter deletePackagingDirs
onlyIf { preparePackaging.ext.mode == 'symlink' }
ext.from = 'build/libs'
ext.into = "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}"
inputs.dir from
outputs.dir into
doLast {
def fromPath = Paths.get from
def intoPath = Paths.get into
Files.createDirectories intoPath
Files.list(fromPath).each {
def fileName = it.fileName.toString()
// Exclude all JARs except the current one
if (fileName ==~ "${project.name}.*\\.jar" && fileName != tasks.jar.archiveFileName.get())
return
Files.createSymbolicLink intoPath.resolve(it.fileName), intoPath.relativize(it)
}
}
}
task copyBuildOutputForPackaging(type: Copy) {
description 'Copies the contents of build/libs into packaging working directory.'
dependsOn build
mustRunAfter deletePackagingDirs
onlyIf { preparePackaging.ext.mode == 'copy' }
from 'build/libs'
filesMatching("${project.name}*.jar") {
include tasks.jar.archiveFileName.get()
}
into "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}"
}
task preparePackaging {
preparePackaging.ext.buildDest = ''
preparePackaging.ext.mode = 'symlink'
dependsOn deletePackagingDirs
dependsOn linkBuildOutputForPackaging
dependsOn copyBuildOutputForPackaging
}
/*
* Apply all packaging scripts
*/
new File(projectDir, 'build_logic/packaging').list().each {
apply from: "build_logic/packaging/$it/script.gradle"
}
/*
* Ensure no more than one packaging task is scheduled
*/
gradle.taskGraph.whenReady { graph ->
if (graph.allTasks.count { it.name ==~ /package[^_]*/ } > 1) {
def offenders = graph.allTasks.findAll { it.name ==~ /package[^_]*/ }
throw new GradleException("Cannot execute multiple package tasks within a single build\n" +
"\tOffending tasks: $offenders")
} }
} }
/*
* Convenience build tasks
*/
task buildCrossPlatform { task buildCrossPlatform {
description 'Builds the project including native libraries for all available platforms.' description 'Builds the project including native libraries for all available platforms.'
group 'Progressia' group 'Progressia'
@ -305,17 +396,17 @@ task buildCrossPlatform {
dependsOn build dependsOn build
doLast { doLast {
println "Native libraries for all platforms have been added" logger.info 'Native libraries for all platforms have been added'
} }
} }
task buildLocal { task buildLocal {
description "Builds the project including only native libraries for current platform ($lwjglNatives)." description "Builds the project including only native libraries for current platform (${lwjgl.localArch})."
group 'Progressia' group 'Progressia'
dependsOn build dependsOn build
doLast { doLast {
println "Native libraries only for platform $lwjglNatives have been added" logger.info "Native libraries only for platform ${lwjgl.localArch} have been added"
} }
} }

View File

@ -1,191 +0,0 @@
#!/bin/bash
#
# Progressia
# Copyright (C) 2020-2021 Wind Corporation and contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
echoerr() { echo "$@" 1>&2; }
buildDebianPackage() {
# Commands that must be available to execute this action
requiredCommands='dpkg-deb fakeroot'
# Package name. Sync with control file manually!
name='progressia-techdemo'
# Version that the package will receive. Sync with control file manually!
version='1.0_all'
# This directory will be copied into $tmpDir
templateDirectory="build_packages/DEB/template"
# Files that must be present
requiredFiles="$templateDirectory/DEBIAN/control"
nameAndVersion="$name-$version"
tmpDir="build_packages/DEB/$nameAndVersion"
outputFile="build_packages/DEB/$nameAndVersion.deb"
echo "Checking environment to build Debian package"
for item in $requiredCommands; do
if command -v "$item" &> /dev/null; then
echo "- $item found"
else
echoerr "Command $item not found, cannot package"
exit 100
fi
done
for file in $requiredFiles; do
if ! [ -r "$file" ]; then
echoerr "$file is missing or not readable, cannot package"
exit 101
else
echo "- $file is present and readable"
fi
done
echo "Environment OK; packaging Debian package"
exitCode=0
{
shareDir="$tmpDir/usr/share/progressia"
mkdir -p "$tmpDir" &&
mkdir -p "$shareDir" &&
cp -r "$templateDirectory"/* "$tmpDir" &&
cp -r 'build/libs/lib' "$shareDir/lib" &&
cp 'build/libs/Progressia.jar' "$shareDir/Progressia.jar" &&
echo "------ DPKG-DEB ------" &&
fakeroot dpkg-deb --build "$tmpDir" &&
echo "---- DPKG-DEB END ----" &&
mv "$outputFile" build_packages
} || {
echoerr "Could not create Debian package"
exitCode=1
}
{
if [ -d "$tmpDir" ]; then
rm -r "$tmpDir"
fi
echo "Cleaned up"
} || {
echoerr "Could not clean up after packaging Debian package"
exitCode=2
}
exit "$exitCode"
}
buildWindowsInstaller() {
# Commands that must be available to execute this action
requiredCommands='makensis'
# NSIS configuration file that must be present
configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi'
# File that will be output
outputFile='build_packages/NSIS/ProgressiaInstaller.exe'
echo "Checking environment to build Windows installer"
for item in $requiredCommands; do
if command -v "$item" &> /dev/null; then
echo "- $item found"
else
echoerr "Command $item not found, cannot build"
exit 100
fi
done
if ! [ -r "$configurationFile" ]; then
echoerr "$configurationFile is missing or not readable, cannot build"
exit 101
else
echo "- $configurationFile is present and readable"
fi
echo "Environment OK; building Windows installer"
exitCode=0
{
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' &&
echo "------ NSIS ------" &&
makensis "$configurationFile" &&
echo "---- NSIS END ----" &&
mv "$outputFile" build_packages
} || {
echoerr "Could not build Windows installer"
exitCode=1
}
{
if [ -d 'build_packages/NSIS/lib' ]; then
rm -r 'build_packages/NSIS/lib'
fi
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
rm 'build_packages/NSIS/Progressia.jar'
fi
if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then
rm 'build_packages/NSIS/LICENSE.txt'
fi
echo "Cleaned up"
} || {
echoerr "Could not clean up after building Windows installer"
exitCode=2
}
exit "$exitCode"
}
printUsage() {
echoerr "Usage: $0 TARGET"
echoerr " where TARGET is 'debian' or 'windows'"
}
if [ -n "$2" ]; then
echoerr "Too many arguments."
printUsage
exit 202
fi
case "$1" in
"debian")
buildDebianPackage
;;
"windows")
buildWindowsInstaller
;;
"")
echoerr "No action specified"
printUsage
exit 200
;;
"--help" | "-help" | "help" | "?")
printUsage
;;
*)
echoerr "Unknown action '$1'"
printUsage
exit 201
;;
esac

149
build_logic/lwjgl.gradle Normal file
View File

@ -0,0 +1,149 @@
/*
* Build logic for Progressia
* LWJGL dependency logic
*/
project.ext.lwjgl = new HashMap<>()
// Version of LWJGL
lwjgl.version = '3.3.0'
/*
* Target platforms for current operation.
* This is filled in by the request* tasks. This is referenced by the addLwjglNatives task.
* When empty, current platform is assumed.
*/
lwjgl.targets = new HashSet<String>()
// LWJGL components. To include org.lwjgl:lwjgl-foobar, add 'foobar' to this list.
lwjgl.libraries = [
'opengl',
'glfw',
'openal',
'stb'
]
// Determine the architecture of the build environment
import org.gradle.internal.os.OperatingSystem
switch (OperatingSystem.current()) {
case OperatingSystem.LINUX:
def osArch = System.getProperty('os.arch')
lwjgl.localArch = osArch.startsWith('arm') || osArch.startsWith('aarch64')
? "linux-${osArch.contains('64') || osArch.startsWith('armv8') ? 'arm64' : 'arm32'}"
: 'linux'
break
case OperatingSystem.MAC_OS:
lwjgl.localArch = System.getProperty('os.arch').startsWith('aarch64') ? 'macos-arm64' : 'macos'
break
case OperatingSystem.WINDOWS:
def osArch = System.getProperty('os.arch')
lwjgl.localArch = osArch.contains('64')
? "windows${osArch.startsWith('aarch64') ? '-arm64' : ''}"
: 'windows-x86'
break
default:
logger.info "Unknown or unsupported OS type according to Gradle's org.gradle.internal.os.OperatingSystem: ${OperatingSystem.current()}"
break
}
configurations {
create 'lwjglNatives'
}
// Declare pure-Java dependencies
dependencies {
// BOM
def bom = platform("org.lwjgl:lwjgl-bom:${lwjgl.version}")
implementation bom
lwjglNatives bom
// Core
implementation 'org.lwjgl:lwjgl'
// Local natives for core
runtimeOnly "org.lwjgl:lwjgl::natives-${lwjgl.localArch}"
// Components
lwjgl.libraries.each { lib ->
implementation "org.lwjgl:lwjgl-$lib"
// Local natives for component
runtimeOnly "org.lwjgl:lwjgl-$lib::natives-${lwjgl.localArch}"
}
}
/*
* Adds LWJGL native libraries to lwjglNatives configuration
*/
task lwjgl_addNativesToRuntimeOnly {
doFirst {
if (project.hasProperty('forceTargets')) {
try {
def oldTargets = lwjgl.targets.join(',')
lwjgl.targets.clear()
lwjgl.targets.addAll project.forceTargets.split(',')*.trim().collect { it == 'local' ? lwjgl.localArch : it }
logger.info 'Overriding selected platforms {} with {}', oldTargets, lwjgl.targets.join(',')
} catch (Exception e) {
throw new GradleException("Could not parse forceTargets \"${project.forceTargets}\", expecting platform-1,platform-2,local", e)
}
}
if (lwjgl.targets.isEmpty()) {
logger.info 'Adding LWJGL native dependencies for local platform only: {}', lwjgl.localArch
lwjgl.targets.add lwjgl.localArch
} else {
logger.info 'Adding LWJGL native dependencies for platforms: {}', lwjgl.targets.sort().join(', ')
}
if (lwjgl.targets.contains(null)) {
if (lwjgl.localArch != null) {
throw new GradleException("Requested local LWJGL natives; could not determine local architecture for OS ${OperatingSystem.current()} with os.arch ${System.getProperty('os.arch')}")
} else {
throw new GradleException("LWJGL targets resolved to ${lwjgl.targets}")
}
}
dependencies {
lwjgl.targets.each { target ->
lwjglNatives "org.lwjgl:lwjgl::natives-$target"
lwjgl.libraries.each { lib ->
lwjglNatives "org.lwjgl:lwjgl-$lib::natives-$target"
}
}
}
}
}
// Replaces LWJGL natives in the given configuration with the requested ones
lwjgl.replaceNativesIn = { config ->
new ArrayList<File>().tap {
addAll config
removeIf { it.name ==~ /.*lwjgl.*natives.*/ }
addAll project.configurations.lwjglNatives
}
}
task requestCrossPlatformDependencies {
description 'Adds LWJGL natives for all available platforms.'
lwjgl_addNativesToRuntimeOnly.mustRunAfter requestCrossPlatformDependencies
}
def requestTask(String name, String... targets) {
def theTask = task "request${name}Dependencies"
theTask.doFirst {
lwjgl.targets.addAll targets
}
theTask.description "Adds LWJGL natives for $name (${targets.join(', ')})."
requestCrossPlatformDependencies.dependsOn theTask
lwjgl_addNativesToRuntimeOnly.mustRunAfter theTask
}
requestTask 'Linux', 'linux', 'linux-arm32', 'linux-arm64'
requestTask 'Windows', 'windows', 'windows-arm64', 'windows-x86'
requestTask 'MacOS', 'macos', 'macos-arm64'

View File

@ -0,0 +1,42 @@
task packageDeb_processResources(type: Copy) {
dependsOn resolveVersion
dependsOn preparePackaging
from 'src/packaging/deb'
filesMatching('DEBIAN/control') {
expand(version: { -> project.version})
}
into 'build/tmp/packaging/workingDir'
}
task packageDeb_configure() {
preparePackaging.mustRunAfter packageDeb_configure
doLast {
tasks.preparePackaging.ext.buildDest = 'usr/share/progressia'
tasks.preparePackaging.ext.mode = 'copy'
}
}
task packageDeb(type: Exec) {
description 'Builds the project and creates a Debian package.'
group 'Progressia'
dependsOn packageDeb_configure
dependsOn requestLinuxDependencies
dependsOn build
dependsOn preparePackaging
dependsOn packageDeb_processResources
doFirst {
mkdir 'build/packages'
}
executable 'dpkg-deb'
args '--root-owner-group'
args '--build', 'build/tmp/packaging/workingDir'
args 'build/packages'
}

View File

@ -0,0 +1,54 @@
task packageNsis_processResources(type: Copy) {
dependsOn preparePackaging
from ('src/packaging/nsis') {
exclude 'left_side.png'
}
from('LICENSE') {
rename 'LICENSE', 'LICENSE.txt'
}
into 'build/tmp/packaging/workingDir'
}
task packageNsis_generateIcon(type: Exec) {
mustRunAfter preparePackaging
executable 'convert'
args files('src/main/resources/assets/icons/*.original.png').files*.path
args 'build/tmp/packaging/workingDir/logo.ico'
}
task packageNsis_generateLeftSide(type: Exec) {
mustRunAfter preparePackaging
executable 'convert'
args 'src/packaging/nsis/left_side.png'
args '-alpha', 'off'
args 'BMP3:build/tmp/packaging/workingDir/left_side.bmp'
}
task packageNsis(type: Exec) {
description 'Builds the project and creates a Windows NSIS installer.'
group 'Progressia'
dependsOn requestWindowsDependencies
dependsOn build
dependsOn resolveVersion
dependsOn preparePackaging
dependsOn packageNsis_processResources
dependsOn packageNsis_generateIcon
dependsOn packageNsis_generateLeftSide
doFirst {
mkdir 'build/packages'
}
executable 'makensis'
args '-NOCONFIG'
args "-DPROJECT_NAME=${project.name}"
args "-DPROJECT_VERSION=${ -> project.version}"
args "-DMAIN_JAR_FILE=${ -> project.tasks.jar.archiveFileName.get()}"
args "-DOUTPUT_DIR=${project.buildDir.absolutePath}/packages"
args 'build/tmp/packaging/workingDir/config.nsi'
}

View File

@ -0,0 +1,45 @@
task packageZip_processResources(type: Copy) {
dependsOn preparePackaging
from ('src/packaging/zip') {
filesMatching('start.*') {
filter(
org.apache.tools.ant.filters.ReplaceTokens,
tokens: [mainJarFile: project.tasks.jar.archiveFileName.get()]
)
}
}
from ('src/main/resource/assets/icons/logo256.original.png') {
rename 'logo256.original.png', 'logo.png'
}
from('LICENSE') {
rename 'LICENSE', 'LICENSE.txt'
}
into 'build/tmp/packaging/workingDir'
}
task packageZip(type: Zip) {
description 'Builds the project and creates a cross-platform ZIP package.'
group 'Progressia'
dependsOn resolveVersion
dependsOn requestCrossPlatformDependencies
dependsOn build
dependsOn preparePackaging
dependsOn packageZip_processResources
archiveBaseName = project.name
archiveAppendix = 'universal'
doFirst {
archiveVersion = project.version
}
doFirst {
mkdir 'build/packages'
}
from 'build/tmp/packaging/workingDir'
destinationDirectory = file('build/packages')
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

View File

@ -1,8 +1,10 @@
# Build Guide # Build Guide
This document is a guide to building Progressia from source. This document is a guide to building Progressia from source. For quick reference, see
[Build Script Reference](BuildScriptReference.md).
Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require Bash. Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require
additional programs in `PATH`.
This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell. This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell.
@ -141,8 +143,8 @@ build.
Some users might find the need to build for a specific set of platforms. Inclusion of GNU/Linux, Windows or MacOS Some users might find the need to build for a specific set of platforms. Inclusion of GNU/Linux, Windows or MacOS
dependencies individually can be controlled with the following arguments to the `./gradlew build` command: dependencies individually can be controlled with the following arguments to the `./gradlew build` command:
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included; - `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included;
- `requestWindowsDependencies` requests that `natives-windows` and `natives-windows-x86` binaries are included; - `requestWindowsDependencies` requests that `natives-windows`, `natives-windows-arm64` and `natives-windows-x86` binaries are included;
- `requestMacOSDependencies` requests that `natives-macos` binaries are included. - `requestMacOSDependencies` requests that `natives-macos`, `natives-macos-arm64` binaries are included.
These requests can be applied in any combination. For example, the following command produces a build containing only These requests can be applied in any combination. For example, the following command produces a build containing only
GNU/Linux and Windows natives: GNU/Linux and Windows natives:
@ -150,54 +152,46 @@ GNU/Linux and Windows natives:
./gradlew build requestLinuxDependencies requestWindowsDependencies ./gradlew build requestLinuxDependencies requestWindowsDependencies
``` ```
For finer control please edit `build.gradle` manually by adding the desired natives to the `project.ext.platforms` set like so:
```
project.ext.platforms = new HashSet<>()
project.ext.platforms.add 'natives-windows-x86'
```
## Packaging ## Packaging
A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated A universal ZIP distribution, a Debian package and a Windows NSIS installer may be created automatically by the build
by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the script.
resulting files are moved into `build_packages`.
### Creating a universal ZIP package
A universal cross-platform ZIP archive can be created with the following Gradle task:
```
./gradlew packageZip
```
Gradle will then build all artifacts necessary to run the game on all available platforms and package game files,
libraries, launch scripts, etc. into a compressed ZIP archive.
The resulting file can be found in `build/packages/`
### Creating a Debian package ### Creating a Debian package
A Debian package can be created with the following Gradle task: A Debian package can be created with the following Gradle task:
``` ```
./gradlew packageDebian ./gradlew packageDeb
``` ```
Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke
`./buildPackages.sh debian`. Commands `dpkg-deb` and `fakeroot` must be available in system path in order to build the package. `dpkg-deb`. Commands `dpkg-deb` must be available in system path in order to build the package.
### Creating a Windows installer ### Creating a Windows installer
A Windows installer can be created with the following Gradle task: A Windows NSIS installer can be created with the following Gradle task:
``` ```
./gradlew packageWindows ./gradlew packageNsis
``` ```
Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke
`./buildPackages.sh windows`. `makensis`.
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` must be available in system Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). [ImageMagick](https://imagemagick.org),
path in order to build the installer. a command-line image editing tool, is used to generate some assets for the installer. Commands `makensis` and
`convert` (from ImageMagick) must be available in system path in order to build the installer.
## Gradle tasks summary
- `buildLocal` creates a build optimized for current platform. Use this to quickly build the game during development.
- `buildCrossPlatform` creates a build that supports all known architectures. Use this to build a universal version of the game.
- `build` currently a synonym of `buildLocal`; creates a default build.
- `packageDebian` creates a Debian package. Do not invoke together with `packageWindows`.
- `packageWindows` creates a Windows installer. Do not invoke together with `packageDebian`.
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
- `requestWindowsDependencies` requests that `natives-windows` and `natives-windows-x86` binaries are included when building.
- `requestMacOSDependencies` requests that `natives-macos` binaries are included when building.
- `requestCrossPlatformDependencies` requests that all binaries are included when building.
All other basic and Java-related Gradle tasks are available as well.

View File

@ -0,0 +1,112 @@
# Build Script Reference
This document is a user's reference for the build script of Progressia. For a beginner-friendly guide, see
[Build Guide](BuildGuide.md).
## Gradle tasks summary
- `buildLocal` creates a build optimized for current platform. Use this to quickly build the game during development.
- `buildCrossPlatform` creates a build that supports all known architectures. Use this to build a universal version of the game.
- `build` currently a synonym of `buildLocal`; creates a default build.
- `packageZip` creates a universal ZIP. Incompatible with other `package` tasks.
- `packageDeb` creates a Debian package. Incompatible with other `package` tasks.
- `packageNsis` creates a Windows NSIS installer. Incompatible with other `package` tasks.
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
- `requestWindowsDependencies` requests that `natives-windows`, `natives-windows-arm64` and `natives-windows-x86` binaries are included when building.
- `requestMacOSDependencies` requests that `natives-macos` and `natives-macos-arm64` binaries are included when building.
- `requestCrossPlatformDependencies` requests that all binaries are included when building.
To execute a task, run `./gradlew <task-name>`.
`build`-type tasks output the executable JAR and all libraries required at runtime into `build/libs`. `package`-type
tasks output packages into `build/packages`.
## Packaging tasks
Some packaging tasks require additional software in `PATH`.
| Task | Commands required in `PATH` |
|---------------|------------------------------------------|
| `packageDeb` | `dpkg-deb` |
| `packageZip` | _none_ |
| `packageNsis` | `makensis`, `convert` (from ImageMagick) |
## Version and metadata
### Version scheme
Progressia builds are identified by four parameters: version, Git commit, Git branch and build ID.
Versions roughly follow [semantic versioning](https://semver.org/spec/v2.0.0.html), with each version fitting the
`MAJOR.MINOR.PATCH[-SUFFIX]` pattern. Depending on the build environment (see below), version is either "real" with
no metadata (e.g. `0.43.2` or `1.2.1-beta`) or a dummy fallback with build metadata (e.g. `999.0.0-2021_07_23` or
`999.0.0-WJ3`).
### Version detection
Build script considers three scenarios when determining the version:
1. `version` project property is set explicitly. This may be done in a variety of ways, for example with command line
argument `-Pversion=1.2.3`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties))
2. Local Git repository is found, and HEAD is tagged appropriately: version is the tag name with leading `v`
stripped. Example: `v1.2.3` is version `1.2.3`
3. Local Git repository is found, and some ancestor of HEAD is tagged appropriately: version is the tag name with
leading `v` stripped and PATCH incremented by one. Example: `v1.2.3` is version `1.2.4`
Tags not named like `vMAJOR.MINOR.PATCH[-SUFFIX]` are ignored for cases 2 and 3.
In all other cases, a fallback dummy value is used for version, appended with build ID or current date.
### Git metadata
Git commit is determined from `GIT_COMMIT` environment variable if it exists, or the state of the local Git
repository, if any, or `-`.
Git branch is determined from `GIT_BRANCH` environment variable if it exists, or the state of the local Git
repository, if any, or `-`.
The names of the environment variables are picked to assist Jenkins builds.
### Build ID
Build ID uniquely identifies artifacts produced by automated build systems. Build ID must be provided explicitly; it
is `-` unless specified otherwise.
The proposed scheme for naming build IDs is `<builder><build-system><build attempt no.>`. For example, builds
executed by WindCorp Jenkins suite have build IDs like `WJ3` or `WJ142`.
Build ID may be set with `buildId` project property. This may be done in a variety of ways, for example with command
line argument `-PbuildId=WJ3`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)).
## Native libraries
LWJGL uses native libraries. Build script declares platform-specific dependencies based on the set of target
platforms, `project.ext.lwjgl.targets` (aka `lwjgl.targets`). These dependencies are added to `runtimeOnly`
configuration.
When this set is empty, the script selects natives for current platform. Otherwise, all platforms in the set are
included.
`lwjgl.targets` is populated automatically by packaging tasks and by `buildCrossPlatform`. To add extra targets,
``requestXxxDependencies` tasks may be used.
Target selection mechanism may be overridden with `forceTargets` project property. This may be done in a variety of
ways, for example with command line argument `-PforceTargets=windows-x86,local`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)). The
value is a comma-separated list of target architectures. `local` target will be replaced with the automatically
detected current architecture.
### Available targets
| Name | Task |
|-----------------|------------------------------|
| `linux` | `requestLinuxDependencies` |
| `linux-arm32` | `requestLinuxDependencies` |
| `linux-arm64` | `requestLinuxDependencies` |
| `windows` | `requestWindowsDependencies` |
| `windows-arm64` | `requestWindowsDependencies` |
| `windows-x86` | `requestWindowsDependencies` |
| `macos` | `requestMacOSDependencies` |
| `macos-arm64` | `requestMacOSDependencies` |

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

269
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,78 +17,113 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # 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 # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` 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. # 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"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" 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. 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 Please set the JAVA_HOME variable in your environment to match the
@ -105,79 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 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" "$@" exec "$JAVACMD" "$@"

25
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% 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. @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" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@ -0,0 +1,45 @@
/*
* JPUtil
* Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.jputil;
public class ConstantsMapException extends RuntimeException {
private static final long serialVersionUID = -4298704891780063127L;
public ConstantsMapException() {
}
public ConstantsMapException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ConstantsMapException(String message, Throwable cause) {
super(message, cause);
}
public ConstantsMapException(String message) {
super(message);
}
public ConstantsMapException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,307 @@
/*
* JPUtil
* Copyright (C) 2019-2022 OLEGSHA/Javapony and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.jputil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public class IntConstantsMap {
private final Map<Integer, String> namesByValue;
private final Map<String, Integer> valuesByName;
protected IntConstantsMap(Map<Integer, String> namesByValue, Map<String, Integer> valuesByName) {
this.namesByValue = namesByValue;
this.valuesByName = valuesByName;
}
public int getValue(String name) {
Integer value = valuesByName.get(name);
if (value == null) {
throw new NoSuchElementException("No constant with name " + name);
}
return value.intValue();
}
public boolean hasConstant(String name) {
return valuesByName.containsKey(name);
}
public String getName(int value) {
String name = namesByValue.get(value);
if (name == null) {
throw new NoSuchElementException("No constant with value " + value);
}
return name;
}
public boolean hasConstant(int value) {
return namesByValue.containsKey(value);
}
public Map<String, Integer> getAll() {
return valuesByName;
}
@Override
public String toString() {
return valuesByName.toString();
}
public static Builder from(Class<?> clazz) {
return new Builder(clazz);
}
public static class Builder {
@FunctionalInterface
public static interface Filter {
boolean test(String name, int value);
}
public class ConstantSpec {
public String name;
public int value;
public void drop() {
if (!extra.contains(name)) {
name = null;
}
}
}
private final List<Consumer<ConstantSpec>> transforms = new ArrayList<>();
private final Set<String> extra = new HashSet<>();
private final Class<?> source;
public Builder(Class<?> source) {
this.source = source;
}
public Builder apply(Consumer<ConstantSpec> transform) {
transforms.add(transform);
return this;
}
public Builder only(Filter filter) {
return apply(s -> {
if (!filter.test(s.name, s.value)) {
s.drop();
}
});
}
public Builder only(Predicate<String> nameFilter) {
return apply(s -> {
if (!nameFilter.test(s.name)) {
s.drop();
}
});
}
public Builder onlyValued(IntPredicate valueFilter) {
return apply(s -> {
if (!valueFilter.test(s.value)) {
s.drop();
}
});
}
public Builder regex(String regex) {
return only(Pattern.compile(regex).asPredicate());
}
public Builder prefix(String prefix) {
return only(n -> n.startsWith(prefix) && n.length() > prefix.length());
}
public Builder exclude(Filter filter) {
return only((n, v) -> !filter.test(n, v));
}
public Builder exclude(Predicate<String> nameFilter) {
return only(nameFilter.negate());
}
public Builder exclude(String... names) {
Set<String> excluded = new HashSet<>();
for (String name : names) {
excluded.add(name);
}
return exclude(excluded::contains);
}
public Builder excludeRegex(String... nameRegexes) {
List<Predicate<String>> tests = new ArrayList<>();
for (String regex : nameRegexes) {
tests.add(Pattern.compile(regex).asPredicate());
}
return only((n, v) -> {
for (Predicate<String> test : tests) {
if (test.test(n)) {
return false;
}
}
return true;
});
}
public Builder extra(String... names) {
for (String name : names) {
extra.add(name);
}
return this;
}
public Builder rename(Function<String, String> renamer) {
apply(s -> {
s.name = renamer.apply(s.name);
});
return this;
}
public Builder stripPrefix(String prefix) {
return apply(s -> {
if (s.name.startsWith(prefix)) {
s.name = s.name.substring(prefix.length());
} else if (extra.contains(s.name)) {
return;
} else {
s.drop();
}
});
}
public IntConstantsMap scan() {
return build(true);
}
public IntConstantsMap scanAll() {
return build(false);
}
private IntConstantsMap build(boolean onlyPublic) {
Map<Integer, String> namesByValue = new HashMap<>();
Map<String, Integer> valuesByName = new HashMap<>();
BiConsumer<String, Integer> putter = (name, value) -> {
if (namesByValue.containsKey(value)) {
throw newDuplicateException("value", value, name, namesByValue.get(value));
}
if (valuesByName.containsKey(name)) {
throw newDuplicateException("name", name, value, valuesByName.get(name));
}
namesByValue.put(value, name);
valuesByName.put(name, value);
};
try {
for (Field field : source.getDeclaredFields()) {
processField(field, putter, onlyPublic);
}
} catch (IllegalAccessException e) {
throw new ConstantsMapException(e);
}
return new IntConstantsMap(
Collections.unmodifiableMap(namesByValue),
Collections.unmodifiableMap(valuesByName)
);
}
private void processField(Field field, BiConsumer<String, Integer> putter, boolean onlyPublic)
throws IllegalAccessException {
if (!Modifier.isStatic(field.getModifiers())) {
return;
}
if (!Modifier.isFinal(field.getModifiers())) {
return;
}
boolean clearAccessible = false;
if (!Modifier.isPublic(field.getModifiers())) {
if (onlyPublic) {
return;
} else if (!isAccessibleFlagSet(field)) {
field.setAccessible(true);
clearAccessible = true;
}
}
try {
ConstantSpec spec = new ConstantSpec();
spec.name = field.getName();
spec.value = field.getInt(null);
for (Consumer<ConstantSpec> t : transforms) {
t.accept(spec);
if (spec.name == null) {
return;
}
}
putter.accept(spec.name, spec.value);
} finally {
if (clearAccessible) {
field.setAccessible(false);
}
}
}
/*
* Yes, this method exists only so that neither Java 8 nor Java 9 complain about deprecation.
*/
@Deprecated
private boolean isAccessibleFlagSet(Field f) {
return f.isAccessible();
}
private ConstantsMapException newDuplicateException(String what, Object common, Object current, Object old) {
return new ConstantsMapException(
String.format(
"Duplicate %1$s: %2$s -> %3$s and %2$s -> %4$s",
what,
common,
current,
old
)
);
}
}
}

View File

@ -15,9 +15,180 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package ru.windcorp.progressia; package ru.windcorp.progressia;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
/**
* A class providing access to build metadata.
*/
public class Progressia { public class Progressia {
private static final String NAME = "Progressia";
private static String version;
private static String gitCommit;
private static String gitBranch;
private static String buildId;
static {
try {
Manifest manifest = findManifest();
if (manifest == null) {
setDevelopmentMetadata();
LogManager.getLogger().info(
"Manifest with Specification-Title not found. "
+ "Either you are in a development environment or something has gone horribly wrong with classloaders."
);
} else {
fillMetadata(manifest);
}
} catch (Throwable t) {
CrashReports.crash(t, "Something went wrong while loading metadata");
}
}
private static Manifest findManifest() {
try {
Enumeration<URL> resources = Progressia.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
Collection<IOException> exceptions = new ArrayList<>();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
try {
Manifest manifest = new Manifest(url.openStream());
Attributes mainAttributes = manifest.getMainAttributes();
if (NAME.equals(mainAttributes.getValue("Specification-Title"))) {
return manifest;
}
} catch (IOException e) {
exceptions.add(e);
}
}
if (exceptions.isEmpty()) {
return null;
}
IOException scapegoat = null;
for (IOException e : exceptions) {
if (scapegoat == null) {
scapegoat = e;
} else {
scapegoat.addSuppressed(e);
}
}
throw CrashReports.report(scapegoat, "Could not read manifest");
} catch (IOException e) {
throw CrashReports.report(e, "Could not read manifest");
}
}
private static void setDevelopmentMetadata() {
version = "dev";
gitCommit = "-";
gitBranch = "-";
buildId = "-";
}
private static void fillMetadata(Manifest manifest) {
version = getAttributeOrCrash(manifest, "Implementation-Version");
gitCommit = getAttributeOrCrash(manifest, "Implementation-Version-Git-Commit");
gitBranch = getAttributeOrCrash(manifest, "Implementation-Version-Git-Branch");
buildId = getAttributeOrCrash(manifest, "Implementation-Version-BuildId");
}
private static String getAttributeOrCrash(Manifest manifest, String key) {
String result = manifest.getMainAttributes().getValue(key);
if (result == null) {
throw CrashReports.report(null, "Manifest exists but attribute " + key + " not found");
}
return result;
}
public static String getName() {
return NAME;
}
/**
* Returns the version of the game as a String. Version data is retrieved
* from a {@code META-INF/MANIFEST.MF} file located in the main JAR. Version
* format depends on way the game was built:
* <ul>
* <li><code>dev</code> if no matching manifest was found, e.g. when launching from an IDE</li>
* <li>The value of <code>Implementation-Version</code> specified in the manifest:
* <ul>
* <li>[Stage-]Major.Minor.Patch, e.g. <code>alpha-0.3.2</code> or <code>1.4.2</code>, for released versions</li>
* <li>BuildId, e.g. <code>WJ7</code>, for snapshots built by automation systems</li>
* <li>YYYY-MM-DD, e.g. <code>2021-12-32</code>, for snapshots built manually</li>
* </ul>
* </li>
* </ul>
*
* @return the version
*/
public static String getVersion() {
return version;
}
public static String getFullerVersion() {
if (isDefaultGitBranch() || "-".equals(gitBranch)) {
return version;
} else {
return String.format("%s/%s", version, gitBranch);
}
}
/**
* @return the buildId or <code>"-"</code>
*/
public static String getBuildId() {
return buildId;
}
/**
* @return the Git commit or <code>"-"</code>
*/
public static String getGitCommit() {
return gitCommit;
}
public static String getGitCommitShort() {
if (gitCommit == null || "-".equals(gitCommit)) {
return gitCommit;
}
return gitCommit.substring(0, Math.min(7, gitCommit.length()));
}
/**
* @return the Git branch or <code>"-"</code>
*/
public static String getGitBranch() {
return gitBranch;
}
public static boolean isDefaultGitBranch() {
return "master".equals(gitBranch) || "main".equals(gitBranch);
}
public static String getFullVersion() {
return String.format("%s/%s/%s/%s", version, gitBranch, getGitCommitShort(), buildId);
}
} }

View File

@ -18,6 +18,8 @@
package ru.windcorp.progressia; package ru.windcorp.progressia;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer; import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
@ -32,6 +34,8 @@ public class ProgressiaLauncher {
public static void launch(String[] args, Proxy proxy) { public static void launch(String[] args, Proxy proxy) {
arguments = args.clone(); arguments = args.clone();
setupCrashReports(); setupCrashReports();
LogManager.getRootLogger().info("Launching " + Progressia.getName() + " version " + Progressia.getFullVersion());
proxy.initialize(); proxy.initialize();
ProgressiaLauncher.proxy = proxy; ProgressiaLauncher.proxy = proxy;
@ -44,6 +48,7 @@ public class ProgressiaLauncher {
private static void setupCrashReports() { private static void setupCrashReports() {
// Context providers // Context providers
CrashReports.registerProvider(new VersionProvider());
CrashReports.registerProvider(new OSContextProvider()); CrashReports.registerProvider(new OSContextProvider());
CrashReports.registerProvider(new RAMContextProvider()); CrashReports.registerProvider(new RAMContextProvider());
CrashReports.registerProvider(new JavaVersionContextProvider()); CrashReports.registerProvider(new JavaVersionContextProvider());

View File

@ -25,6 +25,7 @@ import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram; import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader; import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader;
import ru.windcorp.progressia.client.graphics.font.Typefaces; import ru.windcorp.progressia.client.graphics.font.Typefaces;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.texture.Atlases; import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.localization.Localizer; import ru.windcorp.progressia.client.localization.Localizer;
@ -50,7 +51,8 @@ public class ClientProxy implements Proxy {
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw CrashReports.report(e, "ClientProxy failed"); throw CrashReports.report(e, "ClientProxy failed");
} }
ColorScheme.load(ResourceManager.getResource("assets/default.colorScheme"));
Localizer.getInstance().setLanguage("en-US"); Localizer.getInstance().setLanguage("en-US");
TestContent.registerContent(); TestContent.registerContent();

View File

@ -34,13 +34,7 @@ public class Colors {
DEBUG_BLUE = toVector(0xFF0000FF), DEBUG_BLUE = toVector(0xFF0000FF),
DEBUG_CYAN = toVector(0xFF00FFFF), DEBUG_CYAN = toVector(0xFF00FFFF),
DEBUG_MAGENTA = toVector(0xFFFF00FF), DEBUG_MAGENTA = toVector(0xFFFF00FF),
DEBUG_YELLOW = toVector(0xFFFFFF00), DEBUG_YELLOW = toVector(0xFFFFFF00);
LIGHT_GRAY = toVector(0xFFCBCBD0),
BLUE = toVector(0xFF37A2E6),
HOVER_BLUE = toVector(0xFFC3E4F7),
DISABLED_GRAY = toVector(0xFFE5E5E5),
DISABLED_BLUE = toVector(0xFFB2D8ED);
public static Vec4 toVector(int argb) { public static Vec4 toVector(int argb) {
return toVector(argb, new Vec4()); return toVector(argb, new Vec4());

View File

@ -0,0 +1,56 @@
/*
* Progressia
* Copyright (C) 2020-2022 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.backend;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import ru.windcorp.jputil.ConstantsMapException;
import ru.windcorp.jputil.IntConstantsMap;
import ru.windcorp.progressia.common.util.crash.CrashReports;
public class GLFWErrorHandler {
private static final IntConstantsMap ERROR_CODES;
static {
try {
ERROR_CODES = IntConstantsMap.from(GLFW.class)
.stripPrefix("GLFW_")
.onlyValued(i -> i >= 0x10000 && i <= 0x1FFFF)
.extra("GLFW_NO_ERROR")
.scan();
} catch (ConstantsMapException e) {
throw CrashReports.report(e, "Could not analyze GLFW error codes");
}
}
public void onError(int errorCode, long descriptionPointer) {
String description = GLFWErrorCallback.getDescription(descriptionPointer);
String errorCodeName;
if (ERROR_CODES.hasConstant(errorCode)) {
errorCodeName = ERROR_CODES.getName(errorCode);
} else {
errorCodeName = "<unknown " + Integer.toHexString(errorCode) + ">";
}
throw CrashReports.report(null, "GLFW error detected: " + errorCodeName + " %s", description);
}
}

View File

@ -15,20 +15,31 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package ru.windcorp.progressia.client.graphics.backend; package ru.windcorp.progressia.client.graphics.backend;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.*; import static org.lwjgl.system.MemoryUtil.*;
import java.io.IOException;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.Progressia;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent; import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
import ru.windcorp.progressia.common.resource.Resource;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
class LWJGLInitializer { class LWJGLInitializer {
@ -45,6 +56,7 @@ class LWJGLInitializer {
setupWindowCallbacks(); setupWindowCallbacks();
glfwShowWindow(GraphicsBackend.getWindowHandle()); glfwShowWindow(GraphicsBackend.getWindowHandle());
GraphicsBackend.onFrameResized(GraphicsBackend.getWindowHandle(), 800, 600);
} }
private static void checkEnvironment() { private static void checkEnvironment() {
@ -52,8 +64,12 @@ class LWJGLInitializer {
} }
private static void initializeGLFW() { private static void initializeGLFW() {
// TODO Do GLFW error handling: check glfwInit, setup error callback GLFWErrorCallback.create(new GLFWErrorHandler()::onError).set();
glfwInit();
if (!glfwInit()) {
throw CrashReports.report(null, "GLFW could not be initialized: glfwInit() has failed");
}
GraphicsBackend.setGLFWInitialized(true); GraphicsBackend.setGLFWInitialized(true);
} }
@ -61,16 +77,18 @@ class LWJGLInitializer {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
long handle = glfwCreateWindow(900, 900, "ProgressiaTest", NULL, NULL); String windowTitle = Progressia.getName() + " " + Progressia.getFullerVersion();
long handle = glfwCreateWindow(800, 600, windowTitle, NULL, NULL);
// TODO Check that handle != NULL
if (handle == 0) {
throw CrashReports.report(null, "Could not create game window");
}
GraphicsBackend.setWindowHandle(handle); GraphicsBackend.setWindowHandle(handle);
glfwMakeContextCurrent(handle); glfwMakeContextCurrent(handle);
glfwSwapInterval(0); // TODO: remove after config system is added glfwSwapInterval(0); // TODO: remove after config system is added
} }
private static void positionWindow() { private static void positionWindow() {
@ -79,8 +97,30 @@ class LWJGLInitializer {
} }
private static void createWindowIcons() { private static void createWindowIcons() {
// TODO Auto-generated method stub if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) {
// Wayland does not support changing window icons
return;
}
final String prefix = "assets/icons/";
String[] sizes = ResourceManager.getResource(prefix + "logoSizes.txt").readAsString().split(" ");
try (GLFWImage.Buffer buffer = GLFWImage.malloc(sizes.length)) {
for (int i = 0; i < sizes.length; ++i) {
Resource resource = ResourceManager.getResource(prefix + "logo" + sizes[i].trim() + ".png");
TextureDataEditor icon = TextureLoader.loadPixels(resource, new TextureSettings(false, true));
buffer.position(i)
.width(icon.getContentWidth())
.height(icon.getContentHeight())
.pixels(icon.getData().getData());
}
glfwSetWindowIcon(GraphicsBackend.getWindowHandle(), buffer);
} catch (IOException e) {
throw CrashReports.report(e, "Could not load window icons");
}
} }
private static void initializeOpenGL() { private static void initializeOpenGL() {
@ -112,19 +152,19 @@ class LWJGLInitializer {
glfwSetScrollCallback(handle, InputHandler::handleWheelScroll); glfwSetScrollCallback(handle, InputHandler::handleWheelScroll);
GraphicsInterface.subscribeToInputEvents(new Object() { GraphicsInterface.subscribeToInputEvents(new Object() {
@Subscribe @Subscribe
public void onFrameResized(FrameResizeEvent event) { public void onFrameResized(FrameResizeEvent event) {
GUI.invalidateEverything(); GUI.invalidateEverything();
} }
@Subscribe @Subscribe
public void onInputEvent(InputEvent event) { public void onInputEvent(InputEvent event) {
GUI.dispatchInput(event); GUI.dispatchInput(event);
} }
}); });
} }
} }

View File

@ -45,7 +45,7 @@ public class GNUUnifontLoader {
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12); private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false); private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false, false);
private static final int BITS_PER_HEX_DIGIT = 4; private static final int BITS_PER_HEX_DIGIT = 4;
private static final int PREFIX_LENGTH = "0000:".length(); private static final int PREFIX_LENGTH = "0000:".length();

View File

@ -18,10 +18,8 @@
package ru.windcorp.progressia.client.graphics.gui; package ru.windcorp.progressia.client.graphics.gui;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.Colors;
public class Button extends BasicButton { public class Button extends BasicButton {
@ -39,45 +37,26 @@ public class Button extends BasicButton {
@Override @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
// Border String state;
if (!isEnabled()) {
Vec4 borderColor; state = "Disabled";
if (isPressed() || isHovered() || isFocused()) { } else if (isPressed()) {
borderColor = Colors.BLUE; state = "Pressed";
} else if (isHovered()) {
state = "Hovered";
} else if (isFocused()) {
state = "Focused";
} else { } else {
borderColor = Colors.LIGHT_GRAY; state = "Inactive";
} }
target.fill(getX(), getY(), getWidth(), getHeight(), borderColor);
// Border
target.fill(getX(), getY(), getWidth(), getHeight(), ColorScheme.get("Core:ButtonBorder" + state));
// Inside area // Inside area
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, ColorScheme.get("Core:ButtonFill" + state));
if (isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (isHovered() && isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, backgroundColor);
}
// Change label font color // Change label font color
getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:ButtonText" + state)));
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
} }
} }

View File

@ -18,8 +18,6 @@
package ru.windcorp.progressia.client.graphics.gui; package ru.windcorp.progressia.client.graphics.gui;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces; import ru.windcorp.progressia.client.graphics.font.Typefaces;
@ -42,35 +40,29 @@ public class Checkbox extends BasicButton {
int size = getPreferredSize().x; int size = getPreferredSize().x;
int x = getX(); int x = getX();
int y = getY() + (getHeight() - size) / 2; int y = getY() + (getHeight() - size) / 2;
// Border String state;
if (!Checkbox.this.isEnabled()) {
Vec4 borderColor; state = "Disabled";
if (Checkbox.this.isPressed() || Checkbox.this.isHovered() || Checkbox.this.isFocused()) { } else if (Checkbox.this.isPressed()) {
borderColor = Colors.BLUE; state = "Pressed";
} else if (Checkbox.this.isHovered()) {
state = "Hovered";
} else if (Checkbox.this.isFocused()) {
state = "Focused";
} else { } else {
borderColor = Colors.LIGHT_GRAY; state = "Inactive";
} }
target.fill(x, y, size, size, borderColor);
// Border
target.fill(x, y, size, size, ColorScheme.get("Core:CheckboxBorder" + state));
// Inside area // Inside area
target.fill(x + 2, y + 2, size - 4, size - 4, ColorScheme.get("Core:CheckboxFill" + state));
if (Checkbox.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (Checkbox.this.isHovered() && Checkbox.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
target.fill(x + 2, y + 2, size - 4, size - 4, backgroundColor);
}
// "Tick" // "Tick"
if (Checkbox.this.isChecked()) { if (Checkbox.this.isChecked()) {
target.fill(x + 4, y + 4, size - 8, size - 8, Colors.BLUE); target.fill(x + 4, y + 4, size - 8, size - 8, ColorScheme.get("Core:CheckboxCheck" + state));
} }
} }
@ -128,22 +120,21 @@ public class Checkbox extends BasicButton {
@Override @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
// Change label font color String state;
if (!Checkbox.this.isEnabled()) {
if (isPressed()) { state = "Disabled";
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE)); } else if (Checkbox.this.isPressed()) {
state = "Pressed";
} else if (Checkbox.this.isHovered()) {
state = "Hovered";
} else if (Checkbox.this.isFocused()) {
state = "Focused";
} else { } else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); state = "Inactive";
} }
// Change label font color
getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:CheckboxText" + state)));
} }
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
}
} }

View File

@ -0,0 +1,145 @@
/*
* Progressia
* Copyright (C) 2020-2022 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.gui;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import glm.vec._4.Vec4;
import ru.windcorp.jputil.SyntaxException;
import ru.windcorp.jputil.chars.StringUtil;
import ru.windcorp.progressia.common.resource.Resource;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.namespaces.IllegalIdException;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class ColorScheme {
private static Map<String, Vec4> defaultScheme;
public static Vec4 get(String key) {
Vec4 color = defaultScheme.get(key);
if (color == null) {
throw CrashReports.report(null, "ColorScheme does not contain color %s", key);
}
return color;
}
public static void load(Resource resource) {
Map<String, Vec4> tmpMap = new HashMap<>();
try {
for (String fullLine : StringUtil.split(resource.readAsString(), '\n')) {
String line = fullLine.trim();
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
String[] parts = StringUtil.split(line, '=', 2);
if (parts[1] == null) {
throw new SyntaxException("Could not parse \"" + line + "\": '=' not found");
}
String key = parts[0].trim();
checkKeyName(key);
if (tmpMap.containsKey(key)) {
throw new SyntaxException("Duplicate key " + key);
}
String value = parts[1].trim();
Vec4 color;
if (value.startsWith("#")) {
color = parseValueAsHex(value);
} else if (value.startsWith("$")) {
color = parseValueAsReference(value, tmpMap);
} else {
throw new SyntaxException("Unknown value format \"" + value + "\"");
}
tmpMap.put(key, color);
}
} catch (SyntaxException e) {
throw CrashReports.report(e, "Could not load ColorScheme from %s", resource);
}
defaultScheme = ImmutableMap.copyOf(tmpMap);
}
private static void checkKeyName(String key) throws SyntaxException {
try {
NamespacedUtil.checkId(key);
} catch (IllegalIdException e) {
throw new SyntaxException("Illegal key name \"" + key + "\"", e);
}
}
private static int parseHex(String from, int start, int length) {
return Integer.parseInt(from.substring(start, start + length), 0x10);
}
private static Vec4 parseValueAsHex(String value) throws SyntaxException {
int a = 0xFF;
int r, g, b;
switch (value.length() - 1) {
case 3:
// #RGB
r = parseHex(value, 1, 1) * 0x11;
g = parseHex(value, 2, 1) * 0x11;
b = parseHex(value, 3, 1) * 0x11;
break;
case 4:
// #ARGB
a = parseHex(value, 1, 1) * 0x11;
r = parseHex(value, 2, 1) * 0x11;
g = parseHex(value, 3, 1) * 0x11;
b = parseHex(value, 4, 1) * 0x11;
break;
case 6:
// #RRGGBB
r = parseHex(value, 1, 2);
g = parseHex(value, 3, 2);
b = parseHex(value, 5, 2);
break;
case 8:
// #AARRGGBB
a = parseHex(value, 1, 2);
r = parseHex(value, 3, 2);
g = parseHex(value, 5, 2);
b = parseHex(value, 7, 2);
break;
default:
throw new SyntaxException("Could not parse hex color \"" + value + "\": expecting #RGB, #ARGB, #RRGGBB or #AARRGGBB");
}
return new Vec4(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}
private static Vec4 parseValueAsReference(String value, Map<String, Vec4> map) throws SyntaxException {
String otherKey = value.substring(1);
checkKeyName(otherKey);
if (!map.containsKey(otherKey)) {
throw new SyntaxException("Key $" + otherKey + " not found");
}
return map.get(otherKey);
}
}

View File

@ -20,7 +20,6 @@ package ru.windcorp.progressia.client.graphics.gui;
import java.util.Objects; import java.util.Objects;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
public class Panel extends Group { public class Panel extends Group {
@ -36,7 +35,7 @@ public class Panel extends Group {
} }
public Panel(String name, Layout layout) { public Panel(String name, Layout layout) {
this(name, layout, Colors.WHITE, Colors.LIGHT_GRAY); this(name, layout, ColorScheme.get("Core:Background"), ColorScheme.get("Core:Border"));
} }
/** /**

View File

@ -21,7 +21,6 @@ import org.lwjgl.glfw.GLFW;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces; import ru.windcorp.progressia.client.graphics.font.Typefaces;
@ -51,35 +50,29 @@ public class RadioButton extends BasicButton {
int size = getPreferredSize().x; int size = getPreferredSize().x;
int x = getX(); int x = getX();
int y = getY() + (getHeight() - size) / 2; int y = getY() + (getHeight() - size) / 2;
String state;
if (!RadioButton.this.isEnabled()) {
state = "Disabled";
} else if (RadioButton.this.isPressed()) {
state = "Pressed";
} else if (RadioButton.this.isHovered()) {
state = "Hovered";
} else if (RadioButton.this.isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Border // Border
cross(target, x, y, size, ColorScheme.get("Core:RadioButtonBorder" + state));
Vec4 borderColor;
if (RadioButton.this.isPressed() || RadioButton.this.isHovered() || RadioButton.this.isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
}
cross(target, x, y, size, borderColor);
// Inside area // Inside area
cross(target, x + 2, y + 2, size - 4, ColorScheme.get("Core:RadioButtonFill" + state));
if (RadioButton.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (RadioButton.this.isHovered() && RadioButton.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
cross(target, x + 2, y + 2, size - 4, backgroundColor);
}
// "Tick" // "Tick"
if (RadioButton.this.isChecked()) { if (RadioButton.this.isChecked()) {
cross(target, x + 4, y + 4, size - 8, Colors.BLUE); cross(target, x + 4, y + 4, size - 8, ColorScheme.get("Core:RadioButtonCheck" + state));
} }
} }
@ -146,8 +139,8 @@ public class RadioButton extends BasicButton {
group.selectNext(); group.selectNext();
removeAction(group.listener); removeAction(group.listener);
group.buttons.remove(this); group.buttons.remove(this);
group.getSelected(); // Clear reference if this was the only button // Clear reference if this was the only button in the group
// in the group group.getSelected();
} }
this.group = group; this.group = group;
@ -183,22 +176,21 @@ public class RadioButton extends BasicButton {
@Override @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
// Change label font color String state;
if (!RadioButton.this.isEnabled()) {
if (isPressed()) { state = "Disabled";
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE)); } else if (RadioButton.this.isPressed()) {
state = "Pressed";
} else if (RadioButton.this.isHovered()) {
state = "Hovered";
} else if (RadioButton.this.isFocused()) {
state = "Focused";
} else { } else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); state = "Inactive";
} }
}
@Override // Change label font color
protected void postAssembleSelf(RenderTarget target) { getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:RadioButtonText" + state)));
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
} }
} }

View File

@ -20,9 +20,9 @@ package ru.windcorp.progressia.client.graphics.gui.menu;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.gui.Component; import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.GUILayer; import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.Label;
@ -50,7 +50,7 @@ public class MenuLayer extends GUILayer {
setCursorPolicy(CursorPolicy.REQUIRE); setCursorPolicy(CursorPolicy.REQUIRE);
this.background = new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null); this.background = new Panel(name + ".Background", new LayoutAlign(10), ColorScheme.get("Core:MenuLayerTint"), null);
this.content = content; this.content = content;
background.addChild(content); background.addChild(content);
@ -76,12 +76,12 @@ public class MenuLayer extends GUILayer {
protected void addTitle() { protected void addTitle() {
String translationKey = "Layer" + getName() + ".Title"; String translationKey = "Layer" + getName() + ".Title";
MutableString titleText = new MutableStringLocalized(translationKey); MutableString titleText = new MutableStringLocalized(translationKey);
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f); Font titleFont = new Font().deriveBold().withColor(ColorScheme.get("Core:Text")).withAlign(0.5f);
Label label = new Label(getName() + ".Title", titleFont, titleText); Label label = new Label(getName() + ".Title", titleFont, titleText);
getContent().addChild(label); getContent().addChild(label);
Panel panel = new Panel(getName() + ".Title.Underscore", null, Colors.BLUE, null); Panel panel = new Panel(getName() + ".Title.Underscore", null, ColorScheme.get("Core:Accent"), null);
panel.setLayout(new LayoutFill() { panel.setLayout(new LayoutFill() {
@Override @Override
public Vec2i calculatePreferredSize(Component c) { public Vec2i calculatePreferredSize(Component c) {

View File

@ -163,7 +163,7 @@ public class Atlases {
} }
} }
private static final TextureSettings SETTINGS = new TextureSettings(false); private static final TextureSettings SETTINGS = new TextureSettings(false, false);
private static final Map<Resource, Sprite> LOADED = new HashMap<>(); private static final Map<Resource, Sprite> LOADED = new HashMap<>();
private static final Multimap<AtlasGroup, Atlas> ATLASES = MultimapBuilder.hashKeys().arrayListValues().build(); private static final Multimap<AtlasGroup, Atlas> ATLASES = MultimapBuilder.hashKeys().arrayListValues().build();

View File

@ -28,7 +28,7 @@ import ru.windcorp.progressia.common.util.crash.CrashReports;
public class SimpleTextures { public class SimpleTextures {
private static final TextureSettings SETTINGS = new TextureSettings(false); private static final TextureSettings SETTINGS = new TextureSettings(false, false);
private static final Map<Resource, Texture> TEXTURES = new HashMap<>(); private static final Map<Resource, Texture> TEXTURES = new HashMap<>();

View File

@ -23,7 +23,7 @@ import static org.lwjgl.opengl.GL12.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
class TextureData { public class TextureData {
private final ByteBuffer data; private final ByteBuffer data;

View File

@ -41,9 +41,17 @@ public class TextureLoader {
int width = readResult.getWidth(); int width = readResult.getWidth();
int height = readResult.getHeight(); int height = readResult.getHeight();
int bufferWidth;
int bufferHeight;
int bufferWidth = BinUtil.roundToGreaterPowerOf2(width); if (settings.allocateExactBuffer()) {
int bufferHeight = BinUtil.roundToGreaterPowerOf2(height); bufferWidth = width;
bufferHeight = height;
} else {
bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
}
WritableRaster raster = TextureUtil.createRaster( WritableRaster raster = TextureUtil.createRaster(
bufferWidth, bufferWidth,

View File

@ -21,13 +21,19 @@ package ru.windcorp.progressia.client.graphics.texture;
public class TextureSettings { public class TextureSettings {
private final boolean isFiltered; private final boolean isFiltered;
private final boolean allocateExactBuffer;
public TextureSettings(boolean isFiltered) { public TextureSettings(boolean isFiltered, boolean allocateExactBuffer) {
this.isFiltered = isFiltered; this.isFiltered = isFiltered;
this.allocateExactBuffer = allocateExactBuffer;
} }
public boolean isFiltered() { public boolean isFiltered() {
return isFiltered; return isFiltered;
} }
public boolean allocateExactBuffer() {
return allocateExactBuffer;
}
} }

View File

@ -42,7 +42,7 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRende
ResourceManager.getTextureResource( ResourceManager.getTextureResource(
"entities/" + name "entities/" + name
), ),
new TextureSettings(false) new TextureSettings(false, false)
).getData() ).getData()
); );
} catch (IOException e) { } catch (IOException e) {

View File

@ -0,0 +1,40 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.util.crash.providers;
import java.util.Map;
import ru.windcorp.progressia.Progressia;
import ru.windcorp.progressia.common.util.crash.ContextProvider;
public class VersionProvider implements ContextProvider {
@Override
public void provideContext(Map<String, String> output) {
output.put("Version", Progressia.getVersion());
output.put("Git commit", Progressia.getGitCommit());
output.put("Git branch", Progressia.getGitBranch());
output.put("Build ID", Progressia.getBuildId());
}
@Override
public String getName() {
return "Version Provider";
}
}

View File

@ -18,9 +18,11 @@
package ru.windcorp.progressia.test; package ru.windcorp.progressia.test;
import ru.windcorp.progressia.Progressia;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typeface; import ru.windcorp.progressia.client.graphics.font.Typeface;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.gui.GUILayer; import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Group; import ru.windcorp.progressia.client.graphics.gui.Group;
@ -36,7 +38,7 @@ public class LayerAbout extends GUILayer {
Group group = new Group("ControlDisplays", new LayoutVertical(5)); Group group = new Group("ControlDisplays", new LayoutVertical(5));
Font font = new Font().withColor(Colors.WHITE).deriveOutlined().withAlign(Typeface.ALIGN_RIGHT); Font font = new Font().withColor(Colors.WHITE).deriveOutlined().withAlign(Typeface.ALIGN_RIGHT);
Font aboutFont = font.withColor(0xFF37A3E6).deriveBold(); Font aboutFont = font.withColor(ColorScheme.get("Core:Accent")).deriveBold();
group.addChild( group.addChild(
new Label( new Label(
@ -50,7 +52,7 @@ public class LayerAbout extends GUILayer {
new Label( new Label(
"Version", "Version",
font, font,
new MutableStringLocalized("LayerAbout.Version").format("pre-alpha 3") new MutableStringLocalized("LayerAbout.Version").format(Progressia.getFullerVersion())
) )
); );

View File

@ -20,11 +20,11 @@ package ru.windcorp.progressia.test;
import java.util.Collection; import java.util.Collection;
import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.Button; import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.Checkbox; import ru.windcorp.progressia.client.graphics.gui.Checkbox;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.RadioButton; import ru.windcorp.progressia.client.graphics.gui.RadioButton;
import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup; import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup;
@ -65,7 +65,7 @@ public class LayerButtonTest extends MenuLayer {
getContent().getChild(getContent().getChildren().size() - 1).setEnabled(false); getContent().getChild(getContent().getChildren().size() - 1).setEnabled(false);
getContent().addChild(new Label("Hint", new Font().withColor(Colors.LIGHT_GRAY), "This is a MenuLayer")); getContent().addChild(new Label("Hint", new Font().withColor(ColorScheme.get("Core:Border")), "This is a MenuLayer"));
getContent().addChild(new Button("Continue", "Continue").addAction(b -> { getContent().addChild(new Button("Continue", "Continue").addAction(b -> {
getCloseAction().run(); getCloseAction().run();

View File

@ -1,11 +1,13 @@
package ru.windcorp.progressia.test; package ru.windcorp.progressia.test;
import java.util.function.Consumer; import java.util.function.Consumer;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.gui.GUILayer; import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.localization.MutableString; import ru.windcorp.progressia.client.localization.MutableString;
public class LayerTestText extends GUILayer { public class LayerTestText extends GUILayer {
@ -13,11 +15,13 @@ public class LayerTestText extends GUILayer {
private final Consumer<LayerTestText> remover; private final Consumer<LayerTestText> remover;
public LayerTestText(String name, MutableString value, Consumer<LayerTestText> remover) { public LayerTestText(String name, MutableString value, Consumer<LayerTestText> remover) {
super(name, new LayoutAlign(15)); super(name, new LayoutFill());
this.remover = remover; this.remover = remover;
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f); Panel panel = new Panel(name + ".Background", new LayoutAlign(15), ColorScheme.get("Core:Background"), null);
getRoot().addChild(new Label(name + ".Text", titleFont, value)); Font titleFont = new Font().deriveBold().withColor(ColorScheme.get("Core:Text")).withAlign(0.5f);
panel.addChild(new Label(name + ".Text", titleFont, value));
getRoot().addChild(panel);
} }
@Override @Override

View File

@ -0,0 +1,80 @@
# Default color scheme for Progressia
#
# Line format:
# KEY = VALUE
# KEY must be a legal ID
# VALUE may be one of the following:
# #RGB
# #ARGB
# #RRGGBB
# #AARRGGBB
# $OTHER_KEY
Core:Background = #EFF0F1
Core:Border = #CBCBD0
Core:DisabledBackground = $Core:Background
Core:DisabledBorder = #DEDFE1
Core:Accent = #37A3E6
Core:AccentLighter = #C3E4F7
Core:Text = #31363B
Core:ButtonBorderDisabled = $Core:DisabledBorder
Core:ButtonBorderPressed = $Core:Accent
Core:ButtonBorderHovered = $Core:Accent
Core:ButtonBorderFocused = $Core:Accent
Core:ButtonBorderInactive = $Core:Border
Core:ButtonFillDisabled = $Core:DisabledBackground
Core:ButtonFillPressed = $Core:Accent
Core:ButtonFillHovered = $Core:AccentLighter
Core:ButtonFillFocused = $Core:Background
Core:ButtonFillInactive = $Core:Background
Core:ButtonTextDisabled = $Core:DisabledBorder
Core:ButtonTextPressed = $Core:Background
Core:ButtonTextHovered = $Core:Text
Core:ButtonTextFocused = $Core:Text
Core:ButtonTextInactive = $Core:Text
Core:CheckboxBorderDisabled = $Core:DisabledBorder
Core:CheckboxBorderPressed = $Core:Accent
Core:CheckboxBorderHovered = $Core:Accent
Core:CheckboxBorderFocused = $Core:Accent
Core:CheckboxBorderInactive = $Core:Border
Core:CheckboxFillDisabled = $Core:DisabledBackground
Core:CheckboxFillPressed = $Core:Accent
Core:CheckboxFillHovered = $Core:AccentLighter
Core:CheckboxFillFocused = $Core:Background
Core:CheckboxFillInactive = $Core:Background
Core:CheckboxTextDisabled = $Core:DisabledBorder
Core:CheckboxTextPressed = $Core:Accent
Core:CheckboxTextHovered = $Core:Text
Core:CheckboxTextFocused = $Core:Text
Core:CheckboxTextInactive = $Core:Text
Core:CheckboxCheckDisabled = $Core:DisabledBorder
Core:CheckboxCheckPressed = $Core:Accent
Core:CheckboxCheckHovered = $Core:Accent
Core:CheckboxCheckFocused = $Core:Accent
Core:CheckboxCheckInactive = $Core:Accent
Core:RadioButtonBorderDisabled = $Core:DisabledBorder
Core:RadioButtonBorderPressed = $Core:Accent
Core:RadioButtonBorderHovered = $Core:Accent
Core:RadioButtonBorderFocused = $Core:Accent
Core:RadioButtonBorderInactive = $Core:Border
Core:RadioButtonFillDisabled = $Core:DisabledBackground
Core:RadioButtonFillPressed = $Core:Accent
Core:RadioButtonFillHovered = $Core:AccentLighter
Core:RadioButtonFillFocused = $Core:Background
Core:RadioButtonFillInactive = $Core:Background
Core:RadioButtonTextDisabled = $Core:DisabledBorder
Core:RadioButtonTextPressed = $Core:Accent
Core:RadioButtonTextHovered = $Core:Text
Core:RadioButtonTextFocused = $Core:Text
Core:RadioButtonTextInactive = $Core:Text
Core:RadioButtonCheckDisabled = $Core:DisabledBorder
Core:RadioButtonCheckPressed = $Core:Accent
Core:RadioButtonCheckHovered = $Core:Accent
Core:RadioButtonCheckFocused = $Core:Accent
Core:RadioButtonCheckInactive = $Core:Accent
Core:MenuLayerTint = #6000

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
16 20 22 24 32 40 48 64 96 128 256

View File

@ -1,8 +1,8 @@
Package: progressia-techdemo Package: progressia
Version: 1.0 Version: ${version}
Section: custom Section: custom
Priority: optional Priority: optional
Architecture: all Architecture: all
Maintainer: Javapony <kvadropups@gmail.com> Maintainer: Javapony <kvadropups@gmail.com>
Depends: java8-runtime Depends: java8-runtime
Description: Progressia Techdemo release Description: Progressia - a 3D sandbox survival game

View File

@ -1,161 +1,164 @@
;NSIS Modern User Interface ;NSIS Modern User Interface
;Welcome/Finish Page Example Script ;Welcome/Finish Page Example Script
;Written by Joost Verburg ;Written by Joost Verburg
;-------------------------------- ;--------------------------------
;Include Modern UI ;Include Modern UI
!include "MUI2.nsh" !include "MUI2.nsh"
;-------------------------------- ;--------------------------------
;General ;General
!define PROJECT_NAME "Progressia" ; Expecting the following symbols from caller:
; PROJECT_NAME
; MUI Settings / Icons ; PROJECT_VERSION
!define MUI_ICON "logo.ico" ; MAIN_JAR_FILE
;!define MUI_UNICON ;Uninstall icon
; MUI Settings / Icons
; MUI Settings / Header !define MUI_ICON "logo.ico"
; !define MUI_HEADERIMAGE ;!define MUI_UNICON ;Uninstall icon
; !define MUI_HEADERIMAGE_RIGHT
; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp" ; MUI Settings / Header
; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp" ; !define MUI_HEADERIMAGE
; !define MUI_HEADERIMAGE_RIGHT
; MUI Settings / Wizard ; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp" ; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp"
; MUI Settings / Wizard
;Name and file !define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp"
Name "${PROJECT_NAME}" !define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp"
OutFile "${PROJECT_NAME}Installer.exe"
Unicode True ;Name and file
Name "${PROJECT_NAME}"
;Default installation folder OutFile "${OUTPUT_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}-installer.exe"
InstallDir "$PROGRAMFILES\${PROJECT_NAME}" Unicode True
;Get installation folder from registry if available ;Default installation folder
InstallDirRegKey HKLM "Software\${PROJECT_NAME}" "" InstallDir "$PROGRAMFILES\${PROJECT_NAME}"
;Request application privileges for Windows Vista ;Get installation folder from registry if available
RequestExecutionLevel admin InstallDirRegKey HKLM "Software\${PROJECT_NAME}" ""
;-------------------------------- ;Request application privileges for Windows Vista
;Interface Settings RequestExecutionLevel admin
!define MUI_ABORTWARNING ;--------------------------------
;Interface Settings
;--------------------------------
;Pages !define MUI_ABORTWARNING
!insertmacro MUI_PAGE_WELCOME ;--------------------------------
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt" ;Pages
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
!define MUI_FINISHPAGE_RUN !insertmacro MUI_PAGE_COMPONENTS
!define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}" !insertmacro MUI_PAGE_DIRECTORY
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink" !insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}"
!insertmacro MUI_UNPAGE_WELCOME !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
!insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_COMPONENTS
!insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_COMPONENTS
;-------------------------------- !insertmacro MUI_UNPAGE_INSTFILES
;Languages !insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English" ;--------------------------------
;Languages
;--------------------------------
;Installer Sections !insertmacro MUI_LANGUAGE "English"
Section "Install ${PROJECT_NAME}" SEC0000 ;--------------------------------
;Installer Sections
SectionIn RO ;Make it read-only
SetOutPath "$INSTDIR" Section "Install ${PROJECT_NAME}" SEC0000
SetOverwrite on
SectionIn RO ;Make it read-only
;Files SetOutPath "$INSTDIR"
File Progressia.jar SetOverwrite on
File logo.ico
File /r lib ;Files
File "${MAIN_JAR_FILE}"
;Store installation folder File logo.ico
WriteRegStr HKLM SOFTWARE\Progressia "Install_Dir" "$INSTDIR" File /r lib
;Create uninstaller ;Store installation folder
WriteRegStr HKLM "SOFTWARE\${PROJECT_NAME}" "Install_Dir" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe" ;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
SectionEnd WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
Section "Create Desktop Shortcut" SEC0001
SetOutPath "$APPDATA\${PROJECT_NAME}" SectionEnd
CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
SectionEnd Section "Create Desktop Shortcut" SEC0001
SetOutPath "$APPDATA\${PROJECT_NAME}"
Section "Start Menu Shortcuts" SEC0002 CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico"
SectionEnd
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" Section "Start Menu Shortcuts" SEC0002
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
SectionEnd CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico"
;--------------------------------
;Uninstaller Section SectionEnd
Section "Uninstall" ;--------------------------------
;Uninstaller Section
;ADD YOUR OWN FILES HERE...
Section "Uninstall"
Delete $INSTDIR\Uninstall.exe
Delete $INSTDIR\Progressia.jar ;ADD YOUR OWN FILES HERE...
Delete $INSTDIR\lib\*.*
Delete $INSTDIR\logo.ico Delete $INSTDIR\Uninstall.exe
Delete "$INSTDIR\${MAIN_JAR_FILE}"
RMDir $INSTDIR\lib Delete $INSTDIR\lib\*.*
Delete $INSTDIR\logo.ico
Delete $DESKTOP\${PROJECT_NAME}.lnk
RMDir $INSTDIR\lib
Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk Delete $DESKTOP\${PROJECT_NAME}.lnk
RMDir $INSTDIR Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk
RMDir /r $SMPROGRAMS\${PROJECT_NAME}
RMDir $INSTDIR
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}"
DeleteRegKey HKLM "Software\${PROJECT_NAME}" RMDir /r $SMPROGRAMS\${PROJECT_NAME}
SectionEnd DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}"
DeleteRegKey HKLM "Software\${PROJECT_NAME}"
Section "un.Remove user data"
SectionEnd
RMDir /r "$APPDATA\${PROJECT_NAME}"
Section "un.Remove user data"
SectionEnd
RMDir /r "$APPDATA\${PROJECT_NAME}"
;--------------------------------
;Functions SectionEnd
Function LaunchLink ;--------------------------------
SetOutPath "$APPDATA\${PROJECT_NAME}" ;Functions
ExecShell "" "$INSTDIR\${PROJECT_NAME}.jar"
FunctionEnd Function LaunchLink
SetOutPath "$APPDATA\${PROJECT_NAME}"
;-------------------------------- ExecShell "" "$INSTDIR\${MAIN_JAR_FILE}"
;Descriptions FunctionEnd
;Language strings ;--------------------------------
LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}." ;Descriptions
;Assign language strings to sections ;Language strings
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END ;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,2 @@
@ECHO OFF
java -jar "@mainJarFile@"

11
src/packaging/zip/start.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
jvmFlags=""
case "$OSTYPE" in
"darwin"*)
# On MacOS, use -XstartOnFirstThread to resolve an issue with OpenGL contexts
jvmFlags="$jvmFlags -XstartOnFirstThread"
esac
java $jvmFlags -jar "@mainJarFile@"