SDK restructure, package reorganization, new ModLoader, new resource system

This commit is contained in:
Natty 2022-04-05 19:10:08 +02:00
parent 321a8a66ed
commit 0d80466602
No known key found for this signature in database
GPG Key ID: 40AB22FA416C7019
280 changed files with 5165 additions and 3376 deletions

0
.gitattributes vendored Normal file → Executable file
View File

8
.gitignore vendored Normal file → Executable file
View File

@ -1,13 +1,17 @@
/*/.settings /*/.settings
/.vscode
# Ignore Eclipse project files # Ignore Eclipse project files
/*/.project /*/.project
/*/.classpath /*/.classpath
/.project /.project
/.settings
# Ignore IDEA project files # Ignore IDEA project files
/.idea /.idea
/*/.idea /*/.idea
.idea
*.log *.log
@ -19,3 +23,7 @@
# Ignore Gradle build output directory # Ignore Gradle build output directory
/build /build
/*/build /*/build
/bin
/*/bin
/logs/

2
LICENSE Normal file → Executable file
View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019-2021 493msi Copyright (c) 2019-2022 493msi
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

13
NEXT_RELEASE_DRAFT.md Normal file → Executable file
View File

@ -1,11 +1,4 @@
## Features targeted for 21.0.1.0-alpha.0 ## Features targeted for 22.0.1.0-alpha.0
* `[PlutoLib]` Completely redo the ModLoader system
* The current implementation is a result of 5 years of feature creep
* Large scale API changes, however the general idea should stay the same
* Rethink the class loader system.
* `[PlutoLib]` Redo the resource system
## Features targeted for 21.0.1.0-alpha.1
* The stage subsystem * The stage subsystem
* A "stage", in this context, is a set of assets bound together * A "stage", in this context, is a set of assets bound together
by programming logic, not necessarily a game level. by programming logic, not necessarily a game level.
@ -45,5 +38,5 @@
* Improve upon the support of thread-local Pluto instances * Improve upon the support of thread-local Pluto instances
* The long term goal is to allow an unlimited amount of Pluto applications at any given time * The long term goal is to allow an unlimited amount of Pluto applications at any given time
## Features targeted for 21.0.1.0-alpha.4 ## Features targeted for 22.0.1.0-alpha.1
* The initial minimal release of `[PlutoCommandParser]` * TBD

17
README.md Normal file → Executable file
View File

@ -25,19 +25,18 @@ version numbers.*
* **PlutoCore** - Stable * **PlutoCore** - Stable
* **PlutoFramebuffer** - Stable * **PlutoFramebuffer** - Stable
* **PlutoGUI** - Stable, awaiting a rewrite * **PlutoGUI** - Stable, awaiting a rewrite
* **PlutoLib** - Mostly stable, the module API still has some quirks
* **PlutoMesher** - Stable * **PlutoMesher** - Stable
* **PlutoShader** - Stable * **PlutoShader** - Stable
* **PlutoTexture** - Stable
* **PlutoSpriteSheet** - Stable, some features are unfinished * **PlutoSpriteSheet** - Stable, some features are unfinished
* **PlutoStatic** - Stable, collision API nowhere near completion * **PlutoDisplay** - Stable, collision API nowhere near completion
* **PlutoUSS2** - Stable * **PlutoUSS2** - Stable
* **PlutoLib** - Mostly stable
### Unstable submodules ### Unstable submodules
* **PlutoRuntime** - Somewhat tentative, the module API has been rewritten and might contain bugs
* **PlutoAudio** - Somewhat usable, unfinished * **PlutoAudio** - Somewhat usable, unfinished
### Broken submodules, do NOT use
* **PlutoCommandParser** - Unfinished, broken, unusable
* **PlutoDB** - Broken, unusable
## Current priorities ## Current priorities
@ -45,8 +44,8 @@ See `NEXT_RELEASE_DRAFT.md` for details.
### Very high priority ### Very high priority
[ *Implemented in the current release.* ] [ *Implemented in the current release.* ]
* Rewrite the ModLoader
* Streamline PlutoLib, remove bad APIs and improve code quality * Streamline PlutoLib, remove bad APIs and improve code quality
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
* The stage system and automated asset loading * The stage system and automated asset loading
### High priority ### High priority
@ -54,12 +53,10 @@ See `NEXT_RELEASE_DRAFT.md` for details.
* Rewrite PlutoGUI * Rewrite PlutoGUI
* Finish PlutoAudio * Finish PlutoAudio
* Depends on the stage system * Depends on the stage system
* Finish PlutoCommandParser
### Normal priority ### Normal priority
[ *Planned for an upcoming release.* ] [ *Planned for an upcoming release.* ]
* The collision system for PlutoStatic * The collision system for PlutoStatic
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
### Low priority ### Low priority
[ *Items not required immediately, planned to be implemented eventually.* ] [ *Items not required immediately, planned to be implemented eventually.* ]
@ -67,6 +64,10 @@ See `NEXT_RELEASE_DRAFT.md` for details.
* Alternatively, if this deems too difficult to implement, * Alternatively, if this deems too difficult to implement,
prohibit the creation of more than one instance per JVM to avoid issues prohibit the creation of more than one instance per JVM to avoid issues
* A networking API * A networking API
* Re-add support for external mod jars to the ModLoader
* This feature requires a full rewrite and possibly a complete overhaul
* Mods should have limited execution levels, for example restricted file access
or disabled native library loading (this is probably not possible)
* Expand upon the Color API * Expand upon the Color API
* Color mixing and blending * Color mixing and blending
* Color transformation * Color transformation

66
UPDATE_NOTES.md Normal file → Executable file
View File

@ -1,34 +1,58 @@
## 20.2.0.0-alpha.3 ## 20.2.0.0-alpha.3
* `[SDK]` Restructured the repository
* All build scripts are now written in Kotlin
* **Added runnabled examples**
* **Upgraded to Java 17** to take advantage of new language features and a more efficient JVM
* **The repostiory now contains examples**
* **Moved all classes to the `org.plutoengine` package**
* `[PlutoComponent]` **Added PlutoComponent as a new module**
* `[PlutoLib]` `PlutoLib` now depends on `PlutoComponent`
* `[PlutoUSS2]` **Added USS2 as a new module** * `[PlutoUSS2]` **Added USS2 as a new module**
* `[PlutoLib]` `PlutoLib` now depends on `PlutoUSS2` * `[PlutoLib]` `PlutoLib` now depends on `PlutoUSS2`
* `[PlutoLib]` *Removed* `Severity`, use `SmartSeverity` instead * `[PlutoLib]` **Greatly simplified the API and moved PlutoEngine specific classes to `PlutoRuntime`**
* `[PlutoLib]` *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream` * ***Moved* the module system to `PlutoRuntime`**
* `[PlutoLib]` *Removed* `StaticPlutoEventManager` as the implementation was too obscure * *Removed* `ResourceSubscriber`,
* *Removed* `cz.tefek.pluto.io.pluto.pp`
* *Removed* `RAID`
* *Moved* `Logger`, `OutputSplitStream` to `PlutoRuntime`
* *Removed* `Severity`, use `SmartSeverity` instead
* *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream`
* Use Java's NIO instead
* *Removed* `StaticPlutoEventManager` as the implementation was too obscure
* The module system now uses its own event management * The module system now uses its own event management
* *Removed* the `EventData` class * *Removed* the `EventData` class
* `[PlutoLib]` Made `OutputSplitStream` public as it is now reusable * `[PlutoRuntime]` **Added PlutoRuntime as a new module**
* `[PlutoLib]` Added the `@ConstantExpression` annotation * **Completely rewrote the module system**
* `[PlutoLib]` The `RAID#getIDOf` method now returns `OptionalInt` to avoid NPEs * *Removed* support for external mods as the feature needs a complete overhaul
* `[PlutoLib]` Added an `equals` implementation to `ResourceAddress` * **Revamped resource system now based on NIO**
* `[PlutoStatic]` Added the `ModGLFW` virtual module * *Moved* the logging system from `PlutoLib` to `PlutoRuntime`
* `[PlutoLib]` The transitive dependency JOML is now provided by `PlutoLib` instead of `PlutoStatic` * Made `OutputSplitStream` public as it is now reusable
* `[PlutoLib]` Created a simple Color API * **Added the Version API**
* `[PlutoShader]` Added the 8-bit RGBA `Color` class as a counterpart to AWT's `Color` class
* `[PlutoShader]` Added the `RGBA` and `RGB` single precision float color objects
* `[PlutoShader]` Added the respective `IRGBA` and `IRGB` read-only interfaces
* `[PlutoShader]` Added the `HSBA` and `HSB` single precision float color objects
* `[PlutoShader]` Added methods to convert between HSBA, RGBA, HSB and RGB
* `[PlutoShader]` Added the `UniformRGBA` and `UniformRGB` shader uniform types
* `[PlutoLib]` Created the Version API
* Added the `IVersion` interface * Added the `IVersion` interface
* Added support for version objects * Added support for version objects
* As a result, all fields in `Pluto` except the version string are no longer compile-time constants * As a result, all fields in `Pluto` except the version string are no longer compile-time constants
* `[PlutoDisplay]` **Renamed `PlutoStatic` to `PlutoDisplay`**
* Added the `ModGLFW` virtual module
* `DisplayErrorCallback` and simplified the callbacks in `Display`
* `[PlutoCommandParser]` **Module discontinued as a part of PlutoEngine, it will still be developed seprately**
* `[PlutoTexturing]` Renamed to `PlutoTexture`
* Removed `Texture#load(String)` and `Texture#load(String, MagFilter, MinFilter, WrapMode...)`
* `[PlutoLib]` Added the `@ConstantExpression` annotation
* `[PlutoLib]` The transitive dependency JOML is now provided by `PlutoLib` instead of `PlutoStatic`
* `[PlutoLib]` Created a simple Color API
* `[PlutoLib]` Added the 8-bit RGBA `Color` class as a counterpart to AWT's `Color` class
* `[PlutoLib]` Added the `RGBA` and `RGB` single precision float color objects
* `[PlutoLib]` Added the respective `IRGBA` and `IRGB` read-only interfaces
* `[PlutoLib]` Added the `HSBA` and `HSB` single precision float color objects
* `[PlutoLib]` Added methods to convert between HSBA, RGBA, HSB and RGB
* `[PlutoShader]` Added the `UniformRGBA` and `UniformRGB` shader uniform types
* `[PlutoCore]` Made `PlutoApplication`'s constructor private * `[PlutoCore]` Made `PlutoApplication`'s constructor private
* `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message * `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message
* `build.gradle` *Removed* the prepackaged JVM wrapper introduced in the previous alpha
Awaiting implementation: as it caused numerous issues
* `[PlutoLib]` Moved `cz.tefek.pluto.io.pluto.pp` to `cz.tefek.pluto.io.plutopackage` * In the future, JDKs will be packaged with the SDK
* `[PlutoLib]` Completely reworked the module system
## 20.2.0.0-alpha.2 ## 20.2.0.0-alpha.2
* `build.gradle` Extracted the version numbers into separate variables * `build.gradle` Extracted the version numbers into separate variables

View File

@ -1,83 +0,0 @@
import org.gradle.internal.os.OperatingSystem
plugins {
id "me.filippov.gradle.jvm.wrapper" version "0.9.3"
}
jvmWrapper {
linuxJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-linux-jdk.tar.gz"
macJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-macos-jdk.tar.gz"
windowsJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-windows-jdk.zip"
}
wrapper {
distributionType = Wrapper.DistributionType.ALL
}
project.ext.versionYear = 20
project.ext.versionMajor = 2
project.ext.versionMinor = 0
project.ext.versionPatch = 0
project.ext.isPrerelease = true
project.ext.prereleaseName = "alpha"
project.ext.prerealeaseUpdate = 3
subprojects {
apply plugin: 'java'
apply plugin: 'maven-publish'
project.ext.lwjglVersion = "3.2.3"
project.ext.jomlVersion = "1.9.25"
project.ext.steamworks4jVersion = "1.8.0"
project.ext.steamworks4jServerVersion = "1.8.0"
group = "cz.tefek"
version = isPrerelease ?
"${versionYear}.${versionMajor}.${versionMinor}.${versionPatch}-${prereleaseName}.${prerealeaseUpdate}"
:
"${versionYear}.${versionMajor}.${versionMinor}.${versionPatch}"
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
task sourcesJar(type: Jar, dependsOn: classes) {
from sourceSets.main.allJava
}
publishing {
publications {
maven(MavenPublication) {
from components.java
artifact sourcesJar {
classifier "sources"
}
}
}
}
switch (OperatingSystem.current()) {
case OperatingSystem.LINUX:
project.ext.lwjglNatives = "natives-linux"
break
case OperatingSystem.MAC_OS:
project.ext.lwjglNatives = "natives-macos"
break
case OperatingSystem.WINDOWS:
project.ext.lwjglNatives = "natives-windows"
break
}
repositories {
mavenCentral()
}
}

12
build.gradle.kts Executable file
View File

@ -0,0 +1,12 @@
tasks.withType<Wrapper> {
distributionType = Wrapper.DistributionType.ALL
gradleVersion = "7.4.2"
}
subprojects {
group = "cz.tefek"
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
}

24
buildSrc/build.gradle.kts Executable file
View File

@ -0,0 +1,24 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
}
}
dependencies {
implementation("org.jetbrains.kotlin", "kotlin-gradle-plugin", "1.6.20")
implementation(gradleApi())
}

View File

@ -0,0 +1,35 @@
package org.plutoengine
import org.gradle.internal.os.OperatingSystem
import org.gradle.api.JavaVersion
object Versions {
const val lwjglVersion = "3.3.0"
val lwjglNatives = when (OperatingSystem.current()) {
OperatingSystem.LINUX -> "natives-linux"
OperatingSystem.WINDOWS -> "natives-windows"
else -> throw Error("Unsupported operating system!")
}
const val jomlVersion = "1.10.2"
const val steamworks4jVersion = "1.8.0"
const val steamworks4jServerVersion = "1.8.0"
const val versionYear = 20
const val versionMajor = 2
const val versionMinor = 0
const val versionPatch = 0
const val isPrerelease = true
const val prereleaseName = "alpha"
const val prerealeaseUpdate = 3
val versionFull =
if (isPrerelease)
"$versionYear.$versionMajor.$versionMinor.$versionPatch-$prereleaseName.$prerealeaseUpdate"
else
"$versionYear.$versionMajor.$versionMinor.$versionPatch"
val javaTargetVersion = JavaVersion.VERSION_17
}

28
engine-core/.gitignore vendored Executable file
View File

@ -0,0 +1,28 @@
/*/.settings
/.vscode
# Ignore Eclipse project files
/*/.project
/*/.classpath
/.project
/.settings
# Ignore IDEA project files
/.idea
/*/.idea
.idea
*.log
/.gradle/
# Ignore Gradle project-specific cache directory
.gradle
# Ignore Gradle build output directory
/build
/*/build
/bin
/*/bin

32
engine-core/build.gradle.kts Executable file
View File

@ -0,0 +1,32 @@
import org.plutoengine.Versions
subprojects {
apply(plugin = "java")
apply(plugin = "java-library")
apply(plugin = "maven-publish")
repositories {
mavenCentral()
}
configure<JavaPluginExtension> {
sourceCompatibility = Versions.javaTargetVersion
targetCompatibility = Versions.javaTargetVersion
}
configure<SourceSetContainer> {
named("main") {
tasks.withType<Jar> {
from(allJava)
}
}
}
configure<PublishingExtension> {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
}

View File

@ -0,0 +1,16 @@
import org.plutoengine.Versions
plugins {
java
`java-library`
}
description = "PlutoEngine's sound subsystem."
dependencies {
api(project(":plutoengine:plutodisplay"))
api("org.lwjgl:lwjgl-openal")
runtimeOnly("org.lwjgl", "lwjgl-openal", classifier = Versions.lwjglNatives)
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio; package org.plutoengine.audio;
import org.lwjgl.stb.STBVorbis; import org.lwjgl.stb.STBVorbis;
import org.lwjgl.stb.STBVorbisInfo; import org.lwjgl.stb.STBVorbisInfo;
@ -10,67 +10,64 @@ import java.nio.ByteBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import cz.tefek.pluto.engine.buffer.BufferHelper; import org.plutoengine.buffer.BufferHelper;
import cz.tefek.pluto.io.asl.resource.ResourceAddress; import org.plutoengine.logger.Logger;
import cz.tefek.pluto.io.logger.Logger; import org.plutoengine.logger.SmartSeverity;
import cz.tefek.pluto.io.logger.SmartSeverity;
public class AudioLoader public class AudioLoader
{ {
/** /**
* Loads an audio track denoted by this {@link ResourceAddress} into memory * Loads an audio track denoted by this {@link Path} into memory
* for from-memory Vorbis decoding and streaming. Good for frequently used * for from-memory Vorbis decoding and streaming. Good for frequently used
* medium sized audio files, however it is discouraged to use such a track * medium-sized audio files, however it is discouraged to use such a track
* in multiple audio sources at once due to the cost of seeking. * in multiple audio sources at once due to the cost of seeking.
*/ */
public static ISeekableAudioTrack loadMemoryDecoded(ResourceAddress address) public static ISeekableAudioTrack loadMemoryDecoded(Path path)
{ {
Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", path);
try try
{ {
return new MemoryDecodedVorbisTrack(address); return new MemoryDecodedVorbisTrack(path);
} }
catch (IOException e) catch (IOException e)
{ {
Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", path);
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
} }
/** /**
* Loads an audio track denoted by this {@link ResourceAddress} into memory * Loads an audio track denoted by this {@link Path} into memory
* for from-memory PCM streaming. Good for frequently used small audio * for from-memory PCM streaming. Good for frequently used small audio
* files. * files.
*/ */
public static ISeekableAudioTrack loadMemoryPCM(ResourceAddress address) public static ISeekableAudioTrack loadMemoryPCM(Path path)
{ {
Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", path);
try try
{ {
return new MemoryPCMTrack(address); return new MemoryPCMTrack(path);
} }
catch (IOException e) catch (IOException e)
{ {
Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", path);
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
} }
private static ByteBuffer loadIntoMemory(ResourceAddress addr) throws IOException private static ByteBuffer loadIntoMemory(Path path) throws IOException
{ {
var path = addr.toNIOPath();
var size = Files.size(path); var size = Files.size(path);
if (size > Integer.MAX_VALUE) if (size > Integer.MAX_VALUE)
{ throw new IOException("File '%s' is too big to be loaded!".formatted(path));
throw new IOException("File '" + addr.toString() + "' is too big to be loaded!");
}
var readData = MemoryUtil.memAlloc((int) size); var readData = MemoryUtil.memAlloc((int) size);
@ -112,14 +109,14 @@ public class AudioLoader
private int sampleOffset = 0; private int sampleOffset = 0;
private MemoryPCMTrack(ResourceAddress address) throws IOException private MemoryPCMTrack(Path path) throws IOException
{ {
long handle = MemoryUtil.NULL; long handle = MemoryUtil.NULL;
ByteBuffer audioBytes = null; ByteBuffer audioBytes = null;
try (MemoryStack stack = MemoryStack.stackPush()) try (MemoryStack stack = MemoryStack.stackPush())
{ {
audioBytes = loadIntoMemory(address); audioBytes = loadIntoMemory(path);
IntBuffer error = stack.mallocInt(1); IntBuffer error = stack.mallocInt(1);
handle = STBVorbis.stb_vorbis_open_memory(audioBytes, error, null); handle = STBVorbis.stb_vorbis_open_memory(audioBytes, error, null);
@ -127,10 +124,10 @@ public class AudioLoader
if (handle == MemoryUtil.NULL) if (handle == MemoryUtil.NULL)
{ {
this.close(); this.close();
throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); throw new IOException(String.format("Failed to load '%s', error code %d.\n", path, error.get(0)));
} }
STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); STBVorbisInfo info = STBVorbisInfo.malloc(stack);
STBVorbis.stb_vorbis_get_info(handle, info); STBVorbis.stb_vorbis_get_info(handle, info);
this.channels = info.channels(); this.channels = info.channels();
@ -201,11 +198,11 @@ public class AudioLoader
private final ByteBuffer encodedAudio; private final ByteBuffer encodedAudio;
private MemoryDecodedVorbisTrack(ResourceAddress address) throws IOException private MemoryDecodedVorbisTrack(Path path) throws IOException
{ {
try try
{ {
this.encodedAudio = loadIntoMemory(address); this.encodedAudio = loadIntoMemory(path);
} }
catch (IOException e) catch (IOException e)
{ {
@ -221,10 +218,10 @@ public class AudioLoader
if (this.handle == MemoryUtil.NULL) if (this.handle == MemoryUtil.NULL)
{ {
this.close(); this.close();
throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); throw new IOException(String.format("Failed to load '%s', error code %d.\n", path, error.get(0)));
} }
STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); STBVorbisInfo info = STBVorbisInfo.malloc(stack);
STBVorbis.stb_vorbis_get_info(this.handle, info); STBVorbis.stb_vorbis_get_info(this.handle, info);
this.channels = info.channels(); this.channels = info.channels();
@ -234,7 +231,11 @@ public class AudioLoader
this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(this.handle); this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(this.handle);
Logger.logf(SmartSeverity.AUDIO, "\tSample rate:\t%d\n\t\tChannels:\t%d\n\t\tSamples:\t%d\n", this.sampleRate, this.channels, this.samplesLength); Logger.logf(SmartSeverity.AUDIO, """
\tSample rate:\t%d
\t\tChannels:\t%d
\t\tSamples:\t%d
%n""", this.sampleRate, this.channels, this.samplesLength);
} }
@Override @Override

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio; package org.plutoengine.audio;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio; package org.plutoengine.audio;
public interface ISeekableAudioTrack extends IAudioStream public interface ISeekableAudioTrack extends IAudioStream
{ {

View File

@ -1,23 +1,16 @@
package cz.tefek.pluto.engine.audio.al; package org.plutoengine.audio.al;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.openal.AL; import org.lwjgl.openal.*;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.ALC;
import org.lwjgl.openal.ALC10;
import org.lwjgl.openal.ALCCapabilities;
import org.lwjgl.openal.ALCapabilities;
import org.lwjgl.openal.EXTThreadLocalContext;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import javax.annotation.concurrent.ThreadSafe;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import javax.annotation.concurrent.ThreadSafe; import org.plutoengine.Pluto;
import org.plutoengine.logger.Logger;
import cz.tefek.pluto.Pluto; import org.plutoengine.logger.SmartSeverity;
import cz.tefek.pluto.io.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity;
/** /**
* @author 493msi * @author 493msi
@ -26,23 +19,11 @@ import cz.tefek.pluto.io.logger.SmartSeverity;
@ThreadSafe @ThreadSafe
public class AudioEngine public class AudioEngine
{ {
private static ThreadLocal<Long> device = new ThreadLocal<>() { private static final ThreadLocal<Long> device = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
@Override
protected Long initialValue()
{
return MemoryUtil.NULL;
}
};
private static ThreadLocal<Long> context = new ThreadLocal<>() { private static final ThreadLocal<Long> context = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
@Override
protected Long initialValue()
{
return MemoryUtil.NULL;
}
};
private static ThreadLocal<ALCapabilities> capabilities = new ThreadLocal<>(); private static final ThreadLocal<ALCapabilities> capabilities = new ThreadLocal<>();
public static void initialize() public static void initialize()
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio.al; package org.plutoengine.audio.al;
import org.joml.Vector3fc; import org.joml.Vector3fc;
import org.lwjgl.openal.AL10; import org.lwjgl.openal.AL10;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio.al; package org.plutoengine.audio.al;
import org.lwjgl.openal.AL10; import org.lwjgl.openal.AL10;
import org.lwjgl.openal.SOFTDirectChannels; import org.lwjgl.openal.SOFTDirectChannels;
@ -6,8 +6,8 @@ import org.lwjgl.system.MemoryUtil;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import cz.tefek.pluto.engine.audio.IAudioStream; import org.plutoengine.audio.ISeekableAudioTrack;
import cz.tefek.pluto.engine.audio.ISeekableAudioTrack; import org.plutoengine.audio.IAudioStream;
public class AudioTrack extends AudioSource public class AudioTrack extends AudioSource
{ {
@ -29,17 +29,11 @@ public class AudioTrack extends AudioSource
{ {
this.track = track; this.track = track;
switch (track.getChannels()) this.format = switch (track.getChannels()) {
{ case 1 -> AL10.AL_FORMAT_MONO16;
case 1: case 2 -> AL10.AL_FORMAT_STEREO16;
this.format = AL10.AL_FORMAT_MONO16; default -> throw new UnsupportedOperationException("Unsupported number of channels: " + track.getChannels());
break; };
case 2:
this.format = AL10.AL_FORMAT_STEREO16;
break;
default:
throw new UnsupportedOperationException("Unsupported number of channels: " + track.getChannels());
}
int bufferSize = track.getChannels() * BUFFER_SIZE_PER_CHANNEL; int bufferSize = track.getChannels() * BUFFER_SIZE_PER_CHANNEL;

View File

@ -1,10 +1,10 @@
package cz.tefek.pluto.engine.audio.util; package org.plutoengine.audio.util;
import org.lwjgl.stb.STBVorbis; import org.lwjgl.stb.STBVorbis;
import org.lwjgl.stb.STBVorbisInfo; import org.lwjgl.stb.STBVorbisInfo;
import cz.tefek.pluto.io.logger.Logger; import org.plutoengine.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity; import org.plutoengine.logger.SmartSeverity;
public class AudioUtil public class AudioUtil
{ {

View File

@ -0,0 +1,13 @@
plugins {
java
`java-library`
}
description = "A module acting as glue for all PlutoEngine components."
dependencies {
api("org.apache.commons:commons-lang3:3.12.0")
api("org.apache.commons:commons-collections4:4.4")
api("com.google.code.findbugs:jsr305:3.0.2")
}

View File

@ -0,0 +1,49 @@
package org.plutoengine.component;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
public abstract class AbstractComponent implements IComponent
{
private static final AtomicLong ID_SOURCE = new AtomicLong();
private final long id;
protected AbstractComponent()
{
this.id = ID_SOURCE.getAndIncrement();
}
@Override
public long getID()
{
return this.id;
}
@Override
public void onMount() throws Exception
{
}
@Override
public void onUnmount() throws Exception
{
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
AbstractComponent that = (AbstractComponent) o;
return this.id == that.id;
}
@Override
public int hashCode()
{
return Objects.hash(this.id);
}
}

View File

@ -0,0 +1,140 @@
package org.plutoengine.component;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import org.apache.commons.lang3.ClassUtils;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.stream.Stream;
public class ComponentManager<R extends AbstractComponent>
{
private final Class<R> base;
protected final MultiValuedMap<ComponentToken<? extends R>, R> components;
protected final Map<R, ComponentToken<? extends R>> tokens;
protected final MultiValuedMap<Class<?>, R> implementationProviders;
protected final MultiValuedMap<R, Class<?>> implementationReceivers;
public ComponentManager(@Nonnull Class<R> base)
{
this.base = base;
this.components = new HashSetValuedHashMap<>();
this.tokens = new HashMap<>();
this.implementationProviders = new HashSetValuedHashMap<>();
this.implementationReceivers = new ArrayListValuedHashMap<>();
}
public <T extends R> T addComponent(@Nonnull ComponentToken<T> token)
{
T component = token.createInstance();
var clazz = component.getClass();
if (component.isUnique() && this.implementationProviders.containsKey(clazz))
throw new IllegalArgumentException("Cannot have two components of the same class '%s'".formatted(clazz.getCanonicalName()));
var superclasses = ClassUtils.getAllSuperclasses(clazz);
for (var superclass : superclasses)
{
if (superclass.isAssignableFrom(AbstractComponent.class))
continue;
this.implementationProviders.put(superclass, component);
this.implementationReceivers.put(component, superclass);
}
this.implementationProviders.put(clazz, component);
this.components.put(token, component);
this.tokens.put(component, token);
try
{
component.onMount();
}
catch (Exception e)
{
throw new RuntimeException("An exception has occured while mounting the component", e);
}
return component;
}
public Class<R> getComponentBase()
{
return this.base;
}
public <T extends R> Stream<T> streamComponents(@Nonnull Class<T> componentClazz)
{
var providers = this.implementationProviders.get(componentClazz);
return providers.stream().map(componentClazz::cast);
}
public <T extends R> T getComponent(@Nonnull Class<T> componentClazz) throws NoSuchElementException
{
return this.streamComponents(componentClazz)
.findAny()
.orElseThrow();
}
public <T extends R> T getComponent(@Nonnull Class<T> componentClazz, @Nonnull Comparator<T> heuristic) throws NoSuchElementException
{
return this.streamComponents(componentClazz)
.max(heuristic)
.orElseThrow();
}
public <T extends R> List<T> getComponents(@Nonnull Class<T> componentClazz)
{
return this.streamComponents(componentClazz).toList();
}
public void removeComponent(@Nonnull R component) throws IllegalArgumentException
{
var token = this.tokens.remove(component);
if (token == null)
throw new IllegalArgumentException("Component to token mapping could not be found: %d -> ???".formatted(component.getID()));
this.components.removeMapping(token, component);
var classes = this.implementationReceivers.remove(component);
classes.forEach(clazz -> this.implementationProviders.removeMapping(clazz, component));
try
{
component.onUnmount();
}
catch (Exception e)
{
throw new RuntimeException("An exception has occured whiile unmounting the component", e);
}
}
public <T extends R> void removeComponents(@Nonnull ComponentToken<T> componentToken)
{
var activeComponents = this.components.remove(componentToken);
activeComponents.forEach(component -> {
this.tokens.remove(component);
var classes = this.implementationReceivers.remove(component);
classes.forEach(clazz -> this.implementationProviders.removeMapping(clazz, component));
try
{
component.onUnmount();
}
catch (Exception e)
{
throw new RuntimeException("An exception has occured whiile unmounting the component", e);
}
});
}
}

View File

@ -0,0 +1,50 @@
package org.plutoengine.component;
import javax.annotation.Nonnull;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
public final class ComponentToken<T extends IComponent>
{
private static final AtomicLong ID_SOURCE = new AtomicLong();
private final long id;
private final Supplier<T> supplier;
private ComponentToken(Supplier<T> valueSupplier)
{
this.id = ID_SOURCE.getAndIncrement();
this.supplier = valueSupplier;
}
public static <T extends IComponent> ComponentToken<T> create(@Nonnull Supplier<T> valueSupplier)
{
return new ComponentToken<>(valueSupplier);
}
public T createInstance()
{
return this.supplier.get();
}
public long getID()
{
return this.id;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
ComponentToken<?> that = (ComponentToken<?>) o;
return this.id == that.id;
}
@Override
public int hashCode()
{
return Objects.hash(this.id);
}
}

View File

@ -0,0 +1,22 @@
package org.plutoengine.component;
public interface IComponent
{
/**
* Denotes whether this component should be unique.
* Unique components can only exist once per instance
* in a given {@link ComponentManager}.
*
* @return whether this component should be unique
*
* @author 493msi
* @since 20.2.0.0-alpha.3
*/
boolean isUnique();
long getID();
void onMount() throws Exception;
void onUnmount() throws Exception;
}

View File

@ -0,0 +1,11 @@
plugins {
java
`java-library`
}
description = "The foundation module for games and apps built on top of PlutoEngine."
dependencies {
api(project(":plutoengine:plutogui"))
api(project(":plutoengine:plutoaudio"))
}

View File

@ -1,20 +1,21 @@
package cz.tefek.pluto; package org.plutoengine;
import java.util.Locale;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import org.plutoengine.audio.al.AudioEngine;
import org.plutoengine.buffer.GLFWImageUtil;
import org.plutoengine.component.ComponentToken;
import org.plutoengine.display.Display;
import org.plutoengine.display.DisplayBuilder;
import org.plutoengine.input.InputBus;
import org.plutoengine.l10n.PlutoL10n;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
import org.plutoengine.mod.ModLoader;
import cz.tefek.pluto.engine.audio.al.AudioEngine; import java.nio.file.Path;
import cz.tefek.pluto.engine.buffer.GLFWImageUtil; import java.util.Locale;
import cz.tefek.pluto.engine.display.Display;
import cz.tefek.pluto.engine.display.DisplayBuilder;
import cz.tefek.pluto.engine.input.InputBus;
import cz.tefek.pluto.io.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity;
import cz.tefek.pluto.l10n.PlutoL10n;
import cz.tefek.pluto.modloader.ModLoaderCore;
/** /**
* The main entry point for OpenGL applications built around the Pluto framework. * The main entry point for OpenGL applications built around the Pluto framework.
@ -116,6 +117,13 @@ public abstract class PlutoApplication
private int windowMinHeight = 600; private int windowMinHeight = 600;
private int vsync = 0; private int vsync = 0;
private boolean windowResizable = true; private boolean windowResizable = true;
private Path[] icons = null;
public StartupConfig icons(Path... paths)
{
this.icons = paths;
return this;
}
public StartupConfig coreProfile(boolean coreProfile) public StartupConfig coreProfile(boolean coreProfile)
{ {
@ -174,45 +182,58 @@ public abstract class PlutoApplication
} }
} }
public final void run(String[] args, StartupConfig config) throws Exception /**
* TODO: Start the application in a new thread
* */
public final void run(String[] args, StartupConfig config)
{ {
if (config == null) if (config == null)
{ {
config = new StartupConfig(); config = new StartupConfig();
} }
Logger.setup(); var globalComponents = PlutoGlobal.COMPONENTS;
globalComponents.addComponent(Logger.TOKEN);
Logger.log(SmartSeverity.INFO, "Debug mode: " + (Pluto.DEBUG_MODE ? "enabled" : "disabled")); Logger.log(SmartSeverity.INFO, "Debug mode: " + (Pluto.DEBUG_MODE ? "enabled" : "disabled"));
PlutoL10n.init(Locale.UK); PlutoL10n.init(Locale.UK);
var local = PlutoLocal.instance();
var components = local.COMPONENTS;
DisplayBuilder.initGLFW(); DisplayBuilder.initGLFW();
DisplayBuilder displayBuilder;
if (config.coreProfile) if (config.coreProfile)
{ {
this.display = new DisplayBuilder() displayBuilder = new DisplayBuilder()
.hintOpenGLVersion(config.majorOpenGLVersion, config.minorOpenGLVersion) .hintOpenGLVersion(config.majorOpenGLVersion, config.minorOpenGLVersion)
.hintDebugContext(Pluto.DEBUG_MODE) .hintDebugContext(Pluto.DEBUG_MODE)
.hintMSAA(config.windowMSAA) .hintMSAA(config.windowMSAA)
.hintVisible(true) .hintVisible(true)
.hintResizeable(config.windowResizable) .hintResizeable(config.windowResizable)
.setInitialSize(config.windowInitialWidth, config.windowInitialHeight) .setInitialSize(config.windowInitialWidth, config.windowInitialHeight);
.export();
} }
else else
{ {
this.display = new DisplayBuilder() displayBuilder = new DisplayBuilder()
.hintOpenGLVersionLegacy(config.majorOpenGLVersion, config.minorOpenGLVersion) .hintOpenGLVersionLegacy(config.majorOpenGLVersion, config.minorOpenGLVersion)
.hintDebugContext(Pluto.DEBUG_MODE) .hintDebugContext(Pluto.DEBUG_MODE)
.hintMSAA(config.windowMSAA) .hintMSAA(config.windowMSAA)
.hintVisible(true) .hintVisible(true)
.hintResizeable(config.windowResizable) .hintResizeable(config.windowResizable)
.setInitialSize(config.windowInitialWidth, config.windowInitialHeight) .setInitialSize(config.windowInitialWidth, config.windowInitialHeight);
.export();
} }
this.display.create(config.windowName); var displayToken = ComponentToken.create(displayBuilder::export);
this.display = components.addComponent(displayToken);
this.display.setName(config.windowName);
this.display.create();
this.display.setWindowSizeLimits(config.windowMinWidth, config.windowMinHeight, GLFW.GLFW_DONT_CARE, GLFW.GLFW_DONT_CARE); this.display.setWindowSizeLimits(config.windowMinWidth, config.windowMinHeight, GLFW.GLFW_DONT_CARE, GLFW.GLFW_DONT_CARE);
@ -220,20 +241,24 @@ public abstract class PlutoApplication
this.display.show(); this.display.show();
// TODO Un-hardcode these
var icons = GLFWImageUtil.loadIconSet("data/icon16.png", "data/icon32.png", "data/icon64.png", "data/icon128.png");
this.display.setIcons(icons);
this.display.createOpenGLCapabilities(); this.display.createOpenGLCapabilities();
InputBus.init(this.display); components.addComponent(InputBus.TOKEN);
AudioEngine.initialize(); AudioEngine.initialize();
ModLoaderCore.registerMod(this.getMainModule()); var modLoader = components.addComponent(ModLoader.TOKEN);
modLoader.registerMod(this.getMainModule());
modLoader.load();
if (config.icons != null)
{
var icons = GLFWImageUtil.loadIconSet(config.icons);
this.display.setIcons(icons);
}
ModLoaderCore.loadProcedure();
while (!this.display.isClosing()) while (!this.display.isClosing())
{ {
@ -252,17 +277,21 @@ public abstract class PlutoApplication
AudioEngine.exit(); AudioEngine.exit();
InputBus.destroy(); modLoader.unload();
ModLoaderCore.unloadProcedure();
GL.destroy(); GL.destroy();
components.removeComponent(modLoader);
components.removeComponents(InputBus.TOKEN);
this.display.destroy(); this.display.destroy();
components.removeComponents(displayToken);
DisplayBuilder.destroyGLFW(); DisplayBuilder.destroyGLFW();
Logger.close(); globalComponents.removeComponents(Logger.TOKEN);
} }
public Display getDisplayInstance() public Display getDisplayInstance()

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input; package org.plutoengine.input;
import org.lwjgl.glfw.GLFWCursorPosCallback; import org.lwjgl.glfw.GLFWCursorPosCallback;

View File

@ -0,0 +1,183 @@
package org.plutoengine.input;
import org.lwjgl.glfw.GLFW;
import org.plutoengine.PlutoLocal;
import org.plutoengine.address.ThreadSensitive;
import org.plutoengine.component.ComponentToken;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.display.Display;
@ThreadSensitive(localContexts = true)
public class InputBus extends PlutoLocalComponent
{
public static final ComponentToken<InputBus> TOKEN = ComponentToken.create(InputBus::new);
private final KeyboardInputCallback keyboard = new KeyboardInputCallback();
private final MouseButtonCallback mouseButton = new MouseButtonCallback();
private final CursorPositionCallback cursorPosition = new CursorPositionCallback();
private final ScrollInputCallback scroll = new ScrollInputCallback();
private final KeyboardCharInput charInput = new KeyboardCharInput();
private long windowPointer;
private InputBus()
{
}
private static InputBus instance()
{
return PlutoLocal.components().getComponent(InputBus.class);
}
@Override
public void onMount()
{
var display = PlutoLocal.components().getComponent(Display.class);
this.windowPointer = display.getWindowPointer();
GLFW.glfwSetKeyCallback(this.windowPointer, this.keyboard);
GLFW.glfwSetMouseButtonCallback(this.windowPointer, this.mouseButton);
GLFW.glfwSetCursorPosCallback(this.windowPointer, this.cursorPosition);
GLFW.glfwSetScrollCallback(this.windowPointer, this.scroll);
GLFW.glfwSetCharCallback(this.windowPointer, this.charInput);
}
@Override
public void onUnmount()
{
GLFW.glfwSetKeyCallback(this.windowPointer, null);
GLFW.glfwSetMouseButtonCallback(this.windowPointer, null);
GLFW.glfwSetCursorPosCallback(this.windowPointer, null);
GLFW.glfwSetScrollCallback(this.windowPointer, null);
GLFW.glfwSetCharCallback(this.windowPointer, null);
this.scroll.free();
this.mouseButton.free();
this.keyboard.free();
this.cursorPosition.free();
this.charInput.free();
}
public static KeyboardInputCallback keyboard()
{
return instance().keyboard;
}
public static MouseButtonCallback mouseButtons()
{
return instance().mouseButton;
}
public static ScrollInputCallback scroll()
{
return instance().scroll;
}
public static CursorPositionCallback cursorPosition()
{
return instance().cursorPosition;
}
public static KeyboardCharInput charInput()
{
return instance().charInput;
}
public static void resetStates()
{
var instance = instance();
instance.keyboard.resetPressed();
instance.mouseButton.reset();
instance.scroll.reset();
instance.cursorPosition.reset();
instance.charInput.reset();
}
@Override
public boolean isUnique()
{
return true;
}
@ThreadSensitive(localContexts = true)
public static class Mouse
{
public static boolean clicked(int button)
{
return instance().mouseButton.buttonClicked[button];
}
public static boolean released(int button)
{
return instance().mouseButton.buttonReleased[button];
}
public static boolean isButtonDown(int button)
{
return instance().mouseButton.buttonDown[button];
}
public static double getX()
{
return instance().cursorPosition.getX();
}
public static double getY()
{
return instance().cursorPosition.getY();
}
public static boolean isInside(int x1, int y1, int x2, int y2)
{
return instance().cursorPosition.isInside(x1, y1, x2, y2);
}
public static double getDX()
{
return instance().cursorPosition.getDeltaX();
}
public static double getDY()
{
return instance().cursorPosition.getDeltaY();
}
public static double getScrollX()
{
return instance().scroll.getXScroll();
}
public static double getScrollY()
{
return instance().scroll.getYScroll();
}
}
@ThreadSensitive(localContexts = true)
public static class Keyboard
{
public static boolean pressed(int key)
{
return instance().keyboard.hasBeenPressed(key);
}
public static boolean released(int key)
{
return instance().keyboard.hasBeenReleased(key);
}
public static boolean isKeyDown(int key)
{
return instance().keyboard.isKeyDown(key);
}
public static String getTypedText()
{
return instance().charInput.getTypedText();
}
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input; package org.plutoengine.input;
import org.lwjgl.glfw.GLFWCharCallback; import org.lwjgl.glfw.GLFWCharCallback;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input; package org.plutoengine.input;
import org.lwjgl.glfw.GLFWKeyCallback; import org.lwjgl.glfw.GLFWKeyCallback;
@ -11,8 +11,8 @@ import static org.lwjgl.glfw.GLFW.GLFW_RELEASE;
public class KeyboardInputCallback extends GLFWKeyCallback public class KeyboardInputCallback extends GLFWKeyCallback
{ {
private final Set<Integer> keyPressed = new HashSet<>(); private final Set<Integer> keyPressed = new HashSet<>();
private Set<Integer> keyDown = new HashSet<>(); private final Set<Integer> keyDown = new HashSet<>();
private Set<Integer> keyReleased = new HashSet<>(); private final Set<Integer> keyReleased = new HashSet<>();
@Override @Override
public void invoke(long window, int key, int scancode, int action, int mods) public void invoke(long window, int key, int scancode, int action, int mods)

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input; package org.plutoengine.input;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWMouseButtonCallback; import org.lwjgl.glfw.GLFWMouseButtonCallback;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input; package org.plutoengine.input;
import org.lwjgl.glfw.GLFWScrollCallback; import org.lwjgl.glfw.GLFWScrollCallback;

View File

@ -0,0 +1,24 @@
import org.plutoengine.Versions
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutoruntime"))
api("org.lwjgl", "lwjgl")
api("org.lwjgl", "lwjgl-glfw")
api("org.lwjgl", "lwjgl-opengl")
api("org.lwjgl", "lwjgl-stb")
runtimeOnly("org.lwjgl", "lwjgl", classifier = Versions.lwjglNatives)
runtimeOnly("org.lwjgl", "lwjgl-glfw", classifier = Versions.lwjglNatives)
runtimeOnly("org.lwjgl", "lwjgl-opengl", classifier = Versions.lwjglNatives)
runtimeOnly("org.lwjgl", "lwjgl-stb", classifier = Versions.lwjglNatives)
api("com.code-disaster.steamworks4j", "steamworks4j", Versions.steamworks4jVersion)
api("com.code-disaster.steamworks4j", "steamworks4j-server", Versions.steamworks4jServerVersion)
}

View File

@ -0,0 +1,11 @@
package org.plutoengine;
import org.lwjgl.glfw.GLFW;
import org.plutoengine.mod.ModEntry;
@ModEntry(modID = "glfw", version = ModGLFW.VERSION, dependencies = ModLWJGL.class)
public class ModGLFW
{
public static final String VERSION = GLFW.GLFW_VERSION_MAJOR + "." + GLFW.GLFW_VERSION_MINOR + "." + GLFW.GLFW_VERSION_REVISION;
}

View File

@ -0,0 +1,12 @@
package org.plutoengine;
import org.lwjgl.Version;
import org.plutoengine.mod.ModEntry;
@ModEntry(modID = "lwjgl",
version = ModLWJGL.version)
public class ModLWJGL
{
public static final String version = Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION;
}

View File

@ -1,6 +1,5 @@
package cz.tefek.pluto.engine.buffer; package org.plutoengine.buffer;
import org.apache.commons.io.IOUtils;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import java.io.IOException; import java.io.IOException;
@ -11,8 +10,6 @@ import java.nio.channels.FileChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import cz.tefek.pluto.io.asl.resource.ResourceAddress;
/** /**
* A utility class to handle primitive native buffers. * A utility class to handle primitive native buffers.
* *
@ -101,35 +98,6 @@ public final class BufferHelper
return newBuffer; return newBuffer;
} }
/**
* Loads a file denoted by the specified path and returns a
* {@link ByteBuffer} containing the read bytes.
*
* @param path The file's path.
* @return A {@link ByteBuffer} containing the file's contents.
* @throws IOException Upon standard I/O errors.
*
* @author 493msi
* @since 0.1
*/
public static ByteBuffer readToFlippedByteBuffer(String path) throws IOException
{
try (var fc = FileChannel.open(Path.of(path)))
{
var size = fc.size();
if (size > Integer.MAX_VALUE)
{
throw new IOException("File ' + pah + ' is too big to be read into a ByteBuffer!");
}
ByteBuffer buf = BufferUtils.createByteBuffer((int) size);
fc.read(buf);
buf.flip();
return buf;
}
}
/** /**
* Loads a file denoted by the specified {@link Path} and fills the input * Loads a file denoted by the specified {@link Path} and fills the input
* {@link ByteBuffer} with the read bytes. * {@link ByteBuffer} with the read bytes.
@ -158,25 +126,7 @@ public final class BufferHelper
} }
/** /**
* {@link ResourceAddress} version of * Loads a file denoted by the specified {@link Path} and returns
* {@link BufferHelper#readToByteBuffer(Path path, ByteBuffer buf)}.
*
* @param addr The file's {@link ResourceAddress}.
* @param buf The input buffer to be filled with data.
*
* @return The input {@link ByteBuffer}.
* @throws IOException Upon standard I/O errors.
*
* @author 493msi
* @since 0.3
*/
public static ByteBuffer readToByteBuffer(ResourceAddress addr, ByteBuffer buf) throws IOException
{
return readToByteBuffer(addr.toNIOPath(), buf);
}
/**
* Loads a file denoted by the specified {@link ResourceAddress} and returns
* a {@link ByteBuffer} containing the read bytes. * a {@link ByteBuffer} containing the read bytes.
* *
* @param path The file's path. * @param path The file's path.
@ -186,12 +136,8 @@ public final class BufferHelper
* @author 493msi * @author 493msi
* @since 0.3 * @since 0.3
*/ */
public static ByteBuffer readToFlippedByteBuffer(ResourceAddress path) throws IOException public static ByteBuffer readToFlippedByteBuffer(Path path) throws IOException
{ {
try (var is = Files.newInputStream(path.toNIOPath())) return flippedByteBuffer(Files.readAllBytes(path));
{
var ba = IOUtils.toByteArray(is);
return flippedByteBuffer(ba);
}
} }
} }

View File

@ -1,12 +1,11 @@
package cz.tefek.pluto.engine.buffer; package org.plutoengine.buffer;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFWImage; import org.lwjgl.glfw.GLFWImage;
import org.plutoengine.tpl.ImageLoader;
import java.nio.file.Path; import java.nio.file.Path;
import cz.tefek.pluto.io.tpl.TPL;
/** /**
* A utility class to load image files for use in GLFW. * A utility class to load image files for use in GLFW.
* *
@ -26,13 +25,13 @@ public class GLFWImageUtil
* @author 493msi * @author 493msi
* @since 0.2 * @since 0.2
*/ */
public static GLFWImage.Buffer loadIconSet(String... icons) public static GLFWImage.Buffer loadIconSet(Path... icons)
{ {
var icon = GLFWImage.create(icons.length); var icon = GLFWImage.create(icons.length);
for (String iconPath : icons) for (var iconPath : icons)
{ {
var img = TPL.loadSpecial(Path.of(iconPath), false); var img = ImageLoader.loadSpecial(iconPath, false);
var imgData = img.getData(); var imgData = img.getData();
int imgWidth = img.getWidth(); int imgWidth = img.getWidth();
int imgHeight = img.getHeight(); int imgHeight = img.getHeight();

View File

@ -4,4 +4,4 @@
* @author 493msi * @author 493msi
* *
*/ */
package cz.tefek.pluto.engine.buffer; package org.plutoengine.buffer;

View File

@ -1,16 +1,15 @@
package cz.tefek.pluto.engine.display; package org.plutoengine.display;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import org.lwjgl.opengl.ARBDebugOutput; import org.lwjgl.opengl.*;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLDebugMessageARBCallback;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import cz.tefek.pluto.annotation.ThreadSensitive; import org.plutoengine.Pluto;
import cz.tefek.pluto.engine.gl.GLDebugInfo; import org.plutoengine.address.ThreadSensitive;
import cz.tefek.pluto.io.logger.Logger; import org.plutoengine.component.PlutoLocalComponent;
import cz.tefek.pluto.io.logger.SmartSeverity; import org.plutoengine.gl.GLDebugInfo;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
/** /**
* A wrapper class to provide abstraction over GLFW windows. * A wrapper class to provide abstraction over GLFW windows.
@ -18,37 +17,45 @@ import cz.tefek.pluto.io.logger.SmartSeverity;
* @author 493msi * @author 493msi
* @since 0.2 * @since 0.2
*/ */
@ThreadSensitive @ThreadSensitive(localContexts = true)
public class Display public class Display extends PlutoLocalComponent
{ {
int width; int width;
int height; int height;
boolean debugMode; boolean debugMode;
boolean coreProfile = true; boolean coreProfile = true;
private String name = Pluto.ENGINE_NAME;
private boolean wasResized; private boolean wasResized;
private boolean openGLContext; private boolean openGLContext;
private long windowPointer; private long windowPointer;
private final GLFWErrorCallback glfwErrorCallback; private final GLFWErrorCallbackI glfwErrorCallback;
private GLFWWindowSizeCallback resizeCallback; private GLFWWindowSizeCallbackI resizeCallback;
private GLDebugMessageARBCallback glDebugCallback; private GLDebugMessageARBCallbackI glDebugCallback;
Display() Display()
{ {
this.glfwErrorCallback = new DisplayErrorCallback(); this.windowPointer = MemoryUtil.NULL;
this.glfwErrorCallback = (int error, long description) -> {
Logger.logf(SmartSeverity.ERROR, "GLFW Error code %d:\n", error);
Logger.logf(GLFWErrorCallback.getDescription(description));
};
GLFW.glfwSetErrorCallback(this.glfwErrorCallback); GLFW.glfwSetErrorCallback(this.glfwErrorCallback);
} }
public void create(String name) public void create()
{ {
GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, 4); GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, 4);
this.windowPointer = GLFW.glfwCreateWindow(this.width, this.height, name, MemoryUtil.NULL, MemoryUtil.NULL); this.windowPointer = GLFW.glfwCreateWindow(this.width, this.height, this.name, MemoryUtil.NULL, MemoryUtil.NULL);
if (this.windowPointer == MemoryUtil.NULL) if (this.windowPointer == MemoryUtil.NULL)
{ {
@ -68,25 +75,21 @@ public class Display
GLFW.glfwMakeContextCurrent(this.windowPointer); GLFW.glfwMakeContextCurrent(this.windowPointer);
this.resizeCallback = new GLFWWindowSizeCallback() { this.resizeCallback = (long window, int width, int height) -> {
@Override
public void invoke(long window, int width, int height)
{
if (width > 0 && height > 0) if (width > 0 && height > 0)
{ {
if (Display.this.debugMode) if (this.debugMode)
{ {
Logger.logf(SmartSeverity.INFO, "Resized to %dx%d.\n", width, height); Logger.logf(SmartSeverity.INFO, "Resized to %dx%d.\n", width, height);
} }
Display.this.width = width; this.width = width;
Display.this.height = height; this.height = height;
Display.this.wasResized = true; this.wasResized = true;
if (Display.this.openGLContext) if (this.openGLContext)
{ {
GL33.glViewport(0, 0, Display.this.width, Display.this.height); GL33.glViewport(0, 0, this.width, this.height);
}
} }
} }
}; };
@ -96,7 +99,10 @@ public class Display
public void setName(String newName) public void setName(String newName)
{ {
GLFW.glfwSetWindowTitle(this.windowPointer, newName); this.name = newName;
if (this.windowPointer != MemoryUtil.NULL)
GLFW.glfwSetWindowTitle(this.windowPointer, this.name);
} }
public void show() public void show()
@ -152,19 +158,19 @@ public class Display
public void destroy() public void destroy()
{ {
if (this.glfwErrorCallback != null) if (this.glfwErrorCallback instanceof GLFWErrorCallback glfwErrorCallback)
{ {
this.glfwErrorCallback.free(); glfwErrorCallback.free();
} }
if (this.glDebugCallback != null) if (this.glDebugCallback instanceof GLDebugMessageARBCallback glDebugMessageARBCallback)
{ {
this.glDebugCallback.free(); glDebugMessageARBCallback.free();
} }
if (this.resizeCallback != null) if (this.resizeCallback instanceof GLFWWindowSizeCallback windowSizeCallback)
{ {
this.resizeCallback.free(); windowSizeCallback.free();
} }
if (this.windowPointer != MemoryUtil.NULL) if (this.windowPointer != MemoryUtil.NULL)
@ -201,16 +207,12 @@ public class Display
if (this.debugMode) if (this.debugMode)
{ {
this.glDebugCallback = new GLDebugMessageARBCallback() { this.glDebugCallback = (int source, int type, int id, int severity, int length, long messagePtr, long userParam) -> {
@Override
public void invoke(int source, int type, int id, int severity, int length, long messagePtr, long userParam)
{
var message = GLDebugMessageARBCallback.getMessage(length, messagePtr); var message = GLDebugMessageARBCallback.getMessage(length, messagePtr);
Logger.log(SmartSeverity.WARNING, message); Logger.log(SmartSeverity.WARNING, message);
}
}; };
ARBDebugOutput.glDebugMessageCallbackARB(this.glDebugCallback, 0); ARBDebugOutput.glDebugMessageCallbackARB(this.glDebugCallback, MemoryUtil.NULL);
} }
GL33.glEnable(GL33.GL_CULL_FACE); GL33.glEnable(GL33.GL_CULL_FACE);
@ -218,4 +220,10 @@ public class Display
this.openGLContext = true; this.openGLContext = true;
} }
@Override
public boolean isUnique()
{
return true;
}
} }

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.display; package org.plutoengine.display;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.display; package org.plutoengine.display;
import java.util.Deque; import java.util.Deque;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
@ -20,7 +20,7 @@ public class Framerate
private static boolean firstRemoved = false; private static boolean firstRemoved = false;
private static Deque<Long> drawTimestamps = new LinkedBlockingDeque<>(); private static final Deque<Long> drawTimestamps = new LinkedBlockingDeque<>();
public static double getFrameTime() public static double getFrameTime()
{ {

View File

@ -4,4 +4,4 @@
* @author 493msi * @author 493msi
* *
*/ */
package cz.tefek.pluto.engine.display; package org.plutoengine.display;

View File

@ -1,12 +1,12 @@
package cz.tefek.pluto.engine.gl; package org.plutoengine.gl;
import org.lwjgl.opengl.ARBFramebufferObject; import org.lwjgl.opengl.ARBFramebufferObject;
import org.lwjgl.opengl.ARBUniformBufferObject; import org.lwjgl.opengl.ARBUniformBufferObject;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import cz.tefek.pluto.io.logger.Logger; import org.plutoengine.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity; import org.plutoengine.logger.SmartSeverity;
public class GLDebugInfo public class GLDebugInfo
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.gl; package org.plutoengine.gl;
/** /**
* Denotes the implementing class is a set of OpenGL enums. * Denotes the implementing class is a set of OpenGL enums.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math; package org.plutoengine.math;
import org.joml.Matrix4f; import org.joml.Matrix4f;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math; package org.plutoengine.math;
import org.joml.Matrix3x2f; import org.joml.Matrix3x2f;
import org.joml.Matrix4f; import org.joml.Matrix4f;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math; package org.plutoengine.math;
import org.joml.Matrix3x2f; import org.joml.Matrix3x2f;

View File

@ -4,4 +4,4 @@
* @author 493msi * @author 493msi
* *
*/ */
package cz.tefek.pluto.math; package org.plutoengine.math;

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutotexture"))
}

View File

@ -1,18 +1,18 @@
package cz.tefek.pluto.engine.graphics.gl.fbo; package org.plutoengine.graphics.gl.fbo;
import org.lwjgl.opengl.GL33;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.lwjgl.opengl.GL33; import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
import cz.tefek.pluto.io.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity;
public class Framebuffer public class Framebuffer
{ {
private int id; private int id;
private List<FramebufferTexture> textures; private final List<FramebufferTexture> textures;
private FramebufferDepthTexture depthTexture; private FramebufferDepthTexture depthTexture;

View File

@ -1,10 +1,10 @@
package cz.tefek.pluto.engine.graphics.gl.fbo; package org.plutoengine.graphics.gl.fbo;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import cz.tefek.pluto.engine.graphics.texture.MagFilter; import org.plutoengine.graphics.texture.MagFilter;
import cz.tefek.pluto.engine.graphics.texture.MinFilter; import org.plutoengine.graphics.texture.MinFilter;
import cz.tefek.pluto.engine.graphics.texture.WrapMode; import org.plutoengine.graphics.texture.WrapMode;
public class FramebufferDepthTexture extends FramebufferTexture public class FramebufferDepthTexture extends FramebufferTexture
{ {

View File

@ -1,11 +1,11 @@
package cz.tefek.pluto.engine.graphics.gl.fbo; package org.plutoengine.graphics.gl.fbo;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import cz.tefek.pluto.engine.graphics.texture.MagFilter; import org.plutoengine.graphics.texture.MagFilter;
import cz.tefek.pluto.engine.graphics.texture.MinFilter; import org.plutoengine.graphics.texture.MinFilter;
import cz.tefek.pluto.engine.graphics.texture.WrapMode; import org.plutoengine.graphics.texture.WrapMode;
import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
public class FramebufferTexture extends RectangleTexture public class FramebufferTexture extends RectangleTexture
{ {

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutospritesheet"))
}

View File

@ -0,0 +1,57 @@
package org.plutoengine.graphics;
import org.plutoengine.Pluto;
import org.plutoengine.graphics.font.FontManager;
import org.plutoengine.graphics.font.FontShader;
import org.plutoengine.graphics.texture.MagFilter;
import org.plutoengine.graphics.texture.MinFilter;
import org.plutoengine.graphics.texture.WrapMode;
import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
import org.plutoengine.gui.font.FontRenderer;
import org.plutoengine.mod.IModEntryPoint;
import org.plutoengine.mod.Mod;
import org.plutoengine.mod.ModEntry;
import org.plutoengine.shader.RenderShaderBuilder;
/**
* @author 493msi
*
*/
@ModEntry(modID = PlutoGUIMod.MOD_ID,
dependencies = { PlutoSpriteSheetMod.class },
version = Pluto.VERSION)
public class PlutoGUIMod implements IModEntryPoint
{
public static final String MOD_ID = "tefek.plutogui";
public static Mod instance;
public static RectangleTexture uiElementsAtlas;
private static FontShader fontShader;
public void onLoad(Mod mod)
{
instance = mod;
fontShader = new RenderShaderBuilder(mod.getResource("shaders.VertexFontShader#glsl"), mod.getResource("shaders.FragmentFontShader#glsl")).build(FontShader.class, false);
// Load the default font
FontManager.loadFont(mod.getResource("font.default"));
FontRenderer.load(fontShader);
uiElementsAtlas = new RectangleTexture();
uiElementsAtlas.load(mod.getResource("gui.elements#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE);
}
public void onUnload()
{
uiElementsAtlas.delete();
FontManager.unloadAll();
FontRenderer.unload();
fontShader.dispose();
}
}

View File

@ -0,0 +1,37 @@
package org.plutoengine.graphics.font;
import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes;
import org.plutoengine.shader.ShaderBase;
import org.plutoengine.shader.ShaderProgram;
import org.plutoengine.shader.VertexArrayAttribute;
import org.plutoengine.shader.uniform.*;
import org.plutoengine.shader.uniform.auto.AutoViewportProjection;
@ShaderProgram
public final class FontShader extends ShaderBase
{
@AutoViewportProjection
@Uniform(name = "projection")
public UniformMat4 projectionMatrix;
@Uniform(name = "transformation")
public UniformMat4 transformationMatrix;
@Uniform
public UniformVec2 uvBase;
@Uniform
public UniformVec2 uvDelta;
@Uniform
public UniformRGBA recolor;
@Uniform
public UniformBoolean italic;
@VertexArrayAttribute(ReservedAttributes.POSITION)
public int position;
@VertexArrayAttribute(ReservedAttributes.UV)
public int uvCoords;
}

View File

@ -1,15 +1,15 @@
package cz.tefek.pluto.engine.gui.font; package org.plutoengine.gui.font;
import org.plutoengine.graphics.texture.texture2d.RectangleTexture;
import java.util.HashMap; import java.util.HashMap;
import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture;
public class Font public class Font
{ {
private String name; private String name;
private int width; private int width;
private int height; private int height;
private HashMap<Character, CharacterInfo> definitions; private final HashMap<Character, CharacterInfo> definitions;
private RectangleTexture texture; private RectangleTexture texture;
public Font(String name, int width, int height, HashMap<Character, CharacterInfo> def) public Font(String name, int width, int height, HashMap<Character, CharacterInfo> def)

View File

@ -1,6 +1,6 @@
package cz.tefek.pluto.engine.gui.font; package org.plutoengine.gui.font;
import cz.tefek.pluto.engine.graphics.font.FontManager; import org.plutoengine.graphics.font.FontManager;
public class FontHelper public class FontHelper
{ {
@ -100,9 +100,9 @@ public class FontHelper
charInf = font.getDefinitions().get('?'); charInf = font.getDefinitions().get('?');
} }
totalSpacing -= charInf.getLeftOffset() * relativeSize - relativeSize; totalSpacing -= charInf.leftOffset() * relativeSize - relativeSize;
totalSpacing += absoluteCharWidth * relativeSize; totalSpacing += absoluteCharWidth * relativeSize;
totalSpacing -= charInf.getRightOffset() * relativeSize - 1; totalSpacing -= charInf.rightOffset() * relativeSize - 1;
} }
maxW = Math.max(maxW, totalSpacing); maxW = Math.max(maxW, totalSpacing);
@ -113,9 +113,8 @@ public class FontHelper
public static int calcStringHeight(Object string, float relSize) public static int calcStringHeight(Object string, float relSize)
{ {
int size = (int) (30f * relSize * string.toString().split("\n").length);
return size; return (int) (30f * relSize * string.toString().split("\n").length);
} }
} }

View File

@ -1,15 +1,15 @@
package cz.tefek.pluto.engine.gui.font; package org.plutoengine.gui.font;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import cz.tefek.pluto.engine.graphics.font.FontManager; import org.plutoengine.graphics.font.FontManager;
import cz.tefek.pluto.engine.graphics.font.FontShader; import org.plutoengine.graphics.font.FontShader;
import cz.tefek.pluto.engine.graphics.gl.DrawMode; import org.plutoengine.graphics.gl.DrawMode;
import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; import org.plutoengine.graphics.gl.vao.QuadPresets;
import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; import org.plutoengine.graphics.gl.vao.VertexArray;
import cz.tefek.pluto.engine.math.TransformationMatrix; import org.plutoengine.math.TransformationMatrix;
public class FontRenderer public class FontRenderer
{ {
@ -72,8 +72,8 @@ public class FontRenderer
for (int characterIndex = 0; characterIndex < text.length(); characterIndex++) for (int characterIndex = 0; characterIndex < text.length(); characterIndex++)
{ {
int column = 0; int column;
int row = 0; int row;
var currentChar = text.charAt(characterIndex); var currentChar = text.charAt(characterIndex);
@ -104,17 +104,11 @@ public class FontRenderer
cBef = text.charAt(characterIndex - 2); cBef = text.charAt(characterIndex - 2);
} }
if (c != '\\' || cBef == '\\' && c == '\\') if (c != '\\' || cBef == '\\')
{ {
if (!isShadow) if (!isShadow)
{ {
char[] col = new char[10]; color(text.substring(characterIndex + 3, characterIndex + 13));
text.getChars(characterIndex + 3, characterIndex + 13, col, 0);
String clr = String.valueOf(col);
color(clr);
} }
characterIndex += 13; characterIndex += 13;
@ -141,16 +135,9 @@ public class FontRenderer
cBef = text.charAt(characterIndex - 2); cBef = text.charAt(characterIndex - 2);
} }
if (c != '\\' || cBef == '\\' && c == '\\') if (c != '\\' || cBef == '\\')
{ {
if (text.charAt(characterIndex + 2) == '1') italic(text.charAt(characterIndex + 2) == '1');
{
italic(true);
}
else
{
italic(false);
}
characterIndex += 2; characterIndex += 2;
@ -164,26 +151,19 @@ public class FontRenderer
switch (currentChar) switch (currentChar)
{ {
case '\n': case '\n' -> {
color(color); color(color);
drawX = xPos; drawX = xPos;
drawY += lineHeight; drawY += lineHeight;
continue; continue;
}
case ' ': case ' ' -> {
drawX += spaceWidth; drawX += spaceWidth;
continue; continue;
}
case 'g': case 'g', 'y', 'p', 'j' -> shift = 6 * relativeSize;
case 'y': default -> {
case 'p': }
case 'j':
shift = 6 * relativeSize;
break;
default:
break;
} }
var fontDefs = font.getDefinitions(); var fontDefs = font.getDefinitions();
@ -194,7 +174,7 @@ public class FontRenderer
charInf = fontDefs.get('?'); charInf = fontDefs.get('?');
} }
var atlasIndex = charInf.getNumber(); var atlasIndex = charInf.number();
row = atlasIndex / CHAR_WIDTH; row = atlasIndex / CHAR_WIDTH;
column = atlasIndex % CHAR_WIDTH; column = atlasIndex % CHAR_WIDTH;
@ -204,7 +184,7 @@ public class FontRenderer
float v = row * CHAR_HEIGHT; float v = row * CHAR_HEIGHT;
// Offset from the left // Offset from the left
drawX -= charInf.getLeftOffset() * relativeSize; drawX -= charInf.leftOffset() * relativeSize;
float posY = shift + drawY; float posY = shift + drawY;
@ -218,7 +198,7 @@ public class FontRenderer
charVAO.draw(DrawMode.TRIANGLES); charVAO.draw(DrawMode.TRIANGLES);
drawX += charWidth; drawX += charWidth;
drawX -= charInf.getRightOffset() * relativeSize; drawX -= charInf.rightOffset() * relativeSize;
drawX += relativeSize; drawX += relativeSize;
} }
@ -239,10 +219,8 @@ public class FontRenderer
dark = 0.35f; dark = 0.35f;
} }
if (color instanceof float[]) if (color instanceof float[] c)
{ {
float[] c = (float[]) color;
if (c.length == 4) if (c.length == 4)
{ {
recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]); recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]);
@ -253,10 +231,8 @@ public class FontRenderer
} }
} }
if (color instanceof String) if (color instanceof String col)
{ {
String col = (String) color;
if (col.length() == 7) if (col.length() == 7)
{ {
recolor(Integer.valueOf(col.substring(1, 3), 16) / 256f - dark, Integer.valueOf(col.substring(3, 5), 16) / 256f - dark, Integer.valueOf(col.substring(5, 7), 16) / 256f - dark, 1); recolor(Integer.valueOf(col.substring(1, 3), 16) / 256f - dark, Integer.valueOf(col.substring(3, 5), 16) / 256f - dark, Integer.valueOf(col.substring(5, 7), 16) / 256f - dark, 1);

View File

@ -0,0 +1,27 @@
import org.plutoengine.Versions
plugins {
java
`java-library`
}
description = "Multi-purpose utility library that can be used in basically any project."
dependencies {
api(project(":plutoengine:plutouss2"))
api("org.jetbrains", "annotations", "23.0.0")
api("org.yaml", "snakeyaml", "1.28")
api("com.fasterxml.jackson.core", "jackson-core", "2.13.2")
api("com.fasterxml.jackson.core", "jackson-databind", "2.13.2")
api("com.google.guava", "guava", "28.0-jre")
api("org.joml", "joml", Versions.jomlVersion)
api("commons-io", "commons-io", "2.6")
api("org.apache.commons", "commons-lang3", "3.12.0")
}

View File

@ -0,0 +1,295 @@
package org.plutoengine.address;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@JsonDeserialize(using = VirtualAddress.Deserializer.class, keyUsing = VirtualAddress.KeyAddrDeserializer.class)
@JsonSerialize(using = VirtualAddress.Serializer.class, keyUsing = VirtualAddress.Serializer.class)
public final class VirtualAddress implements Comparable<VirtualAddress>
{
public static final int MAX_LENGTH = 128;
public static final int MAX_KEYS = 32;
public static final int MAX_KEY_LENGTH = 32;
public static final int TOKEN_PATH_SEPARATOR = '.';
public static final int TOKEN_HIERARCHY_UP = '~';
private static final VirtualAddress ROOT_ADDRESS = new VirtualAddress("", List.of(), false, 0);
private final String fullPath;
private final List<String> components;
private final boolean relative;
@Range(from = 0, to = Integer.MAX_VALUE)
private final int rootOffset;
VirtualAddress(String fullPath, List<String> components, boolean relative, int rootOffset)
{
this.fullPath = fullPath;
this.components = Collections.unmodifiableList(components);
this.relative = relative;
this.rootOffset = rootOffset;
}
public static VirtualAddress ofRoot()
{
return ROOT_ADDRESS;
}
public List<String> getComponents()
{
return this.components;
}
public boolean isRelative()
{
return this.relative;
}
public boolean isEmpty()
{
return this.fullPath.isEmpty();
}
@Range(from = 0, to = Integer.MAX_VALUE)
public int getRootOffset()
{
return this.rootOffset;
}
@Override
public String toString()
{
return this.fullPath;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || this.getClass() != o.getClass())
return false;
VirtualAddress that = (VirtualAddress) o;
return this.fullPath.equals(that.fullPath);
}
@Override
public int hashCode()
{
return Objects.hash(this.fullPath);
}
public static VirtualAddress parse(String inputStr)
{
return parse(inputStr, false);
}
public static VirtualAddress parse(String inputStr, boolean permitRelative)
{
var parser = new VirtualAddressParser(permitRelative);
inputStr.codePoints()
.sequential()
.forEachOrdered(parser::accept);
return parser.build();
}
public static VirtualAddressParser createParser(boolean permitRelative)
{
return new VirtualAddressParser(permitRelative);
}
@Override
public int compareTo(VirtualAddress o)
{
if (this.rootOffset != o.rootOffset)
return this.rootOffset - o.rootOffset;
return this.fullPath.compareToIgnoreCase(o.fullPath);
}
public int getNameCount()
{
return this.components.size() + this.rootOffset;
}
public @NotNull VirtualAddress getName(int index)
{
if (index < this.rootOffset)
return new VirtualAddress("", List.of(), true, 1);
var component = this.components.get(index - this.rootOffset);
return new VirtualAddress(component, List.of(component), true, 0);
}
public @NotNull VirtualAddress getParent()
{
if (this.components.isEmpty())
{
if (!this.relative)
{
throw new IllegalStateException("Cannot get a parent of a root absolute address!");
}
else
{
var offset = this.rootOffset - 1;
var components = List.<String>of();
return new VirtualAddress(VirtualAddressParser.getNormalizedString(offset, components), components, true, offset);
}
}
var componentsNew = this.components.subList(0, this.components.size() - 1);
return new VirtualAddress(VirtualAddressParser.getNormalizedString(this.rootOffset, componentsNew), componentsNew, this.relative, this.rootOffset);
}
public @NotNull VirtualAddress relativize(@NotNull VirtualAddress other)
{
if (this.relative != other.relative)
throw new IllegalArgumentException("Cannot relativize an address when only one of the inputs is absolute!");
if (this.relative && this.rootOffset > other.rootOffset)
throw new IllegalArgumentException("Cannot relativize against a relative address with a root offset higher than the target one!");
if (this.isEmpty())
return other;
int newOffset = other.rootOffset - this.rootOffset;
var thIt = this.components.iterator();
var oIt = other.components.iterator();
var newPath = new ArrayList<String>();
if (newOffset == 0)
{
while (thIt.hasNext() && oIt.hasNext())
{
var thComp = thIt.next();
var oComp = oIt.next();
if (!thComp.equals(oComp))
{
newOffset++;
newPath.add(oComp);
break;
}
}
}
while (thIt.hasNext())
{
newOffset++;
thIt.next();
}
while (oIt.hasNext())
newPath.add(oIt.next());
return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, newPath), newPath, true, newOffset);
}
public @NotNull VirtualAddress resolve(@NotNull VirtualAddress other)
{
if (!other.relative)
return other;
int removedComponentsSigned = other.rootOffset - this.components.size();
int newOffset = this.rootOffset + Math.max(removedComponentsSigned, 0);
if (!this.relative && newOffset > 0)
throw new IllegalArgumentException("Cannot resolve a relative address against an absolute address that would make it higher than the root!");
int compListEnd = Math.max(-removedComponentsSigned, 0);
var componentsNew = Stream.concat(this.components.stream().limit(compListEnd), other.components.stream()).toList();
return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, componentsNew), componentsNew, this.relative, newOffset);
}
public @NotNull VirtualAddress subAddress(int startIndex, int endIndex)
{
var componentCount = this.components.size();
var maxComponents = this.rootOffset + componentCount;
if (startIndex == 0 && endIndex == 0)
{
var components = List.<String>of();
return new VirtualAddress(VirtualAddressParser.getNormalizedString(0, components), components, this.relative, 0);
}
if (startIndex < 0 || endIndex > maxComponents || endIndex > startIndex)
throw new IndexOutOfBoundsException();
var newComponents = this.components.subList(startIndex - this.rootOffset, endIndex - this.rootOffset);
var newOffset = Math.max(0, this.rootOffset - startIndex);
return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, newComponents), newComponents, this.relative, newOffset);
}
public boolean startsWith(@NotNull VirtualAddress other)
{
return this.fullPath.startsWith(other.fullPath);
}
public boolean endsWith(@NotNull VirtualAddress other)
{
return this.fullPath.endsWith(other.fullPath);
}
public static class KeyAddrDeserializer extends KeyDeserializer
{
@Override
public VirtualAddress deserializeKey(String key, DeserializationContext ctxt)
{
return VirtualAddress.parse(key, false);
}
}
public static class Serializer extends StdSerializer<VirtualAddress>
{
public Serializer()
{
super(VirtualAddress.class);
}
@Override
public void serialize(VirtualAddress value, JsonGenerator gen, SerializerProvider provider) throws IOException
{
gen.writeString(value.fullPath);
}
}
public static class Deserializer extends StdDeserializer<VirtualAddress>
{
public Deserializer()
{
super(VirtualAddress.class);
}
@Override
public VirtualAddress deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
return VirtualAddress.parse(p.getValueAsString(), false);
}
}
}

View File

@ -0,0 +1,9 @@
package org.plutoengine.address;
public class VirtualAddressParseException extends RuntimeException
{
public VirtualAddressParseException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,155 @@
package org.plutoengine.address;
import org.jetbrains.annotations.Range;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
public class VirtualAddressParser
{
private enum State
{
HIERARCHY_UP,
KEY,
PATH_SEPARATOR
}
private int position;
private int depth;
private State state;
private final Deque<String> components;
private StringBuilder tokenBuilder;
private final boolean permitRelative;
// Decrement for every root-level ~
@Range(from = 0, to = Integer.MAX_VALUE)
private int rootOffset = 0;
VirtualAddressParser(boolean permitRelative)
{
this.depth = 0;
this.position = 0;
this.state = State.PATH_SEPARATOR;
this.components = new ArrayDeque<>(VirtualAddress.MAX_KEYS);
this.permitRelative = permitRelative;
}
public void accept(int codepoint)
{
switch (this.state)
{
case PATH_SEPARATOR -> {
if (Character.isLetter(codepoint))
{
if (this.components.size() >= VirtualAddress.MAX_KEYS)
throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS));
this.state = State.KEY;
this.tokenBuilder = new StringBuilder(VirtualAddress.MAX_KEY_LENGTH);
this.tokenBuilder.appendCodePoint(codepoint);
}
else if (codepoint == VirtualAddress.TOKEN_HIERARCHY_UP)
{
if (!this.permitRelative)
throw new VirtualAddressParseException("Cannot use the hierarchy-up token (%s) in a non-relative context.".formatted(VirtualAddress.TOKEN_HIERARCHY_UP));
this.state = State.HIERARCHY_UP;
this.tokenBuilder = null;
if (!this.components.isEmpty())
this.components.removeLast();
else
this.rootOffset++;
}
else
{
throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint)));
}
}
case KEY -> {
if (Character.isLetterOrDigit(codepoint) || codepoint == '_' || codepoint == '-')
{
if (this.tokenBuilder.length() >= VirtualAddress.MAX_KEY_LENGTH)
throw new VirtualAddressParseException("Single key length (%d) exceeded!".formatted(VirtualAddress.MAX_KEY_LENGTH));
this.state = State.KEY;
this.tokenBuilder.appendCodePoint(codepoint);
}
else if (codepoint == VirtualAddress.TOKEN_PATH_SEPARATOR)
{
if (this.depth >= VirtualAddress.MAX_KEYS)
throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS));
if (this.state != State.HIERARCHY_UP)
this.components.addLast(this.tokenBuilder.toString());
this.state = State.PATH_SEPARATOR;
this.depth++;
}
else
{
throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint)));
}
}
case HIERARCHY_UP -> {
if (codepoint == VirtualAddress.TOKEN_HIERARCHY_UP)
{
if (!this.components.isEmpty())
this.components.removeLast();
else
this.rootOffset++;
}
else if (codepoint == VirtualAddress.TOKEN_PATH_SEPARATOR)
{
if (this.depth >= VirtualAddress.MAX_KEYS)
throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS));
this.state = State.PATH_SEPARATOR;
this.depth++;
}
else
{
throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint)));
}
}
}
this.position++;
if (this.rootOffset >= VirtualAddress.MAX_KEYS)
throw new VirtualAddressParseException("Cannot move than %d levels up in the hierarchy!".formatted(VirtualAddress.MAX_KEYS));
}
public VirtualAddress build()
{
if (this.state == State.KEY && this.tokenBuilder != null)
this.components.addLast(this.tokenBuilder.toString());
var normalizedAddress = getNormalizedString(this.rootOffset, this.components);
return new VirtualAddress(normalizedAddress, List.copyOf(this.components), this.permitRelative, this.rootOffset);
}
static String getNormalizedString(int rootOffset, Iterable<String> components)
{
var separator = Character.toString(VirtualAddress.TOKEN_PATH_SEPARATOR);
var componentsJoined = String.join(separator, components);
var sb = new StringBuilder();
sb.append(Character.toString(VirtualAddress.TOKEN_HIERARCHY_UP).repeat(rootOffset));
if (rootOffset > 0 && !componentsJoined.isEmpty())
sb.append(separator);
sb.append(componentsJoined);
return sb.toString();
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.chrono; package org.plutoengine.chrono;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -45,10 +45,6 @@ public class MiniTime
{ {
private static class MiniTimeParseException extends RuntimeException private static class MiniTimeParseException extends RuntimeException
{ {
public MiniTimeParseException()
{
}
} }
private static final int DAYS_IN_WEEK = 7; private static final int DAYS_IN_WEEK = 7;
@ -72,9 +68,7 @@ public class MiniTime
public static long parse(String input) public static long parse(String input)
{ {
if (input == null) if (input == null)
{ throw new NullPointerException();
throw new IllegalArgumentException("MiniTime string cannot be null!");
}
// Nothing to parse // Nothing to parse
if (input.isEmpty()) if (input.isEmpty())
@ -141,33 +135,28 @@ public class MiniTime
switch (type.toLowerCase()) switch (type.toLowerCase())
{ {
case "w": case "w" -> {
case "W":
allow = Integer.MAX_VALUE; allow = Integer.MAX_VALUE;
multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_WEEK * MILLIS_IN_MINUTE; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_WEEK * MILLIS_IN_MINUTE;
break; }
case "d": case "d" -> {
case "D":
allow = DAYS_IN_WEEK; allow = DAYS_IN_WEEK;
multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * MILLIS_IN_MINUTE; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * MILLIS_IN_MINUTE;
break; }
case "h": case "h" -> {
case "H":
allow = HOURS_IN_DAY; allow = HOURS_IN_DAY;
multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * MILLIS_IN_MINUTE; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * MILLIS_IN_MINUTE;
break; }
case "m": case "m" -> {
case "M":
allow = MINUTES_IN_HOUR; allow = MINUTES_IN_HOUR;
multiplier = SECONDS_IN_MINUTE * MILLIS_IN_MINUTE; multiplier = SECONDS_IN_MINUTE * MILLIS_IN_MINUTE;
break; }
case "s": case "s" -> {
case "S":
allow = SECONDS_IN_MINUTE; allow = SECONDS_IN_MINUTE;
multiplier = MILLIS_IN_MINUTE; multiplier = MILLIS_IN_MINUTE;
break; }
default: default -> {
break; }
} }
// The top one can be more than it normally could have, for example you can // The top one can be more than it normally could have, for example you can

View File

@ -4,4 +4,4 @@
* @author 493msi * @author 493msi
* *
*/ */
package cz.tefek.pluto.chrono; package org.plutoengine.chrono;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.event.lambda; package org.plutoengine.event.lambda;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,66 @@
package org.plutoengine.io.property;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.Tag;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
public class YAMLPropertiesReader
{
public static Map<String, String> loadFromFile(Path file) throws IOException, YAMLException
{
try (var br = Files.newBufferedReader(file))
{
var yaml = new Yaml(new Constructor() {
private void recursivelyBuildTree(Map<String, String> map, String accessor, Node node)
{
if (node instanceof MappingNode mappingNode)
{
var kvps = mappingNode.getValue();
for (var kvp : kvps)
{
var key = kvp.getKeyNode();
if (key.getTag() != Tag.STR || !(key instanceof ScalarNode))
throw new YAMLException("All keys in property trees must be strings!");
var valueNode = kvp.getValueNode();
var newAccessorFormat = valueNode instanceof MappingNode ? "%s%s." : "%s%s";
this.recursivelyBuildTree(map, newAccessorFormat.formatted(accessor, super.constructScalar((ScalarNode) key)), valueNode);
}
}
else if (node instanceof ScalarNode scalarNode)
{
map.put(accessor, super.constructScalar(scalarNode));
}
else
{
throw new YAMLException("Invalid node tag: %s".formatted(node.getTag()));
}
}
@Override
protected Object constructObject(Node node)
{
var propertyMap = new LinkedHashMap<String, String>();
this.recursivelyBuildTree(propertyMap, "", node);
return propertyMap;
}
});
return yaml.load(br);
}
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.math; package org.plutoengine.math;
/** /**
* A clamped sine wave generator, for animations, mostly. * A clamped sine wave generator, for animations, mostly.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.math; package org.plutoengine.math;
/** /**
* A class to generate a cubic bezier interpolation function. Not very * A class to generate a cubic bezier interpolation function. Not very

View File

@ -1,8 +1,9 @@
package cz.tefek.pluto.math; package org.plutoengine.math;
/** /**
* A class to generate a cubic bezier interpolation function. This * A class to generate a cubic bezier interpolation function. This
* implementation creates a lookup table. * implementation creates a lookup table, so create it one and then
* use it with basically no speed penalty.
* *
* @since 0.2 * @since 0.2
* @author 493msi * @author 493msi

View File

@ -4,4 +4,4 @@
* @author 493msi * @author 493msi
* *
*/ */
package cz.tefek.pluto.engine.math; package org.plutoengine.math;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -155,23 +155,16 @@ public final class Color
*/ */
public static Color from(int color, @Nonnull EnumColorFormat colorFormat) public static Color from(int color, @Nonnull EnumColorFormat colorFormat)
{ {
switch (colorFormat) return switch (colorFormat)
{ {
case CF_INT_BGR: case CF_INT_BGR -> new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff);
return new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff); case CF_INT_RGB -> new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
case CF_INT_RGB: case CF_INT_ABGR -> new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff);
return new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); case CF_INT_ARGB -> new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, (color >> 24) & 0xff);
case CF_INT_ABGR: case CF_INT_BGRA -> new Color((color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff, color & 0xff);
return new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); case CF_INT_RGBA -> new Color((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
case CF_INT_ARGB: default -> throw new UnsupportedOperationException("Use the from(byte[], int, ColorFormat) for byte color formats!");
return new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, (color >> 24) & 0xff); };
case CF_INT_BGRA:
return new Color((color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff, color & 0xff);
case CF_INT_RGBA:
return new Color((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
default:
throw new UnsupportedOperationException("Use the from(byte[], int, ColorFormat) for byte color formats!");
}
} }
/** /**
@ -204,23 +197,16 @@ public final class Color
*/ */
public static Color from(@Nonnull byte[] color, int offset, @Nonnull EnumColorFormat colorFormat) public static Color from(@Nonnull byte[] color, int offset, @Nonnull EnumColorFormat colorFormat)
{ {
switch (colorFormat) return switch (colorFormat)
{ {
case CF_3BYTE_BGR: case CF_3BYTE_BGR -> new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF);
return new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF); case CF_3BYTE_RGB -> new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF);
case CF_3BYTE_RGB: case CF_4BYTE_ABGR -> new Color(color[offset + 3] & 0xFF, color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF);
return new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF); case CF_4BYTE_ARGB -> new Color(color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF, color[offset] & 0xFF);
case CF_4BYTE_ABGR: case CF_4BYTE_BGRA -> new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF, color[offset + 3] & 0xFF);
return new Color(color[offset + 3] & 0xFF, color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF); case CF_4BYTE_RGBA -> new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF);
case CF_4BYTE_ARGB: default -> throw new UnsupportedOperationException("Use the from(int, ColorFormat) for int color formats!");
return new Color(color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF, color[offset] & 0xFF); };
case CF_4BYTE_BGRA:
return new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF, color[offset + 3] & 0xFF);
case CF_4BYTE_RGBA:
return new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF);
default:
throw new UnsupportedOperationException("Use the from(int, ColorFormat) for int color formats!");
}
} }
/** /**
@ -253,23 +239,16 @@ public final class Color
*/ */
public int get(@Nonnull EnumColorFormat colorFormat) public int get(@Nonnull EnumColorFormat colorFormat)
{ {
switch (colorFormat) return switch (colorFormat)
{ {
case CF_INT_BGR: case CF_INT_BGR -> (this.blue << 16) | (this.green << 8) | this.red;
return (this.blue << 16) | (this.green << 8) | this.red; case CF_INT_RGB -> (this.red << 16) | (this.green << 8) | this.blue;
case CF_INT_RGB: case CF_INT_ABGR -> (this.alpha << 24) | (this.blue << 16) | (this.green << 8) | this.red;
return (this.red << 16) | (this.green << 8) | this.blue; case CF_INT_ARGB -> (this.alpha << 24) | (this.red << 16) | (this.green << 8) | this.blue;
case CF_INT_ABGR: case CF_INT_BGRA -> (this.blue << 24) | (this.green << 16) | (this.red << 8) | this.alpha;
return (this.alpha << 24) | (this.blue << 16) | (this.green << 8) | this.red; case CF_INT_RGBA -> (this.red << 24) | (this.green << 16) | (this.blue << 8) | this.alpha;
case CF_INT_ARGB: default -> throw new UnsupportedOperationException("Use the get(ColorFormat, byte[], int) for byte color formats!");
return (this.alpha << 24) | (this.red << 16) | (this.green << 8) | this.blue; };
case CF_INT_BGRA:
return (this.blue << 24) | (this.green << 16) | (this.red << 8) | this.alpha;
case CF_INT_RGBA:
return (this.red << 24) | (this.green << 16) | (this.blue << 8) | this.alpha;
default:
throw new UnsupportedOperationException("Use the get(ColorFormat, byte[], int) for byte color formats!");
}
} }
/** /**
@ -293,42 +272,41 @@ public final class Color
{ {
switch (colorFormat) switch (colorFormat)
{ {
case CF_3BYTE_BGR: case CF_3BYTE_BGR -> {
dataOut[offset++] = (byte) this.blue; dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.red; dataOut[offset] = (byte) this.red;
break; }
case CF_3BYTE_RGB: case CF_3BYTE_RGB -> {
dataOut[offset++] = (byte) this.red; dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.blue; dataOut[offset] = (byte) this.blue;
break; }
case CF_4BYTE_ABGR: case CF_4BYTE_ABGR -> {
dataOut[offset++] = (byte) this.alpha; dataOut[offset++] = (byte) this.alpha;
dataOut[offset++] = (byte) this.blue; dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.red; dataOut[offset] = (byte) this.red;
break; }
case CF_4BYTE_ARGB: case CF_4BYTE_ARGB -> {
dataOut[offset++] = (byte) this.alpha; dataOut[offset++] = (byte) this.alpha;
dataOut[offset++] = (byte) this.red; dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.blue; dataOut[offset] = (byte) this.blue;
break; }
case CF_4BYTE_BGRA: case CF_4BYTE_BGRA -> {
dataOut[offset++] = (byte) this.blue; dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset++] = (byte) this.red; dataOut[offset++] = (byte) this.red;
dataOut[offset] = (byte) this.alpha; dataOut[offset] = (byte) this.alpha;
break; }
case CF_4BYTE_RGBA: case CF_4BYTE_RGBA -> {
dataOut[offset++] = (byte) this.red; dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green; dataOut[offset++] = (byte) this.green;
dataOut[offset++] = (byte) this.blue; dataOut[offset++] = (byte) this.blue;
dataOut[offset] = (byte) this.alpha; dataOut[offset] = (byte) this.alpha;
break; }
default: default -> throw new UnsupportedOperationException("Use the get(ColorFormat) for int color formats!");
throw new UnsupportedOperationException("Use the get(ColorFormat) for int color formats!");
} }
} }

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
/** /**
* TODO * TODO

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
import org.apache.commons.lang3.Range; import org.apache.commons.lang3.Range;
@ -78,36 +78,30 @@ public class HSB
float p = this.b * (1 - this.s); float p = this.b * (1 - this.s);
float hueFractCCW = h6 - hueSide; float hueFractCCW = h6 - hueSide;
// The second nearest color component on the hue wheel - counter-clockwise // The second-nearest color component on the hue wheel - counter-clockwise
float q = this.b * (1 - hueFractCCW * this.s); float q = this.b * (1 - hueFractCCW * this.s);
float hueFractCW = 1 - hueFractCCW; float hueFractCW = 1 - hueFractCCW;
// The second nearest color component on the hue wheel - clockwise // The second-nearest color component on the hue wheel - clockwise
float t = this.b * (1 - hueFractCW * this.s); float t = this.b * (1 - hueFractCW * this.s);
switch (hueSide % 6) return switch (hueSide % 6)
{ {
case 1: // Hues 60°-119° -- Green is the brightest color, no blue is present at max saturation // Hues 60°-119° -- Green is the brightest color, no blue is present at max saturation
return new RGBA(q, this.b, p, alpha); case 1 -> new RGBA(q, this.b, p, alpha);
// Hues 120°-179° -- Green is the brightest color, no red is present at max saturation
case 2 -> new RGBA(p, this.b, t, alpha);
// Hues 180°-239° -- Blue is the brightest color, no red is present at max saturation
case 3 -> new RGBA(p, q, this.b, alpha);
// Hues 240°-299° -- Blue is the brightest color, no green is present at max saturation
case 4 -> new RGBA(t, p, this.b, alpha);
// Hues 300°-359° -- Red is the brightest color, no green is present at max saturation
case 5 -> new RGBA(this.b, p, q, alpha);
// Hues 0°-59° -- Red is the brightest color, no blue is present at max saturation
case 0 -> new RGBA(this.b, t, p, alpha);
case 2: // Hues 120°-179° -- Green is the brightest color, no red is present at max saturation default -> throw new IllegalStateException("This HSB object's hue is negative - this is not legal.");
return new RGBA(p, this.b, t, alpha); };
case 3: // Hues 180°-239° -- Blue is the brightest color, no red is present at max saturation
return new RGBA(p, q, this.b, alpha);
case 4: // Hues 240°-299° -- Blue is the brightest color, no green is present at max saturation
return new RGBA(t, p, this.b, alpha);
case 5: // Hues 300°-359° -- Red is the brightest color, no green is present at max saturation
return new RGBA(this.b, p, q, alpha);
case 0: // Hues 0°-59° -- Red is the brightest color, no blue is present at max saturation
return new RGBA(this.b, t, p, alpha);
default:
throw new IllegalStateException("This HSB object's hue is negative - this is not legal.");
}
} }
/** /**

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
import org.apache.commons.lang3.Range; import org.apache.commons.lang3.Range;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
/** /**
* An interface for single precision RGB color objects. * An interface for single precision RGB color objects.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
/** /**
* An interface for single precision RGBA color objects. * An interface for single precision RGBA color objects.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.math.NumberUtils;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color; package org.plutoengine.util.color;
/** /**
* TODO * TODO

View File

@ -0,0 +1,44 @@
package org.plutoengine.address;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
public class VirtualAddressTest
{
public static void main(String[] args)
{
System.out.println();
var basic = VirtualAddress.parse("cz.tefek.pluto");
System.out.println(basic);
// ~.~ - ~.~.~.a => ~.a
// a.b.c.d - ~.a.b.c.d => ~.~.~.~.~.a.b.c.d
// a.b.c.d - d => ~.~.~.~.d
// a.b.x.d - a.b.c.d => ~.~.c.d
// a.b.c - d => ~.~.~.d
// a.b.c - a.b => ~
// a.b.c - a.e.g => ~.~.e.g
var testCases = List.of(
Pair.of("~.~", "~.~.~.a"),
Pair.of("a.b.c.d", "~.a.b.c.d"),
Pair.of("a.b.c.d", "d"),
Pair.of("a.b.x.d", "a.b.c.d"),
Pair.of("a.b.c", "d"),
Pair.of("a.b.c", "a.b"),
Pair.of("a.b.c", "a.e.g"),
Pair.of("a.b.c", "a.b.c"),
Pair.of("a.b.c", "a.b.c.d")
);
testCases.forEach(pair -> {
var a = VirtualAddress.parse(pair.getLeft(), true);
var b = VirtualAddress.parse(pair.getRight(), true);
System.out.printf("\"%s\" - \"%s\" => \"%s\"%n", a, b, a.relativize(b));
});
System.out.println();
}
}

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutodisplay"))
}

View File

@ -1,9 +1,9 @@
package cz.tefek.pluto.engine.graphics.gl; package org.plutoengine.graphics.gl;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL40; import org.lwjgl.opengl.GL40;
import cz.tefek.pluto.engine.gl.IOpenGLEnum; import org.plutoengine.gl.IOpenGLEnum;
public enum DrawMode implements IOpenGLEnum public enum DrawMode implements IOpenGLEnum
{ {

View File

@ -1,6 +1,6 @@
package cz.tefek.pluto.engine.graphics.gl.vao; package org.plutoengine.graphics.gl.vao;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
/** /**
* @author 493msi * @author 493msi

View File

@ -1,18 +1,18 @@
package cz.tefek.pluto.engine.graphics.gl.vao; package org.plutoengine.graphics.gl.vao;
import org.lwjgl.opengl.GL33;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.graphics.gl.DrawMode;
import org.plutoengine.graphics.gl.vbo.ArrayBuffer;
import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import org.lwjgl.opengl.GL33; import org.plutoengine.logger.Logger;
import org.lwjgl.system.MemoryUtil; import org.plutoengine.logger.SmartSeverity;
import cz.tefek.pluto.engine.graphics.gl.DrawMode;
import cz.tefek.pluto.engine.graphics.gl.vbo.ArrayBuffer;
import cz.tefek.pluto.engine.graphics.gl.vbo.IndexArrayBuffer;
import cz.tefek.pluto.io.logger.Logger;
import cz.tefek.pluto.io.logger.SmartSeverity;
public class VertexArray public class VertexArray
{ {

View File

@ -1,9 +1,9 @@
package cz.tefek.pluto.engine.graphics.gl.vao; package org.plutoengine.graphics.gl.vao;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
import cz.tefek.pluto.engine.graphics.gl.vbo.FloatArrayBuffer; import org.plutoengine.graphics.gl.vbo.FloatArrayBuffer;
import cz.tefek.pluto.engine.graphics.gl.vbo.IndexArrayBuffer; import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer;
public class VertexArrayBuilder public class VertexArrayBuilder
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.graphics.gl.vao.attrib; package org.plutoengine.graphics.gl.vao.attrib;
public class ReservedAttributes public class ReservedAttributes
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.graphics.gl.vao.attrib.data; package org.plutoengine.graphics.gl.vao.attrib.data;
import java.lang.reflect.Array; import java.lang.reflect.Array;

View File

@ -1,15 +1,12 @@
package cz.tefek.pluto.engine.graphics.gl.vbo; package org.plutoengine.graphics.gl.vbo;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER; import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; import static org.lwjgl.opengl.GL15.*;
public abstract class ArrayBuffer<T extends VecArray<?>> public abstract class ArrayBuffer<T extends VecArray<?>>
{ {
protected int glID = 0; protected int glID;
private final int vertexDimensions; private final int vertexDimensions;
private final int vertexCount; private final int vertexCount;

View File

@ -1,8 +1,8 @@
package cz.tefek.pluto.engine.graphics.gl.vbo; package org.plutoengine.graphics.gl.vbo;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import cz.tefek.pluto.engine.gl.IOpenGLEnum; import org.plutoengine.gl.IOpenGLEnum;
public enum EnumArrayBufferType implements IOpenGLEnum public enum EnumArrayBufferType implements IOpenGLEnum
{ {

View File

@ -1,8 +1,7 @@
package cz.tefek.pluto.engine.graphics.gl.vbo; package org.plutoengine.graphics.gl.vbo;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray;
public class FloatArrayBuffer extends ArrayBuffer<VecArray<float[]>> public class FloatArrayBuffer extends ArrayBuffer<VecArray<float[]>>
{ {

View File

@ -1,11 +1,11 @@
package cz.tefek.pluto.engine.graphics.gl.vbo; package org.plutoengine.graphics.gl.vbo;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer; import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData; import static org.lwjgl.opengl.GL15.glBufferData;
import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; import org.plutoengine.graphics.gl.vao.attrib.data.VecArray;
public class IndexArrayBuffer extends ArrayBuffer<VecArray<int[]>> public class IndexArrayBuffer extends ArrayBuffer<VecArray<int[]>>
{ {

View File

@ -0,0 +1,44 @@
import org.plutoengine.Versions
sourceSets {
java {
create("config") {
}
main {
java.srcDirs("$buildDir/generated/java")
}
}
}
tasks {
val generateConfigs = register("generateConfigs", Copy::class) {
val projectVariables = mapOf("plutoVersion" to Versions.versionFull)
inputs.properties(projectVariables)
from("src/config/java")
into("$buildDir/generated/java")
expand(projectVariables)
}
compileJava {
dependsOn(generateConfigs)
}
}
dependencies {
api(project(":plutoengine:plutolib"))
api(project(":plutoengine:plutocomponent"))
api(platform("org.lwjgl:lwjgl-bom:${Versions.lwjglVersion}"))
implementation("org.lwjgl:lwjgl")
implementation("org.lwjgl:lwjgl-xxhash")
implementation("org.lwjgl:lwjgl-zstd")
runtimeOnly("org.lwjgl", "lwjgl", classifier = Versions.lwjglNatives)
runtimeOnly("org.lwjgl", "lwjgl-xxhash", classifier = Versions.lwjglNatives)
runtimeOnly("org.lwjgl", "lwjgl-zstd", classifier = Versions.lwjglNatives)
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto; package org.plutoengine;
class PlutoVersionConfig class PlutoVersionConfig
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto; package org.plutoengine;
public interface IVersion<T> extends Comparable<T> public interface IVersion<T> extends Comparable<T>
{ {

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto; package org.plutoengine;
/** /**
* Constants shared by all Pluto libraries. * Constants shared by all Pluto libraries.
@ -9,6 +9,13 @@ package cz.tefek.pluto;
*/ */
public class Pluto extends PlutoVersionConfig public class Pluto extends PlutoVersionConfig
{ {
/**
* The name of this engine.
*
* @since 20.2.0.0-alpha.3
* */
public static final String ENGINE_NAME = "PlutoEngine";
public static final boolean DEBUG_MODE = Boolean.parseBoolean(System.getProperty("cz.tefek.pluto.debug")); public static final boolean DEBUG_MODE = Boolean.parseBoolean(System.getProperty("cz.tefek.pluto.debug"));
/** /**

View File

@ -0,0 +1,9 @@
package org.plutoengine;
import org.plutoengine.component.ComponentManager;
import org.plutoengine.component.PlutoGlobalComponent;
public class PlutoGlobal
{
public static final ComponentManager<PlutoGlobalComponent> COMPONENTS = new ComponentManager<>(PlutoGlobalComponent.class);
}

View File

@ -0,0 +1,33 @@
package org.plutoengine;
import org.plutoengine.address.ThreadSensitive;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.component.ComponentManager;
/**
* @since 20.2.0.0-alpha.3
*
* @author 493msi
*/
@ThreadSensitive(localContexts = true)
public class PlutoLocal
{
private static final ThreadLocal<PlutoLocal> local = ThreadLocal.withInitial(PlutoLocal::new);
public final ComponentManager<PlutoLocalComponent> COMPONENTS;
private PlutoLocal()
{
this.COMPONENTS = new ComponentManager<>(PlutoLocalComponent.class);
}
public static PlutoLocal instance()
{
return local.get();
}
public static ComponentManager<PlutoLocalComponent> components()
{
return instance().COMPONENTS;
}
}

View File

@ -1,11 +1,10 @@
package cz.tefek.pluto; package org.plutoengine;
import org.plutoengine.address.ConstantExpression;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Objects; import java.util.Objects;
import cz.tefek.pluto.annotation.ConstantExpression;
public final class PlutoVersion implements IVersion<PlutoVersion> public final class PlutoVersion implements IVersion<PlutoVersion>
{ {
private final int year; private final int year;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.annotation; package org.plutoengine.address;
import javax.annotation.meta.TypeQualifier; import javax.annotation.meta.TypeQualifier;
import java.lang.annotation.*; import java.lang.annotation.*;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.annotation; package org.plutoengine.address;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

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