Compare commits

...
130 Commits
Author SHA1 Message Date
Jim C K Flaten 73da993dc3 Merge branch 'release/0.7.0' 2025-01-12 04:39:00 +01:00
Jim C K Flaten e3962a6965 Release 0.7.0. 2025-01-12 04:38:55 +01:00
Jim C K Flaten 709a7f20a1 Add missing return. 2024-12-28 01:42:56 +01:00
Jim C K Flaten fe4764042a Run chunk loads on main thread when async is not an option. 2024-12-28 01:34:45 +01:00
Jim C K Flaten c67e4b858c Remove generate_new_chunks config option. 2024-12-28 01:15:39 +01:00
Jim C K Flaten 0831e8dd0b Reject requests for down-sampled LODs. 2024-12-27 18:50:07 +01:00
Jim C K Flaten 4e949b9547 Build for 1.21.3 and 1.21.4. 2024-12-25 21:34:11 +01:00
Jim C K Flaten 2e069a7f14 Protocol v8. 2024-12-25 21:24:04 +01:00
Jim C K Flaten f79f06b90e Show targeted MC version in log on startup. 2024-12-25 18:43:29 +01:00
Jim C K Flaten 125fac4c39 Add logo 🤩 2024-10-24 19:48:55 +00:00
Jim C K Flaten e4988053d8 Bump version number. 2024-10-21 23:26:32 +02:00
Jim C K Flaten e6c1edd26e Merge tag '0.6.1' into develop
0.6.1
2024-10-21 23:26:07 +02:00
Jim C K Flaten be8da0fd48 Merge branch 'release/0.6.1' 2024-10-21 23:25:59 +02:00
Jim C K Flaten 3688ef80c1 Release 0.6.1. 2024-10-21 23:25:34 +02:00
Jim C K Flaten e3fe077f02 Do unsafe things after logger is set. 2024-10-21 23:23:01 +02:00
Jim C K Flaten 888fc62aa3 Bump version number. 2024-10-20 01:41:33 +02:00
Jim C K Flaten 96de5a5e08 Merge tag '0.6.0' into develop
0.6.0
2024-10-20 01:40:41 +02:00
Jim C K Flaten e73441d1a9 Merge branch 'release/0.6.0' 2024-10-20 01:40:32 +02:00
Jim C K Flaten 0d58b7abc7 Release 0.6.0. 2024-10-20 01:40:28 +02:00
Jim C K Flaten 4b9d21191e Bump config version. 2024-10-20 01:40:11 +02:00
Jim C K Flaten 96323adf03 Always use this when referencing members. 2024-10-20 01:09:43 +02:00
Jim C K Flaten c5546107ca Protocol v6. 2024-10-20 01:09:08 +02:00
Jim C K Flaten d84977998f Optional. 2024-10-14 04:27:28 +02:00
Jim C K Flaten b1b576f8e4 Make non-colliding top layer detection optional. 2024-10-14 04:20:04 +02:00
Jim C K Flaten 45612e36eb Revert to yStep=1 when sampling air that's under someting else. 2024-10-14 04:00:16 +02:00
Jim C K Flaten f9d327b6b9 Force yStep=1 for non-colliding top layer. 2024-10-14 01:27:29 +02:00
Jim C K Flaten 30b82dbec4 Don't copy output for parent module. 2024-10-14 00:52:04 +02:00
Jim C K Flaten 246b37ee82 SAST 2024-10-14 00:18:41 +02:00
Jim C K Flaten b9b0feb0f0 Update readme. 2024-10-12 05:53:21 +02:00
Jim C K Flaten 1530edf221 Remove more Github stuff. 2024-10-12 04:40:06 +02:00
Jim C K Flaten 6004d56140 Simplify pipeline. 2024-10-12 02:36:46 +00:00
Jim C K Flaten 6b454927cb Build pipeline. 2024-10-12 03:21:52 +02:00
Jim C K Flaten ce5ac6224a Remove Github config. 2024-10-12 01:43:16 +02:00
Jim C K Flaten e39bec20e0 Option to always scan to sea level. 2024-10-10 23:10:31 +02:00
Jim C K Flaten 0825358a32 Fixed non-colliding top layer detection. 2024-10-08 01:57:00 +02:00
Jim C K Flaten 0ec9733384 Fixed world border center coordinates parsing. 2024-10-07 23:35:42 +02:00
Jim C K Flaten 9b5d68a120 Bump version number. 2024-10-06 23:44:06 +02:00
Jim C K Flaten b47cde87dd Merge tag '0.5.0' into develop
0.5.0
2024-10-06 23:43:36 +02:00
Jim C K Flaten 667694e5e7 Merge branch 'release/0.5.0' 2024-10-06 23:43:33 +02:00
Jim C K Flaten c22665da86 Release 0.5.0. 2024-10-06 23:43:29 +02:00
Jim C K Flaten 7bcc35bf0a Don't walk backwards into the void. 2024-10-06 23:02:00 +02:00
Jim C K Flaten 68a17c4e99 Better handling of non-colliding top layers. 2024-10-06 21:25:02 +02:00
Jim C K Flaten 517bbe2ff2 Pre-load chunk async when possible. 2024-10-06 20:38:09 +02:00
Jim C K Flaten a81b36960d Better Y loop. 2024-10-06 18:11:57 +02:00
Jim C K Flaten 456fdcdf91 Add author. 2024-10-06 17:19:14 +02:00
Jim C K Flaten 24b8f65abf Updates to make Folia work again (#86) 2024-10-06 17:08:40 +02:00
Jim C K Flaten 72a2758709 Support for custom biomes (#85) 2024-10-06 13:44:26 +02:00
Jim C K Flaten ce2837ebe6 Remove unused imports. 2024-10-06 00:52:28 +02:00
Jim C K Flaten aba5dc659c Use chunk snapshots (#84)
Use chunk snapshots instead of accessing live world.

TODO: Fetch the snapshots using region scheduler before passing them to
a builder thread. Will give a _massive_ performance boost to Folia
servers.
2024-10-06 00:51:54 +02:00
Jim C K Flaten 2a8cc8f2d3 Use different threads for Folia. 2024-10-05 21:40:58 +02:00
Jim C K Flaten 107514dc20 This is just a Supplier. 2024-10-05 20:43:15 +02:00
Jim C K Flaten e142d5b85d Beacons (#83) 2024-10-05 20:40:51 +02:00
Jim C K Flaten a348f2fb9b Protocol v5. 2024-10-05 16:10:04 +02:00
Jim C K Flaten 8410ea41b2 Early load is no longer necessary. 2024-10-05 15:57:25 +02:00
Jim C K Flaten 9e4f81d65a Config version (#82)
Closes #78
2024-10-05 15:55:56 +02:00
Jim C K Flaten 932908e6d4 Async DB (#81)
#80
2024-10-05 04:10:54 +02:00
Jim C K Flaten 7d5ff19bf2 Async DB load. 2024-10-03 22:29:45 +02:00
dependabot[bot] b289b7e94f Bump systems.manifold:manifold-preprocessor from 2024.1.33 to 2024.1.34 (#77)
Bumps systems.manifold:manifold-preprocessor from 2024.1.33 to
2024.1.34.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=systems.manifold:manifold-preprocessor&package-manager=maven&previous-version=2024.1.33&new-version=2024.1.34)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 23:28:51 +02:00
dependabot[bot] a79b5f3322 Bump org.jetbrains:annotations from 24.1.0 to 25.0.0 (#76)
Bumps
[org.jetbrains:annotations](https://github.com/JetBrains/java-annotations)
from 24.1.0 to 25.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/JetBrains/java-annotations/releases">org.jetbrains:annotations's
releases</a>.</em></p>
<blockquote>
<h2>25.0.0</h2>
<ul>
<li>Added Kotlin Multiplatform artifact
(multiplatform-annotations).</li>
<li>Removed Java 5 artifact.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md">org.jetbrains:annotations's
changelog</a>.</em></p>
<blockquote>
<h2>Version 25.0.0</h2>
<ul>
<li>Added Kotlin Multiplatform artifact
(multiplatform-annotations).</li>
<li>Removed Java 5 artifact.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/daa47891c4b233820ac528947a1bd4a191de1822"><code>daa4789</code></a>
Merge pull request <a
href="https://redirect.github.com/JetBrains/java-annotations/issues/112">#112</a>
from serjsysoev/signall</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/b1d593d73430e540b1fed0a3017a8819a19f0613"><code>b1d593d</code></a>
Add signAll task that signs all artifacts</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/e1ad90fe468c1d46fc15c0fd435b068a9a8a11a4"><code>e1ad90f</code></a>
Merge pull request <a
href="https://redirect.github.com/JetBrains/java-annotations/issues/111">#111</a>
from serjsysoev/multiplatform-publication</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/229e51a058b3672eaccb979f4dd0704d37e3db4f"><code>229e51a</code></a>
Add javadoc and common sources publishing</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/5d7e0230b12d4f5f59e95fbfb1fd961febfc6ba7"><code>5d7e023</code></a>
Set up multiplatform publishing maintaining jvm artifact name</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/bc94c2ffbb0ba073b3f3ae5bc877844b23f3a081"><code>bc94c2f</code></a>
Move the multiplatform project to the root</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/7bc931ab0d2cb10a5fa65eba869e049b269c1957"><code>7bc931a</code></a>
Make multiplatform expect/actual implicitly actualizing declarations
with jav...</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/42bb869f44c3a3ea12666dc09d1ed1db7857d592"><code>42bb869</code></a>
Fix multiplatform artifact module-info.java</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/243b2f7a3c197bed7d8a6510f742defd745c9f94"><code>243b2f7</code></a>
Remove Java artifact</li>
<li><a
href="https://github.com/JetBrains/java-annotations/commit/a1060cdc5b9a5665768b3f32a49343efef10681c"><code>a1060cd</code></a>
Move java annotations to jvm part of multiplatform artifact</li>
<li>Additional commits viewable in <a
href="https://github.com/JetBrains/java-annotations/compare/24.1.0...25.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.jetbrains:annotations&package-manager=maven&previous-version=24.1.0&new-version=25.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 23:28:34 +02:00
dependabot[bot] dab76ae2b7 Bump com.google.guava:guava from 33.3.0-jre to 33.3.1-jre (#75)
Bumps [com.google.guava:guava](https://github.com/google/guava) from
33.3.0-jre to 33.3.1-jre.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google/guava/releases">com.google.guava:guava's
releases</a>.</em></p>
<blockquote>
<h2>33.3.1</h2>
<h3>Maven</h3>
<pre lang="xml"><code>&lt;dependency&gt;
  &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
  &lt;artifactId&gt;guava&lt;/artifactId&gt;
  &lt;version&gt;33.3.1-jre&lt;/version&gt;
  &lt;!-- or, for Android: --&gt;
  &lt;version&gt;33.3.1-android&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<h3>Jar files</h3>
<ul>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar">33.3.1-jre.jar</a></li>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/guava/33.3.1-android/guava-33.3.1-android.jar">33.3.1-android.jar</a></li>
</ul>
<p>Guava requires <a
href="https://github.com/google/guava/wiki/UseGuavaInYourBuild#what-about-guavas-own-dependencies">one
runtime dependency</a>, which you can download here:</p>
<ul>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar">failureaccess-1.0.1.jar</a></li>
</ul>
<h3>Javadoc</h3>
<ul>
<li><a
href="https://guava.dev/releases/33.3.1-jre/api/docs/">33.3.1-jre</a></li>
<li><a
href="https://guava.dev/releases/33.3.1-android/api/docs/">33.3.1-android</a></li>
</ul>
<h3>JDiff</h3>
<ul>
<li><a
href="https://guava.dev/releases/33.3.1-jre/api/diffs/">33.3.1-jre vs.
33.3.0-jre</a></li>
<li><a
href="https://guava.dev/releases/33.3.1-android/api/diffs/">33.3.1-android
vs. 33.3.0-android</a></li>
<li><a
href="https://guava.dev/releases/33.3.1-android/api/androiddiffs/">33.3.1-android
vs. 33.3.1-jre</a></li>
</ul>
<h3>Changelog</h3>
<ul>
<li>Added <code>j2objc-annotations</code> to the Gradle runtime
classpath to stop producing <a
href="https://redirect.github.com/google/guava/issues/7397">an Android
Gradle Plugin error</a>. (a3b51888c2)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/google/guava/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.google.guava:guava&package-manager=maven&previous-version=33.3.0-jre&new-version=33.3.1-jre)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 23:28:10 +02:00
Jim C K Flaten c2b7a49492 Login sync (#73) 2024-09-24 23:00:44 +02:00
Jim C K Flaten c3c5362cfe These can be NOT NULL 2024-09-24 22:58:21 +02:00
Jim C K Flaten 061992d5b9 Pass the full model. 2024-09-24 17:53:17 +02:00
Jim C K Flaten dcf2213625 Pass logger to repo. 2024-09-24 17:00:31 +02:00
Jim C K Flaten 5f1d243022 Check and update the write queue as well. 2024-09-24 16:52:36 +02:00
Jim C K Flaten b00efd0798 Update LODs when the world changes (#72) 2024-09-24 16:25:58 +02:00
Jim C K Flaten 44e4eff851 Coordinate utility class, LOD repository class, and general cleanup (#71) 2024-09-23 18:16:29 +02:00
dependabot[bot] 5265438b1c Bump org.tukaani:xz from 1.9 to 1.10 (#70)
Bumps [org.tukaani:xz](https://github.com/tukaani-project/xz-java) from
1.9 to 1.10.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tukaani-project/xz-java/blob/master/NEWS.md">org.tukaani:xz's
changelog</a>.</em></p>
<blockquote>
<h2>1.10 (2024-07-29)</h2>
<ul>
<li>
<p>Licensing change: From version 1.10 onwards, XZ for Java is under
the BSD Zero Clause License (0BSD). 1.9 and older are in the
public domain and obviously remain so; the change only affects
the new releases.</p>
<p>0BSD is an extremely permissive license which doesn't require
retaining or reproducing copyright or license notices when
distributing the code, thus in practice there is extremely
little difference to public domain.</p>
</li>
<li>
<p>Mark copyright and license information in the source package so
that it is compliant to the <a
href="https://reuse.software/spec-3.2/">REUSE Specification version
3.2</a>.</p>
</li>
<li>
<p>Improve LZMAInputStream.enableRelaxedEndCondition():</p>
<ul>
<li>
<p>Error detection is slightly better.</p>
</li>
<li>
<p>The input position will always be at the end of the stream
after successful decompression.</p>
</li>
</ul>
</li>
<li>
<p>Support .lzma files that have both a known uncompressed size and
the end marker. Such files are uncommon but valid. The same issue
was fixed in XZ Utils 5.2.6 in 2022.</p>
</li>
<li>
<p>Add ARM64 and RISC-V BCJ filters.</p>
</li>
<li>
<p>Speed optimizations:</p>
<ul>
<li>Delta filter</li>
<li>LZMA/LZMA2 decoder</li>
<li>LZMA/LZMA2 encoder (partially Java &gt;= 9 only)</li>
<li>CRC64 (Java &gt;= 9 only)</li>
</ul>
</li>
<li>
<p>Changes that affect API/ABI compatibility:</p>
<ul>
<li>
<p>Change XZOutputStream constructors to not call the method
<code>public void updateFilters(FilterOptions[]
filterOptions)</code>.</p>
</li>
<li>
<p>In SeekableXZInputStream, change the method
<code>public void seekToBlock(int blockNumber)</code> to not call
the method <code>public long getBlockPos(int blockNumber)</code>.</p>
</li>
<li>
<p>Make the filter options classes <code>final</code>:</p>
<ul>
<li>ARM64Options</li>
<li>ARMOptions</li>
<li>ARMThumbOptions</li>
<li>DeltaOptions</li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/0f5ee02ca950b5e7cdadf89f8c314388092ad64c"><code>0f5ee02</code></a>
Bump the version number to 1.10</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/4cd5a8b4ee9240e5b29cbbe4d002703e7c59880b"><code>4cd5a8b</code></a>
Update NEWS.md for 1.10</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/21edbdf8838d0b208f069540d9a2f595d9f8a016"><code>21edbdf</code></a>
README.md: Add a section about reproducible builds</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/372dc487e817d95889229e0a03a8ac8d451e4b61"><code>372dc48</code></a>
README.md: Use a subheading for old build environments</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/5ec1e8e3ad1cc20c1014adfec89245ef79fa24e7"><code>5ec1e8e</code></a>
build.xml: Add Build-Jdk-Spec to MANIFEST.MF</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/2103742fb418d6e5f5231a02382a0e2b8bf664e8"><code>2103742</code></a>
build.xml: Make MANIFEST.MF friendlier to reproducible builds</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/d18a12a4336f51801fa5d66e53651059affa3ab9"><code>d18a12a</code></a>
build.xml: Use &lt;macrodef&gt; to make JARs of the demo programs</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/39b7371c45adeb858c5b34d46f20de95d2abdff0"><code>39b7371</code></a>
build.xml: Add SOURCE_DATE_EPOCH support</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/5689e647209e5f3201f0b083ca93d584017e61ae"><code>5689e64</code></a>
build.xml: Change the &quot;pom&quot; target to use unversioned
filename</li>
<li><a
href="https://github.com/tukaani-project/xz-java/commit/7f4ccd64e588c8f026d0b4030bbb096bf82aa6f5"><code>7f4ccd6</code></a>
Simplify building with OpenJDK 8</li>
<li>Additional commits viewable in <a
href="https://github.com/tukaani-project/xz-java/compare/v1.9...v1.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.tukaani:xz&package-manager=maven&previous-version=1.9&new-version=1.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 14:22:24 +02:00
dependabot[bot] 9baf68fb9e Bump org.bstats:bstats-bukkit from 3.0.3 to 3.1.0 (#69)
Bumps
[org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics)
from 3.0.3 to 3.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Bastian/bStats-Metrics/releases">org.bstats:bstats-bukkit's
releases</a>.</em></p>
<blockquote>
<h2>Release 3.1.0</h2>
<p>Version 3.1.0</p>
<h2>What's Changed</h2>
<ul>
<li>Support for Folia by <a
href="https://github.com/ricsal1"><code>@​ricsal1</code></a> in <a
href="https://redirect.github.com/Bastian/bstats-metrics/pull/126">Bastian/bstats-metrics#126</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Bastian/bstats-metrics/compare/v3.0.3...v3.1.0">https://github.com/Bastian/bstats-metrics/compare/v3.0.3...v3.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/3e8319112fa82d2ae90e1bffa3afab208587fff8"><code>3e83191</code></a>
Release version 3.1.0</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/388d72aedbb7ab95241226a69780cb02170e13d0"><code>388d72a</code></a>
Add support for Folia (<a
href="https://redirect.github.com/Bastian/bStats-Metrics/issues/126">#126</a>)</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/6f97853f9033067f06fa9fc317abf55dc89e22d9"><code>6f97853</code></a>
Increment patch version and append -SNAPSHOT suffix</li>
<li>See full diff in <a
href="https://github.com/Bastian/bStats-Metrics/compare/v3.0.3...v3.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.bstats:bstats-bukkit&package-manager=maven&previous-version=3.0.3&new-version=3.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 14:20:26 +02:00
Jim C K Flaten 3c2abdcfc1 Clean up biome var. 2024-09-22 23:45:14 +02:00
Jim C K Flaten 595e439934 Do a cheaper region file check before asking for a chunk load. 2024-09-22 22:47:59 +02:00
Jim C K Flaten 3b5eb199b1 Batch database inserts. 2024-09-22 21:16:47 +02:00
Jim C K Flaten 81f725267a Quick mafs. 2024-09-22 19:38:15 +02:00
Jim C K Flaten 1845c94f47 Remove leftovers in POM. 2024-09-21 22:22:48 +02:00
Jim C K Flaten 6e307ca594 Bottom layer should not be 0 blocks high. 2024-09-21 17:45:03 +02:00
Jim C K Flaten a8a1bd12f5 Less is more. 2024-09-21 16:40:17 +02:00
Jim C K Flaten eee2993010 Don't copy defaults. 2024-09-21 16:40:00 +02:00
Jim C K Flaten e68e252c10 Target 1.21.1. 2024-09-21 14:43:10 +02:00
Jim C K Flaten 6d09fe674b Vertical resolution (#68)
Closes #33
2024-09-21 01:24:37 +02:00
Jim C K Flaten 0ea287332b Up default max view distance to 1024 chunks. 2024-09-21 01:24:23 +02:00
Jim C K Flaten c603e339b2 Load full config. 2024-09-21 01:18:05 +02:00
Jim C K Flaten b3c98babcb LZMA (#67)
Closes #66
2024-09-20 21:41:21 +02:00
Jim C K Flaten 248fa5be73 Remove unused method. 2024-09-20 21:05:44 +02:00
Jim C K Flaten e7c30b7732 Add missing file headers. 2024-09-20 21:05:31 +02:00
Jim C K Flaten 2ae0213bc8 LOD storage (#65)
This is a bit of a mess, but works. Ideally I'd want to abstract away
everything from the `java.sql` namespace to keep the implementation
specifics within the database classes, but the amount of work that would
take feels a bit unnecessary.

Closes #32
2024-09-20 14:53:33 +02:00
Jim C K Flaten 191531d28e Add back world interface cloning. 2024-09-20 01:57:50 +02:00
Jim C K Flaten 8e36adce9d Remove emojis. 2024-09-19 19:23:48 +02:00
Jim C K Flaten 79aac2a4b5 Fast builder for overworld, full builder for netherworld. 2024-09-19 19:22:19 +02:00
Jim C K Flaten d102330432 Bump version number. 2024-09-18 18:40:27 +02:00
Jim C K Flaten 48f0ded08f Merge tag '0.4.0' into develop
0.4.0
2024-09-18 18:40:04 +02:00
Jim C K Flaten 2eedc4a588 Merge branch 'release/0.4.0' 2024-09-18 18:39:55 +02:00
Jim C K Flaten 9accd512d0 Release 0.4.0. 2024-09-18 18:39:48 +02:00
Jim C K Flaten 96ef181f7b Update to protocol v4 (#61) 2024-09-18 18:37:51 +02:00
dependabot[bot] c6d69bd52a Bump systems.manifold:manifold-preprocessor from 2024.1.32 to 2024.1.33 (#60)
Bumps systems.manifold:manifold-preprocessor from 2024.1.32 to
2024.1.33.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=systems.manifold:manifold-preprocessor&package-manager=maven&previous-version=2024.1.32&new-version=2024.1.33)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 13:10:23 +02:00
dependabot[bot] 480f4ca0a3 Bump systems.manifold:manifold-preprocessor from 2024.1.30 to 2024.1.32 (#59)
Bumps systems.manifold:manifold-preprocessor from 2024.1.30 to
2024.1.32.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=systems.manifold:manifold-preprocessor&package-manager=maven&previous-version=2024.1.30&new-version=2024.1.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-09 05:58:16 +02:00
dependabot[bot] 6b45e447cc Bump org.bstats:bstats-bukkit from 3.0.2 to 3.0.3 (#57)
Bumps
[org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics)
from 3.0.2 to 3.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Bastian/bStats-Metrics/releases">org.bstats:bstats-bukkit's
releases</a>.</em></p>
<blockquote>
<h2>Release 3.0.3</h2>
<p>Version 3.0.3</p>
<h2>Changes</h2>
<ul>
<li>Use Plugin instead of JavaPlugin in Bukkit Metrics constructor (<a
href="https://redirect.github.com/Bastian/bstats-metrics/pull/121">Bastian/bstats-metrics#121</a>)</li>
<li>Mark bStats threads as daemon (<a
href="https://redirect.github.com/Bastian/bstats-metrics/issues/123">Bastian/bstats-metrics#123</a>)</li>
<li>Add option to <code>MetricsBase</code> constructor to skip
relocation check (only for server implementations - not available to
regular Metrics classes)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/041c82ec5d96921a109ec131589d55c26146f947"><code>041c82e</code></a>
Release version 3.0.3</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/a4eaa75746462d12af3a3de7a01372b90bc5c5c3"><code>a4eaa75</code></a>
Use Plugin instead of JavaPlugin in Bukkit Metrics constructor</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/184ceb943f989ad74eaac8783678abfb54e729ed"><code>184ceb9</code></a>
Add option in MetricsBase to disable relocate check</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/e8d3b1c2b74f3ebcaa77356c4384c495d0b9046e"><code>e8d3b1c</code></a>
Mark bStats threads as daemon</li>
<li><a
href="https://github.com/Bastian/bstats-metrics/commit/6660b2b308c768df622088d2aea029ca6e2cf2c1"><code>6660b2b</code></a>
Increment patch version and append -SNAPSHOT suffix</li>
<li>See full diff in <a
href="https://github.com/Bastian/bStats-Metrics/compare/v3.0.2...v3.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.bstats:bstats-bukkit&package-manager=maven&previous-version=3.0.2&new-version=3.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-27 02:01:41 +02:00
dependabot[bot] 1bad8ceee8 Bump org.apache.maven.plugins:maven-dependency-plugin from 2.8 to 3.8.0 (#56)
Bumps
[org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin)
from 2.8 to 3.8.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/apache/maven-dependency-plugin/releases">org.apache.maven.plugins:maven-dependency-plugin's
releases</a>.</em></p>
<blockquote>
<h2>3.7.1</h2>
<!-- raw HTML omitted -->
<h2>🐛 Bug Fixes</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-943">[MDEP-943]</a> -
Fix appendOutput option (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/414">#414</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
</ul>
<h2>📦 Dependency updates</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-944">[MDEP-944]</a> -
Bump org.apache.maven.shared:maven-common-artifact-filters from 3.3.2 to
3.4.0 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/413">#413</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
</ul>
<h2>👻 Maintenance</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-945">[MDEP-945]</a> -
Fix documentation about get goal (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/415">#415</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-940">[MDEP-940]</a> -
Use Resolver API instead of m-a-t for resolving artifacts (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/410">#410</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
</ul>
<h2>3.7.0</h2>
<!-- raw HTML omitted -->
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-941">[MDEP-941]</a> -
Deprecate dependency:sources in favor of dependency:resolv… (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/411">#411</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-838">[MDEP-838]</a> -
&quot;Artifact has not been packaged yet&quot; error message is not …
(<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/412">#412</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-939">[MDEP-939]</a> -
Lock down classifier in dependency:sources goal (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/409">#409</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
<li>Revert &quot;Fix sources goal for multimodule projects&quot; (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/408">#408</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-923">[MDEP-923]</a> -
Move methods in place where are used (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/407">#407</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li>MDEP-938] Correct invalid property name and add deprecated old one
(<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/404">#404</a>)
<a href="https://github.com/hazendaz"><code>@​hazendaz</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-835">[MDEP-835]</a> -
Add optional dependency for tree IT (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/403">#403</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li>Add missing dependency (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/401">#401</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
</ul>
<h2>🚀 New features and improvements</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-799">[MDEP-799]</a> -
tree: add optional output type json (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/391">#391</a>)
<a href="https://github.com/LogFlames"><code>@​LogFlames</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-928">[MDEP-928]</a> -
Allow excluding classes from dependency:analyze (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/393">#393</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-924">[MDEP-924]</a> -
Get rid of maven-artifact-transfer from list-classes goal (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/382">#382</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-925">[MDEP-925]</a> -
Require Maven 3.6.3 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/381">#381</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-922">[MDEP-922]</a> -
dependency:analyze-exclusions - should report issue only in current
project (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/378">#378</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-917">[MDEP-917]</a> -
dependency:analyze-exclusions - use Resolver API instead of
ProjectBuilder (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/374">#374</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-317">[MDEP-317]</a> -
add mojo to analyze invalid exclusions (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/362">#362</a>)
<a href="https://github.com/vbreivik"><code>@​vbreivik</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-894">[MDEP-894]</a> -
Use <code>@Component</code> only - fix tests (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/360">#360</a>)
<a href="https://github.com/michael-o"><code>@​michael-o</code></a></li>
</ul>
<h2>🐛 Bug Fixes</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-914">[MDEP-914]</a> -
Fix link in collect goal description (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/380">#380</a>)
<a
href="https://github.com/slawekjaranowski"><code>@​slawekjaranowski</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-895">[MDEP-895]</a> -
dependency:sources fails for multi-module project (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/349">#349</a>)
<a href="https://github.com/jmle"><code>@​jmle</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-771">[MDEP-771]</a> -
Remove broken 404 link (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/344">#344</a>)
<a href="https://github.com/elharo"><code>@​elharo</code></a></li>
</ul>
<h2>📦 Dependency updates</h2>
<ul>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-936">[MDEP-936]</a> -
Bump org.apache.maven.shared:maven-dependency-tree from 3.2.1 to 3.3.0
(<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/397">#397</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-920">[MDEP-920]</a> -
Bump org.assertj:assertj-core from 3.25.3 to 3.26.0 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/395">#395</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-929">[MDEP-929]</a> -
Bump org.apache.maven.shared:maven-dependency-analyzer from 1.14.0 to
1.14.1 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/394">#394</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
<li>Bump org.apache.maven.shared:maven-dependency-analyzer from 1.13.2
to 1.14.0 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/392">#392</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
<li><a
href="https://issues.apache.org/jira/browse/MDEP-921">[MDEP-921]</a> -
Bump org.apache.commons:commons-text from 1.11.0 to 1.12.0 (<a
href="https://redirect.github.com/apache/maven-dependency-plugin/pull/376">#376</a>)
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/75814c79197672264078eb6e181aa2b343150dea"><code>75814c7</code></a>
[maven-release-plugin] prepare release
maven-dependency-plugin-3.8.0</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/50397c42b9f4ede4ed82c055caaf3ef0733f3aba"><code>50397c4</code></a>
[MDEP-903] Upgrade to Doxia 2.0.0 Milestone Stack</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/1115ecb4fa11d08f15435b9d094708667e123d18"><code>1115ecb</code></a>
Bump org.codehaus.plexus:plexus-archiver from 4.9.2 to 4.10.0</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/5288cece722923651fbcf5e13741d3f94e910dbf"><code>5288cec</code></a>
Bump org.apache.maven.plugins:maven-plugins from 42 to 43</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/ea4d8e249dc7d7c134c128f25e2a62c6772be1fd"><code>ea4d8e2</code></a>
Bump jettyVersion from 9.4.54.v20240208 to 9.4.55.v20240627</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/2d0b82a865441a683897f5429b9986ed8c75a835"><code>2d0b82a</code></a>
Bump org.codehaus.plexus:plexus-io from 3.4.2 to 3.5.0</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/86b7772498216280c948f8887f20ce2d03269e8a"><code>86b7772</code></a>
(doc) Remove repeated word</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/94e1caf57c84a9da81317b19b3611299d6db700d"><code>94e1caf</code></a>
Bump org.jsoup:jsoup from 1.17.2 to 1.18.1</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/06b427327bfb28a26c9be78ff840948d26eba197"><code>06b4273</code></a>
Bump org.assertj:assertj-core from 3.26.0 to 3.26.3</li>
<li><a
href="https://github.com/apache/maven-dependency-plugin/commit/71cee33151c85d11b4d4ca2f63b5be72f1c482f3"><code>71cee33</code></a>
Remove outdated invoker conditions</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-2.8...maven-dependency-plugin-3.8.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.apache.maven.plugins:maven-dependency-plugin&package-manager=maven&previous-version=2.8&new-version=3.8.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-27 02:01:32 +02:00
Jim C K Flaten b249c1ce90 Bump version number. 2024-08-24 01:51:06 +02:00
Jim C K Flaten dd1fc52fbe Merge tag '0.3.1' into develop
0.3.1
2024-08-24 01:50:23 +02:00
Jim C K Flaten 5591c5b7ec Merge branch 'release/0.3.1' 2024-08-24 01:50:19 +02:00
Jim C K Flaten 1f2f393803 Release 0.3.1. 2024-08-24 01:50:15 +02:00
Jim C K Flaten c404cb3322 Disable these log messages again. 2024-08-24 01:45:41 +02:00
Jim C K Flaten cb7ba37356 Send null byte as well. 2024-08-24 01:36:18 +02:00
Jim C K Flaten ce9bb1060c Bump version. 2024-08-23 23:12:08 +02:00
Jim C K Flaten 9402e6dee9 Merge tag '0.3.0' into develop
0.3.0
2024-08-23 23:11:42 +02:00
Jim C K Flaten 3b04ae153e Merge branch 'release/0.3.0' 2024-08-23 23:11:35 +02:00
Jim C K Flaten f7f723b7c6 Release 0.3.0. 2024-08-23 23:11:30 +02:00
Jim C K Flaten 4e8cb7fdf2 World border (#55)
Introduces per-world configuration and world borders.
2024-08-23 23:02:53 +02:00
Jim C K Flaten 3e49275c63 Drop Forge packet ID on 1.20.4 and below. 2024-08-23 22:58:23 +02:00
Jim C K Flaten d349cd038e Property parsing appears to work client-side now. 2024-08-19 22:27:03 +02:00
Jim C K Flaten 5452cab99a Update config comments. 2024-08-19 21:13:28 +02:00
Jim C K Flaten f22fb514b2 Initialize early. 2024-08-19 20:43:41 +02:00
Jim C K Flaten 3e5fa994dc Log prefix. 2024-08-19 20:43:25 +02:00
Jim C K Flaten e5989134cc Only include patch level from 1.20.5 and up. 2024-08-19 20:39:14 +02:00
Jim C K Flaten 8c830e30ae Only use Paper scheduler for Folia. 2024-08-19 20:22:41 +02:00
dependabot[bot] aca9769658 Bump com.github.technicallycoded:FoliaLib from main-SNAPSHOT to v0.4.3 (#52)
Bumps
[com.github.technicallycoded:FoliaLib](https://github.com/TechnicallyCoded/FoliaLib)
from main-SNAPSHOT to v0.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/TechnicallyCoded/FoliaLib/releases">com.github.technicallycoded:FoliaLib's
releases</a>.</em></p>
<blockquote>
<h2>BETA 0.4.3 - Chain Futures, New Methods, and Bug Patches</h2>
<h2>What's Changed</h2>
<ul>
<li>Chain by <a
href="https://github.com/CJCrafter"><code>@​CJCrafter</code></a> in <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/pull/16">TechnicallyCoded/FoliaLib#16</a></li>
<li>Thread check by <a
href="https://github.com/CJCrafter"><code>@​CJCrafter</code></a> in <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/pull/18">TechnicallyCoded/FoliaLib#18</a></li>
<li>Check projectile death state, fixes <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/issues/14">#14</a>
by <a href="https://github.com/PikaMug"><code>@​PikaMug</code></a> in <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/pull/24">TechnicallyCoded/FoliaLib#24</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/CJCrafter"><code>@​CJCrafter</code></a>
made their first contribution in <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/pull/16">TechnicallyCoded/FoliaLib#16</a></li>
<li><a href="https://github.com/PikaMug"><code>@​PikaMug</code></a> made
their first contribution in <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/pull/24">TechnicallyCoded/FoliaLib#24</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/TechnicallyCoded/FoliaLib/compare/v0.3.4...v0.4.3">https://github.com/TechnicallyCoded/FoliaLib/compare/v0.3.4...v0.4.3</a></p>
<h2>BETA 0.3.4 - Add runAtEntityLater and runAtEntityLater &amp;
Fixes</h2>
<p>Changes</p>
<ul>
<li>Add runAtEntityLater and runAtEntityLater. Resolves <a
href="https://redirect.github.com/TechnicallyCoded/FoliaLib/issues/12">#12</a></li>
<li>Fix a few NPEs</li>
</ul>
<h2>BETA 0.3.1 - Relocation Checks, Task Consumers &amp; Tick Duration
Corrections</h2>
<p>Changes:</p>
<ul>
<li>Added relocation (shade plugin) check at startup</li>
<li>Added task consumers</li>
<li>Added better platform detection</li>
<li>Automatically rewrite tick values to be &gt; 0 (Will show a warning
in console unless you call
FoliaLib#disableInvalidTickValueWarning())</li>
<li>Update README with new examples and information</li>
<li>Minor bug fixes</li>
</ul>
<h2>BETA 0.2.5 - Java 8 support, Support ticks as a time unit &amp;
Fixes</h2>
<p>Changes:</p>
<ul>
<li>Migrate the project to being a multi-module project</li>
<li>Move non-Folia classes to be compiled in Java 8</li>
<li>Add support for legacy Minecraft versions (don't load Java 16
classes at runtime if not running on Folia)</li>
<li>Add ability to check is a task is cancelled</li>
<li>Add isCancelled() compatibility with legacy Bukkit versions using
isCurrentlyRunning() and isQueued()</li>
<li>Minor fixes &amp; documentation updates</li>
</ul>
<h2>BETA 0.2.0 - Cancellable tasks, entity tasks &amp; fixes</h2>
<p>Changes:</p>
<ul>
<li>Added cancellable tasks,</li>
<li>Added entity tasks,</li>
<li>Removed scopes and use separate method names instead,</li>
<li>Changed server version checks to use class names instead of version
name</li>
</ul>
<h2>BETA 0.1.1 - Release</h2>
<p>Initial release + minor updates and fixes</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/TechnicallyCoded/FoliaLib/commits/v0.4.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.github.technicallycoded:FoliaLib&package-manager=maven&previous-version=main-SNAPSHOT&new-version=v0.4.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:33:50 +02:00
dependabot[bot] 6aa41955fe Bump com.google.guava:guava from 33.2.1-jre to 33.3.0-jre (#53)
Bumps [com.google.guava:guava](https://github.com/google/guava) from
33.2.1-jre to 33.3.0-jre.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google/guava/releases">com.google.guava:guava's
releases</a>.</em></p>
<blockquote>
<h2>33.3.0</h2>
<h3>Maven</h3>
<pre lang="xml"><code>&lt;dependency&gt;
  &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
  &lt;artifactId&gt;guava&lt;/artifactId&gt;
  &lt;version&gt;33.3.0-jre&lt;/version&gt;
  &lt;!-- or, for Android: --&gt;
  &lt;version&gt;33.3.0-android&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<h3>Jar files</h3>
<ul>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/guava/33.3.0-jre/guava-33.3.0-jre.jar">33.3.0-jre.jar</a></li>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/guava/33.3.0-android/guava-33.3.0-android.jar">33.3.0-android.jar</a></li>
</ul>
<p>Guava requires <a
href="https://github.com/google/guava/wiki/UseGuavaInYourBuild#what-about-guavas-own-dependencies">one
runtime dependency</a>, which you can download here:</p>
<ul>
<li><a
href="https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar">failureaccess-1.0.1.jar</a></li>
</ul>
<h3>Javadoc</h3>
<ul>
<li><a
href="https://guava.dev/releases/33.3.0-jre/api/docs/">33.3.0-jre</a></li>
<li><a
href="https://guava.dev/releases/33.3.0-android/api/docs/">33.3.0-android</a></li>
</ul>
<h3>JDiff</h3>
<ul>
<li><a
href="https://guava.dev/releases/33.3.0-jre/api/diffs/">33.3.0-jre vs.
33.2.1-jre</a></li>
<li><a
href="https://guava.dev/releases/33.3.0-android/api/diffs/">33.3.0-android
vs. 33.2.1-android</a></li>
<li><a
href="https://guava.dev/releases/33.3.0-android/api/androiddiffs/">33.3.0-android
vs. 33.3.0-jre</a></li>
</ul>
<h3>Changelog</h3>
<ul>
<li><code>base</code>: Removed <code>@Beta</code> from the
<code>Duration</code> overload of
<code>Suppliers.memoizeWithExpiration</code>.
(76fca99db95ce9c8e55bb9c37fd0e44ef0451a80)</li>
<li><code>cache</code>: Added <code>CacheBuilder</code>
<code>Duration</code> overloads to <code>guava-android</code>.
(a5f9bcafd6)</li>
<li><code>collect</code>: Removed <code>@Beta</code> from the
<code>guava-android</code> <code>Collector</code> APIs.
(c86c09dc3d)</li>
<li><code>collect</code>: Added
<code>ImmutableMultimap.builderWithExpectedKeys</code> and
<code>ImmutableMultimap.Builder.expectedValuesPerKey</code>.
(c3d5b17dc2)</li>
<li><code>graph</code>: Improved <code>Graphs.hasCycle</code> to avoid
causing <code>StackOverflowError</code> for long paths.
(63734b9dfc)</li>
<li><code>net</code>: Added <code>text/markdown</code> to
<code>MediaType</code>. (2466a099ae)</li>
<li><code>net</code>: Deprecated <code>HttpHeaders</code> constant for
<code>Sec-Ch-UA-Form-Factor</code> in favor of
<code>Sec-Ch-UA-Form-Factors</code> to follow the latest spec.
(b310b7e1ee)</li>
<li><code>testing</code>: Changed some test libraries to throw
<code>AssertionError</code> (instead of the more specific
<code>AssertionFailedError</code>) in some cases. (fdfbed1985)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/google/guava/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.google.guava:guava&package-manager=maven&previous-version=33.2.1-jre&new-version=33.3.0-jre)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:33:20 +02:00
Jim C K Flaten 50730d0f11 Option to disable chunk generation when building LODs (#51)
Closes #49
2024-08-18 02:03:49 +02:00
Jim C K Flaten 2f4a070be8 Regional scheduling takes block coordinates, not chunk coordinates. 2024-08-17 22:57:23 +02:00
Jim C K Flaten 0c76ae7748 Fix Folialib shading. 2024-08-17 22:34:23 +02:00
Jim C K Flaten b8b6bd1b84 Build for multiple versions of Minecraft (#48)
![image](https://github.com/user-attachments/assets/36369286-76a5-45cc-88a6-92187c24b38a)

Closes #47
2024-08-14 16:30:49 +02:00
Jim C K Flaten 49beaea384 Update README.md 2024-08-12 03:06:43 +02:00
Jim C K Flaten 1b71004a64 Create FUNDING.yml 2024-08-12 02:55:06 +02:00
Jim C K Flaten 3e530589f4 Add Folia to readme. 2024-08-12 01:01:05 +02:00
Jim C K Flaten 43695a4c22 Folia (#46)
Closes #44
2024-08-12 00:44:39 +02:00
Jim C K Flaten b57d40742f Add level_key_prefix config option. (#45) 2024-08-07 12:13:09 +02:00
Jim C K Flaten 6e78561671 Show DH players in console/log. 2024-08-06 00:54:22 +02:00
Jim C K Flaten d48fe0934f Merge tag '0.2.2' into develop
0.2.2
2024-07-19 00:17:47 +02:00
Jim C K Flaten 7a2992d47c Bump version number. 2024-07-18 23:40:41 +02:00
Jim C K Flaten de03cad4f0 Merge tag '0.2.1' into develop
0.2.1
2024-07-18 23:40:03 +02:00
60 changed files with 2921 additions and 447 deletions

No files matched your search

-13
View File
@@ -1,13 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "maven" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
ignore:
- dependency-name: "io.netty:*"
-35
View File
@@ -1,35 +0,0 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Java CI with Maven
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
+3
View File
@@ -25,10 +25,13 @@ replay_pid*
# IntelliJ IDEA project config
/.idea
/*.iml
# Maven output
/dependency-reduced-pom.xml
/target
/jars
# Test server
/server
/folia
+49
View File
@@ -0,0 +1,49 @@
include:
- template: Security/SAST.gitlab-ci.yml
variables:
MAVEN_OPTS: >-
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=.m2/repository
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
MAVEN_CLI_OPTS: >-
--batch-mode
--errors
--fail-at-end
--show-version
-DinstallAtEnd=true
-DdeployAtEnd=true
stages:
- build
- test
default:
image: maven:3.9-eclipse-temurin-21-alpine
cache:
key: maven-repo-${CI_COMMIT_REF_SLUG}
unprotect: true
paths:
- .m2/repository
build:
stage: build
parallel:
matrix:
- MC_VER: [ "1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4", ]
script:
- mvn package --projects pom-${MC_VER}.xml ${MAVEN_CLI_OPTS}
- mv jars/*.jar ./
artifacts:
name: DHS-${MC_VER}-${CI_COMMIT_REF_SLUG}-${CI_JOB_STARTED_AT}
paths:
- "./*.jar"
-1
View File
@@ -1 +0,0 @@
dhs.jckf.no
+9 -5
View File
@@ -1,19 +1,23 @@
# Server-side support for [Distant Horizons](https://gitlab.com/jeseibel/distant-horizons)
<img src="https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/new/SVG/Distant-Horizons-Plugin.svg?ref_type=heads" height="128" alt="Plugin logo">
DH Support is a Bukkit/Spigot/Paper server plugin that transmits Level Of Detail (LOD) data to connected clients in order to provide a much higher view distance than Minecraft normally would allow. Distant Horizons _will_ work fine without this plugin, but then each client will have to be within normal view distance of chunks to load them, and they will not receive updates for distant chunks when they change.
# Distant Horizons Server Plugin
DH Support is a Bukkit/Spigot/Paper/Folia server plugin that transmits Level Of Detail (LOD) data to players with Distant Horizons installed. Distant Horizons _will_ work fine without this plugin, but then each client will have to be within normal view distance of chunks to load them, and they will not receive updates for distant chunks when they change.
## Installation
Download the [latest release](https://github.com/Jckf/DH-Support/releases) and drop the JAR in your plugins folder, and you're done!
Download the [latest release](https://gitlab.com/distant-horizons-team/distant-horizons-server-plugin/-/releases) and drop the JAR in your plugins folder, and you're done!
## Configuration
The default values should be pretty solid, but you may tweak them to better suit your specific needs. Everything you need to know should be in config.yml.
## Compilation
## Building
The project uses Maven, so just run `mvn` in the project directory to compile and package a new JAR.
## Contribution
There are several ways to contribute to this project. You can offer your feedback on [the DH Discord](https://discord.gg/xAB8G4cENx) in [this thread](https://discord.com/channels/881614130614767666/1154490009735417989), report any issues or bugs you find, or attack an open issue and submit a pull request.
There are several ways to contribute to this project. You can offer your feedback on [the DH Discord](https://discord.gg/xAB8G4cENx), report any issues or bugs you find, or attack an open issue and submit a pull request ❤️
![Plugin usage statistics](https://bstats.org/signatures/bukkit/DH%20Support.svg)
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.16</mc.api-version>
<mc.api-version-patch>.5</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.17</mc.api-version>
<mc.api-version-patch>.1</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.18</mc.api-version>
<mc.api-version-patch>.2</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.19</mc.api-version>
<mc.api-version-patch>.2</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.19</mc.api-version>
<mc.api-version-patch>.4</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.20</mc.api-version>
<mc.api-version-patch>.1</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.20</mc.api-version>
<mc.api-version-patch>.2</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.20</mc.api-version>
<mc.api-version-patch>.4</mc.api-version-patch>
<dhs.read-forge-byte>true</dhs.read-forge-byte>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+26
View File
@@ -0,0 +1,26 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.20.6</mc.api-version>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+26
View File
@@ -0,0 +1,26 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.21.1</mc.api-version>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+26
View File
@@ -0,0 +1,26 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.21.3</mc.api-version>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+26
View File
@@ -0,0 +1,26 @@
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>DHSupport-${mc.api-version}</artifactId>
<parent>
<groupId>no.jckf</groupId>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<relativePath>./pom.xml</relativePath>
</parent>
<properties>
<skip-copy>false</skip-copy>
<mc.api-version>1.21.4</mc.api-version>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+107 -24
View File
@@ -2,14 +2,19 @@
<modelVersion>4.0.0</modelVersion>
<groupId>no.jckf</groupId>
<name>dh-support</name>
<version>0.2.2</version>
<artifactId>DHSupport</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<properties>
<jdk>17</jdk>
<revision>0.7.0</revision>
<jdk>21</jdk>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.class>no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin</main.class>
<mc.api-version></mc.api-version>
<mc.api-version-patch></mc.api-version-patch>
<dhs.read-forge-byte></dhs.read-forge-byte>
<skip-copy>true</skip-copy>
</properties>
<developers>
@@ -30,48 +35,57 @@
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public</url>
</repository>
<repository>
<id>jitpack</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.1-jre</version>
<version>33.3.1-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.1.0</version>
<version>25.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.lz4</groupId>
<artifactId>lz4-java</artifactId>
<version>1.8.0</version>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.10</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.technicallycoded</groupId>
<artifactId>FoliaLib</artifactId>
<version>v0.4.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean install</defaultGoal>
<finalName>${project.artifactId}</finalName>
<defaultGoal>clean package</defaultGoal>
<plugins>
<plugin>
@@ -83,6 +97,19 @@
<source>${jdk}</source>
<target>${jdk}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArgs>
<arg>-Xplugin:Manifold</arg>
<arg>-AREAD_FORGE_BYTE=${dhs.read-forge-byte}</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>systems.manifold</groupId>
<artifactId>manifold-preprocessor</artifactId>
<version>2024.1.34</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
@@ -92,18 +119,18 @@
<version>3.6.0</version>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedArtifactAttached>false</shadedArtifactAttached>
<shadedClassifierName>all</shadedClassifierName>
<relocations>
<relocation>
<pattern>net.jpountz</pattern>
<shadedPattern>no.jckf.dhsupport.lz4</shadedPattern>
<pattern>org.bstats</pattern>
<shadedPattern>no.jckf.dhsupport.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>no.jckf.dhsupport.bstats</shadedPattern>
<pattern>com.tcoded.folialib</pattern>
<shadedPattern>no.jckf.dhsupport.folialib</shadedPattern>
</relocation>
</relocations>
</configuration>
@@ -117,16 +144,57 @@
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*</exclude>
</excludes>
</filter>
</filters>
<artifactSet>
<includes>
<include>org.lz4</include>
<include>org.tukaani</include>
<include>org.bstats</include>
<include>com.github.technicallycoded</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.8.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>jars</outputDirectory>
<skip>${skip-copy}</skip>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>${project.packaging}</type>
<destFileName>DHS-${project.version}_for_MC-${mc.api-version}${mc.api-version-patch}.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
@@ -150,4 +218,19 @@
</resource>
</resources>
</build>
<modules>
<module>pom-1.16.5.xml</module>
<module>pom-1.17.1.xml</module>
<module>pom-1.18.2.xml</module>
<module>pom-1.19.2.xml</module>
<module>pom-1.19.4.xml</module>
<module>pom-1.20.1.xml</module>
<module>pom-1.20.2.xml</module>
<module>pom-1.20.4.xml</module>
<module>pom-1.20.6.xml</module>
<module>pom-1.21.1.xml</module>
<module>pom-1.21.3.xml</module>
<module>pom-1.21.4.xml</module>
</modules>
</project>
@@ -0,0 +1,120 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.bukkit;
import com.tcoded.folialib.FoliaLib;
import no.jckf.dhsupport.core.Utils;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.scheduling.Scheduler;
import org.bukkit.Location;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
public class BukkitScheduler implements Scheduler
{
protected DhSupportBukkitPlugin plugin;
protected FoliaLib foliaLib;
protected Executor executor;
public BukkitScheduler(DhSupportBukkitPlugin plugin)
{
this.plugin = plugin;
this.foliaLib = new FoliaLib(this.plugin);
this.executor = Executors.newFixedThreadPool(this.plugin.getDhSupport().getConfig().getInt(DhsConfig.SCHEDULER_THREADS));
this.plugin.getDhSupport().info("Using " + Utils.ucFirst(this.foliaLib.getImplType().name().toLowerCase().replace('_', ' ')) + " scheduler.");
}
@Override
public boolean canReadWorldAsync()
{
return !this.foliaLib.isFolia();
}
@Override
public <U> CompletableFuture<U> runOnMainThread(Supplier<U> supplier)
{
CompletableFuture<U> future = new CompletableFuture<>();
this.foliaLib.getScheduler().runNextTick((task) -> {
try {
future.complete(supplier.get());
} catch (Exception exception) {
future.completeExceptionally(exception);
}
});
return future;
}
@Override
public <U> CompletableFuture<U> runOnRegionThread(UUID worldId, int x, int z, Supplier<U> supplier)
{
CompletableFuture<U> future = new CompletableFuture<>();
Location location = new Location(
this.plugin.getServer().getWorld(worldId),
x,
0,
z
);
this.foliaLib.getScheduler().runAtLocation(
location,
(task) -> {
try {
future.complete(supplier.get());
} catch (Exception exception) {
future.completeExceptionally(exception);
}
}
);
return future;
}
@Override
public <U> CompletableFuture<U> runOnSeparateThread(Supplier<U> supplier)
{
CompletableFuture<U> future = new CompletableFuture<>();
this.executor.execute(() -> {
try {
future.complete(supplier.get());
} catch (Exception exception) {
future.completeExceptionally(exception);
}
});
return future;
}
public void runTimer(Runnable runnable, long initialDelay, long interval)
{
this.foliaLib.getScheduler().runTimer(runnable, initialDelay, interval);
}
}
@@ -18,38 +18,208 @@
package no.jckf.dhsupport.bukkit;
import no.jckf.dhsupport.core.Coordinates;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.WorldConfiguration;
import no.jckf.dhsupport.core.dataobject.Beacon;
import no.jckf.dhsupport.core.world.WorldInterface;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.*;
import org.bukkit.block.BlockState;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import java.awt.Color;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
public class BukkitWorldInterface implements WorldInterface
{
protected static boolean BIOME_KEY_WARNING_SENT = false;
protected static boolean ASYNC_LOAD_WARNING_SENT = false;
protected DhSupportBukkitPlugin plugin;
protected World world;
protected Block block;
protected Configuration config;
public BukkitWorldInterface(World world)
protected WorldConfiguration worldConfig;
protected Logger logger;
protected Map<String, ChunkSnapshot> chunks = new HashMap<>();
protected UnsafeValues unsafeValues;
@Nullable
protected Method getBiomeKey;
@Nullable
protected Method getChunkAtAsync;
public BukkitWorldInterface(DhSupportBukkitPlugin plugin, World world, Configuration config)
{
this.plugin = plugin;
this.world = world;
this.config = config;
this.worldConfig = new WorldConfiguration(this, config);
}
public void setLogger(Logger logger)
{
this.logger = logger;
}
public Logger getLogger()
{
return this.logger;
}
public void doUnsafeThings()
{
this.unsafeValues = Bukkit.getUnsafe();
// Detect if we're running under Paper (or a Paper fork) that has this patch:
// https://github.com/PaperMC/Paper/commit/5bf259115c1ce29dd96df5fcf53739c94d39f902
try {
Class<?> regionAccessor = Class.forName("org.bukkit.RegionAccessor");
this.getBiomeKey = this.unsafeValues.getClass().getMethod("getBiomeKey", regionAccessor, int.class, int.class, int.class);
} catch (ClassNotFoundException | NoSuchMethodException e) {
if (!BIOME_KEY_WARNING_SENT) {
this.getLogger().warning("Custom biomes are not supported on this server.");
BIOME_KEY_WARNING_SENT = true;
}
}
try {
this.getChunkAtAsync = this.world.getClass().getMethod("getChunkAtAsyncUrgently", int.class, int.class);
} catch (NoSuchMethodException exception) {
if (!ASYNC_LOAD_WARNING_SENT) {
this.getLogger().warning("Async chunk loading is not supported on this server. Performance will suffer.");
ASYNC_LOAD_WARNING_SENT = true;
}
}
}
@Override
public WorldInterface newInstance()
{
return new BukkitWorldInterface(this.world);
BukkitWorldInterface newInstace = new BukkitWorldInterface(this.plugin, this.world, this.config);
newInstace.setLogger(this.getLogger());
newInstace.doUnsafeThings();
return newInstace;
}
// Shitty cache :)
protected Block getBlock(int x, int y, int z)
protected ChunkSnapshot getChunk(int x, int z)
{
if (this.block == null || this.block.getY() != y || this.block.getX() != x || this.block.getZ() != z) {
this.block = this.world.getBlockAt(x, y, z);
int chunkX = Coordinates.blockToChunk(x);
int chunkZ = Coordinates.blockToChunk(z);
String key = chunkX + "x" + chunkZ;
if (this.chunks.containsKey(key)) {
return this.chunks.get(key);
}
return this.block;
ChunkSnapshot chunk = this.world.getChunkAt(chunkX, chunkZ).getChunkSnapshot(true, true, false);
this.chunks.put(key, chunk);
return chunk;
}
@Override
public UUID getId()
{
return this.world.getUID();
}
@Override
public String getName()
{
return this.world.getName();
}
@Override
public boolean chunkExists(int x, int z)
{
int chunkX = Coordinates.blockToChunk(x);
int chunkZ = Coordinates.blockToChunk(z);
boolean alreadyLoaded = this.world.isChunkLoaded(chunkX, chunkZ);
if (alreadyLoaded) {
return true;
}
int regionX = Coordinates.chunkToRegion(chunkX);
int regionZ = Coordinates.chunkToRegion(chunkZ);
File regionFile = new File(world.getWorldFolder() + "/region/r." + regionX + "." + regionZ + ".mca");
if (!regionFile.exists()) {
return false;
}
boolean exists = this.world.loadChunk(chunkX, chunkZ, false);
if (exists) {
this.world.unloadChunk(chunkX, chunkZ, false);
}
return exists;
}
@Override
public boolean loadChunk(int x, int z)
{
int chunkX = Coordinates.blockToChunk(x);
int chunkZ = Coordinates.blockToChunk(z);
this.world.loadChunk(chunkX, chunkZ);
return true;
}
@Override
public CompletableFuture<Boolean> loadChunkAsync(int x, int z)
{
if (this.getChunkAtAsync == null) {
return this.plugin.getDhSupport().getScheduler().runOnMainThread(() -> this.loadChunk(x, z));
}
int chunkX = Coordinates.blockToChunk(x);
int chunkZ = Coordinates.blockToChunk(z);
try {
CompletableFuture<Chunk> chunkFuture = (CompletableFuture<Chunk>) this.getChunkAtAsync.invoke(this.world, chunkX, chunkZ);
return chunkFuture.thenApply((chunk) -> true);
} catch (InvocationTargetException | IllegalAccessException exception) {
throw new RuntimeException(exception);
}
}
@Override
public boolean unloadChunk(int x, int z)
{
return this.world.unloadChunk(x, z);
}
@Override
public boolean unloadChunkAsync(int x, int z)
{
return this.world.unloadChunkRequest(x, z);
}
@Override
@@ -73,25 +243,36 @@ public class BukkitWorldInterface implements WorldInterface
@Override
public int getHighestYAt(int x, int z)
{
return this.world.getHighestBlockAt(x, z).getY();
return this.getChunk(x, z).getHighestBlockYAt(Coordinates.blockToChunkRelative(x), Coordinates.blockToChunkRelative(z));
}
@Override
public String getBiomeAt(int x, int z)
{
return this.getBlock(x, this.world.getSeaLevel(), z).getBiome().getKey().toString();
NamespacedKey key = this.getChunk(x, z).getBiome(Coordinates.blockToChunkRelative(x), this.getSeaLevel(), Coordinates.blockToChunkRelative(z)).getKey();
// If the server just reports "custom" and we have access to getBiomeKey, try to get the correct biome name.
if (key.toString().equals("minecraft:custom") && this.getBiomeKey != null) {
try {
key = (NamespacedKey) this.getBiomeKey.invoke(this.unsafeValues, this.world, x, this.getSeaLevel(), z);
} catch (IllegalAccessException | InvocationTargetException exception) {
throw new RuntimeException(exception);
}
}
return key.toString();
}
@Override
public String getMaterialAt(int x, int y, int z)
{
return this.getBlock(x, y, z).getBlockData().getMaterial().getKey().toString();
return this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getKey().toString();
}
@Override
public String getBlockStateAsStringAt(int x, int y, int z)
{
return this.getBlock(x, y, z).getBlockData().getAsString();
return this.getChunk(x, z).getBlockData(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getAsString();
}
@Override
@@ -121,18 +302,43 @@ public class BukkitWorldInterface implements WorldInterface
@Override
public byte getBlockLightAt(int x, int y, int z)
{
return this.getBlock(x, y, z).getLightFromBlocks();
return (byte) this.getChunk(x, z).getBlockEmittedLight(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z));
}
@Override
public byte getSkyLightAt(int x, int y, int z)
{
return this.getBlock(x, y, z).getLightFromSky();
return (byte) this.getChunk(x, z).getBlockSkyLight(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z));
}
@Override
public boolean isTransparent(int x, int y, int z)
public Collection<Beacon> getBeaconsInChunk(int x, int z)
{
return this.getBlock(x, y, z).getBlockData().getMaterial().isTransparent();
Collection<Beacon> beacons = new ArrayList<>();
BlockState[] blocks = this.world.getChunkAt(Coordinates.blockToChunk(x), Coordinates.blockToChunk(z)).getTileEntities();
for (BlockState block : blocks) {
if (!block.getType().equals(Material.BEACON)) {
continue;
}
beacons.add(
new Beacon(
block.getX(),
block.getY(),
block.getZ(),
Color.WHITE.getRGB() // TODO?
)
);
}
return beacons;
}
@Override
public Configuration getConfig()
{
return this.worldConfig;
}
}
@@ -19,85 +19,93 @@
package no.jckf.dhsupport.bukkit;
import no.jckf.dhsupport.bukkit.handler.ConfigLoader;
import no.jckf.dhsupport.bukkit.handler.Handler;
import no.jckf.dhsupport.bukkit.handler.PluginMessageProxy;
import no.jckf.dhsupport.bukkit.handler.WorldHandler;
import no.jckf.dhsupport.core.DhSupport;
import org.bstats.bukkit.Metrics;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.io.InputStreamReader;
public class DhSupportBukkitPlugin extends JavaPlugin
{
protected static int LOD_REFRESH_INTERVAL = 60 * 20;
protected DhSupport dhSupport;
protected Metrics metrics;
protected final Map<Class<? extends Handler>, Handler> handlers = new HashMap<>()
{{
this.put(ConfigLoader.class, null);
this.put(PluginMessageProxy.class, null);
}};
protected ConfigLoader configLoader;
protected PluginMessageProxy pluginMessageProxy;
protected BukkitScheduler scheduler;
@Override
public void onEnable()
{
String pluginVersion = "N/A";
String mcVersion = "N/A";
try {
YamlConfiguration meta = new YamlConfiguration();
meta.load(new InputStreamReader(this.getResource("plugin.yml")));
pluginVersion = meta.getString("version");
mcVersion = meta.getString("mc-version");
} catch (Exception exception) {
}
this.getLogger().info("I am DHS " + pluginVersion + " for MC " + mcVersion + ".");
this.dhSupport = new DhSupport();
this.dhSupport.setLogger(this.getLogger());
this.dhSupport.setDataDirectory(this.getDataFolder().getAbsolutePath());
this.metrics = new Metrics(this, 21843);
for (Class<? extends Handler> className : this.handlers.keySet()) {
try {
Handler instance = className
.getConstructor(DhSupportBukkitPlugin.class)
.newInstance(this);
this.configLoader = new ConfigLoader(this);
this.configLoader.onEnable();
instance.onEnable();
this.handlers.replace(className, instance);
} catch (Exception exception) {
this.getLogger().warning("Failed to enable handler: " + className);
this.getLogger().warning(exception.getClass().getSimpleName() + " - " + exception.getMessage());
this.getServer().getPluginManager().disablePlugin(this);
return;
}
}
this.pluginMessageProxy = new PluginMessageProxy(this);
this.pluginMessageProxy.onEnable();
this.dhSupport.onEnable();
this.scheduler = new BukkitScheduler(this);
this.dhSupport.setScheduler(this.scheduler);
this.scheduler.runTimer(() -> {
this.dhSupport.updateTouchedLods();
}, LOD_REFRESH_INTERVAL, LOD_REFRESH_INTERVAL);
this.getServer().getPluginManager().registerEvents(new WorldHandler(this), this);
this.getLogger().info("Ready 😀");
this.getLogger().info("Ready!");
}
@Override
public void onDisable()
{
if (this.pluginMessageProxy != null) {
this.pluginMessageProxy.onDisable();
this.pluginMessageProxy = null;
}
if (this.configLoader != null) {
this.configLoader.onDisable();
this.configLoader = null;
}
if (this.dhSupport != null) {
this.dhSupport.onDisable();
this.dhSupport = null;
}
for (Class<? extends Handler> className : this.handlers.keySet()) {
Handler instance = this.handlers.get(className);
try {
if (instance != null) {
instance.onDisable();
}
} catch (Exception exception) {
this.getLogger().warning("Failed to disable handler: " + className);
this.getLogger().warning(exception.getClass().getSimpleName() + " - " + exception.getMessage());
}
this.handlers.replace(className, null);
}
this.getLogger().info("👋😭");
this.getLogger().info("Lights out!");
}
@Nullable
@@ -20,6 +20,8 @@ package no.jckf.dhsupport.bukkit.handler;
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import org.bukkit.configuration.MemoryConfiguration;
import org.bukkit.configuration.file.FileConfiguration;
public class ConfigLoader extends Handler
@@ -35,17 +37,34 @@ public class ConfigLoader extends Handler
// Create config file if none is present.
this.plugin.saveDefaultConfig();
// Bukkit plugin config.
// The server's current config.
FileConfiguration pluginConfig = this.plugin.getConfig();
// Load missing values from default file.
pluginConfig.options().copyDefaults(true);
// The plugin's default config.
org.bukkit.configuration.Configuration defaultConfig = pluginConfig.getDefaults();
// Make sure nothing is returned for missing keys.
pluginConfig.setDefaults(new MemoryConfiguration());
// Get the config version value from the default config file.
Integer pluginConfigVersion = defaultConfig.getInt(DhsConfig.CONFIG_VERSION);
// Get the config version value from the server's config file.
Integer serverConfigVersion = pluginConfig.getInt(DhsConfig.CONFIG_VERSION);
// If the two values differ, load defaults for missing values.
if (!pluginConfigVersion.equals(serverConfigVersion)) {
this.plugin.getLogger().warning("Your config file is for an older version of this plugin.");
pluginConfig.setDefaults(defaultConfig);
pluginConfig.options().copyDefaults(true);
}
// DH Support config.
Configuration dhsConfig = this.plugin.getDhSupport().getConfig();
// Populate DH Support config.
pluginConfig.getKeys(false).forEach((key) -> dhsConfig.set(key, pluginConfig.get(key)));
pluginConfig.getKeys(true).forEach((key) -> dhsConfig.set(key, pluginConfig.get(key)));
}
@Override
@@ -20,11 +20,12 @@ package no.jckf.dhsupport.bukkit.handler;
import no.jckf.dhsupport.bukkit.BukkitWorldInterface;
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
@@ -41,7 +42,13 @@ public class WorldHandler implements Listener
protected void addWorldInterface(World world)
{
this.plugin.getDhSupport().setWorldInterface(world.getUID(), new BukkitWorldInterface(world));
BukkitWorldInterface worldInterface = new BukkitWorldInterface(this.plugin, world, this.plugin.getDhSupport().getConfig());
worldInterface.setLogger(this.plugin.getDhSupport().getLogger());
worldInterface.doUnsafeThings();
this.plugin.getDhSupport().setWorldInterface(world.getUID(), worldInterface);
}
protected void removeWorldInterface(World world)
@@ -49,6 +56,11 @@ public class WorldHandler implements Listener
this.plugin.getDhSupport().setWorldInterface(world.getUID(), null);
}
protected void touchLod(Location location)
{
this.plugin.getDhSupport().touchLod(location.getWorld().getUID(), location.getBlockX(), location.getBlockZ());
}
@EventHandler
public void onWorldLoad(WorldLoadEvent worldLoad)
{
@@ -62,14 +74,14 @@ public class WorldHandler implements Listener
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent chunkLoad)
public void onBlockPlace(BlockPlaceEvent blockPlace)
{
this.touchLod(blockPlace.getBlock().getLocation());
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent chunkUnload)
public void onBlockBreak(BlockBreakEvent blockBreak)
{
this.touchLod(blockBreak.getBlock().getLocation());
}
}
@@ -0,0 +1,104 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core;
// Block -> chunk -> section -> region
public class Coordinates
{
protected static final int BLOCK_TO_CHUNK_POWER = 4;
protected static final int CHUNK_TO_SECTION_POWER = 2;
protected static final int SECTION_TO_REGION_POWER = 4;
// UP
public static int blockToChunk(int value)
{
return value >> BLOCK_TO_CHUNK_POWER;
}
public static int chunkToSection(int value)
{
return value >> CHUNK_TO_SECTION_POWER;
}
public static int sectionToRegion(int value)
{
return value >> SECTION_TO_REGION_POWER;
}
// DOWN
public static int regionToSection(int value)
{
return value << SECTION_TO_REGION_POWER;
}
public static int sectionToChunk(int value)
{
return value << CHUNK_TO_SECTION_POWER;
}
public static int chunkToBlock(int value)
{
return value << BLOCK_TO_CHUNK_POWER;
}
// UP VIA
public static int blockToSection(int value)
{
return value >> (BLOCK_TO_CHUNK_POWER + CHUNK_TO_SECTION_POWER);
}
public static int blockToRegion(int value)
{
return value >> (BLOCK_TO_CHUNK_POWER + CHUNK_TO_SECTION_POWER + SECTION_TO_REGION_POWER);
}
public static int chunkToRegion(int value)
{
return value >> (CHUNK_TO_SECTION_POWER + SECTION_TO_REGION_POWER);
}
// DOWN VIA
public static int regionToChunk(int value)
{
return value << (CHUNK_TO_SECTION_POWER + SECTION_TO_REGION_POWER);
}
public static int regionToBlock(int value)
{
return value << (BLOCK_TO_CHUNK_POWER + CHUNK_TO_SECTION_POWER + SECTION_TO_REGION_POWER);
}
public static int sectionToBlock(int value)
{
return value << (BLOCK_TO_CHUNK_POWER + CHUNK_TO_SECTION_POWER);
}
// RELATIVE
public static int blockToChunkRelative(int value)
{
return value & 0x0F;
}
}
@@ -21,48 +21,78 @@ package no.jckf.dhsupport.core;
import no.jckf.dhsupport.core.bytestream.Encoder;
import no.jckf.dhsupport.core.configuration.Configurable;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.database.Database;
import no.jckf.dhsupport.core.database.migrations.CreateLodsTable;
import no.jckf.dhsupport.core.database.models.LodModel;
import no.jckf.dhsupport.core.database.repositories.AsyncLodRepository;
import no.jckf.dhsupport.core.dataobject.Beacon;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.handler.LodHandler;
import no.jckf.dhsupport.core.handler.PlayerConfigHandler;
import no.jckf.dhsupport.core.handler.PluginMessageHandler;
import no.jckf.dhsupport.core.lodbuilders.LodBuilder;
import no.jckf.dhsupport.core.message.plugin.PluginMessageSender;
import no.jckf.dhsupport.core.scheduling.Scheduler;
import no.jckf.dhsupport.core.world.WorldInterface;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class DhSupport implements Configurable
{
protected String dataDirectory;
protected Database database;
protected AsyncLodRepository lodRepository;
protected Configuration configuration;
protected Logger logger;
protected Scheduler scheduler;
protected Map<UUID, WorldInterface> worldInterfaces = new HashMap<>();
protected PluginMessageHandler pluginMessageHandler;
protected PluginMessageSender pluginMessageSender;
protected ExecutorService executor;
protected Map<String, CompletableFuture<Lod>> queuedBuilders = new HashMap<>();
protected Map<UUID, Map<String, byte[]>> lodCache = new HashMap<>();
protected Map<String, LodModel> touchedLods = new ConcurrentHashMap<>();
public DhSupport()
{
// Mumble mumble. Something about passing references to an incomplete "this".
this.configuration = new Configuration();
this.database = new Database();
this.lodRepository = new AsyncLodRepository(this.database);
this.pluginMessageHandler = new PluginMessageHandler(this);
}
public void onEnable()
{
this.executor = Executors.newFixedThreadPool(4);
this.lodRepository.setLogger(this.getLogger());
try {
this.database.open(this.getDataDirectory() + "/data.sqlite");
this.database.addMigration(CreateLodsTable.class);
this.database.migrate();
} catch (Exception exception) {
this.warning("Failed to initialize database: " + exception.getMessage());
// TODO: Disable the plugin?
}
(new PlayerConfigHandler(this, this.pluginMessageHandler)).register();
(new LodHandler(this, this.pluginMessageHandler)).register();
@@ -77,20 +107,34 @@ public class DhSupport implements Configurable
}
}
public void setDataDirectory(String dataDirectory)
{
this.dataDirectory = dataDirectory;
}
public String getDataDirectory()
{
return this.dataDirectory;
}
public void setScheduler(Scheduler scheduler)
{
this.scheduler = scheduler;
}
public Scheduler getScheduler()
{
return this.scheduler;
}
public void setWorldInterface(UUID id, @Nullable WorldInterface worldInterface)
{
if (worldInterface == null) {
//this.info("Removing world interface for " + id);
this.worldInterfaces.remove(id);
this.lodCache.remove(id);
return;
}
//this.info("Adding world interface for " + id);
this.worldInterfaces.put(id, worldInterface);
this.lodCache.put(id, new HashMap<>());
}
@Nullable
@@ -99,7 +143,6 @@ public class DhSupport implements Configurable
return this.worldInterfaces.get(id);
}
@Nullable
public PluginMessageHandler getPluginMessageHandler()
{
return this.pluginMessageHandler;
@@ -116,13 +159,13 @@ public class DhSupport implements Configurable
return this.pluginMessageSender;
}
@Override
public AsyncLodRepository getLodRepository()
{
return this.lodRepository;
}
public Configuration getConfig()
{
if (this.configuration == null) {
this.configuration = new Configuration();
}
return this.configuration;
}
@@ -147,33 +190,188 @@ public class DhSupport implements Configurable
this.getLogger().warning(message);
}
public CompletableFuture<Lod> queueBuilder(LodBuilder builder)
public LodBuilder getBuilder(WorldInterface world, SectionPosition position)
{
return CompletableFuture.supplyAsync(builder::generate, this.executor);
}
String builderType = world.getConfig().getString(DhsConfig.BUILDER_TYPE);
public CompletableFuture<byte[]> getLodData(UUID worldId, SectionPosition position)
{
String key = position.getX() + "x" + position.getZ();
LodBuilder builder;
if (!this.lodCache.get(worldId).containsKey(key)) {
//this.info("Cache miss: " + worldId + " " + key);
try {
Class<? extends LodBuilder> builderClass = Class.forName(LodBuilder.class.getPackageName() + "." + builderType).asSubclass(LodBuilder.class);
LodBuilder builder = new LodBuilder(this.getWorldInterface(worldId).newInstance(), position);
Constructor<?> constructor = builderClass.getConstructor(WorldInterface.class, SectionPosition.class);
return this.queueBuilder(builder).thenApply((lod) -> {
Encoder encoder = new Encoder();
lod.encode(encoder);
byte[] data = encoder.toByteArray();
this.lodCache.get(worldId).put(key, data);
return data;
});
builder = (LodBuilder) constructor.newInstance(world, position);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException exception) {
return null;
}
//this.info("Cache hit: " + worldId + " " + key);
return builder;
}
return CompletableFuture.completedFuture(this.lodCache.get(worldId).get(key));
public CompletableFuture<Lod> queueBuilder(UUID worldId, SectionPosition position, LodBuilder builder)
{
String key = LodModel.create()
.setWorldId(worldId)
.setX(position.getX())
.setZ(position.getZ())
.toString();
if (this.queuedBuilders.containsKey(key)) {
return this.queuedBuilders.get(key);
}
Scheduler scheduler = this.getScheduler();
CompletableFuture<Lod> queued;
// TODO: Fetch chunk data on region thread, then move to builder thread.
if (scheduler.canReadWorldAsync()) {
queued = this.getScheduler().runOnSeparateThread(
builder::generate
);
} else {
queued = this.getScheduler().runOnRegionThread(
worldId,
Coordinates.sectionToBlock(position.getX()),
Coordinates.sectionToBlock(position.getZ()),
builder::generate
);
}
queued = queued.thenApply((lod) -> {
this.queuedBuilders.remove(key);
return lod;
})
.exceptionally((exception) -> {
exception.printStackTrace();
this.queuedBuilders.remove(key);
return null;
});
this.queuedBuilders.put(key, queued);
return queued;
}
public CompletableFuture<LodModel> getLod(UUID worldId, SectionPosition position)
{
int worldX = Coordinates.sectionToBlock(position.getX());
int worldZ = Coordinates.sectionToBlock(position.getZ());
return this.getLodRepository()
.loadLodAsync(worldId, position.getX(), position.getZ())
.thenCompose((modelFromDb) -> {
// If a LOD was found in the database, return it.
if (modelFromDb != null) {
return CompletableFuture.completedFuture(modelFromDb);
}
WorldInterface world = this.getWorldInterface(worldId).newInstance();
List<CompletableFuture<Boolean>> loads = new ArrayList<>();
// Load all the chunks we need for this request.
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
for (int zMultiplayer = 0; zMultiplayer < 4; zMultiplayer++) {
loads.add(world.loadChunkAsync(worldX + 16 * xMultiplier, worldZ + 16 * zMultiplayer));
}
}
// Wait for chunk loads, then...
return CompletableFuture.allOf(loads.toArray(new CompletableFuture[loads.size()]))
.thenCompose((asd) -> {
// No LOD was found. Start building a new one.
CompletableFuture<Lod> lodFuture = this.queueBuilder(worldId, position, this.getBuilder(world, position));
// Find any beacons that should appear in this LOD.
CompletableFuture<Collection<Beacon>> beaconFuture = this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
Collection<Beacon> accumulator = new ArrayList<>();
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
for (int zMultiplayer = 0; zMultiplayer < 4; zMultiplayer++) {
accumulator.addAll(world.getBeaconsInChunk(worldX + 16 * xMultiplier, worldZ + 16 * zMultiplayer));
}
}
return accumulator;
});
// Combine the LOD and beacons and save the result in the database.
return lodFuture.thenCombine(beaconFuture, (lod, beacons) -> {
// Ask for all the chunks in the area to be unloaded.
// It's unlikely that they all should be, but we'll leave that up to the server.
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
for (int zMultiplayer = 0; zMultiplayer < 4; zMultiplayer++) {
world.unloadChunkAsync(worldX + 16 * xMultiplier, worldZ + 16 * zMultiplayer);
}
}
return null;
});
Encoder lodEncoder = new Encoder();
lod.encode(lodEncoder);
Encoder beaconEncoder = new Encoder();
beaconEncoder.writeCollection(beacons);
return this.lodRepository.saveLodAsync(
worldId,
position.getX(),
position.getZ(),
lodEncoder.toByteArray(),
beaconEncoder.toByteArray()
);
})
.thenCompose((f) -> f); // Unwrap the nested future.
});
});
}
public void touchLod(UUID worldId, int x, int z)
{
int sectionX = Coordinates.blockToSection(x);
int sectionZ = Coordinates.blockToSection(z);
LodModel lodModel = LodModel.create()
.setWorldId(worldId)
.setX(sectionX)
.setZ(sectionZ);
String key = lodModel.toString();
if (this.touchedLods.containsKey(key)) {
return;
}
this.touchedLods.put(key, lodModel);
}
public void updateTouchedLods()
{
for (String key : this.touchedLods.keySet()) {
LodModel lodModel = this.touchedLods.get(key);
this.getLodRepository().deleteLodAsync(lodModel.getWorldId(), lodModel.getX(), lodModel.getZ())
.thenAccept((deleted) -> {
if (!deleted) {
return;
}
SectionPosition position = new SectionPosition();
position.setDetailLevel(6);
position.setX(lodModel.getX());
position.setZ(lodModel.getZ());
this.getLod(lodModel.getWorldId(), position);
this.touchedLods.remove(key);
});
}
}
}
@@ -1,149 +0,0 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core;
import no.jckf.dhsupport.core.dataobject.DataPoint;
import no.jckf.dhsupport.core.dataobject.IdMapping;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.world.WorldInterface;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LodBuilder
{
protected WorldInterface worldInterface;
protected SectionPosition position;
public LodBuilder(WorldInterface worldInterface, SectionPosition position)
{
this.worldInterface = worldInterface;
this.position = position;
}
public Lod generate()
{
int minY = this.worldInterface.getMinY();
int maxY = this.worldInterface.getMaxY();
int height = maxY - minY;
int seaLevel = this.worldInterface.getSeaLevel();
int relativeSeaLevel = seaLevel - minY;
int offsetX = this.position.getX() * 64;
int offsetZ = this.position.getZ() * 64;
List<IdMapping> idMappings = new ArrayList<>();
Map<String, Integer> mapMap = new HashMap<>();
List<List<DataPoint>> columns = new ArrayList<>();
String biome = ""; // Initialize to squelch warnings.
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
int worldX = offsetX + relativeX;
int worldZ = offsetZ + relativeZ;
// Actual Y of top-most block.
int topLayer = this.worldInterface.getHighestYAt(worldX, worldZ);
// Distance from bottom to top-most block.
int relativeTopLayer = topLayer - minY;
biome = this.worldInterface.getBiomeAt(offsetX + relativeX, offsetZ + relativeZ);
List<DataPoint> column = new ArrayList<>();
@Nullable
DataPoint previous = null;
@Nullable
Integer solidGround = null;
for (int relativeY = height; (solidGround == null || relativeY >= solidGround) && relativeY >= 0; relativeY--) {
int worldY = minY + relativeY;
String material = this.worldInterface.getMaterialAt(worldX, worldY, worldZ);
if (solidGround == null) {
switch (material) {
case "minecraft:stone":
case "minecraft:grass":
case "minecraft:dirt":
case "minecraft:sand":
case "minecraft:sandstone":
case "minecraft:mycelium":
solidGround = Math.min(relativeY - 10, relativeSeaLevel - 10);
}
}
String compositeKey = biome + "|" + material;
//String compositeKey = biome + "|" + material + "|" + this.worldInterface.getBlockStateAsStringAt(worldX, worldY, worldZ);
@Nullable
Integer id = mapMap.get(compositeKey);
if (id == null) {
idMappings.add(new IdMapping(biome, material, null));
//idMappings.add(new IdMapping(biome, material, this.worldInterface.getBlockPropertiesAt(worldX, worldY, worldZ)));
id = idMappings.size() - 1;
mapMap.put(compositeKey, id);
}
DataPoint point;
if (previous != null && previous.getMappingId() == id) {
point = previous;
point.setStartY(point.getStartY() - 1);
point.setHeight(point.getHeight() + 1);
} else {
point = new DataPoint();
column.add(point);
point.setStartY(relativeY);
point.setMappingId(id);
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, worldY + 1, worldZ));
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, worldY + 1, worldZ));
if (relativeY == height && (material.equals("minecraft:air") || material.equals("minecraft:void_air"))) {
point.setStartY(relativeTopLayer + 1);
point.setHeight(height - relativeTopLayer);
relativeY = point.getStartY();
}
}
previous = point;
}
columns.add(column);
}
}
return new Lod(this.worldInterface, this.position, idMappings, columns);
}
}
@@ -19,6 +19,7 @@
package no.jckf.dhsupport.core;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
public class Utils
{
@@ -34,4 +35,18 @@ public class Utils
}
return new String(hexChars, StandardCharsets.UTF_8);
}
public static String ucFirst(String string, Locale locale)
{
if (string.isEmpty()) {
return "";
}
return string.substring(0, 1).toUpperCase(locale) + string.substring(1);
}
public static String ucFirst(String string)
{
return ucFirst(string, Locale.getDefault());
}
}
@@ -18,6 +18,7 @@
package no.jckf.dhsupport.core.configuration;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -25,28 +26,53 @@ public class Configuration
{
protected Map<String, Object> variables = new HashMap<>();
public void set(String key, Object value)
public void set(String key, @Nullable Object value)
{
this.variables.put(key, value);
}
public Object get(String key)
public void unset(String key)
{
return this.variables.get(key);
this.variables.remove(key);
}
public boolean getBool(String key)
public @Nullable Object get(String key, @Nullable Object defaultValue)
{
return (boolean) this.get(key);
return this.variables.getOrDefault(key, defaultValue);
}
public Integer getInt(String key)
public @Nullable Object get(String key)
{
return (Integer) this.get(key);
return this.get(key, null);
}
public String getString(String key)
public @Nullable Boolean getBool(String key, @Nullable Boolean defaultValue)
{
return (String) this.get(key);
return (Boolean) this.get(key, defaultValue);
}
public @Nullable Boolean getBool(String key)
{
return this.getBool(key, null);
}
public @Nullable Integer getInt(String key, @Nullable Integer defaultValue)
{
return (Integer) this.get(key, defaultValue);
}
public @Nullable Integer getInt(String key)
{
return this.getInt(key, null);
}
public @Nullable String getString(String key, @Nullable String defaultValue)
{
return (String) this.get(key, defaultValue);
}
public @Nullable String getString(String key)
{
return (String) this.get(key, null);
}
}
@@ -18,14 +18,10 @@
package no.jckf.dhsupport.core.configuration;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public abstract class DhsConfig
{
public static String CONFIG_VERSION = "config_version";
public static String RENDER_DISTANCE = "render_distance";
public static String DISTANT_GENERATION_ENABLED = "distant_generation_enabled";
@@ -34,24 +30,31 @@ public abstract class DhsConfig
public static String REAL_TIME_UPDATES_ENABLED = "real_time_updates_enabled";
public static String REAL_TIME_UPDATE_RADIUS = "real_time_update_radius";
public static String LOGIN_DATA_SYNC_ENABLED = "login_data_sync_enabled";
public static String LOGIN_DATA_SYNC_RADIUS = "login_data_sync_radius";
public static String LOGIN_DATA_SYNC_RC_LIMIT = "login_data_sync_rc_limit";
public static Collection<String> getKeys()
{
List<String> keys = new ArrayList<>();
public static String MAX_DATA_TRANSFER_SPEED = "max_data_transfer_speed";
Arrays.stream(DhsConfig.class.getDeclaredFields())
.filter((field) -> Modifier.isStatic(field.getModifiers()))
.forEach((field) -> {
try {
keys.add((String) field.get(DhsConfig.class));
} catch (IllegalAccessException exception) {
throw new RuntimeException(exception);
}
});
public static String SCHEDULER_THREADS = "scheduler_threads";
return keys;
}
public static String GENERATE_NEW_CHUNKS = "generate_new_chunks";
public static String LEVEL_KEY_PREFIX = "level_key_prefix";
public static String BORDER_CENTER = "border_center";
public static String BORDER_RADIUS = "border_radius";
public static String BUILDER_TYPE = "builder_type";
public static String BUILDER_RESOLUTION = "builder_resolution";
public static String SCAN_TO_SEA_LEVEL= "scan_to_sea_level";
public static String INCLUDE_NON_COLLIDING_TOP_LAYER = "include_non-colliding_top_layer";
}
@@ -0,0 +1,40 @@
package no.jckf.dhsupport.core.configuration;
import no.jckf.dhsupport.core.world.WorldInterface;
import javax.annotation.Nullable;
public class WorldConfiguration extends Configuration
{
protected static String WORLD_PREFIX = "worlds.%s.";
protected WorldInterface world;
protected Configuration config;
public WorldConfiguration(WorldInterface world, Configuration config)
{
this.world = world;
this.config = config;
}
@Override
public void set(String key, @Nullable Object value)
{
this.config.set(WORLD_PREFIX.formatted(this.world.getName()) + key, value);
}
@Override
public void unset(String key)
{
this.config.unset(WORLD_PREFIX.formatted(this.world.getName()) + key);
}
@Override
public @Nullable Object get(String key, @Nullable Object defaultValue)
{
Object specific = this.config.get(WORLD_PREFIX.formatted(this.world.getName()) + key);
return specific == null ? this.config.get(key, defaultValue) : specific;
}
}
@@ -0,0 +1,120 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database;
import no.jckf.dhsupport.core.database.migrations.Migration;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
public class Database
{
protected String path;
protected Connection connection;
protected Map<String, Class<? extends Migration>> migrations = new HashMap<>();
public Connection getConnection() throws SQLException
{
if (this.connection == null || this.connection.isClosed()) {
this.connection = DriverManager.getConnection("jdbc:sqlite:" + this.path);
}
return this.connection;
}
public void open(String path) throws SQLException
{
this.path = path;
this.getConnection();
}
public void close() throws SQLException
{
if (this.connection == null || this.connection.isClosed()) {
return;
}
this.getConnection().close();
}
protected void createMigrationsTable() throws SQLException
{
String sql = """
CREATE TABLE IF NOT EXISTS migrations (
name STRING PRIMARY KEY,
timestamp INTEGER
);
""";
try (Statement statement = this.getConnection().createStatement()) {
statement.execute(sql);
}
}
public void addMigration(Class<? extends Migration> migration)
{
this.migrations.put(migration.getSimpleName(), migration);
}
public boolean hasMigrationRan(String name) throws SQLException
{
String sql = "SELECT 1 FROM migrations WHERE name = ?;";
try (PreparedStatement statement = this.getConnection().prepareStatement(sql)) {
statement.setString(1, name);
ResultSet result = statement.executeQuery();
return result.next();
}
}
public void markMigrationAsRan(String name) throws SQLException
{
String sql = "INSERT INTO migrations (name, timestamp) VALUES (?, ?);";
try (PreparedStatement statement = this.getConnection().prepareStatement(sql)) {
statement.setString(1, name);
statement.setInt(2, (int) (System.currentTimeMillis() / 1000));
statement.executeUpdate();
}
}
public void migrate() throws Exception
{
this.createMigrationsTable();
for (String name : this.migrations.keySet()) {
if (this.hasMigrationRan(name)) {
continue;
}
Migration migration = this.migrations.get(name).getConstructor(this.getClass()).newInstance(this);
migration.up();
this.markMigrationAsRan(name);
}
}
}
@@ -0,0 +1,49 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.migrations;
import no.jckf.dhsupport.core.database.Database;
import java.sql.Statement;
public class CreateLodsTable extends Migration
{
public CreateLodsTable(Database database)
{
super(database);
}
@Override
public void up() throws Exception
{
try (Statement statement = this.database.getConnection().createStatement()) {
statement.execute("""
CREATE TABLE IF NOT EXISTS lods (
worldId STRING NOT NULL,
x INTEGER NOT NULL,
z INTEGER NOT NULL,
data BLOB NOT NULL,
beacons BLOB NOT NULL,
timestamp INTEGER NOT NULL,
PRIMARY KEY (worldId, x, z)
);
""");
}
}
}
@@ -0,0 +1,33 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.migrations;
import no.jckf.dhsupport.core.database.Database;
public abstract class Migration
{
protected Database database;
public Migration(Database database)
{
this.database = database;
}
public abstract void up() throws Exception;
}
@@ -0,0 +1,118 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.models;
import java.util.UUID;
public class LodModel extends Model
{
public static LodModel create()
{
return new LodModel();
}
protected UUID worldId;
protected int x;
protected int z;
protected byte[] data;
protected byte[] beacons;
protected int timestamp;
public LodModel setWorldId(UUID worldId)
{
this.worldId = worldId;
return this;
}
public UUID getWorldId()
{
return this.worldId;
}
public LodModel setX(int x)
{
this.x = x;
return this;
}
public int getX()
{
return x;
}
public LodModel setZ(int z)
{
this.z = z;
return this;
}
public int getZ()
{
return z;
}
public LodModel setData(byte[] data)
{
this.data = data;
return this;
}
public byte[] getData()
{
return data;
}
public LodModel setBeacons(byte[] beacons)
{
this.beacons = beacons;
return this;
}
public byte[] getBeacons()
{
return beacons;
}
public LodModel setTimestamp(int timestamp)
{
this.timestamp = timestamp;
return this;
}
public int getTimestamp()
{
return this.timestamp;
}
public String toString()
{
return this.getWorldId() + "@" + this.getX() + "x" + this.getZ();
}
}
@@ -0,0 +1,24 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.models;
public abstract class Model
{
public abstract String toString();
}
@@ -0,0 +1,73 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.repositories;
import no.jckf.dhsupport.core.database.Database;
import no.jckf.dhsupport.core.database.models.LodModel;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
public class AsyncLodRepository extends LodRepository
{
protected Executor executor = Executors.newSingleThreadExecutor();
public AsyncLodRepository(Database database)
{
super(database);
}
protected <T> CompletableFuture<T> queueTask(Supplier<T> task)
{
CompletableFuture<T> future = new CompletableFuture<>();
this.executor.execute(() -> {
try {
future.complete(task.get());
} catch (Exception exception) {
future.completeExceptionally(exception);
}
});
return future;
}
public CompletableFuture<LodModel> saveLodAsync(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons)
{
return this.queueTask(() -> this.saveLod(worldId, sectionX, sectionZ, data, beacons));
}
public CompletableFuture<LodModel> loadLodAsync(UUID worldId, int sectionX, int sectionZ)
{
return this.queueTask(() -> this.loadLod(worldId, sectionX, sectionZ));
}
public CompletableFuture<Boolean> lodExistsAsync(UUID worldId, int sectionX, int sectionZ)
{
return this.queueTask(() -> this.lodExists(worldId, sectionX, sectionZ));
}
public CompletableFuture<Boolean> deleteLodAsync(UUID worldId, int sectionX, int sectionZ)
{
return this.queueTask(() -> this.deleteLod(worldId, sectionX, sectionZ));
}
}
@@ -0,0 +1,149 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.database.repositories;
import no.jckf.dhsupport.core.database.Database;
import no.jckf.dhsupport.core.database.models.LodModel;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
import java.util.logging.Logger;
public class LodRepository
{
protected Database database;
protected Logger logger;
public LodRepository(Database database)
{
this.database = database;
}
public void setLogger(Logger logger)
{
this.logger = logger;
}
public Logger getLogger()
{
return this.logger;
}
public LodModel saveLod(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons)
{
int timestamp = (int) (System.currentTimeMillis() / 1000);
String sql = "REPLACE INTO lods (worldId, x, z, data, beacons, timestamp) VALUES (?, ?, ?, ?, ?, ?)";
try (PreparedStatement statement = this.database.getConnection().prepareStatement(sql)) {
statement.setString(1, worldId.toString());
statement.setInt(2, sectionX);
statement.setInt(3, sectionZ);
statement.setBytes(4, data);
statement.setBytes(5, beacons);
statement.setInt(6, timestamp);
statement.executeUpdate();
return LodModel.create()
.setWorldId(worldId)
.setX(sectionX)
.setZ(sectionZ)
.setData(data)
.setBeacons(beacons)
.setTimestamp(timestamp);
} catch (SQLException exception) {
this.getLogger().warning("Could not save LOD: " + exception);
return null;
}
}
public LodModel loadLod(UUID worldId, int sectionX, int sectionZ)
{
String sql = "SELECT data, beacons, timestamp FROM lods WHERE worldId = ? AND x = ? AND z = ? LIMIT 1";
try (PreparedStatement statement = this.database.getConnection().prepareStatement(sql)) {
statement.setString(1, worldId.toString());
statement.setInt(2, sectionX);
statement.setInt(3, sectionZ);
ResultSet result = statement.executeQuery();
byte[] data = result.getBytes("data");
if (data == null) {
return null;
}
return LodModel.create()
.setWorldId(worldId)
.setX(sectionX)
.setZ(sectionZ)
.setData(data)
.setBeacons(result.getBytes("beacons"))
.setTimestamp(result.getInt("timestamp"));
} catch (SQLException exception) {
this.getLogger().warning("Could not load LOD: " + exception);
return null;
}
}
public boolean lodExists(UUID worldId, int sectionX, int sectionZ)
{
String sql = "SELECT EXISTS( SELECT 1 FROM lods WHERE worldId = ? AND x = ? AND z = ? )";
try (PreparedStatement statement = this.database.getConnection().prepareStatement(sql)) {
statement.setString(1, worldId.toString());
statement.setInt(2, sectionX);
statement.setInt(3, sectionZ);
ResultSet result = statement.executeQuery();
return result.getInt(1) == 1;
} catch (SQLException exception) {
this.getLogger().warning("Could not check LOD existence: " + exception);
return false;
}
}
public boolean deleteLod(UUID worldId, int sectionX, int sectionZ)
{
String sql = "DELETE FROM lods WHERE worldId = ? AND x = ? AND z = ?";
try (PreparedStatement statement = this.database.getConnection().prepareStatement(sql)) {
statement.setString(1, worldId.toString());
statement.setInt(2, sectionX);
statement.setInt(3, sectionZ);
statement.executeUpdate();
return true;
} catch (SQLException exception) {
this.getLogger().warning("Could not delete LOD: " + exception);
return false;
}
}
}
@@ -0,0 +1,52 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.dataobject;
import no.jckf.dhsupport.core.bytestream.Encoder;
public class Beacon extends DataObject
{
protected BlockPosition position;
protected int color;
public Beacon()
{
}
public Beacon(BlockPosition position, int color)
{
this.position = position;
this.color = color;
}
public Beacon(int x, int y, int z, int color)
{
this(new BlockPosition(x, y, z), color);
}
@Override
public void encode(Encoder encoder)
{
this.position.encode(encoder);
encoder.writeInt(this.color);
}
}
@@ -0,0 +1,80 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.dataobject;
import no.jckf.dhsupport.core.bytestream.Encoder;
public class BlockPosition extends DataObject
{
protected int x;
protected int y;
protected int z;
public BlockPosition()
{
}
public BlockPosition(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public void setX(int x)
{
this.x = x;
}
public int getX()
{
return this.x;
}
public void setY(int y)
{
this.y = y;
}
public int getY()
{
return this.y;
}
public void setZ(int z)
{
this.z = z;
}
public int getZ()
{
return this.z;
}
@Override
public void encode(Encoder encoder)
{
encoder.writeInt(this.x);
encoder.writeInt(this.y);
encoder.writeInt(this.z);
}
}
@@ -18,16 +18,16 @@
package no.jckf.dhsupport.core.dataobject;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FrameOutputStream;
import net.jpountz.xxhash.XXHashFactory;
import no.jckf.dhsupport.core.bytestream.Encoder;
import no.jckf.dhsupport.core.enums.CompressionType;
import no.jckf.dhsupport.core.enums.GenerationStep;
import no.jckf.dhsupport.core.world.WorldInterface;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZ;
import org.tukaani.xz.XZOutputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
@@ -38,7 +38,7 @@ public class Lod extends DataObject
{
public static int dataFormatVersion = 1;
public static CompressionType compressionType = CompressionType.LZ4;
public static CompressionType compressionType = CompressionType.LZMA2;
public static int width = 64;
@@ -140,14 +140,7 @@ public class Lod extends DataObject
ByteArrayOutputStream compressedStream = new ByteArrayOutputStream();
try {
LZ4FrameOutputStream compressorStream = new LZ4FrameOutputStream(
new BufferedOutputStream(compressedStream),
LZ4FrameOutputStream.BLOCKSIZE.SIZE_4MB,
uncompressedData.length,
LZ4Factory.fastestInstance().highCompressor(17),
XXHashFactory.fastestInstance().hash32(),
LZ4FrameOutputStream.FLG.Bits.BLOCK_INDEPENDENCE
);
OutputStream compressorStream = new XZOutputStream(compressedStream, new LZMA2Options(3), XZ.CHECK_CRC64);
compressorStream.write(uncompressedData);
compressorStream.flush();
@@ -41,6 +41,18 @@ public class SectionPosition extends DataObject
protected int z;
public SectionPosition()
{
}
public SectionPosition(int x, int z, int detailLevel)
{
this.x = x;
this.z = z;
this.detailLevel = detailLevel;
}
public void setDetailLevel(int level)
{
this.detailLevel = level;
@@ -18,10 +18,17 @@
package no.jckf.dhsupport.core.handler;
import no.jckf.dhsupport.core.Coordinates;
import no.jckf.dhsupport.core.DhSupport;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.message.plugin.ExceptionMessage;
import no.jckf.dhsupport.core.message.plugin.FullDataChunkMessage;
import no.jckf.dhsupport.core.message.plugin.FullDataSourceRequestMessage;
import no.jckf.dhsupport.core.message.plugin.FullDataSourceResponseMessage;
import no.jckf.dhsupport.core.world.WorldInterface;
import org.bukkit.Bukkit;
import java.util.Arrays;
@@ -48,38 +55,120 @@ public class LodHandler
this.pluginMessageHandler.getEventBus().registerHandler(FullDataSourceRequestMessage.class, (requestMessage) -> {
//this.dhSupport.info("LOD request for " + requestMessage.getPosition().getX() + " x " + requestMessage.getPosition().getZ());
int myBufferId = this.bufferId++;
if (requestMessage.getPosition().getDetailLevel() != 6) {
ExceptionMessage exceptionMessage = new ExceptionMessage();
exceptionMessage.isResponseTo(requestMessage);
exceptionMessage.setTypeId(ExceptionMessage.TYPE_SECTION_REQUIRES_SPLITTING);
exceptionMessage.setMessage("Only detail level 6 is supported");
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
return;
}
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
UUID worldUuid = Bukkit.getPlayer(requestMessage.getSender()).getWorld().getUID();
this.dhSupport.getLodData(worldUuid, requestMessage.getPosition())
.thenAccept((lod) -> {
int chunkCount = (int) Math.ceil((double) lod.length / CHUNK_SIZE);
WorldInterface world = this.dhSupport.getWorldInterface(worldUuid);
for (int chunkNo = 0; chunkNo < chunkCount; chunkNo++) {
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
chunkResponse.setBufferId(myBufferId);
chunkResponse.setIsFirst(chunkNo == 0);
chunkResponse.setData(Arrays.copyOfRange(
lod,
CHUNK_SIZE * chunkNo,
Math.min(CHUNK_SIZE * chunkNo + CHUNK_SIZE, lod.length)
));
Configuration config = world.getConfig();
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), chunkResponse);
SectionPosition position = requestMessage.getPosition();
int worldX = Coordinates.sectionToBlock(position.getX());
int worldZ = Coordinates.sectionToBlock(position.getZ());
String borderCenter = config.getString(DhsConfig.BORDER_CENTER);
Integer borderRadius = config.getInt(DhsConfig.BORDER_RADIUS);
if (borderCenter != null && borderRadius != null) {
String[] centerXz = borderCenter.split(",");
if (centerXz.length != 2) {
this.dhSupport.warning("Border for world " + world.getName() + " is misconfigured.");
} else {
int centerX = Integer.parseInt(centerXz[0]);
int centerZ = Integer.parseInt(centerXz[1]);
int minX = centerX - borderRadius;
int maxX = centerX + borderRadius;
int minZ = centerZ - borderRadius;
int maxZ = centerZ + borderRadius;
int higherLodX = worldX + 64;
int higherLodZ = worldZ + 64;
if (higherLodX < minX || worldX > maxX || higherLodZ < minZ || worldZ > maxZ) {
ExceptionMessage exceptionMessage = new ExceptionMessage();
exceptionMessage.isResponseTo(requestMessage);
exceptionMessage.setTypeId(ExceptionMessage.TYPE_REQUEST_REJECTED);
exceptionMessage.setMessage("World border");
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
return;
}
}
}
/*boolean generate = config.getBool(DhsConfig.GENERATE_NEW_CHUNKS);
if (!generate) {
for (int relativeChunkX = 0; relativeChunkX < Lod.width / 16; relativeChunkX++) {
for (int relativeChunkZ = 0; relativeChunkZ < Lod.width / 16; relativeChunkZ++) {
if (!world.chunkExists(worldX + relativeChunkX * 16, worldZ + relativeChunkZ * 16)) {
ExceptionMessage exceptionMessage = new ExceptionMessage();
exceptionMessage.isResponseTo(requestMessage);
exceptionMessage.setTypeId(ExceptionMessage.TYPE_REQUEST_REJECTED);
exceptionMessage.setMessage("Fog of war");
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
return;
}
}
}
}*/
this.dhSupport.getLod(worldUuid, position)
.thenAccept((lodModel) -> {
FullDataSourceResponseMessage responseMessage = new FullDataSourceResponseMessage();
responseMessage.isResponseTo(requestMessage);
responseMessage.setBufferId(myBufferId);
boolean sendData = requestMessage.getTimestamp() == null || (requestMessage.getTimestamp() / 1000) < lodModel.getTimestamp();
if (sendData) {
int myBufferId = this.bufferId++;
responseMessage.setBufferId(myBufferId);
responseMessage.setBeacons(lodModel.getBeacons());
byte[] data = lodModel.getData();
int chunkCount = (int) Math.ceil((double) data.length / CHUNK_SIZE);
for (int chunkNo = 0; chunkNo < chunkCount; chunkNo++) {
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
chunkResponse.setBufferId(myBufferId);
chunkResponse.setIsFirst(chunkNo == 0);
chunkResponse.setData(Arrays.copyOfRange(
data,
CHUNK_SIZE * chunkNo,
Math.min(CHUNK_SIZE * chunkNo + CHUNK_SIZE, data.length)
));
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), chunkResponse);
}
//this.dhSupport.info("LOD in " + chunkCount + " parts sent for " + requestMessage.getPosition().getX() + " x " + requestMessage.getPosition().getZ());
}
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), responseMessage);
//this.dhSupport.info("LOD in " + chunkCount + " parts sent for " + requestMessage.getPosition().getX() + " x " + requestMessage.getPosition().getZ());
})
.exceptionally((exception) -> {
this.dhSupport.warning(exception.getMessage());
exception.printStackTrace();
ExceptionMessage exceptionMessage = new ExceptionMessage();
exceptionMessage.isResponseTo(requestMessage);
exceptionMessage.setTypeId(ExceptionMessage.TYPE_REQUEST_REJECTED);
exceptionMessage.setMessage("Internal error");
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
return null;
});
});
@@ -21,9 +21,10 @@ package no.jckf.dhsupport.core.handler;
import no.jckf.dhsupport.core.DhSupport;
import no.jckf.dhsupport.core.configuration.Configuration;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.message.plugin.CurrentLevelKeyMessage;
import no.jckf.dhsupport.core.message.plugin.LevelInitMessage;
import no.jckf.dhsupport.core.message.plugin.RemotePlayerConfigMessage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class PlayerConfigHandler
{
@@ -41,18 +42,28 @@ public class PlayerConfigHandler
{
this.pluginMessageHandler.getEventBus().registerHandler(RemotePlayerConfigMessage.class, (configMessage) -> {
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
// TODO: Should this be unique?
String levelKey = Bukkit.getPlayer(configMessage.getSender()).getWorld().getName();
Player player = Bukkit.getPlayer(configMessage.getSender());
CurrentLevelKeyMessage levelKeyResponse = new CurrentLevelKeyMessage();
Configuration dhsConfig = this.dhSupport.getWorldInterface(player.getWorld().getUID()).getConfig();
String levelKeyPrefix = dhsConfig.getString(DhsConfig.LEVEL_KEY_PREFIX);
String levelKey = player.getWorld().getName();
if (levelKeyPrefix != null) {
levelKey = levelKeyPrefix + levelKey;
}
this.dhSupport.info("Received DH config for " + player.getName() + " in " + levelKey);
LevelInitMessage levelKeyResponse = new LevelInitMessage();
levelKeyResponse.setKey(levelKey);
levelKeyResponse.setTime(System.currentTimeMillis());
this.pluginMessageHandler.sendPluginMessage(configMessage.getSender(), levelKeyResponse);
Configuration dhsConfig = this.dhSupport.getConfig();
Configuration clientConfig = configMessage.toConfiguration();
// This is not very flexible, but will do for now.
for (String key : DhsConfig.getKeys()) {
for (String key : RemotePlayerConfigMessage.KEYS) {
//this.dhSupport.getLogger().info("Config key " + key + ":");
Object dhsValue = dhsConfig.get(key);
@@ -38,7 +38,7 @@ public class PluginMessageHandler
public final String pluginChannel = "distant_horizons:message";
public final int protocolVersion = 3;
public final int protocolVersion = 8;
private EventBus<PluginMessage> eventBus;
@@ -52,7 +52,7 @@ public class PluginMessageHandler
this.messageTypeRegistry = new MessageTypeRegistry();
this.messageTypeRegistry.registerMessageType(0, null);
this.messageTypeRegistry.registerMessageType(1, CloseReasonMessage.class);
this.messageTypeRegistry.registerMessageType(2, CurrentLevelKeyMessage.class);
this.messageTypeRegistry.registerMessageType(2, LevelInitMessage.class);
this.messageTypeRegistry.registerMessageType(3, RemotePlayerConfigMessage.class);
this.messageTypeRegistry.registerMessageType(4, CancelMessage.class);
this.messageTypeRegistry.registerMessageType(5, ExceptionMessage.class);
@@ -105,6 +105,10 @@ public class PluginMessageHandler
Decoder decoder = new Decoder(data);
#if READ_FORGE_BYTE == "true"
decoder.readByte();
#endif
// Read the client's protocol version.
short protocolVersion = decoder.readShort();
@@ -154,12 +158,17 @@ public class PluginMessageHandler
message.encode(encoder);
data = encoder.toByteArray();
} catch (Exception exception) {
this.dhSupport.warning("Failed to encode " + message.getClass() + ": " + exception.getClass() + " - " + exception.getMessage());
this.dhSupport.warning("Failed to encode " + message.getClass().getSimpleName());
exception.printStackTrace();
return;
}
Encoder encoder = new Encoder();
#if READ_FORGE_BYTE == "true"
encoder.writeByte(0);
#endif
encoder.writeShort(this.protocolVersion);
encoder.writeShort(messageTypeId);
@@ -0,0 +1,198 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.lodbuilders;
import no.jckf.dhsupport.core.Coordinates;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.dataobject.DataPoint;
import no.jckf.dhsupport.core.dataobject.IdMapping;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.world.WorldInterface;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FastOverworldBuilder extends LodBuilder
{
public FastOverworldBuilder(WorldInterface worldInterface, SectionPosition position)
{
super(worldInterface, position);
}
public Lod generate()
{
int minY = this.worldInterface.getMinY();
int maxY = this.worldInterface.getMaxY();
int height = maxY - minY;
int seaLevel = this.worldInterface.getSeaLevel();
int relativeSeaLevel = seaLevel - minY;
int offsetX = Coordinates.sectionToBlock(this.position.getX());
int offsetZ = Coordinates.sectionToBlock(this.position.getZ());
int yStep = this.worldInterface.getConfig().getInt(DhsConfig.BUILDER_RESOLUTION);
boolean scanToSeaLevel = this.worldInterface.getConfig().getBool(DhsConfig.SCAN_TO_SEA_LEVEL, false);
boolean includeNonCollidingTopLayer = this.worldInterface.getConfig().getBool(DhsConfig.INCLUDE_NON_COLLIDING_TOP_LAYER, false);
List<IdMapping> idMappings = new ArrayList<>();
Map<String, Integer> mapMap = new HashMap<>();
List<List<DataPoint>> columns = new ArrayList<>();
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
int worldX = offsetX + relativeX;
int worldZ = offsetZ + relativeZ;
// Actual Y of top-most block.
int topLayer = this.worldInterface.getHighestYAt(worldX, worldZ);
// Copies of the original values.
int hardTopLayer = topLayer;
int originalStep = yStep;
if (includeNonCollidingTopLayer) {
outer: while (topLayer + 1 < maxY) {
String topSample = this.worldInterface.getMaterialAt(worldX, topLayer + 1, worldZ);
switch (topSample) {
case "minecraft:air":
case "minecraft:void_air":
break outer;
}
topLayer++;
}
}
// If these differ, the top layer is non-colliding and likely requires yStep=1.
if (topLayer != hardTopLayer) {
yStep = 1;
}
// Distance from bottom to top-most block.
int relativeTopLayer = topLayer - minY;
String biome = this.worldInterface.getBiomeAt(worldX, worldZ);
List<DataPoint> column = new ArrayList<>();
@Nullable
DataPoint previous = null;
@Nullable
Integer solidGround = null;
int firstY = height - yStep;
for (int relativeY = firstY; (solidGround == null || relativeY >= solidGround) && relativeY >= 1 - yStep; relativeY -= yStep) {
int thisStep = yStep;
if (relativeY < 0) {
thisStep -= -relativeY;
relativeY = 0;
}
int lowWorldY = minY + relativeY;
int highWorldY = lowWorldY + thisStep - 1;
// We've reached the top-most colliding block. Restore yStep.
if (highWorldY == hardTopLayer) {
yStep = originalStep;
}
String material = this.worldInterface.getMaterialAt(worldX, highWorldY, worldZ);
if (solidGround == null && (!scanToSeaLevel || highWorldY <= seaLevel)) {
switch (material) {
case "minecraft:stone":
case "minecraft:grass":
case "minecraft:dirt":
case "minecraft:gravel":
case "minecraft:sand":
case "minecraft:sandstone":
case "minecraft:mycelium":
solidGround = Math.min(relativeY - 10, relativeSeaLevel - 10);
}
}
String compositeKey = biome + "|" + material;
@Nullable
Integer id = mapMap.get(compositeKey);
if (id == null) {
idMappings.add(new IdMapping(biome, material, null));
id = idMappings.size() - 1;
mapMap.put(compositeKey, id);
}
DataPoint point;
if (previous != null && previous.getMappingId() == id) {
point = previous;
point.setStartY(point.getStartY() - thisStep);
point.setHeight(point.getHeight() + thisStep);
} else {
point = new DataPoint();
column.add(point);
point.setStartY(relativeY);
point.setHeight(thisStep);
point.setMappingId(id);
if (highWorldY + 1 < maxY) {
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
}
if (material.equals("minecraft:air") || material.equals("minecraft:void_air")) {
// Start by filling the top of the column with air, then jump down to the top layer.
if (relativeY == firstY) {
point.setStartY(relativeTopLayer + 1);
point.setHeight(height - relativeTopLayer);
relativeY = point.getStartY();
} else {
// Encountered air that is below a non-air block. Set yStep=1 to avoid stretching the air into the ground or sea.
yStep = 1;
}
} else {
// Entered a new material. Reset yStep in case we came from air with yStep=1.
yStep = originalStep;
}
}
previous = point;
}
columns.add(column);
}
}
return new Lod(this.worldInterface, this.position, idMappings, columns);
}
}
@@ -0,0 +1,178 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.lodbuilders;
import no.jckf.dhsupport.core.Coordinates;
import no.jckf.dhsupport.core.configuration.DhsConfig;
import no.jckf.dhsupport.core.dataobject.DataPoint;
import no.jckf.dhsupport.core.dataobject.IdMapping;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.world.WorldInterface;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FullBuilder extends LodBuilder
{
public FullBuilder(WorldInterface worldInterface, SectionPosition position)
{
super(worldInterface, position);
}
public Lod generate()
{
int minY = this.worldInterface.getMinY();
int maxY = this.worldInterface.getMaxY();
int height = maxY - minY;
int offsetX = Coordinates.sectionToBlock(this.position.getX());
int offsetZ = Coordinates.sectionToBlock(this.position.getZ());
int yStep = this.worldInterface.getConfig().getInt(DhsConfig.BUILDER_RESOLUTION);
boolean includeNonCollidingTopLayer = this.worldInterface.getConfig().getBool(DhsConfig.INCLUDE_NON_COLLIDING_TOP_LAYER, false);
List<IdMapping> idMappings = new ArrayList<>();
Map<String, Integer> mapMap = new HashMap<>();
List<List<DataPoint>> columns = new ArrayList<>();
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
int worldX = offsetX + relativeX;
int worldZ = offsetZ + relativeZ;
// Actual Y of top-most block.
int topLayer = this.worldInterface.getHighestYAt(worldX, worldZ);
// Copies of the original values.
int hardTopLayer = topLayer;
int originalStep = yStep;
if (includeNonCollidingTopLayer) {
outer: while (topLayer + 1 < maxY) {
String topSample = this.worldInterface.getMaterialAt(worldX, topLayer + 1, worldZ);
switch (topSample) {
case "minecraft:air":
case "minecraft:void_air":
break outer;
}
topLayer++;
}
}
// If these differ, the top layer is non-colliding and likely requires yStep=1.
if (topLayer != hardTopLayer) {
yStep = 1;
}
// Distance from bottom to top-most block.
int relativeTopLayer = topLayer - minY;
String biome = this.worldInterface.getBiomeAt(worldX, worldZ);
List<DataPoint> column = new ArrayList<>();
@Nullable
DataPoint previous = null;
int firstY = height - yStep;
for (int relativeY = firstY; relativeY >= 1 - yStep; relativeY -= yStep) {
int thisStep = yStep;
if (relativeY < 0) {
thisStep -= -relativeY;
relativeY = 0;
}
int lowWorldY = minY + relativeY;
int highWorldY = lowWorldY + thisStep - 1;
// We've reached the top-most colliding block. Restore yStep.
if (highWorldY == hardTopLayer) {
yStep = originalStep;
}
String material = this.worldInterface.getMaterialAt(worldX, highWorldY, worldZ);
String compositeKey = biome + "|" + material + "|" + this.worldInterface.getBlockStateAsStringAt(worldX, highWorldY, worldZ);
@Nullable
Integer id = mapMap.get(compositeKey);
if (id == null) {
idMappings.add(new IdMapping(biome, material, this.worldInterface.getBlockPropertiesAt(worldX, highWorldY, worldZ)));
id = idMappings.size() - 1;
mapMap.put(compositeKey, id);
}
DataPoint point;
if (previous != null && previous.getMappingId() == id) {
point = previous;
point.setStartY(point.getStartY() - thisStep);
point.setHeight(point.getHeight() + thisStep);
} else {
point = new DataPoint();
column.add(point);
point.setStartY(relativeY);
point.setHeight(thisStep);
point.setMappingId(id);
if (highWorldY + 1 < maxY) {
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
}
if (material.equals("minecraft:air") || material.equals("minecraft:void_air")) {
// Start by filling the top of the column with air, then jump down to the top layer.
if (relativeY == firstY) {
point.setStartY(relativeTopLayer + 1);
point.setHeight(height - relativeTopLayer);
relativeY = point.getStartY();
} else {
// Encountered air that is below a non-air block. Set yStep=1 to avoid stretching the air into the ground or sea.
yStep = 1;
}
} else {
// Entered a new material. Reset yStep in case we came from air with yStep=1.
yStep = originalStep;
}
}
previous = point;
}
columns.add(column);
}
}
return new Lod(this.worldInterface, this.position, idMappings, columns);
}
}
@@ -0,0 +1,38 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.lodbuilders;
import no.jckf.dhsupport.core.dataobject.Lod;
import no.jckf.dhsupport.core.dataobject.SectionPosition;
import no.jckf.dhsupport.core.world.WorldInterface;
public abstract class LodBuilder
{
protected WorldInterface worldInterface;
protected SectionPosition position;
public LodBuilder(WorldInterface worldInterface, SectionPosition position)
{
this.worldInterface = worldInterface;
this.position = position;
}
public abstract Lod generate();
}
@@ -32,7 +32,7 @@ public class CloseReasonMessage extends PluginMessage
public String getReason()
{
return reason;
return this.reason;
}
@Override
@@ -24,9 +24,9 @@ import no.jckf.dhsupport.core.bytestream.Encoder;
public class ExceptionMessage extends TrackablePluginMessage
{
public static int TYPE_RATE_LIMITED = 0;
public static int TYPE_INVALID_LEVEL = 1;
public static int TYPE_INVALID_POSITION = 2;
public static int TYPE_REQUEST_REJECTED = 3;
public static int TYPE_REQUEST_OUT_OF_RANGE = 1;
public static int TYPE_REQUEST_REJECTED = 2;
public static int TYPE_SECTION_REQUIRES_SPLITTING = 3;
protected int typeId;
@@ -39,7 +39,7 @@ public class ExceptionMessage extends TrackablePluginMessage
public int getTypeId()
{
return typeId;
return this.typeId;
}
public void setMessage(String message)
@@ -49,7 +49,7 @@ public class ExceptionMessage extends TrackablePluginMessage
public String getMessage()
{
return message;
return this.message;
}
@Override
@@ -35,7 +35,7 @@ public class FullDataChunkMessage extends PluginMessage
public int getBufferId()
{
return bufferId;
return this.bufferId;
}
public void setData(byte[] data)
@@ -45,7 +45,7 @@ public class FullDataChunkMessage extends PluginMessage
public byte[] getData()
{
return data;
return this.data;
}
public void setIsFirst(boolean isFirst)
@@ -36,7 +36,7 @@ public class FullDataSourceRequestMessage extends TrackablePluginMessage
public String getWorldName()
{
return worldName;
return this.worldName;
}
public void setPosition(SectionPosition position)
@@ -46,7 +46,7 @@ public class FullDataSourceRequestMessage extends TrackablePluginMessage
public SectionPosition getPosition()
{
return position;
return this.position;
}
public void setTimestamp(Long timestamp)
@@ -56,7 +56,7 @@ public class FullDataSourceRequestMessage extends TrackablePluginMessage
public Long getTimestamp()
{
return timestamp;
return this.timestamp;
}
@Override
@@ -20,24 +20,40 @@ package no.jckf.dhsupport.core.message.plugin;
import no.jckf.dhsupport.core.bytestream.Encoder;
import javax.annotation.Nullable;
public class FullDataSourceResponseMessage extends TrackablePluginMessage
{
protected int bufferId;
protected Integer bufferId;
public void setBufferId(int bufferId)
protected byte[] beacons;
public void setBufferId(@Nullable Integer bufferId)
{
this.bufferId = bufferId;
}
public int getBufferId()
{
return bufferId;
return this.bufferId;
}
public void setBeacons(byte[] beacons)
{
this.beacons = beacons;
}
public byte[] getBeacons()
{
return this.beacons;
}
@Override
public void encode(Encoder encoder)
{
encoder.writeBoolean(true);
encoder.writeInt(this.bufferId);
if (encoder.writeOptional(this.bufferId)) {
encoder.writeInt(this.bufferId);
encoder.write(this.beacons);
}
}
}
@@ -21,10 +21,12 @@ package no.jckf.dhsupport.core.message.plugin;
import no.jckf.dhsupport.core.bytestream.Decoder;
import no.jckf.dhsupport.core.bytestream.Encoder;
public class CurrentLevelKeyMessage extends PluginMessage
public class LevelInitMessage extends PluginMessage
{
protected String key;
protected long time;
public void setKey(String key)
{
this.key = key;
@@ -35,15 +37,27 @@ public class CurrentLevelKeyMessage extends PluginMessage
return this.key;
}
public void setTime(long time)
{
this.time = time;
}
public long getTime()
{
return this.time;
}
@Override
public void encode(Encoder encoder)
{
encoder.writeShortString(this.key);
encoder.writeLong(this.time);
}
@Override
public void decode(Decoder decoder)
{
this.key = decoder.readShortString();
this.time = decoder.readLong();
}
}
@@ -25,6 +25,18 @@ import no.jckf.dhsupport.core.configuration.DhsConfig;
public class RemotePlayerConfigMessage extends PluginMessage
{
public static String[] KEYS = {
DhsConfig.DISTANT_GENERATION_ENABLED,
DhsConfig.RENDER_DISTANCE,
DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT,
DhsConfig.REAL_TIME_UPDATES_ENABLED,
DhsConfig.REAL_TIME_UPDATE_RADIUS,
DhsConfig.LOGIN_DATA_SYNC_ENABLED,
DhsConfig.LOGIN_DATA_SYNC_RADIUS,
DhsConfig.LOGIN_DATA_SYNC_RC_LIMIT,
DhsConfig.MAX_DATA_TRANSFER_SPEED,
};
protected int renderDistance;
protected boolean distantGenerationEnabled;
@@ -33,10 +45,16 @@ public class RemotePlayerConfigMessage extends PluginMessage
protected boolean realTimeUpdatesEnabled;
protected int realTimeUpdateRadius;
protected boolean loginDataSyncEnabled;
protected int loginDataSyncRadius;
protected int loginDataSyncRcLimit;
protected int maxDataTransferSpeed;
public void setRenderDistance(int distance)
{
this.renderDistance = distance;
@@ -64,7 +82,7 @@ public class RemotePlayerConfigMessage extends PluginMessage
public int getFullDataRequestConcurrencyLimit()
{
return fullDataRequestConcurrencyLimit;
return this.fullDataRequestConcurrencyLimit;
}
public void setRealTimeUpdatesEnabled(boolean enabled)
@@ -74,7 +92,17 @@ public class RemotePlayerConfigMessage extends PluginMessage
public boolean isRealTimeUpdatesEnabled()
{
return realTimeUpdatesEnabled;
return this.realTimeUpdatesEnabled;
}
public void setRealTimeUpdateRadius(int radius)
{
this.realTimeUpdateRadius = radius;
}
public int getRealTimeUpdateRadius()
{
return this.realTimeUpdateRadius;
}
public void setLoginDataSyncEnabled(boolean enabled)
@@ -84,7 +112,17 @@ public class RemotePlayerConfigMessage extends PluginMessage
public boolean getLoginDataSyncEnabled()
{
return loginDataSyncEnabled;
return this.loginDataSyncEnabled;
}
public void setLoginDataSyncRadius(int radius)
{
this.loginDataSyncRadius = radius;
}
public int getLoginDataSyncRadius()
{
return this.loginDataSyncRadius;
}
public void setLoginDataSyncRcLimit(int limit)
@@ -94,51 +132,73 @@ public class RemotePlayerConfigMessage extends PluginMessage
public int getLoginDataSyncRcLimit()
{
return loginDataSyncRcLimit;
return this.loginDataSyncRcLimit;
}
public void setMaxDataTransferSpeed(int speed)
{
this.maxDataTransferSpeed = speed;
}
public int getMaxDataTransferSpeed()
{
return this.maxDataTransferSpeed;
}
@Override
public void encode(Encoder encoder)
{
encoder.writeInt(this.renderDistance);
encoder.writeBoolean(this.distantGenerationEnabled);
encoder.writeInt(this.renderDistance);
encoder.writeInt(this.fullDataRequestConcurrencyLimit);
encoder.writeBoolean(this.realTimeUpdatesEnabled);
encoder.writeInt(this.realTimeUpdateRadius);
encoder.writeBoolean(this.loginDataSyncEnabled);
encoder.writeInt(this.loginDataSyncRadius);
encoder.writeInt(this.loginDataSyncRcLimit);
encoder.writeInt(this.maxDataTransferSpeed);
}
@Override
public void decode(Decoder decoder)
{
this.renderDistance = decoder.readInt();
this.distantGenerationEnabled = decoder.readBoolean();
this.renderDistance = decoder.readInt();
this.fullDataRequestConcurrencyLimit = decoder.readInt();
this.realTimeUpdatesEnabled = decoder.readBoolean();
this.realTimeUpdateRadius = decoder.readInt();
this.loginDataSyncEnabled = decoder.readBoolean();
this.loginDataSyncRadius = decoder.readInt();
this.loginDataSyncRcLimit = decoder.readInt();
this.maxDataTransferSpeed = decoder.readInt();
}
public void fromConfiguration(Configuration config)
{
this.renderDistance = config.getInt(DhsConfig.RENDER_DISTANCE);
this.distantGenerationEnabled = config.getBool(DhsConfig.DISTANT_GENERATION_ENABLED);
this.renderDistance = config.getInt(DhsConfig.RENDER_DISTANCE);
this.fullDataRequestConcurrencyLimit = config.getInt(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT);
this.realTimeUpdatesEnabled = config.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED);
this.realTimeUpdateRadius = config.getInt(DhsConfig.REAL_TIME_UPDATE_RADIUS);
this.loginDataSyncEnabled = config.getBool(DhsConfig.LOGIN_DATA_SYNC_ENABLED);
this.loginDataSyncRadius = config.getInt(DhsConfig.LOGIN_DATA_SYNC_RADIUS);
this.loginDataSyncRcLimit = config.getInt(DhsConfig.LOGIN_DATA_SYNC_RC_LIMIT);
this.maxDataTransferSpeed = config.getInt(DhsConfig.MAX_DATA_TRANSFER_SPEED);
}
public Configuration toConfiguration()
{
Configuration config = new Configuration();
config.set(DhsConfig.RENDER_DISTANCE, this.renderDistance);
config.set(DhsConfig.DISTANT_GENERATION_ENABLED, this.distantGenerationEnabled);
config.set(DhsConfig.RENDER_DISTANCE, this.renderDistance);
config.set(DhsConfig.FULL_DATA_REQUEST_CONCURRENCY_LIMIT, this.fullDataRequestConcurrencyLimit);
config.set(DhsConfig.REAL_TIME_UPDATES_ENABLED, this.realTimeUpdatesEnabled);
config.set(DhsConfig.REAL_TIME_UPDATE_RADIUS, this.realTimeUpdateRadius);
config.set(DhsConfig.LOGIN_DATA_SYNC_ENABLED, this.loginDataSyncEnabled);
config.set(DhsConfig.LOGIN_DATA_SYNC_RADIUS, this.loginDataSyncRadius);
config.set(DhsConfig.LOGIN_DATA_SYNC_RC_LIMIT, this.loginDataSyncRcLimit);
config.set(DhsConfig.MAX_DATA_TRANSFER_SPEED, this.maxDataTransferSpeed);
return config;
}
@@ -29,7 +29,7 @@ public class TrackablePluginMessage extends PluginMessage
public int getTracker()
{
return tracker;
return this.tracker;
}
public void isResponseTo(TrackablePluginMessage other)
@@ -0,0 +1,34 @@
/*
* DH Support, server-side support for Distant Horizons.
* Copyright (C) 2024 Jim C K Flaten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package no.jckf.dhsupport.core.scheduling;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public interface Scheduler
{
boolean canReadWorldAsync();
<U> CompletableFuture<U> runOnMainThread(Supplier<U> supplier);
<U> CompletableFuture<U> runOnRegionThread(UUID worldId, int x, int z, Supplier<U> supplier);
<U> CompletableFuture<U> runOnSeparateThread(Supplier<U> supplier);
}
@@ -18,12 +18,32 @@
package no.jckf.dhsupport.core.world;
import java.util.Map;
import no.jckf.dhsupport.core.configuration.Configurable;
import no.jckf.dhsupport.core.dataobject.Beacon;
public interface WorldInterface
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface WorldInterface extends Configurable
{
WorldInterface newInstance();
UUID getId();
String getName();
boolean chunkExists(int x, int z);
boolean loadChunk(int x, int z);
CompletableFuture<Boolean> loadChunkAsync(int x, int z);
boolean unloadChunk(int x, int z);
boolean unloadChunkAsync(int x, int z);
int getMinY();
int getMaxY();
@@ -44,5 +64,5 @@ public interface WorldInterface
byte getSkyLightAt(int x, int y, int z);
boolean isTransparent(int x, int y, int z);
Collection<Beacon> getBeaconsInChunk(int x, int z);
}
+87 -7
View File
@@ -1,18 +1,98 @@
# Maximum supported render distance. Clients will be capped to this value.
# Required.
render_distance: 128
# Default: 1024
render_distance: 1024
# TODO
# Is LOD generation enabled at all?
# Disable if you only want to use the plugin for level key info in a proxied setup.
# Required.
# Default: true
distant_generation_enabled: true
# TODO
# Number of parallel LOD requests per client.
# Required.
# Default: 20
full_data_request_concurrency_limit: 20
# TODO
real_time_updates_enabled: true
# Currently unsupported.
# Required.
# Default: false
real_time_updates_enabled: false
# TODO
# Currently unsupported.
# Required.
# Default: 128
real_time_update_radius: 128
# Let clients ask for updates to LODs they've previously downloaded.
# Required.
# Default: true
login_data_sync_enabled: true
# TODO
# Currently unsupported.
# Required.
# Default: 128
login_data_sync_radius: 128
# Currently unsupported.
# Required.
# Default: 50
login_data_sync_rc_limit: 50
# Currently unsupported.
# Required.
# Default: 0
max_data_transfer_speed: 0
# Select which LOD builder to use.
# Required.
# Default: FullBuilder
builder_type: FullBuilder
# Number of blocks to advance between each sample on the Y axis when generating LODs.
# A setting of 1 will sample every block, while 2 will sample every other block, and so on.
# Required.
# Default: 2
builder_resolution: 2
# Whether to include non-colliding blocks when they are the top-most block.
# True means tall grass, flowers, and snow will color the block below them.
# Optional.
# Default: false
#include_non-colliding_top_layer: true
# When running DHS on a proxied server, set this to a value that is unique to this specific server.
# Optional.
#level_key_prefix:
# World specific settings.
# You can override any of the above settings on a per-world basis.
worlds:
world:
# Use builder that is optimized for overworld.
builder_type: FastOverworldBuilder
# When using the FastOverworldBuilder, should LODs always contain data down to at least sea level?
# Enable if your world has floating islands or mountains/structures with large overhangs above sea level.
# Optional.
# Default: false
#scan_to_sea_level: true
# Limit LOD generation to within this area.
# Optional.
#border_center: 0,0
#border_radius: 5000
# It might make sense to have a lower view distance in the nether, since visibility will be low simply because of
# the level design anyway.
world_nether:
render_distance: 64
# Number of worker threads used to generate LODs.
# Does not affect Folia servers.
# Required.
# Default: 4
scheduler_threads: 4
# Don't touch :)
config_version: 3
+7 -3
View File
@@ -1,4 +1,8 @@
name: DHSupport
version: 0.2.2
main: no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin
api-version: 1.21
version: ${project.version}
main: ${main.class}
api-version: ${mc.api-version}
mc-version: ${mc.api-version}${mc.api-version-patch}
prefix: DHS
folia-supported: true
author: Jckf