77 Commits

Author SHA1 Message Date
opfromthestart
22a744f65a Added Options
-Added options menu
-You can move from options to title and back
-Added language change button
*Double check the Russian, I just copied and pasted from other files
2022-01-08 21:16:51 -05:00
opfromthestart
c6de19cf19 Rebased everything on master
Now has the dynamic version thing
fixed warnings
2022-01-08 13:57:57 -05:00
opfromthestart
c1a57f7d7a Slightly better alignment
The mode for alignment is more concise
2022-01-08 12:45:28 -05: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
92f9639c4b Improved input event handling
- Added Component.passInputToChildren()
- Input events can now be processed in parallel or recursively
2021-12-21 00:31:35 +03:00
f350467942 Removed jCenter(); formatted build.gradle
- Fixed #23
- Fixed whitespace in build.gradle
2021-12-21 00:13:04 +03:00
23b0af1731 Update log4j dependency to version 2.17.0 😡
Fix for CVE-2021-45105
2021-12-19 23:46:30 +03:00
0389f97e59 Update log4j dependency to version 2.16.0
Fix for CVE-2021-45046
2021-12-16 18:09:37 +03:00
706800218c Merge branch 'overhaul-input'
Summary of changes:
- Refactored input handling
- Added noclip/freecam mode for debugging
- Added drag events
- Removed useless info from debug layer
- Movement controls now work in menus (feature? bug? not sure)
2021-12-16 15:17:08 +03:00
ca2c7b58d8 Added drag events 2021-12-16 15:12:49 +03:00
359879b0fe Completed input refactoring
- Moved everything related to controls into controls package
- Moved controls related to placing/breaking stuff into
InteractionControls
- Localizer now provides access to the list of languages
- Fixed a random bug in TestMusicPlayer because why not amirite
2021-12-15 00:20:50 +03:00
a06d8ee056 Fixed temporary player entity data transfer 2021-12-13 20:49:32 +03:00
c8138faabe Refactored camera controls and added noclip mode
- Refactored camera controls into a separate class
- Added noclip mode
  - Toggle with V
  - Simply uncouples camera from character; does not change interactions
or load chunks!
  - Implemented sloppily, expect bugs in the future
- EntityData's .equals() is now equivalent to == and .hashCode() hashes
entity ID only
- Collision can now be disabled for an object by setting its
CollisionModel to null
2021-12-13 19:36:26 +03:00
49d283e7a3 Refactored F3 layer
- Renamed from LayerTestGUI to LayerDebug
- Many of the displays have been removed
- Fullscreen and VSync displays have been merged with FPS display
- Updates are automatic
2021-12-13 17:03:23 +03:00
c93f0df30d Refactored movement controls
- Movement control code is now in its own class
- Movement direction controls are now more bug-resistant
  - No longer event-driven; uses InputTracker instead
  - Temporarily? function with menus open
- Other movement controls are now registered local Controls
- Removed flying and sprinting indicators from F3 layer
- TestPlayerControls is now a singleton
  - resetInstance() now resets the instance's fields
2021-12-13 11:06:28 +03:00
75ea7baf9c Update junit dependency to version 4.13.2
Fix for CVE-2020-15250
2021-12-12 15:41:10 +03:00
c4a9a17c7c Update log4j dependency to version 2.15.0 2021-12-10 11:05:30 +03:00
b4ff5114bd Refactored input event pipeline
- Merged Input functionality into InputEvent; removed Input
  - InputEvents can now be consumed directly
  - Removed Input.Target; functionality moved into InputBus
- Refactored GUI input event handling
  - Optimized and reduced recursion by unrolling recursion
  - All input events are now delivered to all components
    - Filtering occurs at listener level
- Refactored InputBus
  - Listeners may override former Input.Target logic
  - Improved registration method
  - Added exception handling
- Improved KeyMatcher
  - Removed builder in favour of copying with modifications
  - Added String parsing
  - Added KeyMatcher.matchesIgnoringAction()
  - Added integration with InputBus
- Renamed Component.{add,remove}Listener methods about InputListeners to
Component.{add,remove}InputListener to avoid confusion
2021-12-08 18:06:30 +03:00
efc6a8ecd0 Hastily added pine trees and hazel bushes
- Added ShapePrototype wrapper for ShapePart batch creation
- Added pine trees and hazel bushes
  - Leaves with custom block models
  - Improved tree generator
2021-11-19 21:26:04 +03:00
4b10dc82ed Improved formatting and phrasing 2021-10-31 16:19:21 +03:00
opfromthestart
c04894a0c9 Finally made this
I think its pretty good, but I cant think very well rn.
2021-10-24 20:09:44 -04:00
576cfed99f Fixed import warnings 2021-10-17 14:05:40 +03:00
opfromthestart
5af1b7309d Better Alignment
-Created some Layouts to properly adjust where things go
    -LayoutEdges for text on the same row on opposite sides of the screen
    -LayoutColumn for a non-max width column
-More accessible version string
2021-10-13 23:37:18 -04:00
opfromthestart
0f0a94811f Cleaning
-Cleaned up offsets to make it look like a cube and not 6 squares.
-Fixed error where regions would incorrectly reset files
-Some additions to try to match the intended look. I really dont
understand layouts.
2021-09-25 20:45:17 -04:00
28b19c8f35 Added more flowers and more grass types, fixed cross tile render
- Added Clover, Daisy, Dandelion, Geranium, Knapweed, Yellow Pea,
Bluegrass
- Renamed grass herbs and flat flowers
- Cross tile renderer now uses a kostyl that forces face normals to be
oriented vertically
- Refactored flower generation
- Added a coherent error message when a texture could not be found
2021-09-22 23:48:38 +03:00
opfromthestart
51bcca1499 Betterish title screen
-Black background used
	-New Background class for displaying a single texture to the back of
the screen(probably not the best way)
-Title now uses an image
	-Made TextureComponent, I think this probably exists elsewhere but I
couldnt find it
-Cube faces now change color based on where they are facing, looks a lot
like the source material except for rearranging
2021-09-13 13:05:59 -04:00
opfromthestart
ce9e95e5ce Well, its [i]a[/i] cube.
-Added CubeComponent to render a cube to the screen
-Added it to the title screen
2021-09-11 21:11:59 -04:00
eb5aa59941 Removed plain sand block and tile; incremented version
To be honest, I kinda forgot to increment version in last commit, so I
decided to make a pointless change to justify an entire commit =\
2021-09-11 21:12:42 +03:00
409bbdb680 Merge branch 'save-world' 2021-09-11 19:42:36 +03:00
opfromthestart
0c41350ae7 Added file identification header
-All files now start with a 16 byte header consisting of "PROG" followed
by the integer position of the region in region coordinates.
	-regionCoords is passed through some functions to allow for this to be
confirmed.
2021-09-10 22:19:18 -04:00
a633c8324e Added Reset World button and fixed some translations 2021-09-11 00:47:28 +03:00
6b33f231b4 Moved region implementation to .server.world.io.region 2021-09-10 23:59:03 +03:00
e2308b825d Removed warnings and formatted code 2021-09-10 23:31:14 +03:00
opfromthestart
e0f6a08740 Bug fixes
-Now offset technically starts at 1, so any chunks with offset 0 are
ignored.
-Reduced number of unused chunks, storage efficiency is at about 99% (if
null sectors are counted as useful)
2021-09-10 14:14:18 -04:00
opfromthestart
2820e01974 Finished Partition Logic
Note: it still does waste a lot of space, I will work on that next
-Added back the confirmHeaderHealth logic
-Checks to make sure it will not overwrite important chunks
	-Uses PartitionLink chunks to move to a different part of the file
-Added allocateEmptySector() to allow for the file size to be increased
without moving the origin point of the chunk.
2021-09-10 13:51:37 -04:00
opfromthestart
46bcb85044 Region File better????
-Moved most of the file accessing of Region to RegionFile
-Disabled most of the header check except the length check(it will be
back soon)
-Max chunk size arbitrarily raised to 4MiB because I wanted sectors
longer than 16B
-Sectors now have a mandatory 1B header that identifies it
	-0 is a null sector, it ends every chunk
	-1 is a normal data sector, it has a "parity" byte that makes sure it
is reading chunks in linear order(fun fact: it isnt at the moment)
	-2 is a jump to a different location. this isnt implemented well yet
	-3 will be a "bulk data" sector. Multiple chunks with identical data
can point here. Probably only useful when it is easily identifiable,
like multiple chunks being one entire block, like air.
-Removed all chunk length references as I think they do not make sense
when it can use different sectors for non-data purposes.
2021-09-09 19:58:44 -04:00
opfromthestart
c5dfe3d0b7 Saving Modifications
-Safer saving, it waits for the file to stop being used to close(still
has holes)
-If a chunk is too large, it is moved to the end of the file.
	-Calculates the efficiency of each region file in confirmHeaderHealth
-Fixed import warning
2021-09-09 16:52:55 -04:00
a85fc27f8b Added _UNFINISHED_ water, beaches and mantle
- Refactored terrain generation
- Added PiecewiseLinearFunction
- Added some placeholder content
  - Added Test:Water, the solid, opaque water
  - Added beaches
  - Added Test:Mantle
- Tweaked rock distribution parameters
2021-08-31 17:27:08 +03:00
711e4a2bb4 Added formatting templates guide for Intellij IDEA 2021-08-30 19:51:28 +03:00
0100c8791d Added player saving and loading from disk 2021-08-30 18:23:42 +03:00
opfromthestart
e967a64401 More Compact region files
-Sector length increased to a short, the minimum sector size is now 16
bytes
2021-08-30 10:52:56 -04:00
d2ffe1fe0e Fixed the bug that opfromthestart found, reloading now works 2021-08-29 12:04:02 +03:00
f4300558d5 Formatted and broke the saving mechanism. I'm too tired to bugfix
- Refactored and formatted TestWorldDiskIO
- Removed HashableVec3i
- Added Coordinates methods for custom bit count
- Properly reverted commit 98250cd
- Known bugs:
  - Server shutdown close()s regions too early
  - Re-entering a world does not show saved changes
2021-08-29 02:08:19 +03:00
cd16334db8 Moved TestWorldDiskIO to a subpackage and introduced some abstractions 2021-08-28 23:31:50 +03:00
41a2909f7c Reverted last two commits because no one wants to fix the bugs 2021-08-28 21:31:34 +03:00
a222ea8f67 Fixed multithread chunk IO 2021-08-28 21:14:35 +03:00
opfromthestart
98250cd524 Some changes for efficiency(not yet)
-Tried to use threads/executors in ChunkRequestDaemon, it just hangs.
-Added isEmpty and isOpaque attributes to DefaultChunkData (should this
just be in ChunkData?)
	-Added compute and getter functions to access(for everything after
loading)
	-Doesn't render empty chunks(not yet used)
-Using format 65537 allows the empty and opaqueness to be saved. They do
not do anything yet and there is no way to set them yet
	-Added loadRegionX and ".progressia_chunkx" file
-removed formats 0 and 1, which use individual chunk files.
2021-08-27 16:59:05 -04:00
f74c731a3d Fixed GUI buttons and a rare crash on startup 2021-08-27 12:20:58 +03:00
98c383bf7d Fixed a stupid typo in previous commit 2021-08-26 12:11:21 +03:00
dd80df2cf2 Fixed a GUI reassembly issue and added cursor disabling suppression
- Buttons no longer reassemble every frame
- Label.setFont() no longer requests reassembly
- Component.reassembleAt now actually works
- Using a very long flag, cursor capturing can be disabled to facilitate
GUI debugging
2021-08-26 12:09:22 +03:00
opfromthestart
9dcb3a7748 -Better HashableVec3i compare
-Added RandomFileMapped class
	-Made it a wrapper for RandomAccessFile and contains maps of locations
and lengths
-Added confirmHeaderHealth method to made sure the file isnt corrupted
-Changed everything from MappedByteBuffers to RandomFileMappeds.
2021-08-25 16:15:38 -04:00
1727a2a4a1 Added Object fields to StatefulObjects and added some utility methods
- Added ObjectStateField
- Added WorldGenericContextRO.findClosestEntity, .forEachEntity
- Added VectorUtil.lookAt, .distance{,Sq}
2021-08-25 16:47:56 +03:00
20fb8f0597 Fixed missing popTrasnform in Statie render 2021-08-24 15:12:35 +03:00
1d28f32865 Implemented entity spawning and despawning and changed some stuff
- Non-player entities can now be added and removed properly
  - WorldLogic.spawnEntity can be used to add entity and create an
entity ID
- Statie is back, more beautiful than ever!
  - Place Test:StatieSpawner block and wait to make her spawn
- TPS display now features a visual tick indicator
- Updated Fern texture
2021-08-24 13:59:28 +03:00
0ccc108ddd Merge branch 'save-world'
Third time's the charm!
2021-08-24 01:38:22 +03:00
c7e7d3bdac Added ferns, fixed herb render and cleaned up TestContent
- Added Test:Fern
- Herbs are no longer stretched horizontally
- Formatted and cleaned up TestContent
2021-08-23 18:36:15 +03:00
62729f5873 Added packet buffering and fix crash when placing flowers on leaves
- Packets are now buffered before processing to reduce stack depth
- Attempts to place tiles on invalid locations get rejected earlier
2021-08-23 17:37:25 +03:00
84864f8947 Added more grass variants
- Added flat grass tiles with varying opaqueness
- Renamed Test:Grass to Test:GrassOpaque
- Added Chernozem
2021-08-23 15:18:37 +03:00
d01ef3654f Added grasses and flowers
- Added CROSimple to optimize simple non-Surface textures
- Added TileRenderCross
- Added Low, Medium and Tall grass
- Added Blue, Purple and White flat flowers
- Added Bushes
- Added tiny Dandelions and tiny Lavanders
- Improved grass and log textures
2021-08-22 22:18:42 +03:00
opfromthestart
fae09edb16 Fixes n Speed n Stuff
-Better comparison for HashableVec3i
-Changed RandomAccessFile to MappedByteBuffer
-natFromInt now works properly
-better region selection
-Changes to make different strategies work
2021-08-19 10:47:16 -04:00
opfromthestart
e4ced6507e Hash Things
-Removed unused imports
-Using HashMap to keep track of RandomAccessFile instances
-Using those instances to do stuff
-Made new HashableVec3i class that allows for hashing of 3d vectors.
2021-08-18 18:50:48 -04:00
opfromthestart
b7dcbb0f30 Fixing stuff
-Got rid of some annoying logging
-Format
2021-08-18 13:21:54 -04:00
opfromthestart
9c26418354 Well, it works now ig
-Better file lengthening
-Made temp files
    -Exchanged temp files for ByteArrayIn/OutputBuffers
-Fixed loadRegion to actually read sector sizes
2021-08-18 13:15:30 -04:00
opfromthestart
6891d3a095 Trying to get it to work
-Uses many streams to keep track of things
-Fixed some bad things, still are a ton.
2021-08-06 12:56:21 -04:00
opfromthestart
8167c40f64 IDK these errors are weird
-Changed the loading to fit the byte changes
2021-08-05 14:12:37 -04:00
opfromthestart
8bc23acb61 idk changes so I can show OLEGSHA
-changed number of index bytes from 4 to 3
-exposed some variables for help with debugger
2021-08-05 13:39:42 -04:00
opfromthestart
254faca0a5 Better deletion
-Makes new TestPlayerControls instance each time you enter a world
-Some dynamic sizing of chunk data within region files.
2021-08-05 12:29:29 -04:00
opfromthestart
0c66f1751e Made outline of region file
-Improved loading screen loading(so it doesnt crash)
-Implemented region file related things, but it doesnt quite work
    -Uses file header to try to look up location of data
    -Writes the data at the end of the file(I need info to how much it writes)
2021-08-04 19:17:49 -04:00
opfromthestart
c88dea6030 Fixing Bugs mostly
-Cleaning up imports
-Better error detection
-Actual thread deletion(still needs a bit of work to delete all conected objects)
-Starting making format file and inplementing it
2021-08-04 16:57:21 -04:00
opfromthestart
6521cb5749 Better Listeners
-Specific listeners for the start and stop of loading
-Better(?) layer deletion after exiting a world
-Actual server thread shutdown
2021-08-04 12:52:37 -04:00
opfromthestart
94db44e443 Im tired i need sleep
-Added listeners for saving and loading chunks
-Made loading screens for between title and game(they dont work yet)
-Added localized text(some)
-Safeish deletion and saving of chunks
    -It still keeps them in memory I think so this needs work too
2021-08-03 22:42:46 -04:00
opfromthestart
53f72b068a Merge branch 'title-screen' into save-world 2021-08-03 20:08:39 -04:00
opfromthestart
a9ca5f6b17 Everything Excpeti polishing and options
-Added LayerTitle class that is the title menu
-Edited the launcher and proxy so it only starts the game when it needs to
-Made buttons work with MutableString objects
2021-08-03 19:42:04 -04:00
opfromthestart
4ab7cb738e Testing some stuff
-Made functions to convert integers into natural numbers (cause why not)
-Ideas/plans of dynamic/custom region file sizes
2021-08-03 17:53:49 -04:00
552 changed files with 12133 additions and 4980 deletions

2
.gitattributes vendored
View File

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

View File

@@ -1,25 +1,31 @@
/*
* build.gradle for Progressia
* Build logic for Progressia
* build.gradle
*
* Please refer to
*
* docs/building/BuildScriptReference.md
*
* for user reference.
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
/*
* Uncomment the following line to enable the Eclipse plugin.
* This is only necessary if you don't use Buildship plugin from the IDE
*/
//id 'eclipse'
id 'java'
// GrGit
// A JGit wrapper for Groovy used to access git commit info
id 'org.ajoberstar.grgit' version '4.1.1'
}
/*
* Configure Java version
*/
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
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
compileJava {
@@ -29,287 +35,373 @@ compileJava {
* However, on JDK 9 and later versions, '--release' option is required,
* which is missing on JDK 8.
*/
if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
options.compilerArgs.addAll(['--release', '8'])
}
if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
options.compilerArgs.addAll(['--release', '8'])
}
}
/*
* Dependencies
*/
repositories {
mavenCentral()
jcenter()
/*
* Specify Windcorp Maven repository
* Currently used by:
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
*/
maven { url 'https://windcorp.ru/./maven' }
/*
* Windcorp Maven repository
* Currently used by:
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
*/
maven { url 'https://windcorp.ru/./maven' }
}
dependencies {
// Google Guava
// A generic utilities library
implementation 'com.google.guava:guava:30.0-jre'
// Trove4j
// Provides optimized Collections for primitive types
implementation 'net.sf.trove4j:trove4j:3.0.3'
// java-graphics
// A GLM (OpenGL Mathematics) port to Java
// Unfortunately, Maven Central Repository provides an outdated version of this library, which contains several critical bugs
implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1'
implementation 'com.google.guava:guava:30.0-jre'
// Trove4j
// Provides optimized Collections for primitive types
implementation 'net.sf.trove4j:trove4j:3.0.3'
// java-graphics
// A GLM (OpenGL Mathematics) port to Java
// Unfortunately, Maven Central Repository provides an outdated version of this library, which contains several critical bugs
implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1'
// Log4j
// A logging library
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3'
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3'
implementation 'org.apache.logging.log4j:log4j-api:2.17.0'
implementation 'org.apache.logging.log4j:log4j-core:2.17.0'
// JUnit
// A unit-testing library
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.2'
// Also see LWJGL dependencies in build_logic/lwjgl.gradle
}
/*
* Version resolution
*/
import org.ajoberstar.grgit.*
// Pattern: vMAJOR.MINOR.PATCH[-SUFFIX]
project.ext.tagFormat = /^v(\d+)\.(\d+)\.(\d+)(-[\w\-]*)?$/
String version_parseVersion(String tag, boolean increment) {
try {
// See LWJGL dependencies below
}
/*
* Progressia uses the following LWJGL libraries:
* - Core libraries
* - OpenGL
* - OpenAL
* - GLFW
* - STB
*/
/*
* LWJGL
* (auto-generated script)
* ((here be dragons))
*/
import org.gradle.internal.os.OperatingSystem
project.ext.lwjglVersion = "3.2.3"
switch (OperatingSystem.current()) {
case OperatingSystem.LINUX:
def osArch = System.getProperty("os.arch")
project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64")
? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}"
: "natives-linux"
break
case OperatingSystem.MAC_OS:
project.ext.lwjglNatives = "natives-macos"
break
case OperatingSystem.WINDOWS:
project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86"
break
}
dependencies {
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
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
def data = (tag =~ tagFormat)[0]
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")
def major = data[1]
def minor = data[2]
def patch = data[3] as int
def suffix = data[4] ?: ''
if (increment) {
def oldVersion = "$major.$minor.$patch$suffix"
patch++
def newVersion = "$major.$minor.$patch$suffix"
logger.info "Version parsed from Git: $oldVersion, incremented to $newVersion"
return newVersion
} else {
def newVersion = "$major.$minor.$patch$suffix"
logger.info "Version parsed from Git: $newVersion"
return newVersion
}
if (project.ext.lwjglNatives.isEmpty()) println "WTF"
} catch (any) {
logger.warn "Could not parse version from tag \"$tag\""
return tag
}
}
Tag version_findRelevantTag(Grgit git) {
def tags = git.tag.list()
def commits = [ git.head() ]
def visited = new HashSet<>()
while (true) {
if (commits.isEmpty()) return null
def nextCommits = new HashSet<>()
dependencies {
archs.each { arch ->
runtimeOnly "org.lwjgl:lwjgl::$arch"
runtimeOnly "org.lwjgl:lwjgl-glfw::$arch"
runtimeOnly "org.lwjgl:lwjgl-openal::$arch"
runtimeOnly "org.lwjgl:lwjgl-opengl::$arch"
runtimeOnly "org.lwjgl:lwjgl-stb::$arch"
}
}
}
}
compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved
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 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
}
}
def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') }
task resolveVersion {
description 'Resolves version information from Git repository or project properties.'
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())
doFirst {
try {
def git = Grgit.open(dir: project.projectDir)
project.ext.commit = git.head().id
project.ext.branch = git.branch.current().name
if (project.version != 'unspecified') {
// Leave version as-is
return
}
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 = '-'
project.ext.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
}
}
return !dep.contains("natives-") ||
project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length()))
doLast {
if (!project.hasProperty('buildId')) {
project.ext.buildId = '-'
}
}
}
/*
* Manifest specification
* Configure JAR manifest
*/
task specifyLocalManifest {
dependsOn addNativeDependencies // Make sure all native dependencies are specified
task configureManifest {
description 'Populates JAR manifest with Main-Class, Class-Path and version metadata.'
jar.dependsOn configureManifest
dependsOn resolveVersion
doFirst {
def classPath = []
configurations.runtimeClasspath.each {
if (isDependencyRequested(it.getName())) {
classPath.add("lib/" + it.getName())
} else {
println "\tRemoving from JAR classpath (not requested): " + it.getName()
jar.manifest.attributes(
'Main-Class': 'ru.windcorp.progressia.client.ProgressiaClientMain',
'Class-Path': configurations.runtimeClasspath.collect { "lib/${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,
)
}
}
/*
* Copy libraries into buil/libs/lib directory, next to Progressia.jar
*/
task exportLibs(type: Sync) {
description 'Copies runtime libraries into a subdirectory next to the output JAR.'
jar.dependsOn exportLibs
from configurations.runtimeClasspath
into 'build/libs/lib'
}
/*
* Apply LWJGL logic
*/
apply from: 'build_logic/lwjgl.gradle'
/*
* Packaging working directory configuration
*/
import java.nio.file.*
import java.nio.file.attribute.*
task createPackagingDirs() {
description 'Resets build/tmp/packaging directory.'
doLast {
def tmpDir = buildDir.toPath().resolve 'tmp/packaging'
def nuke = new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
if (e == null) {
Files.delete(dir);
return FileVisitResult.CONTINUE;
} else {
// directory iteration failed
throw e;
}
}
}
if (classPath.size() == configurations.runtimeClasspath.size()) {
println "Nothing removed from JAR classpath"
// Fckn nuke tmpDir from orbit
// I'm so done with deleting file trees in Java/Groovy/whatever
// ...Not using File.deleteDir() because the latter recurses into symlinks, and we don't want to wipe build/libs/lib
if (Files.exists(tmpDir)) {
Files.walkFileTree tmpDir, nuke
}
Files.createDirectories tmpDir.resolve('workingDir')
Files.createDirectories buildDir.toPath().resolve('packages')
}
}
task linkBuildOutputForPackaging() {
description 'Symlinks the contents of build/libs into packaging working directory.'
jar {
manifest {
attributes(
"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain",
"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' ')
)
}
}
dependsOn build
dependsOn createPackagingDirs
onlyIf { preparePackaging.ext.mode == 'symlink' }
doLast {
def from = buildDir.toPath().resolve 'libs'
def into = buildDir.toPath().resolve "tmp/packaging/workingDir/${preparePackaging.ext.buildDest}"
Files.createDirectories into
Files.list(from).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 into.resolve(it.fileName), it
}
}
}
task copyBuildOutputForPackaging(type: Copy) {
description 'Copies the contents of build/libs into packaging working directory.'
dependsOn build
dependsOn createPackagingDirs
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 createPackagingDirs
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")
}
}
jar.dependsOn specifyLocalManifest
/*
* Library export
* Convenience build tasks
*/
task exportLibs(type: Sync) {
mustRunAfter addNativeDependencies
into libsDirectory.get().getAsFile().getPath() + "/lib"
exclude { !isDependencyRequested(it.getName()) }
from configurations.runtimeClasspath
}
jar.dependsOn(exportLibs)
/*
* Packaging
*/
task packageDebian(type: Exec) {
description 'Builds the project and creates a Debain package.'
group 'Progressia'
dependsOn build
dependsOn requestLinuxDependencies
commandLine './buildPackages.sh', 'debian'
doLast {
println "Debian package available in build_packages/"
}
}
task packageWindows(type: Exec) {
description 'Builds the project and creates a Windows installer.'
group 'Progressia'
dependsOn build
dependsOn requestWindowsDependencies
commandLine './buildPackages.sh', 'windows'
doLast {
println "Windows installer available in build_packages/"
}
}
task buildCrossPlatform {
description 'Builds the project including native libraries for all available platforms.'
group 'Progressia'
dependsOn requestCrossPlatformDependencies
dependsOn build
doLast {
println "Native libraries for all platforms have been added"
}
description 'Builds the project including native libraries for all available platforms.'
group 'Progressia'
dependsOn requestCrossPlatformDependencies
dependsOn build
doLast {
logger.info 'Native libraries for all platforms have been added'
}
}
task buildLocal {
description "Builds the project including only native libraries for current platform ($lwjglNatives)."
group 'Progressia'
dependsOn build
doLast {
println "Native libraries only for platform $lwjglNatives have been added"
}
description "Builds the project including only native libraries for current platform (${lwjgl.localArch})."
group 'Progressia'
dependsOn build
doLast {
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

117
build_logic/lwjgl.gradle Normal file
View File

@@ -0,0 +1,117 @@
/*
* Build logic for Progressia
* LWJGL dependency logic
*/
project.ext.lwjgl = new HashMap<>()
// Version of LWJGL
lwjgl.version = '3.2.3'
/*
* 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<>()
// 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 = 'macos'
break
case OperatingSystem.WINDOWS:
lwjgl.localArch = System.getProperty('os.arch').contains('64') ? 'windows' : 'windows-x86'
break
}
// Declare pure-Java dependencies
dependencies {
// BOM
implementation platform("org.lwjgl:lwjgl-bom:${lwjgl.version}")
// Core
implementation 'org.lwjgl:lwjgl'
// Components
lwjgl.libraries.each { implementation "org.lwjgl:lwjgl-$it" }
}
/*
* Adds LWJGL native libraries to runtimeOnly configuration
*/
task lwjgl_addNativesToRuntimeOnly {
// Make sure runtimeOnly has not been resolved
compileJava.dependsOn lwjgl_addNativesToRuntimeOnly
configureManifest.dependsOn lwjgl_addNativesToRuntimeOnly
exportLibs.dependsOn 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(', ')
}
dependencies {
lwjgl.targets.each { target ->
runtimeOnly "org.lwjgl:lwjgl::natives-$target"
lwjgl.libraries.each { lib ->
runtimeOnly "org.lwjgl:lwjgl-$lib::natives-$target"
}
}
}
}
}
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-x86'
requestTask 'MacOS', 'macos'

View File

@@ -0,0 +1,38 @@
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
executable 'dpkg-deb'
args '--root-owner-group'
args '--build', 'build/tmp/packaging/workingDir'
args 'build/packages'
}

View File

@@ -0,0 +1,50 @@
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
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,41 @@
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
}
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

12
docs/ProgressiaRegion.md Normal file
View File

@@ -0,0 +1,12 @@
# Progressia Region File
## Description
The `.progressia_region` file type is used for all region files in the game Progressia. Each region file contains a cube of 16x16x16 chunks.
## Header
The header of the file is 16&nbsp;400 bytes. Every file starts with the string byte sequence `\x50\x52\x4F\x47` (UTF-8 for `PROG`), followed with the three integer values of the region position, in region coordinates. After this, there is exactly 16KiB of space in the header, which stores the offsets to the chunks' data. This space holds an integer, 4 bytes, for each chunk in the region. The integer value starts at 0 for every chunk, and is changed to the location of the chunk data once created. These are indexed in order by flattening the 3D in-chunk coordinates into a number between 0 and 4095 according to the formula `offset = 256*x+ 16*y + z` for chunk at (x, y, z). To convert from this offset value to the offset in bytes, use `byte_offset = 16400 + 64*n`.
## Sectors
Sectors are what is used to store chunk data, and are not linear, but are followed until they reach an ending block. Each is 64 bytes, which is used in the header section to find the byte offset. Each sector starts with a identification byte, followed by the sector data.
0. Ending - This sector is empty, and marks the end of the chunk data (This may change in the future.
1. Data - This sector contains chunk data for a single chunk. The second byte of this sector contains a counter byte, which is a form of "checksum" to make sure that the program is reading the proper sectors in order. This starts at 0 for the first data sector and increments by one for each new data sector.
2. Partition Link - This sector only contains another offset value, which is where the next sector is. This allows for infinite chunk size, avoiding "chunk dupes" as were present in Minecraft without reverting any chunks.
3. Bulk Data - These would be used for many chunks in the same region that contain exactly the same data, e.g. all solid chunks underground. Exists so the program knows not to overwrite them, and just make new chunk data if modified. Not yet implemented.

View File

@@ -1,8 +1,10 @@
# 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.
@@ -150,54 +152,46 @@ GNU/Linux and Windows natives:
./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
A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated
by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the
resulting files are moved into `build_packages`.
A universal ZIP distribution, a Debian package and a Windows NSIS installer may be created automatically by the build
script.
### 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
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
`./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
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
`./buildPackages.sh windows`.
`makensis`.
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` 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.
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). [ImageMagick](https://imagemagick.org),
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.

View File

@@ -0,0 +1,103 @@
# 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` 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.
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 and Git branch are correspond to the state of the local Git repository, if any. In case Git metadata is
unavailable, `-` fallback is used for both fields.
### Build ID
Build ID uniquely identifies artifacts produced by automated build systems. For example, builds executed by WindCorp
Jenkins suite have build IDs like `WJ3` or `WJ142`. Build ID must be provided explicitly; it is `-` unless specified
otherwise.
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-x86` | `requestWindowsDependencies` |
| `macos` | `requestMacOSDependencies` |

View File

@@ -41,4 +41,17 @@ Run configurations are used by Intellij IDEA to specify how a project must be ru
8. Append `\run` to the 'Working directory' field. Alternatively, specify another location outside of the project's root directory.
9. Click 'Apply' to save changes.
Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA.
Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA.
### Applying formatting templates
Windcorp's Progressia repository is formatted with a style defined for Eclipse IDE (sic) in
`templates_and_presets/eclipse_ide`.
Please apply these templates to the project to automatically format the source in a similar fashion.
1. In project context menu, click 'File->Properties'. (`Ctrl+Alt+S`)
2. In 'Editor' > 'Code Style' > 'Java', press gear icon, then click 'Import Scheme' > 'Eclipse code style'
3. In Scheme select 'Project'
4. Open the file `templates_and_presets/eclipse_ide/FormatterProfile.xml` in 'Select Path'.
5. Inside 'Import Scheme' widow click 'Current Scheme' check box after press OK

1
logs/game.log Normal file
View File

@@ -0,0 +1 @@
22:26:25.948 [Music Thread ] WARN ru.windcorp.progressia.test.TestMusicPlayer > No music found

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
* 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.Array;
@@ -611,7 +611,8 @@ public class ArrayUtil {
int end = offset + length;
if (end > arrayLength || offset < 0)
throw new IllegalArgumentException(
"Array contains [0; " + arrayLength + "), requested [" + offset + "; " + end + ")");
"Array contains [0; " + arrayLength + "), requested [" + offset + "; " + end + ")"
);
return length;
}
@@ -627,7 +628,8 @@ public class ArrayUtil {
if (end > arrayLength || start < 0)
throw new IllegalArgumentException(
"Array contains [0; " + arrayLength + "), requested [" + start + "; " + end + ")");
"Array contains [0; " + arrayLength + "), requested [" + start + "; " + end + ")"
);
return end;
}

View File

@@ -15,7 +15,7 @@
* 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.io.OutputStream;

View File

@@ -15,7 +15,7 @@
* 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.util.HashMap;
@@ -30,8 +30,18 @@ public class PrimitiveUtil {
private static final Map<Class<?>, Object> PRIMITIVE_TO_NULL = new HashMap<>();
static {
for (Class<?> boxed : new Class<?>[] { Boolean.class, Byte.class, Short.class, Character.class, Integer.class,
Long.class, Float.class, Double.class }) {
for (
Class<?> boxed : new Class<?>[] {
Boolean.class,
Byte.class,
Short.class,
Character.class,
Integer.class,
Long.class,
Float.class,
Double.class
}
) {
try {
PRIMITIVE_TO_BOXED.put((Class<?>) boxed.getField("TYPE").get(null), boxed);
} catch (Exception e) {

View File

@@ -15,7 +15,7 @@
* 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.util.function.*;
@@ -40,7 +40,8 @@ import java.util.stream.Stream;
/**
* Contains static methods to create {@link Stream Streams} that synchronize
* their <a href=
* their
* <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
* terminal operations</a> on a given monitor.
*
@@ -49,7 +50,7 @@ import java.util.stream.Stream;
*/
// SonarLint: "Stream.peek" should be used with caution (java:S3864)
// We are implementing Stream, so peek() is required.
// We are implementing Stream, so peek() is required.
@SuppressWarnings("squid:S3864")
public class SyncStreams {
@@ -1069,18 +1070,21 @@ public class SyncStreams {
}
/**
* Wraps the given {@link Stream} to make all <a href=
* Wraps the given {@link Stream} to make all
* <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
* terminal operations</a> acquire the provided monitor's lock before
* execution. Intermediate operations return streams that are also
* synchronized on the same object. The created stream will behave
* identically to the provided stream in all other aspects. Use this to
* synchronize access to stream's source.
* execution. Intermediate operations
* return streams that are also synchronized on the same object. The created
* stream will behave identically
* to the provided stream in all other aspects. Use this to synchronize
* access to stream's source.
* <p>
* <i>The returned {@code Stream}'s {@link Stream#iterator() iterator()} and
* {@link Stream#spliterator() spliterator()} methods return regular
* non-synchronized iterators and spliterators respectively</i>. It is the
* user's responsibility to avoid concurrency issues:
* {@link Stream#spliterator()
* spliterator()} methods return regular non-synchronized iterators and
* spliterators respectively</i>. It
* is the user's responsibility to avoid concurrency issues:
*
* <pre>
* synchronized (stream.getMonitor()) {
@@ -1099,17 +1103,14 @@ public class SyncStreams {
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
* </pre>
*
* @param <T>
* the class of objects in the Stream
* @param stream
* the stream to wrap.
* @param monitor
* the object that the stream will use for synchronization. When
* {@code null}, the stream will synchronize on itself.
* @param <T> the class of objects in the Stream
* @param stream the stream to wrap.
* @param monitor the object that the stream will use for synchronization.
* When {@code null}, the stream
* will synchronize on itself.
* @return a {@link SyncStream SyncStream&lt;T&gt;} synchronized on
* {@code monitor} and backed by {@code stream}.
* @throws NullPointerException
* if {@code stream == null}.
* @throws NullPointerException if {@code stream == null}.
*/
public static <T> SyncStream<T> synchronizedStream(Stream<T> stream, Object monitor) {
Objects.requireNonNull(stream, "stream cannot be null");
@@ -1117,19 +1118,22 @@ public class SyncStreams {
}
/**
* Wraps the given {@link IntStream} to make all <a href=
* Wraps the given {@link IntStream} to make all
* <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
* terminal operations</a> acquire the provided monitor's lock before
* execution. Intermediate operations return streams that are also
* synchronized on the same object. The created stream will behave
* identically to the provided stream in all other aspects. Use this to
* synchronize access to stream's source.
* execution. Intermediate operations
* return streams that are also synchronized on the same object. The created
* stream will behave identically
* to the provided stream in all other aspects. Use this to synchronize
* access to stream's source.
* <p>
* <i>The returned {@code IntStream}'s {@link IntStream#iterator()
* iterator()} and {@link IntStream#spliterator() spliterator()} methods
* return regular non-synchronized iterators and spliterators
* respectively</i>. It is the user's responsibility to avoid concurrency
* issues:
* iterator()} and
* {@link IntStream#spliterator() spliterator()} methods return regular
* non-synchronized iterators and
* spliterators respectively</i>. It is the user's responsibility to avoid
* concurrency issues:
*
* <pre>
* synchronized (stream.getMonitor()) {
@@ -1148,15 +1152,13 @@ public class SyncStreams {
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
* </pre>
*
* @param stream
* the stream to wrap.
* @param monitor
* the object that the stream will use for synchronization. When
* {@code null}, the stream will synchronize on itself.
* @param stream the stream to wrap.
* @param monitor the object that the stream will use for synchronization.
* When {@code null}, the stream
* will synchronize on itself.
* @return a {@link SyncIntStream} synchronized on {@code monitor} and
* backed by {@code stream}.
* @throws NullPointerException
* if {@code stream == null}.
* @throws NullPointerException if {@code stream == null}.
*/
public static SyncIntStream synchronizedStream(IntStream stream, Object monitor) {
Objects.requireNonNull(stream, "stream cannot be null");
@@ -1164,19 +1166,22 @@ public class SyncStreams {
}
/**
* Wraps the given {@link LongStream} to make all <a href=
* Wraps the given {@link LongStream} to make all
* <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
* terminal operations</a> acquire the provided monitor's lock before
* execution. Intermediate operations return streams that are also
* synchronized on the same object. The created stream will behave
* identically to the provided stream in all other aspects. Use this to
* synchronize access to stream's source.
* execution. Intermediate operations
* return streams that are also synchronized on the same object. The created
* stream will behave identically
* to the provided stream in all other aspects. Use this to synchronize
* access to stream's source.
* <p>
* <i>The returned {@code LongStream}'s {@link LongStream#iterator()
* iterator()} and {@link LongStream#spliterator() spliterator()} methods
* return regular non-synchronized iterators and spliterators
* respectively</i>. It is the user's responsibility to avoid concurrency
* issues:
* iterator()} and
* {@link LongStream#spliterator() spliterator()} methods return regular
* non-synchronized iterators and
* spliterators respectively</i>. It is the user's responsibility to avoid
* concurrency issues:
*
* <pre>
* synchronized (stream.getMonitor()) {
@@ -1195,15 +1200,13 @@ public class SyncStreams {
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
* </pre>
*
* @param stream
* the stream to wrap.
* @param monitor
* the object that the stream will use for synchronization. When
* {@code null}, the stream will synchronize on itself.
* @param stream the stream to wrap.
* @param monitor the object that the stream will use for synchronization.
* When {@code null}, the stream
* will synchronize on itself.
* @return a {@link SyncLongStream} synchronized on {@code monitor} and
* backed by {@code stream}.
* @throws NullPointerException
* if {@code stream == null}.
* @throws NullPointerException if {@code stream == null}.
*/
public static SyncLongStream synchronizedStream(LongStream stream, Object monitor) {
Objects.requireNonNull(stream, "stream cannot be null");
@@ -1211,19 +1214,22 @@ public class SyncStreams {
}
/**
* Wraps the given {@link DoubleStream} to make all <a href=
* Wraps the given {@link DoubleStream} to make all
* <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps">
* terminal operations</a> acquire the provided monitor's lock before
* execution. Intermediate operations return streams that are also
* synchronized on the same object. The created stream will behave
* identically to the provided stream in all other aspects. Use this to
* synchronize access to stream's source.
* execution. Intermediate operations
* return streams that are also synchronized on the same object. The created
* stream will behave identically
* to the provided stream in all other aspects. Use this to synchronize
* access to stream's source.
* <p>
* <i>The returned {@code DoubleStream}'s {@link DoubleStream#iterator()
* iterator()} and {@link DoubleStream#spliterator() spliterator()} methods
* return regular non-synchronized iterators and spliterators
* respectively</i>. It is the user's responsibility to avoid concurrency
* issues:
* iterator()} and
* {@link DoubleStream#spliterator() spliterator()} methods return regular
* non-synchronized iterators and
* spliterators respectively</i>. It is the user's responsibility to avoid
* concurrency issues:
*
* <pre>
* synchronized (stream.getMonitor()) {
@@ -1242,15 +1248,13 @@ public class SyncStreams {
* stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
* </pre>
*
* @param stream
* the stream to wrap.
* @param monitor
* the object that the stream will use for synchronization. When
* {@code null}, the stream will synchronize on itself.
* @param stream the stream to wrap.
* @param monitor the object that the stream will use for synchronization.
* When {@code null}, the stream
* will synchronize on itself.
* @return a {@link SyncDoubleStream} synchronized on {@code monitor} and
* backed by {@code stream}.
* @throws NullPointerException
* if {@code stream == null}.
* @throws NullPointerException if {@code stream == null}.
*/
public static SyncDoubleStream synchronizedStream(DoubleStream stream, Object monitor) {
Objects.requireNonNull(stream, "stream cannot be null");

View File

@@ -15,7 +15,7 @@
* 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 SyntaxException extends Exception {

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.text.CharacterIterator;
@@ -108,7 +108,7 @@ public class CharArrayIterator implements CharacterIterator {
return pos;
}
// @SuppressWarnings("all") Just STFU, this _is_ terrific
// @SuppressWarnings("all") Just STFU, this _is_ terrific
// SonarLint: "clone" should not be overridden (java:S2975)
// And I wouldn't have done that if only CharacterIterator had not required

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.util.function.IntConsumer;

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.util.Objects;

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.util.Arrays;

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.util.function.IntSupplier;

View File

@@ -15,7 +15,7 @@
* 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.chars;
public class EscapeException extends Exception {

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.text.CharacterIterator;
@@ -103,8 +103,14 @@ public class Escaper {
}
public static final Escaper JAVA = new Escaper('\\', 'u', "tbnrf'\"".toCharArray(), "\t\b\n\r\f\'\"".toCharArray(),
true, true);
public static final Escaper JAVA = new Escaper(
'\\',
'u',
"tbnrf'\"".toCharArray(),
"\t\b\n\r\f\'\"".toCharArray(),
true,
true
);
private final char escapeChar;
private final char unicodeEscapeChar;
@@ -114,8 +120,14 @@ public class Escaper {
private final boolean preferUnicode;
private final boolean strict;
protected Escaper(char escapeChar, char unicodeEscapeChar, char[] safes, char[] unsafes, boolean preferUnicode,
boolean strict) {
protected Escaper(
char escapeChar,
char unicodeEscapeChar,
char[] safes,
char[] unsafes,
boolean preferUnicode,
boolean strict
) {
this.escapeChar = escapeChar;
this.unicodeEscapeChar = unicodeEscapeChar;
this.safes = safes;
@@ -140,7 +152,8 @@ public class Escaper {
for (char c : unsafes) {
if (c == escapeChar)
throw new IllegalArgumentException(
"Unsafe characters contain escape chatacter (escape character is escaped automatically)");
"Unsafe characters contain escape chatacter (escape character is escaped automatically)"
);
if (c == unicodeEscapeChar)
throw new IllegalArgumentException("Unsafe characters contain Unicode escape chatacter");
}
@@ -160,7 +173,11 @@ public class Escaper {
end = Integer.MAX_VALUE;
else
end = src.getPosition() + length;
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current())))
while (
src.has() &&
src.getPosition() < end &&
(until == null || !until.test(src.current()))
)
escape(src.consume(), output);
}
@@ -208,7 +225,11 @@ public class Escaper {
int result = 0;
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
while (
src.has() &&
src.getPosition() < end &&
(until == null || !until.test(src.current()))
) {
result += getEscapedLength(src.consume());
}
@@ -236,7 +257,11 @@ public class Escaper {
end = Integer.MAX_VALUE;
else
end = src.getPosition() + length;
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
while (
src.has() &&
src.getPosition() < end &&
(until == null || !until.test(src.current()))
) {
output.accept(unescapeOneSequence(src));
}
}
@@ -257,8 +282,10 @@ public class Escaper {
if (src.current() == unicodeEscapeChar) {
src.next();
return (char) (hexValue(src.consume()) << (4 * 3) | hexValue(src.consume()) << (4 * 2)
| hexValue(src.consume()) << (4 * 1) | hexValue(src.consume()) << (4 * 0));
return (char) (hexValue(src.consume()) << (4 * 3) |
hexValue(src.consume()) << (4 * 2) |
hexValue(src.consume()) << (4 * 1) |
hexValue(src.consume()) << (4 * 0));
}
int index = ArrayUtil.firstIndexOf(safes, src.current());
@@ -288,7 +315,11 @@ public class Escaper {
int result = 0;
while (src.has() && src.getPosition() < end && (until == null || !until.test(src.current()))) {
while (
src.has() &&
src.getPosition() < end &&
(until == null || !until.test(src.current()))
) {
skipOneSequence(src);
result++;
}
@@ -297,7 +328,11 @@ public class Escaper {
}
public void skipOneSequence(CharReader src) {
if (src.current() == escapeChar && src.next() == unicodeEscapeChar) {
if (
src.current() == escapeChar
&&
src.next() == unicodeEscapeChar
) {
src.advance(4);
}
src.next();

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.text.CharacterIterator;
@@ -86,7 +86,7 @@ public class FancyCharacterIterator implements CharacterIterator {
return sb.toString();
}
// @SuppressWarnings("all") Just STFU, this _is_ terrific
// @SuppressWarnings("all") Just STFU, this _is_ terrific
// SonarLint: "clone" should not be overridden (java:S2975)
// And I wouldn't have done that if only CharacterIterator had not required

View File

@@ -15,7 +15,7 @@
* 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.chars;
public class IndentedStringBuilder {

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.io.IOException;
@@ -41,8 +41,13 @@ public class StringUtil {
private static final String EMPTY_PLACEHOLDER = "[empty]";
private static final String DEFAULT_SEPARATOR = "; ";
public static <T> String arrayToString(T[] array, String separator, String empty, String nullPlaceholder,
String nullArray) {
public static <T> String arrayToString(
T[] array,
String separator,
String empty,
String nullPlaceholder,
String nullArray
) {
if (separator == null) {
throw new IllegalArgumentException(new NullPointerException());
@@ -74,8 +79,13 @@ public class StringUtil {
return arrayToString(array, DEFAULT_SEPARATOR);
}
public static String iteratorToString(Iterator<?> iterator, String separator, String empty, String nullPlaceholder,
String nullIterator) {
public static String iteratorToString(
Iterator<?> iterator,
String separator,
String empty,
String nullPlaceholder,
String nullIterator
) {
if (separator == null) {
throw new IllegalArgumentException(new NullPointerException());
@@ -109,8 +119,13 @@ public class StringUtil {
return iteratorToString(iterator, DEFAULT_SEPARATOR);
}
public static String iterableToString(Iterable<?> iterable, String separator, String empty, String nullPlaceholder,
String nullIterable) {
public static String iterableToString(
Iterable<?> iterable,
String separator,
String empty,
String nullPlaceholder,
String nullIterable
) {
if (separator == null) {
throw new IllegalArgumentException(new NullPointerException());
@@ -131,8 +146,14 @@ public class StringUtil {
return iterableToString(iterable, DEFAULT_SEPARATOR);
}
public static <T> String supplierToString(IntFunction<T> supplier, int length, String separator, String empty,
String nullPlaceholder, String nullSupplier) {
public static <T> String supplierToString(
IntFunction<T> supplier,
int length,
String separator,
String empty,
String nullPlaceholder,
String nullSupplier
) {
if (separator == null)
throw new IllegalArgumentException(new NullPointerException());
@@ -142,15 +163,28 @@ public class StringUtil {
return empty;
if (length > 0) {
return supplierToStringExactly(supplier, length, separator, nullPlaceholder);
return supplierToStringExactly(
supplier,
length,
separator,
nullPlaceholder
);
} else {
return supplierToStringUntilNull(supplier, separator, empty);
return supplierToStringUntilNull(
supplier,
separator,
empty
);
}
}
private static <T> String supplierToStringExactly(IntFunction<T> supplier, int length, String separator,
String nullPlaceholder) {
private static <T> String supplierToStringExactly(
IntFunction<T> supplier,
int length,
String separator,
String nullPlaceholder
) {
T element = supplier.apply(0);
StringBuilder sb = new StringBuilder(element == null ? nullPlaceholder : element.toString());
@@ -164,7 +198,11 @@ public class StringUtil {
return sb.toString();
}
private static <T> String supplierToStringUntilNull(IntFunction<T> supplier, String separator, String empty) {
private static <T> String supplierToStringUntilNull(
IntFunction<T> supplier,
String separator,
String empty
) {
T element = supplier.apply(0);
if (element == null) {
@@ -328,7 +366,11 @@ public class StringUtil {
StringBuilder sb = new StringBuilder();
charLoop: for (char c : src.toCharArray()) {
if ((resultIndex + 1) < arrayLength && test.test(c)) {
if (
(resultIndex + 1) < arrayLength
&&
test.test(c)
) {
result[resultIndex] = resetStringBuilder(sb);
++resultIndex;
continue charLoop;
@@ -347,17 +389,17 @@ public class StringUtil {
* index.
* <p>
* Indices {@code 0} and {@code src.length() - 1} produce {@code str}
* excluding the specified character and {@code ""}.
* excluding
* the specified character and {@code ""}.
* <p>
*
* @param src
* the String to split
* @param at
* index to split at
* @throws IllegalArgumentException
* if the index is out of bounds for {@code src}
* @param src the String to split
* @param at index to split at
* @throws IllegalArgumentException if the index is out of bounds for
* {@code src}
* @return an array containing the substrings, in order of encounter in
* {@code src}. Its length is always 2.
* {@code src}.
* Its length is always 2.
*/
public static String[] splitAt(String src, int at) {
Objects.requireNonNull(src, "src");
@@ -374,7 +416,10 @@ public class StringUtil {
return new String[] { src.substring(0, src.length() - 1), "" };
}
return new String[] { src.substring(0, at), src.substring(at + 1) };
return new String[] {
src.substring(0, at),
src.substring(at + 1)
};
}
/**
@@ -382,7 +427,8 @@ public class StringUtil {
* indices.
* <p>
* Indices {@code 0} and {@code src.length() - 1} produce extra zero-length
* outputs. Duplicate indices produce extra zero-length outputs.
* outputs.
* Duplicate indices produce extra zero-length outputs.
* <p>
* Examples:
*
@@ -393,14 +439,13 @@ public class StringUtil {
* splitAt("a.b", 1, 1, 1) -> {"a", "", "", "b"}
* </pre>
*
* @param src
* the String to split
* @param at
* indices to split at, in any order
* @throws IllegalArgumentException
* if some index is out of bounds for {@code src}
* @param src the String to split
* @param at indices to split at, in any order
* @throws IllegalArgumentException if some index is out of bounds for
* {@code src}
* @return an array containing the substrings, in order of encounter in
* {@code src}. Its length is always {@code at.length + 1}.
* {@code src}.
* Its length is always {@code at.length + 1}.
*/
public static String[] splitAt(String src, int... at) {
Objects.requireNonNull(src, "src");
@@ -508,8 +553,10 @@ public class StringUtil {
}
if (endPos < beginPos) {
throw new IllegalArgumentException("endPos must be greater than or equal to beginPos (endPos=" + endPos
+ ", beginPos=" + beginPos + ")");
throw new IllegalArgumentException(
"endPos must be greater than or equal to beginPos (endPos="
+ endPos + ", beginPos=" + beginPos + ")"
);
}
if (endPos >= Math.min(a.length, b.length)) {
@@ -545,7 +592,8 @@ public class StringUtil {
/**
* Finds and returns the index of the specified appearance of the specified
* character in the given array. The search starts at index 0.
* character
* in the given array. The search starts at index 0.
* <p>
* Examples:
* <p>
@@ -582,12 +630,10 @@ public class StringUtil {
* </tr>
* </table>
*
* @param src
* - the array to search in.
* @param target
* - the character to search for.
* @param skip
* - the amount of <code>target</code> characters to be skipped.
* @param src - the array to search in.
* @param target - the character to search for.
* @param skip - the amount of <code>target</code> characters to be
* skipped.
* @return The index of the <code>skip+1</code>th <code>target</code>
* character or -1, if none found.
* @see StringUtil#indexFromEnd(char[], char, int)
@@ -607,7 +653,8 @@ public class StringUtil {
/**
* Finds and returns the index of the specified appearance of the specified
* character in the given array. The search starts at index
* character
* in the given array. The search starts at index
* <code>src.length - 1</code>.
* <p>
* Examples:
@@ -645,15 +692,13 @@ public class StringUtil {
* </tr>
* </table>
*
* @param src
* - the array to search in.
* @param target
* - the character to search for.
* @param skip
* - the amount of <code>target</code> characters to be skipped.
* @param src - the array to search in.
* @param target - the character to search for.
* @param skip - the amount of <code>target</code> characters to be
* skipped.
* @return The index of the <code>skip+1</code>th
* <code>target</code>character from the end of the array or -1, if
* none found.
* <code>target</code>character
* from the end of the array or -1, if none found.
* @see StringUtil#indexFromBeginning(char[], char, int)
*/
public static int indexFromEnd(char[] src, char target, int skip) {
@@ -828,8 +873,12 @@ public class StringUtil {
return result;
}
private static void buildCombinations(StringBuilder sb, Collection<String> result, Iterable<String>[] parts,
int index) {
private static void buildCombinations(
StringBuilder sb,
Collection<String> result,
Iterable<String>[] parts,
int index
) {
if (index >= parts.length) {
result.add(sb.toString());
} else {
@@ -855,8 +904,13 @@ public class StringUtil {
return result;
}
private static void buildCombinations(StringBuilder sb, String[] result, int[] resultIndex, String[][] parts,
int index) {
private static void buildCombinations(
StringBuilder sb,
String[] result,
int[] resultIndex,
String[][] parts,
int index
) {
if (index >= parts.length) {
result[resultIndex[0]++] = sb.toString();
} else {
@@ -931,7 +985,10 @@ public class StringUtil {
}
private static char hexDigit(long value, int digit) {
return hexDigit((int) (value >>> (4 * digit)) & 0xF);
return hexDigit(
(int) (value >>> (4 * digit))
& 0xF
);
}
public static char hexDigit(int value) {

View File

@@ -15,7 +15,7 @@
* 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.chars;
public class UncheckedEscapeException extends RuntimeException {

View File

@@ -15,7 +15,7 @@
* 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.chars;
import java.io.IOException;

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
/**
@@ -27,9 +27,10 @@ public abstract class AbstractCharReader implements CharReader {
/**
* Current position of this CharReader. The reader maps its input to
* positions starting from 0. Positions that are negative or lower than 0
* are invalid. {@link #current()} will throw an exception if position is
* invalid.
* positions starting from 0.
* Positions that are negative or lower than 0 are invalid.
* {@link #current()}
* will throw an exception if position is invalid.
*/
protected int position = 0;

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
import java.util.Objects;

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
/**
@@ -51,12 +51,9 @@ public abstract class BufferedCharReader extends AbstractCharReader {
/**
* Acquires next characters and stores them in the array.
*
* @param buffer
* the output array
* @param offset
* index of the first character
* @param length
* maximum amount of characters to be pulled
* @param buffer the output array
* @param offset index of the first character
* @param length maximum amount of characters to be pulled
* @return the amount of characters actually pulled
*/
protected int pullChars(char[] buffer, int offset, int length) {

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
import java.io.IOException;
@@ -30,7 +30,7 @@ import ru.windcorp.jputil.chars.Escaper;
*/
// SonarLint: Constants should not be defined in interfaces (java:S1214)
// DONE is an essential part of the interface
// DONE is an essential part of the interface
@SuppressWarnings("squid:S1214")
public interface CharReader {
@@ -179,7 +179,8 @@ public interface CharReader {
/**
* Skips to the end of the current line. Both <code>"\n"</code>,
* <code>"\r"</code> and <code>"\r\n"</code> are considered line separators.
* <code>"\r"</code>
* and <code>"\r\n"</code> are considered line separators.
*
* @return the amount of characters in the skipped line
*/

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
import java.io.InputStream;

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
import java.io.IOException;

View File

@@ -15,7 +15,7 @@
* 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.chars.reader;
import java.util.Objects;
@@ -38,7 +38,8 @@ public class StringCharReader extends AbstractCharReader {
int end = offset + length;
if (end > str.length() || offset < 0)
throw new IllegalArgumentException(
"String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")");
"String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")"
);
this.offset = offset;
this.length = length;

View File

@@ -15,7 +15,7 @@
* 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.functions;
@FunctionalInterface

View File

@@ -15,7 +15,7 @@
* 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.functions;
import java.util.function.BiConsumer;
@@ -44,16 +44,9 @@ public interface ThrowingBiConsumer<T, U, E extends Exception> {
}
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
ThrowingBiConsumer<? super T, ? super U, ? extends E> first,
ThrowingBiConsumer<? super T, ? super U, ? extends E> second) {
return (t, u) -> {
first.accept(t, u);
second.accept(t, u);
};
}
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(BiConsumer<? super T, ? super U> first,
ThrowingBiConsumer<? super T, ? super U, E> second) {
ThrowingBiConsumer<? super T, ? super U, ? extends E> first,
ThrowingBiConsumer<? super T, ? super U, ? extends E> second
) {
return (t, u) -> {
first.accept(t, u);
second.accept(t, u);
@@ -61,7 +54,19 @@ public interface ThrowingBiConsumer<T, U, E extends Exception> {
}
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
ThrowingBiConsumer<? super T, ? super U, E> first, BiConsumer<? super T, ? super U> second) {
BiConsumer<? super T, ? super U> first,
ThrowingBiConsumer<? super T, ? super U, E> second
) {
return (t, u) -> {
first.accept(t, u);
second.accept(t, u);
};
}
public static <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> concat(
ThrowingBiConsumer<? super T, ? super U, E> first,
BiConsumer<? super T, ? super U> second
) {
return (t, u) -> {
first.accept(t, u);
second.accept(t, u);

View File

@@ -15,7 +15,7 @@
* 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.functions;
import java.util.function.BiConsumer;
@@ -39,24 +39,30 @@ public interface ThrowingConsumer<T, E extends Exception> {
};
}
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first,
ThrowingConsumer<? super T, ? extends E> second) {
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
ThrowingConsumer<? super T, ? extends E> first,
ThrowingConsumer<? super T, ? extends E> second
) {
return t -> {
first.accept(t);
second.accept(t);
};
}
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(Consumer<? super T> first,
ThrowingConsumer<? super T, ? extends E> second) {
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
Consumer<? super T> first,
ThrowingConsumer<? super T, ? extends E> second
) {
return t -> {
first.accept(t);
second.accept(t);
};
}
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first,
Consumer<? super T> second) {
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(
ThrowingConsumer<? super T, ? extends E> first,
Consumer<? super T> second
) {
return t -> {
first.accept(t);
second.accept(t);

View File

@@ -15,7 +15,7 @@
* 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.functions;
import java.util.function.BiConsumer;
@@ -28,8 +28,10 @@ public interface ThrowingFunction<T, R, E extends Exception> {
R apply(T t) throws E;
@SuppressWarnings("unchecked")
default Function<T, R> withHandler(BiConsumer<? super T, ? super E> handler,
Function<? super T, ? extends R> value) {
default Function<T, R> withHandler(
BiConsumer<? super T, ? super E> handler,
Function<? super T, ? extends R> value
) {
return t -> {
try {
return apply(t);
@@ -56,18 +58,23 @@ public interface ThrowingFunction<T, R, E extends Exception> {
}
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
ThrowingFunction<? super T, I, ? extends E> first,
ThrowingFunction<? super I, ? extends R, ? extends E> second) {
return t -> second.apply(first.apply(t));
}
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(Function<? super T, I> first,
ThrowingFunction<? super I, ? extends R, E> second) {
ThrowingFunction<? super T, I, ? extends E> first,
ThrowingFunction<? super I, ? extends R, ? extends E> second
) {
return t -> second.apply(first.apply(t));
}
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
ThrowingFunction<? super T, I, E> first, Function<? super I, ? extends R> second) {
Function<? super T, I> first,
ThrowingFunction<? super I, ? extends R, E> second
) {
return t -> second.apply(first.apply(t));
}
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
ThrowingFunction<? super T, I, E> first,
Function<? super I, ? extends R> second
) {
return t -> second.apply(first.apply(t));
}

View File

@@ -15,7 +15,7 @@
* 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.functions;
import java.util.function.Consumer;
@@ -38,8 +38,10 @@ public interface ThrowingRunnable<E extends Exception> {
};
}
public static <E extends Exception> ThrowingRunnable<E> concat(ThrowingRunnable<? extends E> first,
ThrowingRunnable<? extends E> second) {
public static <E extends Exception> ThrowingRunnable<E> concat(
ThrowingRunnable<? extends E> first,
ThrowingRunnable<? extends E> second
) {
return () -> {
first.run();
second.run();

View File

@@ -15,7 +15,7 @@
* 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.functions;
import java.util.function.Consumer;

View File

@@ -15,7 +15,7 @@
* 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.iterators;
import java.util.Iterator;

View File

@@ -15,7 +15,7 @@
* 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.iterators;
import java.util.Iterator;

View File

@@ -15,7 +15,7 @@
* 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.iterators;
import java.util.Iterator;

View File

@@ -15,7 +15,7 @@
* 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.iterators;
import java.util.Iterator;
@@ -49,8 +49,10 @@ public class RangeIterator<E> implements Iterator<E> {
public E next() {
update();
if (nextIndex >= from + amount) {
throw new NoSuchElementException("RangeIterator about to retrieve element " + nextIndex
+ " which exceeds upper boundary " + (from + amount));
throw new NoSuchElementException(
"RangeIterator about to retrieve element " + nextIndex
+ " which exceeds upper boundary " + (from + amount)
);
}
E result = parent.next();

View File

@@ -15,7 +15,7 @@
* 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.iterators;
import java.util.ArrayList;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
public abstract class AbstractSelectorOperator implements SelectorOperator {

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import ru.windcorp.jputil.SyntaxException;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import ru.windcorp.jputil.SyntaxException;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.function.Predicate;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.function.Predicate;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.Deque;

View File

@@ -15,7 +15,7 @@
* 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.selectors;
import java.util.ArrayList;
@@ -37,7 +37,7 @@ public class SelectorSystem<T> {
private final Collection<Selector<T>> selectors = Collections.synchronizedCollection(new ArrayList<Selector<T>>());
private final Collection<SelectorOperator> operators = Collections
.synchronizedCollection(new ArrayList<SelectorOperator>());
.synchronizedCollection(new ArrayList<SelectorOperator>());
private String stackPrefix = null;

View File

@@ -18,6 +18,177 @@
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 {
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,22 +18,37 @@
package ru.windcorp.progressia;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
import ru.windcorp.progressia.common.util.crash.providers.*;
import ru.windcorp.progressia.test.LayerTitle;
public class ProgressiaLauncher {
public static String[] arguments;
private static Proxy proxy;
public static void launch(String[] args, Proxy proxy) {
arguments = args.clone();
setupCrashReports();
LogManager.getRootLogger().info("Launching " + Progressia.getName() + " version " + Progressia.getFullVersion());
proxy.initialize();
ProgressiaLauncher.proxy = proxy;
GUI.addTopLayer(new LayerTitle("Title"));
}
public static Proxy getProxy() {
return proxy;
}
private static void setupCrashReports() {
// Context providers
CrashReports.registerProvider(new VersionProvider());
CrashReports.registerProvider(new OSContextProvider());
CrashReports.registerProvider(new RAMContextProvider());
CrashReports.registerProvider(new JavaVersionContextProvider());

View File

@@ -15,7 +15,7 @@
* 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;
public interface Proxy {

View File

@@ -15,7 +15,7 @@
* 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;
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
@@ -69,7 +69,11 @@ public class Client {
return;
}
getCamera().setAnchor(new EntityAnchor(getWorld().getEntityRenderable(entity)));
getCamera().setAnchor(
new EntityAnchor(
getWorld().getEntityRenderable(entity)
)
);
}
}

View File

@@ -30,7 +30,6 @@ import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.TestContent;
import ru.windcorp.progressia.test.TestMusicPlayer;
@@ -38,12 +37,16 @@ public class ClientProxy implements Proxy {
@Override
public void initialize() {
GraphicsBackend.initialize();
try {
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
RenderTaskQueue.waitAndInvoke(() -> Typefaces
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
RenderTaskQueue.waitAndInvoke(
() -> Typefaces
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz")))
);
} catch (InterruptedException e) {
throw CrashReports.report(e, "ClientProxy failed");
}
@@ -56,10 +59,6 @@ public class ClientProxy implements Proxy {
AudioSystem.initialize();
ServerState.startServer();
ClientState.connectToLocalServer();
TestMusicPlayer.start();
}
}

View File

@@ -20,10 +20,13 @@ package ru.windcorp.progressia.client;
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.Layer;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerTestText;
import ru.windcorp.progressia.test.LayerTestUI;
import ru.windcorp.progressia.test.TestContent;
@@ -43,18 +46,48 @@ public class ClientState {
DefaultWorldData world = new DefaultWorldData();
LocalServerCommsChannel channel = new LocalServerCommsChannel(ServerState.getInstance());
LocalServerCommsChannel channel = new LocalServerCommsChannel(
ServerState.getInstance()
);
Client client = new Client(world, channel);
channel.connect(TestContent.PLAYER_LOGIN);
setInstance(client);
displayLoadingScreen();
GUI.addBottomLayer(new LayerWorld(client));
GUI.addTopLayer(new LayerTestUI());
GUI.addTopLayer(new LayerAbout());
}
private static void displayLoadingScreen() {
GUI.addTopLayer(new LayerTestText("Text", new MutableStringLocalized("LayerText.Load"), layer -> {
Client client = ClientState.getInstance();
// TODO refacetor and remove
if (client != null) {
client.getComms().processPackets();
}
if (client != null && client.getLocalPlayer().hasEntity()) {
GUI.removeLayer(layer);
// TODO refactor, this shouldn't be here
LayerWorld layerWorld = new LayerWorld(client);
LayerTestUI layerUI = new LayerTestUI();
LayerAbout layerAbout = new LayerAbout();
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerUI);
GUI.addTopLayer(layerAbout);
}
}));
}
public static void disconnectFromLocalServer() {
getInstance().getComms().disconnect();
for (Layer layer : GUI.getLayers()) {
GUI.removeLayer(layer);
}
}
private ClientState() {

View File

@@ -15,7 +15,7 @@
* 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;
import ru.windcorp.progressia.ProgressiaLauncher;

View File

@@ -15,7 +15,7 @@
* 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.audio;
public enum AudioFormat {

View File

@@ -15,7 +15,7 @@
* 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.audio;
import org.lwjgl.openal.*;
@@ -43,7 +43,10 @@ public class AudioManager {
private static Speaker musicSpeaker;
public static void initAL() {
String defaultDeviceName = alcGetString(0, ALC_DEFAULT_DEVICE_SPECIFIER);
String defaultDeviceName = alcGetString(
0,
ALC_DEFAULT_DEVICE_SPECIFIER
);
device = alcOpenDevice(defaultDeviceName);
@@ -72,7 +75,10 @@ public class AudioManager {
lastSoundIndex = 0;
}
speaker = soundSpeakers.get(lastSoundIndex);
} while (speaker.getState().equals(Speaker.State.PLAYING_LOOP));
} while (
speaker.getState()
.equals(Speaker.State.PLAYING_LOOP)
);
return speaker;
}

View File

@@ -21,9 +21,9 @@ import ru.windcorp.progressia.client.audio.backend.SoundType;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class AudioRegistry extends NamespacedInstanceRegistry<SoundType> {
private static final AudioRegistry INSTANCE = new AudioRegistry();
/**
* @return the instance
*/

View File

@@ -15,7 +15,7 @@
* 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.audio;
import ru.windcorp.progressia.common.resource.ResourceManager;
@@ -29,7 +29,10 @@ public class AudioSystem {
}
static void loadAudioData() {
AudioManager.loadSound(ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
"Progressia:BlockDestroy", AudioFormat.MONO);
AudioManager.loadSound(
ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
"Progressia:BlockDestroy",
AudioFormat.MONO
);
}
}

View File

@@ -15,15 +15,18 @@
* 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.audio;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.audio.backend.SoundType;
import ru.windcorp.progressia.client.audio.backend.Speaker;
public class Music extends Sound {
public class Music
extends Sound {
public Music(SoundType soundType, int timeLength, float pitch, float gain) {
super(soundType, timeLength, new Vec3(), new Vec3(), pitch, gain);
}
@@ -44,7 +47,7 @@ public class Music extends Sound {
protected Speaker initSpeaker() {
return AudioManager.initMusicSpeaker(soundType);
}
@Override
public void setPosition(Vec3 position) {
throw new UnsupportedOperationException();

View File

@@ -15,7 +15,7 @@
* 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.audio;
import glm.vec._3.Vec3;
@@ -29,9 +29,9 @@ public class Sound {
protected float pitch = 1.0f;
protected float gain = 1.0f;
protected int timeLength = 0;
protected SoundType soundType;
public Sound(SoundType soundType) {
this.soundType = soundType;
}
@@ -39,23 +39,37 @@ public class Sound {
public Sound(String id) {
this(AudioRegistry.getInstance().get(id));
}
public Sound(String id, int timeLength, Vec3 position, Vec3 velocity, float pitch, float gain) {
public Sound(
String id,
int timeLength,
Vec3 position,
Vec3 velocity,
float pitch,
float gain
) {
this(id);
this.position = position;
this.velocity = velocity;
this.pitch = pitch;
this.gain = gain;
}
public Sound(SoundType soundType, int timeLength, Vec3 position, Vec3 velocity, float pitch, float gain) {
public Sound(
SoundType soundType,
int timeLength,
Vec3 position,
Vec3 velocity,
float pitch,
float gain
) {
this(soundType);
this.position = position;
this.velocity = velocity;
this.pitch = pitch;
this.gain = gain;
}
protected Speaker initSpeaker() {
return AudioManager.initSpeaker(soundType);
}
@@ -105,7 +119,7 @@ public class Sound {
public float getPitch() {
return pitch;
}
public double getDuration() {
return soundType.getDuration();
}

View File

@@ -15,7 +15,7 @@
* 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.audio.backend;
import org.lwjgl.BufferUtils;
@@ -39,7 +39,12 @@ public class AudioReader {
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
return new SoundType(id, rawAudio, format, rateBuffer.get(0));
return new SoundType(
id,
rawAudio,
format,
rateBuffer.get(0)
);
}
public static SoundType readAsMono(Resource resource, String id) {
@@ -50,7 +55,15 @@ public class AudioReader {
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
}
private static ShortBuffer decodeVorbis(Resource dataToDecode, IntBuffer channelsBuffer, IntBuffer rateBuffer) {
return stb_vorbis_decode_memory(dataToDecode.readAsBytes(), channelsBuffer, rateBuffer);
private static ShortBuffer decodeVorbis(
Resource dataToDecode,
IntBuffer channelsBuffer,
IntBuffer rateBuffer
) {
return stb_vorbis_decode_memory(
dataToDecode.readAsBytes(),
channelsBuffer,
rateBuffer
);
}
}

View File

@@ -15,7 +15,7 @@
* 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.audio.backend;
import glm.vec._3.Vec3;
@@ -55,8 +55,9 @@ public class Listener {
if (isInWorld) {
if (wasInWorld) {
velocity.set(camera.getLastAnchorPosition()).sub(position)
.div((float) GraphicsInterface.getFrameLength());
velocity.set(camera.getLastAnchorPosition()).sub(position).div(
(float) GraphicsInterface.getFrameLength()
);
} else {
// If !wasInWorld, previous position is nonsence. Assume 0.
velocity.set(0);
@@ -71,9 +72,9 @@ public class Listener {
}
/*
* Only apply if there is a chance that params changed. This can only
* happen if we are in world now (isInWorld) or we just left world
* (wasInWorld, then we need to reset).
* Only apply if there is a chance that params changed.
* This can only happen if we are in world now (isInWorld) or we just
* left world (wasInWorld, then we need to reset).
*/
if (isInWorld || wasInWorld) {
applyParams();
@@ -90,7 +91,17 @@ public class Listener {
private void applyParams() {
alListener3f(AL_POSITION, position.x, position.y, position.z);
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
alListenerfv(AL_ORIENTATION, new float[] { oriAt.x, oriAt.y, oriAt.z, oriUp.x, oriUp.y, oriUp.z });
alListenerfv(
AL_ORIENTATION,
new float[] {
oriAt.x,
oriAt.y,
oriAt.z,
oriUp.x,
oriUp.y,
oriUp.z
}
);
}
}

View File

@@ -15,7 +15,7 @@
* 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.audio.backend;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
@@ -34,7 +34,12 @@ public class SoundType extends Namespaced {
private int audioBuffer;
private double duration;
public SoundType(String id, ShortBuffer rawAudio, int format, int sampleRate) {
public SoundType(
String id,
ShortBuffer rawAudio,
int format,
int sampleRate
) {
super(id);
this.rawAudio = rawAudio;
this.sampleRate = sampleRate;
@@ -51,7 +56,7 @@ public class SoundType extends Namespaced {
public void initSpeaker(Speaker speaker) {
speaker.setAudioData(audioBuffer);
}
public double getDuration() {
return duration;
}

View File

@@ -15,7 +15,7 @@
* 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.audio.backend;
import glm.vec._3.Vec3;
@@ -24,7 +24,9 @@ import static org.lwjgl.openal.AL11.*;
public class Speaker {
public enum State {
NOT_PLAYING, PLAYING, PLAYING_LOOP
NOT_PLAYING,
PLAYING,
PLAYING_LOOP
}
// Buffers
@@ -47,7 +49,13 @@ public class Speaker {
setAudioData(audioData);
}
public Speaker(int audioData, Vec3 position, Vec3 velocity, float pitch, float gain) {
public Speaker(
int audioData,
Vec3 position,
Vec3 velocity,
float pitch,
float gain
) {
setAudioData(audioData);
setPosition(position);
setVelocity(velocity);
@@ -55,7 +63,12 @@ public class Speaker {
setGain(gain);
}
public Speaker(Vec3 position, Vec3 velocity, float pitch, float gain) {
public Speaker(
Vec3 position,
Vec3 velocity,
float pitch,
float gain
) {
setPosition(position);
setVelocity(velocity);
setPitch(pitch);

View File

@@ -15,7 +15,7 @@
* 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.comms;
import java.io.IOException;
@@ -39,7 +39,9 @@ public class DefaultClientCommsListener implements CommsListener {
@Override
public void onPacketReceived(Packet packet) {
if (packet instanceof PacketAffectWorld) {
((PacketAffectWorld) packet).apply(getClient().getWorld().getData());
((PacketAffectWorld) packet).apply(
getClient().getWorld().getData()
);
} else if (packet instanceof PacketSetLocalPlayer) {
setLocalPlayer((PacketSetLocalPlayer) packet);
}

View File

@@ -15,7 +15,7 @@
* 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.comms;
import ru.windcorp.progressia.common.comms.CommsChannel;

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import ru.windcorp.progressia.client.graphics.input.InputEvent;

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import java.util.function.BiConsumer;
@@ -33,12 +33,17 @@ public class ControlTriggerLambda extends ControlTriggerInputBased {
private final Predicate<InputEvent> predicate;
private final BiConsumer<InputEvent, ControlData> dataWriter;
public ControlTriggerLambda(String id, Predicate<InputEvent> predicate,
BiConsumer<InputEvent, ControlData> dataWriter) {
public ControlTriggerLambda(
String id,
Predicate<InputEvent> predicate,
BiConsumer<InputEvent, ControlData> dataWriter
) {
super(id);
this.packetId = NamespacedUtil.getId(NamespacedUtil.getNamespace(id),
"ControlKeyPress" + NamespacedUtil.getName(id));
this.packetId = NamespacedUtil.getId(
NamespacedUtil.getNamespace(id),
"ControlKeyPress" + NamespacedUtil.getName(id)
);
this.predicate = predicate;
this.dataWriter = dataWriter;
@@ -49,7 +54,10 @@ public class ControlTriggerLambda extends ControlTriggerInputBased {
if (!predicate.test(event))
return null;
PacketControl packet = new PacketControl(packetId, ControlDataRegistry.getInstance().create(getId()));
PacketControl packet = new PacketControl(
packetId,
ControlDataRegistry.getInstance().create(getId())
);
dataWriter.accept(event, packet.getControl());

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import java.util.function.Consumer;
@@ -29,7 +29,11 @@ public class ControlTriggerLocalLambda extends ControlTriggerInputBased {
private final Predicate<InputEvent> predicate;
private final Consumer<InputEvent> action;
public ControlTriggerLocalLambda(String id, Predicate<InputEvent> predicate, Consumer<InputEvent> action) {
public ControlTriggerLocalLambda(
String id,
Predicate<InputEvent> predicate,
Consumer<InputEvent> action
) {
super(id);
this.predicate = predicate;

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;

View File

@@ -15,7 +15,7 @@
* 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.comms.controls;
import java.util.function.BiConsumer;
@@ -27,119 +27,216 @@ import ru.windcorp.progressia.common.comms.controls.ControlData;
public class ControlTriggers {
public static ControlTriggerInputBased of(String id, BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent> predicate) {
public static ControlTriggerInputBased of(
String id,
BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent> predicate
) {
return new ControlTriggerLambda(id, predicate, dataWriter);
}
public static ControlTriggerInputBased of(String id, Consumer<ControlData> dataWriter,
Predicate<InputEvent> predicate) {
return of(id, (input, control) -> dataWriter.accept(control), predicate);
public static ControlTriggerInputBased of(
String id,
Consumer<ControlData> dataWriter,
Predicate<InputEvent> predicate
) {
return of(
id,
(input, control) -> dataWriter.accept(control),
predicate
);
}
public static ControlTriggerInputBased of(String id, Predicate<InputEvent> predicate) {
return of(id, (input, control) -> {
}, predicate);
public static ControlTriggerInputBased of(
String id,
Predicate<InputEvent> predicate
) {
return of(
id,
(input, control) -> {
},
predicate
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
BiConsumer<I, ControlData> dataWriter, Predicate<I>... predicates) {
return of(id, createCheckedDataWriter(inputType, dataWriter),
createCheckedCompoundPredicate(inputType, predicates));
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
BiConsumer<I, ControlData> dataWriter,
Predicate<I>... predicates
) {
return of(
id,
createCheckedDataWriter(inputType, dataWriter),
createCheckedCompoundPredicate(inputType, predicates)
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
Consumer<ControlData> dataWriter, Predicate<I>... predicates) {
return of(id, inputType, (input, control) -> dataWriter.accept(control), predicates);
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
Consumer<ControlData> dataWriter,
Predicate<I>... predicates
) {
return of(
id,
inputType,
(input, control) -> dataWriter.accept(control),
predicates
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Class<I> inputType,
Predicate<I>... predicates) {
return of(id, (input, control) -> {
}, createCheckedCompoundPredicate(inputType, predicates));
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Class<I> inputType,
Predicate<I>... predicates
) {
return of(
id,
(input, control) -> {
},
createCheckedCompoundPredicate(inputType, predicates)
);
}
@SafeVarargs
public static ControlTriggerInputBased of(String id, BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent>... predicates) {
return of(id, InputEvent.class, dataWriter, predicates);
public static ControlTriggerInputBased of(
String id,
BiConsumer<InputEvent, ControlData> dataWriter,
Predicate<InputEvent>... predicates
) {
return of(
id,
InputEvent.class,
dataWriter,
predicates
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased of(String id, Consumer<ControlData> dataWriter,
Predicate<InputEvent>... predicates) {
return of(id, (input, control) -> dataWriter.accept(control), predicates);
public static <I extends InputEvent> ControlTriggerInputBased of(
String id,
Consumer<ControlData> dataWriter,
Predicate<InputEvent>... predicates
) {
return of(
id,
(input, control) -> dataWriter.accept(control),
predicates
);
}
@SafeVarargs
public static ControlTriggerInputBased of(String id, Predicate<InputEvent>... predicates) {
return of(id, InputEvent.class, (input, control) -> {
}, predicates);
public static ControlTriggerInputBased of(
String id,
Predicate<InputEvent>... predicates
) {
return of(
id,
InputEvent.class,
(input, control) -> {
},
predicates
);
}
//
//
///
///
//
//
//
//
//
//
//
//
//
public static ControlTriggerInputBased localOf(String id, Consumer<InputEvent> action,
Predicate<InputEvent> predicate) {
public static ControlTriggerInputBased localOf(
String id,
Consumer<InputEvent> action,
Predicate<InputEvent> predicate
) {
return new ControlTriggerLocalLambda(id, predicate, action);
}
public static ControlTriggerInputBased localOf(String id, Runnable action, Predicate<InputEvent> predicate) {
return localOf(id, input -> action.run(), predicate);
public static ControlTriggerInputBased localOf(
String id,
Runnable action,
Predicate<InputEvent> predicate
) {
return localOf(
id,
input -> action.run(),
predicate
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Class<I> inputType,
Consumer<I> action, Predicate<I>... predicates) {
return localOf(id, createCheckedAction(inputType, action),
createCheckedCompoundPredicate(inputType, predicates));
public static <I extends InputEvent> ControlTriggerInputBased localOf(
String id,
Class<I> inputType,
Consumer<I> action,
Predicate<I>... predicates
) {
return localOf(
id,
createCheckedAction(inputType, action),
createCheckedCompoundPredicate(inputType, predicates)
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Class<I> inputType,
Runnable action, Predicate<I>... predicates) {
return localOf(id, inputType, input -> action.run(), predicates);
public static <I extends InputEvent> ControlTriggerInputBased localOf(
String id,
Class<I> inputType,
Runnable action,
Predicate<I>... predicates
) {
return localOf(
id,
inputType,
input -> action.run(),
predicates
);
}
@SafeVarargs
public static ControlTriggerInputBased localOf(String id, Consumer<InputEvent> action,
Predicate<InputEvent>... predicates) {
return localOf(id, InputEvent.class, action, predicates);
public static ControlTriggerInputBased localOf(
String id,
Consumer<InputEvent> action,
Predicate<InputEvent>... predicates
) {
return localOf(
id,
InputEvent.class,
action,
predicates
);
}
@SafeVarargs
public static <I extends InputEvent> ControlTriggerInputBased localOf(String id, Runnable action,
Predicate<InputEvent>... predicates) {
return of(id, input -> action.run(), predicates);
public static <I extends InputEvent> ControlTriggerInputBased localOf(
String id,
Runnable action,
Predicate<InputEvent>... predicates
) {
return of(
id,
input -> action.run(),
predicates
);
}
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
Class<I> inputType, BiConsumer<I, ControlData> dataWriter) {
Class<I> inputType,
BiConsumer<I, ControlData> dataWriter
) {
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
}
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(Class<I> inputType,
Consumer<I> action) {
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(
Class<I> inputType,
Consumer<I> action
) {
return inputEvent -> action.accept(inputType.cast(inputEvent));
}
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(Class<I> inputType,
Predicate<I>[] predicates) {
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
Class<I> inputType,
Predicate<I>[] predicates
) {
return new CompoundCastPredicate<>(inputType, predicates);
}

View File

@@ -15,11 +15,11 @@
* 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.comms.controls;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.common.comms.packets.Packet;
public class InputBasedControls {
@@ -32,15 +32,16 @@ public class InputBasedControls {
this.client = client;
this.controls = ControlTriggerRegistry.getInstance().values().stream()
.filter(ControlTriggerInputBased.class::isInstance).toArray(ControlTriggerInputBased[]::new);
.filter(ControlTriggerInputBased.class::isInstance)
.toArray(ControlTriggerInputBased[]::new);
}
public void handleInput(Input input) {
public void handleInput(InputEvent event) {
for (ControlTriggerInputBased c : controls) {
Packet packet = c.onInputEvent(input.getEvent());
Packet packet = c.onInputEvent(event);
if (packet != null) {
input.consume();
event.consume();
client.getComms().sendPacket(packet);
break;
}

View File

@@ -21,6 +21,7 @@ package ru.windcorp.progressia.client.comms.localhost;
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
import ru.windcorp.progressia.common.comms.packets.Packet;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.ServerState;
public class LocalServerCommsChannel extends ServerCommsChannel {
@@ -34,7 +35,11 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
public void connect(String login) {
setState(State.CONNECTED);
this.localClient = new LocalClient(server.getClientManager().grabClientId(), login, this);
this.localClient = new LocalClient(
server.getClientManager().grabClientId(),
login,
this
);
server.getClientManager().addClient(localClient);
}
@@ -50,7 +55,7 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
@Override
public void disconnect() {
// Do nothing
ServerState.getInstance().getClientManager().disconnectClient(localClient);
}
}

View File

@@ -21,16 +21,10 @@ package ru.windcorp.progressia.client.graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.common.eventbus.Subscribe;
import java.util.Objects;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.CursorEvent;
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.WheelEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
public class GUI {
@@ -43,21 +37,13 @@ public class GUI {
}
private static final List<LayerStackModification> MODIFICATION_QUEUE = Collections
.synchronizedList(new ArrayList<>());
private static class ModifiableInput extends Input {
@Override
public void initialize(InputEvent event, Target target) {
super.initialize(event, target);
}
}
private static final ModifiableInput THE_INPUT = new ModifiableInput();
.synchronizedList(new ArrayList<>());
private GUI() {
}
public static void addBottomLayer(Layer layer) {
Objects.requireNonNull(layer, "layer");
modify(layers -> {
layers.add(layer);
layer.onAdded();
@@ -65,6 +51,7 @@ public class GUI {
}
public static void addTopLayer(Layer layer) {
Objects.requireNonNull(layer, "layer");
modify(layers -> {
layers.add(0, layer);
layer.onAdded();
@@ -72,6 +59,7 @@ public class GUI {
}
public static void removeLayer(Layer layer) {
Objects.requireNonNull(layer, "layer");
modify(layers -> {
layers.remove(layer);
layer.onRemoved();
@@ -88,33 +76,33 @@ public class GUI {
public static void render() {
synchronized (LAYERS) {
if (!MODIFICATION_QUEUE.isEmpty()) {
MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS));
MODIFICATION_QUEUE.clear();
boolean isMouseCurrentlyCaptured = GraphicsInterface.isMouseCaptured();
Layer.CursorPolicy policy = Layer.CursorPolicy.REQUIRE;
for (Layer layer : LAYERS) {
Layer.CursorPolicy currentPolicy = layer.getCursorPolicy();
if (currentPolicy != Layer.CursorPolicy.INDIFFERENT) {
policy = currentPolicy;
break;
}
}
boolean shouldCaptureMouse = (policy == Layer.CursorPolicy.FORBID);
if (shouldCaptureMouse != isMouseCurrentlyCaptured) {
GraphicsInterface.setMouseCaptured(shouldCaptureMouse);
}
}
for (int i = LAYERS.size() - 1; i >= 0; --i) {
LAYERS.get(i).render();
}
}
}
@@ -122,43 +110,12 @@ public class GUI {
LAYERS.forEach(Layer::invalidate);
}
private static void dispatchInputEvent(InputEvent event) {
Input.Target target;
if (event instanceof KeyEvent) {
if (((KeyEvent) event).isMouse()) {
target = Input.Target.HOVERED;
} else {
target = Input.Target.FOCUSED;
public static void dispatchInput(InputEvent event) {
synchronized (LAYERS) {
for (int i = 0; i < LAYERS.size(); ++i) {
LAYERS.get(i).handleInput(event);
}
} else if (event instanceof CursorEvent) {
target = Input.Target.HOVERED;
} else if (event instanceof WheelEvent) {
target = Input.Target.HOVERED;
} else if (event instanceof FrameResizeEvent) {
return;
} else {
target = Input.Target.ALL;
}
THE_INPUT.initialize(event, target);
LAYERS.forEach(l -> l.handleInput(THE_INPUT));
}
public static Object getEventSubscriber() {
return new Object() {
@Subscribe
public void onFrameResized(FrameResizeEvent event) {
GUI.invalidateEverything();
}
@Subscribe
public void onInput(InputEvent event) {
dispatchInputEvent(event);
}
};
}
}

View File

@@ -15,13 +15,13 @@
* 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;
import java.util.concurrent.atomic.AtomicBoolean;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
public abstract class Layer {
@@ -106,7 +106,7 @@ public abstract class Layer {
protected abstract void doRender();
protected abstract void handleInput(Input input);
public abstract void handleInput(InputEvent input);
protected int getWidth() {
return GraphicsInterface.getFrameWidth();

View File

@@ -15,7 +15,7 @@
* 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 java.util.ArrayDeque;

View File

@@ -15,10 +15,11 @@
* 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 glm.vec._2.i.Vec2i;
import org.lwjgl.glfw.GLFWVidMode;
import static org.lwjgl.glfw.GLFW.*;
@@ -42,6 +43,13 @@ public class GraphicsBackend {
private static boolean vSyncEnabled = false;
private static boolean isGLFWInitialized = false;
private static boolean isOpenGLInitialized = false;
private static boolean allowDisablingCursor;
static {
String key = GraphicsBackend.class.getName() + ".allowDisablingCursor";
allowDisablingCursor = Boolean.parseBoolean(System.getProperty(key, "true"));
}
private static boolean forceCursorToCenter = false;
private GraphicsBackend() {
}
@@ -61,7 +69,7 @@ public class GraphicsBackend {
static void setOpenGLInitialized(boolean isOpenGLInitialized) {
GraphicsBackend.isOpenGLInitialized = isOpenGLInitialized;
}
public static void initialize() {
startRenderThread();
}
@@ -114,6 +122,10 @@ public class GraphicsBackend {
frameLength = now - frameStart;
frameStart = now;
}
if (forceCursorToCenter) {
glfwSetCursorPos(windowHandle, FRAME_SIZE.x / 2.0, FRAME_SIZE.y / 2.0);
}
}
static void endFrame() {
@@ -159,14 +171,27 @@ public class GraphicsBackend {
public static void setFullscreen() {
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(getWindowHandle(), glfwGetPrimaryMonitor(), 0, 0, vidmode.width(), vidmode.height(), 0);
glfwSetWindowMonitor(
getWindowHandle(),
glfwGetPrimaryMonitor(),
0,
0,
vidmode.width(),
vidmode.height(),
0);
isFullscreen = true;
}
public static void setWindowed() {
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(getWindowHandle(), 0, (vidmode.width() - getFrameWidth()) / 2,
(vidmode.height() - getFrameHeight()) / 2, getFrameWidth(), getFrameHeight(), 0);
glfwSetWindowMonitor(
getWindowHandle(),
0,
(vidmode.width() - getFrameWidth()) / 2,
(vidmode.height() - getFrameHeight()) / 2,
getFrameWidth(),
getFrameHeight(),
0);
isFullscreen = false;
}
@@ -181,10 +206,18 @@ public class GraphicsBackend {
}
public static boolean isMouseCaptured() {
if (!allowDisablingCursor) {
return forceCursorToCenter;
}
return glfwGetInputMode(windowHandle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
}
public static void setMouseCaptured(boolean capture) {
if (!allowDisablingCursor) {
forceCursorToCenter = capture;
return;
}
int mode = capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
glfwSetInputMode(windowHandle, GLFW_CURSOR, mode);

View File

@@ -15,7 +15,7 @@
* 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 glm.vec._2.i.Vec2i;
@@ -68,6 +68,10 @@ public class GraphicsInterface {
public static void subscribeToInputEvents(Object listener) {
InputHandler.register(listener);
}
public static void unsubscribeFromInputEvents(Object listener) {
InputHandler.unregister(listener);
}
public static void startNextLayer() {
GraphicsBackend.startNextLayer();

View File

@@ -15,7 +15,7 @@
* 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;
@@ -39,6 +39,7 @@ public class InputHandler {
public void initialize(int key, int scancode, int action, int mods) {
this.setTime(GraphicsInterface.getTime());
this.setConsumed(false);
this.key = key;
this.scancode = scancode;
this.action = action;
@@ -49,11 +50,17 @@ public class InputHandler {
private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent();
static void handleKeyInput(long window, int key, int scancode, int action, int mods) {
static void handleKeyInput(
long window,
int key,
int scancode,
int action,
int mods
) {
if (GraphicsBackend.getWindowHandle() != window)
return;
THE_KEY_EVENT.initialize(key, scancode, action, mods);
dispatch(THE_KEY_EVENT);
INPUT_EVENT_BUS.post(THE_KEY_EVENT);
switch (action) {
case GLFW.GLFW_PRESS:
@@ -65,7 +72,12 @@ public class InputHandler {
}
}
static void handleMouseButtonInput(long window, int key, int action, int mods) {
static void handleMouseButtonInput(
long window,
int key,
int action,
int mods
) {
handleKeyInput(window, key, Integer.MAX_VALUE - key, action, mods);
}
@@ -79,6 +91,7 @@ public class InputHandler {
public void initialize(double x, double y) {
this.setTime(GraphicsInterface.getTime());
this.setConsumed(false);
getNewPosition().set(x, y);
}
@@ -86,7 +99,11 @@ public class InputHandler {
private static final ModifiableCursorMoveEvent THE_CURSOR_MOVE_EVENT = new ModifiableCursorMoveEvent();
static void handleMouseMoveInput(long window, double x, double y) {
static void handleMouseMoveInput(
long window,
double x,
double y
) {
if (GraphicsBackend.getWindowHandle() != window)
return;
y = GraphicsInterface.getFrameHeight() - y; // Flip y axis
@@ -94,7 +111,7 @@ public class InputHandler {
InputTracker.initializeCursorPosition(x, y);
THE_CURSOR_MOVE_EVENT.initialize(x, y);
dispatch(THE_CURSOR_MOVE_EVENT);
INPUT_EVENT_BUS.post(THE_CURSOR_MOVE_EVENT);
InputTracker.getCursorPosition().set(x, y);
}
@@ -109,6 +126,7 @@ public class InputHandler {
public void initialize(double xOffset, double yOffset) {
this.setTime(GraphicsInterface.getTime());
this.setConsumed(false);
this.getOffset().set(xOffset, yOffset);
}
@@ -116,11 +134,15 @@ public class InputHandler {
private static final ModifiableWheelScrollEvent THE_WHEEL_SCROLL_EVENT = new ModifiableWheelScrollEvent();
static void handleWheelScroll(long window, double xoffset, double yoffset) {
static void handleWheelScroll(
long window,
double xoffset,
double yoffset
) {
if (GraphicsBackend.getWindowHandle() != window)
return;
THE_WHEEL_SCROLL_EVENT.initialize(xoffset, yoffset);
dispatch(THE_WHEEL_SCROLL_EVENT);
INPUT_EVENT_BUS.post(THE_WHEEL_SCROLL_EVENT);
}
// FrameResizeEvent
@@ -133,6 +155,7 @@ public class InputHandler {
public void initialize(int width, int height) {
this.setTime(GraphicsInterface.getTime());
this.setConsumed(false);
this.getNewSize().set(width, height);
}
@@ -143,19 +166,22 @@ public class InputHandler {
/*
* NB: this is NOT a GLFW callback, the raw callback is in GraphicsBackend
*/
static void handleFrameResize(int width, int height) {
static void handleFrameResize(
int width,
int height
) {
THE_FRAME_RESIZE_EVENT.initialize(width, height);
dispatch(THE_FRAME_RESIZE_EVENT);
INPUT_EVENT_BUS.post(THE_FRAME_RESIZE_EVENT);
}
// Misc
private static void dispatch(InputEvent event) {
INPUT_EVENT_BUS.post(event);
}
public static void register(Object listener) {
INPUT_EVENT_BUS.register(listener);
}
public static void unregister(Object listener) {
INPUT_EVENT_BUS.unregister(listener);
}
}

View File

@@ -15,7 +15,7 @@
* 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 glm.vec._2.d.Vec2d;
@@ -24,7 +24,10 @@ import gnu.trove.set.hash.TIntHashSet;
public class InputTracker {
private static final Vec2d CURSOR_POSITION = new Vec2d(Double.NaN, Double.NaN);
private static final Vec2d CURSOR_POSITION = new Vec2d(
Double.NaN,
Double.NaN
);
private static final TIntSet PRESSED_KEYS = new TIntHashSet(256);

View File

@@ -22,9 +22,23 @@ import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.*;
import java.io.IOException;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.opengl.GL;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.Progressia;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
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 {
@@ -59,7 +73,13 @@ class LWJGLInitializer {
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
long handle = glfwCreateWindow(900, 900, "ProgressiaTest", NULL, NULL);
long handle = glfwCreateWindow(
800,
600,
Progressia.getName() + " " + Progressia.getFullerVersion(),
NULL,
NULL
);
// TODO Check that handle != NULL
@@ -75,8 +95,30 @@ class LWJGLInitializer {
}
private static void createWindowIcons() {
// TODO Auto-generated method stub
if (glfwGetVersionString().toLowerCase().contains("wayland")) {
// glfwSetWindowIcon is not supported on Wayland
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() {
@@ -92,16 +134,35 @@ class LWJGLInitializer {
private static void setupWindowCallbacks() {
long handle = GraphicsBackend.getWindowHandle();
glfwSetFramebufferSizeCallback(handle, GraphicsBackend::onFrameResized);
glfwSetFramebufferSizeCallback(
handle,
GraphicsBackend::onFrameResized
);
glfwSetKeyCallback(handle, InputHandler::handleKeyInput);
glfwSetMouseButtonCallback(handle, InputHandler::handleMouseButtonInput);
glfwSetMouseButtonCallback(
handle,
InputHandler::handleMouseButtonInput
);
glfwSetCursorPosCallback(handle, InputHandler::handleMouseMoveInput);
glfwSetScrollCallback(handle, InputHandler::handleWheelScroll);
GraphicsInterface.subscribeToInputEvents(GUI.getEventSubscriber());
GraphicsInterface.subscribeToInputEvents(new Object() {
@Subscribe
public void onFrameResized(FrameResizeEvent event) {
GUI.invalidateEverything();
}
@Subscribe
public void onInputEvent(InputEvent event) {
GUI.dispatchInput(event);
}
});
}
}

View File

@@ -15,7 +15,7 @@
* 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 java.lang.ref.PhantomReference;
@@ -34,13 +34,19 @@ public class OpenGLObjectTracker {
private static final ReferenceQueue<OpenGLDeletable> DELETE_QUEUE = new ReferenceQueue<>();
public synchronized static void register(OpenGLDeletable object, IntConsumer glDeleter) {
GLPhantomReference<OpenGLDeletable> glRef = new GLPhantomReference<>(object, DELETE_QUEUE, object.getHandle(),
glDeleter);
GLPhantomReference<OpenGLDeletable> glRef = new GLPhantomReference<>(
object,
DELETE_QUEUE,
object.getHandle(),
glDeleter
);
TO_DELETE.add(glRef);
}
public static void deleteAllObjects() {
for (GLPhantomReference<OpenGLDeletable> glRef : TO_DELETE) {
for (
GLPhantomReference<OpenGLDeletable> glRef : TO_DELETE
) {
glRef.clear();
}
}
@@ -69,16 +75,20 @@ public class OpenGLObjectTracker {
* It is possible to create a phantom reference with a {@code null}
* queue, but such a reference is completely useless: Its {@code get}
* method will always return {@code null} and, since it does not have a
* queue, it will never be enqueued.
* queue,
* it will never be enqueued.
*
* @param referent
* the object the new phantom reference will refer to
* @param q
* the queue with which the reference is to be registered, or
* {@code null} if registration is not required
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be
* registered,
* or {@code null} if registration is not required
*/
public GLPhantomReference(T referent, ReferenceQueue<? super T> q, int referentGLhandle,
IntConsumer GLDeleter) {
public GLPhantomReference(
T referent,
ReferenceQueue<? super T> q,
int referentGLhandle,
IntConsumer GLDeleter
) {
super(referent, q);
this.referentGLhandle = referentGLhandle;
this.GLDeleter = GLDeleter;

View File

@@ -15,7 +15,7 @@
* 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 ru.windcorp.jputil.functions.ThrowingRunnable;
@@ -41,7 +41,11 @@ public class RenderTaskQueue {
HANDLER.invokeNow(task);
}
public static <E extends Exception> void waitAndInvoke(ThrowingRunnable<E> task) throws InterruptedException, E {
public static <E extends Exception> void waitAndInvoke(
ThrowingRunnable<E> task
)
throws InterruptedException,
E {
HANDLER.waitAndInvoke(task);
}

View File

@@ -15,7 +15,7 @@
* 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 static org.lwjgl.glfw.GLFW.*;

Some files were not shown because too many files have changed in this diff Show More