Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b746a9534 |
@@ -1 +0,0 @@
|
||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
||||
@@ -1,7 +1,7 @@
|
||||
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files%2Flogo%20files%2Fnew%2FSVG%2FDistant-Horizons-Core.svg" height="128px">
|
||||
# Distant Horizons
|
||||
|
||||
This repo is for the Distant Horizons mod.
|
||||
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and helping us port to different versions faster and easier.
|
||||
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and potentially helping us port to different versions faster and easier.
|
||||
|
||||
Check out the mod's main GitLab page here:
|
||||
https://gitlab.com/jeseibel/distant-horizons
|
||||
|
||||
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
@@ -1,44 +0,0 @@
|
||||
# Distant Horizons brand guidelines
|
||||
|
||||
To keep our look consistent and recognizable, we’ve created some simple guidelines for using our logos. We have a Primary logo, Core and API logos. Keep them cool 😎
|
||||
|
||||
|
||||
## Logos
|
||||
Please do not edit, change, distort, recolour, or reconfigure our logos.
|
||||
|  |  |  |
|
||||
|--|--|--|
|
||||
|Primary [.png](/PNG/Distant-Horizons.png) [.svg](./SVG/Distant-Horizons-Logo.svg)| Core [.png](/PNG/Distant-Horizons-Core.png) [.svg](./SVG/Distant-Horizons-Core.svg) | API [.png](./PNG/Distant-Horizons-API.png) [.svg](./SVG/Distant-Horizons-API.svg) |
|
||||
|
||||
|
||||
## Marks
|
||||
Use these only when the Distant Horizons brand is clearly visible or has been well established elsewhere on the page or in the design. (When in doubt, use the other ones.)
|
||||
|
||||
> *Keep in mind the Distant Horizons API mark needs to be optically centered to align.*
|
||||
|
||||
<!-- |  |  |  |
|
||||
|--|--|--|
|
||||
|Primary [.png](/PNG/Distant-Horizons-Mark.png.png) [.svg](./SVG/Distant-Horizons-Mark.svg.svg)| Core [.png](/PNG/Distant-Horizons-Core-Mark.png.png) [.svg](./SVG/Distant-Horizons-Core-Mark.svg.svg) | API [.png](./PNG/Distant-Horizons-API-Mark.png.png) [.svg](./SVG/Distant-Horizons-API-Mark.svg.svg) | -->
|
||||
|
||||
| <img src="./PNG/Distant-Horizons-Mark.png" width="100"/> | <img src="./PNG/Distant-Horizons-Core-Mark.png" width="100"/> | <img src="./PNG/Distant-Horizons-API-Mark.png" width="100"/> |
|
||||
|--|--|--|
|
||||
|Primary [.png](/PNG/Distant-Horizons-Mark.png.png) [.svg](./SVG/Distant-Horizons-Mark.svg.svg)| Core [.png](/PNG/Distant-Horizons-Core-Mark.png.png) [.svg](./SVG/Distant-Horizons-Core-Mark.svg.svg) | API [.png](./PNG/Distant-Horizons-API-Mark.png.png) [.svg](./SVG/Distant-Horizons-API-Mark.svg.svg) |
|
||||
|
||||
|
||||
|
||||
## Spacing
|
||||
Every logo needs room to breathe.
|
||||
Ours needs the free space of the height and width of the letter **"H"** in Distant **H**orizons.
|
||||
|
||||
> *Some leeway is allowed for the Distant Horizons API mark due to it's shape.*
|
||||
|
||||
|  |  |
|
||||
|--|--|
|
||||
|
||||
|
||||
___
|
||||
|
||||
The logotype we are using in our logos is a modified [Karmatic Arcade font by Vic Fieger](https://www.dafont.com/karmatic-arcade.font?fpp=100&psize=s)
|
||||
|
||||
This branding guideline was influenced by [Discord's own](https://discord.com/branding)
|
||||
|
||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
||||
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 78 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 306.5 359.13"><defs><style>.cls-1{fill:#7ec138;}.cls-2{fill:#12af68;}.cls-3{fill:#fff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M175.21,356.1l123.72-71.92A15.22,15.22,0,0,0,306.5,271V126.5a22.32,22.32,0,0,0-11.1-19.29L173.18,36.13a18.26,18.26,0,0,0-18.36,0L32.59,107.21A22.31,22.31,0,0,0,21.5,126.5V271a15.22,15.22,0,0,0,7.56,13.15L152.78,356.1A22.29,22.29,0,0,0,175.21,356.1Z"/><g id="Right_Path_" data-name="Right <Path>"><path class="cls-1" d="M224.5,254.1v51.61a8,8,0,0,1-4,6.92l-44,25.4a4,4,0,0,1-6-3.46V283a8,8,0,0,1,4-6.92l44-25.41A4,4,0,0,1,224.5,254.1Z"/></g><g id="_Path_" data-name="<Path>"><path class="cls-2" d="M113.82,173.07l-11-6.36a4,4,0,0,0-6,3.46v51.72a8,8,0,0,0,4,6.92l11,6.35a4,4,0,0,0,6-3.46V180A8,8,0,0,0,113.82,173.07Z"/></g><g id="_Path_2" data-name="<Path>"><path class="cls-2" d="M129.26,150.11l11.19-6.46a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.46A8,8,0,0,0,129.26,150.11Z"/></g><g id="Right_Path_2" data-name="Right <Path>"><path class="cls-2" d="M170.5,244v12.74a4,4,0,0,0,6,3.46l44-25.4a8,8,0,0,0,4-6.92V215.15a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,170.5,244Z"/></g><g id="Right_Path_3" data-name="Right <Path>"><path class="cls-1" d="M243.5,299.35l45-26a8,8,0,0,0,4-6.92V136.92a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.92V295.89A4,4,0,0,0,243.5,299.35Z"/></g><g id="Left_Path_" data-name="Left <Path>"><path class="cls-2" d="M157.5,244v12.74a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.92V234.2a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,157.5,244Z"/></g><g id="Left_Path_2" data-name="Left <Path>"><path class="cls-1" d="M153.5,276.08l-112-64.66a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.92L151.5,338a4,4,0,0,0,6-3.46V283A8,8,0,0,0,153.5,276.08Z"/></g><g id="_Path_3" data-name="<Path>"><path class="cls-3" d="M157.5,205v12.74a4,4,0,0,1-6,3.46l-11-6.36a8,8,0,0,1-4-6.92V195.23a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,157.5,205Z"/></g><path d="M91.15,171.19V235a16,16,0,0,1-24,13.85L11.59,216.81A23.27,23.27,0,0,1,0,196.73V132.89A16,16,0,0,1,24,119l55.56,32.07A23.24,23.24,0,0,1,91.15,171.19Z"/><g id="_Path_4" data-name="<Path>"><path class="cls-1" d="M14,138.09v58.64a9.17,9.17,0,0,0,4.6,8l51.05,29.48a5,5,0,0,0,7.5-4.33V171.19a9.18,9.18,0,0,0-4.59-8L21.5,133.76A5,5,0,0,0,14,138.09Z"/></g><g id="_Path_5" data-name="<Path>"><path class="cls-3" d="M170.5,217.78V205.05a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V208a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,170.5,217.78Z"/></g><g id="_Path_6" data-name="<Path>"><path class="cls-2" d="M209.5,202.19l11-6.36a8,8,0,0,0,4-6.92V176.18a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,209.5,202.19Z"/></g><path d="M300.65,84.93a15.94,15.94,0,0,1-7.92,13.73L245.5,125.91a20,20,0,0,1-20.06,0L164,90.44l-61.45,35.48a20.11,20.11,0,0,1-20.06,0L35.27,98.66a15.84,15.84,0,0,1,0-27.44L154,2.69a20,20,0,0,1,20.06,0l118.7,68.53A15.89,15.89,0,0,1,300.65,84.93Z"/><g id="_Path_7" data-name="<Path>"><path class="cls-2" d="M169,117.4l-11,6.34a4,4,0,0,0,0,6.93l44.93,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L177,117.4A8,8,0,0,0,169,117.4Z"/></g><g id="_Path_8" data-name="<Path>"><path class="cls-3" d="M168,187.21l11.19-6.46a4,4,0,0,0,0-6.93L168,167.36a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.46A8,8,0,0,0,168,187.21Z"/></g><g id="_Path_9" data-name="<Path>"><path class="cls-1" d="M280.42,87.31l-44.73,25.82a8,8,0,0,1-8,0L168,78.66a8,8,0,0,0-8,0l-59.7,34.47a8,8,0,0,1-8,0L47.58,87.31a4,4,0,0,1,0-6.93L160,15.47a8,8,0,0,1,8,0L280.42,80.38A4,4,0,0,1,280.42,87.31Z"/></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 10 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 325.47"><defs><style>.cls-1{fill:#e13e1e;}.cls-2{fill:#f7a612;}.cls-3{fill:#fff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M153.72,322.45l123.71-71.93A15.22,15.22,0,0,0,285,237.36V92.84a22.32,22.32,0,0,0-11.1-19.29L151.68,2.47a18.31,18.31,0,0,0-18.36,0L11.1,73.55A22.32,22.32,0,0,0,0,92.84V237.35A15.2,15.2,0,0,0,7.57,250.5l123.71,71.94A22.31,22.31,0,0,0,153.72,322.45Z"/><g id="Right_Path_" data-name="Right <Path>"><path class="cls-1" d="M203,220.44v51.61a8,8,0,0,1-4,6.93l-44,25.39a4,4,0,0,1-6-3.46v-51.6a8,8,0,0,1,4-6.92L197,217A4,4,0,0,1,203,220.44Z"/></g><g id="Right_Path_2" data-name="Right <Path>"><path class="cls-2" d="M149,210.36v12.73a4,4,0,0,0,6,3.46l44-25.39a8,8,0,0,0,4-6.93V181.49a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,149,210.36Z"/></g><g id="Right_Path_3" data-name="Right <Path>"><path class="cls-1" d="M222,265.69l45-26a8,8,0,0,0,4-6.93V103.26a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.93V262.23A4,4,0,0,0,222,265.69Z"/></g><g id="Left_Path_" data-name="Left <Path>"><path class="cls-2" d="M136,210.36v12.73a4,4,0,0,1-6,3.46l-11-6.34a8,8,0,0,1-4-6.93V200.54a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,210.36Z"/></g><g id="Left_Path_2" data-name="Left <Path>"><path class="cls-1" d="M132,242.42,20,177.76a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.93l112,64.65a4,4,0,0,0,6-3.46V249.35A8,8,0,0,0,132,242.42Z"/></g><g id="_Path_" data-name="<Path>"><path class="cls-3" d="M136,171.39v12.73a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.93V161.57a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,171.39Z"/></g><g id="_Path_2" data-name="<Path>"><path class="cls-2" d="M99,145.41l-11-6.35a4,4,0,0,0-6,3.46v51.71a8,8,0,0,0,4,6.93l11,6.34a4,4,0,0,0,6-3.46v-51.7A8,8,0,0,0,99,145.41Z"/></g><g id="_Path_3" data-name="<Path>"><path class="cls-1" d="M14.33,103.26V155a8,8,0,0,0,4,6.93l45,26a4,4,0,0,0,6-3.46v-51.7a8,8,0,0,0-4-6.93l-45-26A4,4,0,0,0,14.33,103.26Z"/></g><g id="_Path_4" data-name="<Path>"><path class="cls-3" d="M149,184.12V171.39a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V174.3a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,149,184.12Z"/></g><g id="_Path_5" data-name="<Path>"><path class="cls-2" d="M188,168.53l11-6.35a8,8,0,0,0,4-6.93V142.52a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,188,168.53Z"/></g><g id="_Path_6" data-name="<Path>"><path class="cls-1" d="M258.92,88.65l-44.73,25.82a8,8,0,0,1-8,0L146.5,80a8,8,0,0,0-8,0L78.8,114.47a8,8,0,0,1-8,0L26.08,88.65a4,4,0,0,1,0-6.93L138.5,16.82a8,8,0,0,1,8,0l112.42,64.9A4,4,0,0,1,258.92,88.65Z"/></g><g id="_Path_7" data-name="<Path>"><path class="cls-2" d="M138.5,94.74l-11,6.35a4,4,0,0,0,0,6.92l44.94,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L146.5,94.74A8,8,0,0,0,138.5,94.74Z"/></g><g id="_Path_8" data-name="<Path>"><path class="cls-2" d="M112.76,134.08,124,127.61a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.47A8,8,0,0,0,112.76,134.08Z"/></g><g id="_Path_9" data-name="<Path>"><path class="cls-3" d="M146.5,153.56l11.19-6.47a4,4,0,0,0,0-6.93L146.5,133.7a8,8,0,0,0-8,0l-11.19,6.46a4,4,0,0,0,0,6.93l11.19,6.47A8,8,0,0,0,146.5,153.56Z"/></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 325.47"><defs><style>.cls-1{fill:#38c138;}.cls-2{fill:#f6d182;}.cls-3{fill:#fff;}.cls-4{fill:#00a9f4;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M153.72,322.45l123.71-71.93A15.22,15.22,0,0,0,285,237.36V92.84a22.32,22.32,0,0,0-11.1-19.29L151.68,2.47a18.31,18.31,0,0,0-18.36,0L11.1,73.55A22.32,22.32,0,0,0,0,92.84V237.35A15.2,15.2,0,0,0,7.57,250.5l123.71,71.94A22.31,22.31,0,0,0,153.72,322.45Z"/><path id="Green" class="cls-1" d="M203,220.44v51.61a8,8,0,0,1-4,6.93l-44,25.39a4,4,0,0,1-6-3.46v-51.6a8,8,0,0,1,4-6.92L197,217A4,4,0,0,1,203,220.44Zm-54-10.08v12.73a4,4,0,0,0,6,3.46l44-25.39a8,8,0,0,0,4-6.93V181.49a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,149,210.36Zm73,55.33,45-26a8,8,0,0,0,4-6.93V103.26a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.93V262.23A4,4,0,0,0,222,265.69Z"/><path id="Yellow" class="cls-2" d="M136,210.36v12.73a4,4,0,0,1-6,3.46l-11-6.34a8,8,0,0,1-4-6.93V200.54a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,210.36Zm-4,32.06L20,177.76a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.93l112,64.65a4,4,0,0,0,6-3.46V249.35A8,8,0,0,0,132,242.42Z"/><g id="_Path_" data-name="<Path>"><path class="cls-3" d="M136,171.39v12.73a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.93V161.57a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,171.39Z"/></g><g id="_Path_2" data-name="<Path>"><path class="cls-4" d="M99,145.41l-11-6.35a4,4,0,0,0-6,3.46v51.71a8,8,0,0,0,4,6.93l11,6.34a4,4,0,0,0,6-3.46v-51.7A8,8,0,0,0,99,145.41Z"/></g><g id="_Path_3" data-name="<Path>"><path class="cls-4" d="M14,103.26V155a8,8,0,0,0,4,6.93l45,26a4,4,0,0,0,6-3.46v-51.7a8,8,0,0,0-4-6.93l-45-26A4,4,0,0,0,14,103.26Z"/></g><g id="_Path_4" data-name="<Path>"><path class="cls-3" d="M149,184.12V171.39a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V174.3a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,149,184.12Z"/></g><g id="_Path_5" data-name="<Path>"><path class="cls-4" d="M188,168.53l11-6.35a8,8,0,0,0,4-6.93V142.52a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,188,168.53Z"/></g><g id="_Path_6" data-name="<Path>"><path class="cls-4" d="M258.92,88.65l-44.73,25.82a8,8,0,0,1-8,0L146.5,80a8,8,0,0,0-8,0L78.8,114.47a8,8,0,0,1-8,0L26.08,88.65a4,4,0,0,1,0-6.93L138.5,16.82a8,8,0,0,1,8,0l112.42,64.9A4,4,0,0,1,258.92,88.65Z"/></g><g id="_Path_7" data-name="<Path>"><path class="cls-4" d="M138.5,94.74l-11,6.35a4,4,0,0,0,0,6.92l44.94,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L146.5,94.74A8,8,0,0,0,138.5,94.74Z"/></g><g id="_Path_8" data-name="<Path>"><path class="cls-4" d="M112.76,134.08,124,127.61a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.47A8,8,0,0,0,112.76,134.08Z"/></g><g id="_Path_9" data-name="<Path>"><path class="cls-3" d="M146.5,153.56l11.19-6.47a4,4,0,0,0,0-6.93L146.5,133.7a8,8,0,0,0-8,0l-11.19,6.46a4,4,0,0,0,0,6.93l11.19,6.47A8,8,0,0,0,146.5,153.56Z"/></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -33,12 +33,9 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.sqlite.SQLiteJDBCLoader;
|
||||
import org.sqlite.util.OSInfo;
|
||||
import org.tukaani.xz.XZOutputStream;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
/** Handles first time Core setup. */
|
||||
public class Initializer
|
||||
@@ -60,44 +57,12 @@ public class Initializer
|
||||
Class<?> config = com.electronwill.nightconfig.core.Config.class;
|
||||
Class<?> oldFastUtil = it.unimi.dsi.fastutil.longs.LongArrayList.class; // available in 8.2.1
|
||||
//Class<?> newFastUtil = it.unimi.dsi.fastutil.ints.IntUnaryOperator.class; // available in 8.5.13
|
||||
Class<?> sqliteJava = org.sqlite.SQLiteConnection.class;
|
||||
Class<?> sqliteNative = org.sqlite.core.NativeDB.class;
|
||||
|
||||
//// maybe these lines are needed to shade SQLite, James isn't sure.
|
||||
//// Although they never seemed to fail, which is a bit odd.
|
||||
//try
|
||||
//{
|
||||
// // needed by Forge to load the Java database connection
|
||||
// Class.forName("org.sqlite.JDBC");
|
||||
// LOGGER.info("loaded normal SQLITE");
|
||||
//}
|
||||
//catch (ClassNotFoundException e)
|
||||
//{
|
||||
// LOGGER.warn("normal: " + e.getMessage(), e);
|
||||
//}
|
||||
//
|
||||
//try
|
||||
//{
|
||||
// // needed by Forge to load the Java database connection
|
||||
// Class.forName("DistantHorizons.libraries.sqlite.JDBC");
|
||||
// LOGGER.info("loaded shaded SQLITE");
|
||||
//}
|
||||
//catch (ClassNotFoundException e)
|
||||
//{
|
||||
// LOGGER.warn("shaded: " + e.getMessage(), e);
|
||||
//}
|
||||
|
||||
boolean sqliteLoaded = SQLiteJDBCLoader.initialize();
|
||||
if (!sqliteLoaded)
|
||||
{
|
||||
throw new RuntimeException("Failed to load SQLite native library. Hopefully SQLite logged a reason for this failure.");
|
||||
}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].", e);
|
||||
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].");
|
||||
// throwing here should crash the game, notifying the developer that something is wrong
|
||||
throw new RuntimeException(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// confirm the resource directory is present
|
||||
|
||||
@@ -33,19 +33,19 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
|
||||
|
||||
|
||||
|
||||
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
||||
@Override public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||
@Override public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> renderWireframe()
|
||||
@Override public IDhApiConfigValue<Boolean> renderWireframe()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.renderWireframe); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> lodOnlyMode()
|
||||
@Override public IDhApiConfigValue<Boolean> lodOnlyMode()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.lodOnlyMode); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> debugWireframeRendering()
|
||||
@Override public IDhApiConfigValue<Boolean> debugWireframeRendering()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.DebugWireframe.enableRendering); }
|
||||
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ public class DhApiMultiplayerConfig implements IDhApiMultiplayerConfig
|
||||
|
||||
|
||||
|
||||
public IDhApiConfigValue<EDhApiServerFolderNameMode> folderSavingMode()
|
||||
@Override public IDhApiConfigValue<EDhApiServerFolderNameMode> folderSavingMode()
|
||||
{ return new DhApiConfigValue<EDhApiServerFolderNameMode, EDhApiServerFolderNameMode>(Config.Client.Advanced.Multiplayer.serverFolderNameMode); }
|
||||
|
||||
public IDhApiConfigValue<Double> multiverseSimilarityRequirement()
|
||||
@Override public IDhApiConfigValue<Double> multiverseSimilarityRequirement()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent); }
|
||||
|
||||
}
|
||||
|
||||
@@ -149,37 +149,37 @@ public class ServerApi
|
||||
public void serverPlayerJoinEvent(IServerPlayerWrapper player)
|
||||
{
|
||||
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
|
||||
LOGGER.info("Player [${player.getName()}] joined.");
|
||||
if (serverWorld != null)
|
||||
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
|
||||
{
|
||||
serverWorld.addPlayer(player);
|
||||
LOGGER.info("Player [" + player.getName()+ "] joined.");
|
||||
((DhServerWorld) serverWorld).addPlayer(player);
|
||||
}
|
||||
}
|
||||
public void serverPlayerDisconnectEvent(IServerPlayerWrapper player)
|
||||
{
|
||||
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
|
||||
LOGGER.info("Player [${player.getName()}] disconnected.");
|
||||
if (serverWorld != null)
|
||||
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
|
||||
{
|
||||
serverWorld.removePlayer(player);
|
||||
LOGGER.info("Player [" + player.getName() + "] disconnected.");
|
||||
((DhServerWorld) serverWorld).removePlayer(player);
|
||||
}
|
||||
}
|
||||
public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
|
||||
{
|
||||
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
|
||||
LOGGER.info("Player [${player.getName()}] changed level: [${originLevel.getKeyedLevelDimensionName()}] -> [${destinationLevel.getKeyedLevelDimensionName()}].");
|
||||
if (serverWorld != null)
|
||||
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
|
||||
{
|
||||
serverWorld.changePlayerLevel(player, originLevel, destinationLevel);
|
||||
LOGGER.info("Player [" + player.getName() + "] changed level: ["+originLevel.getKeyedLevelDimensionName()+"] -> ["+destinationLevel.getKeyedLevelDimensionName()+"].");
|
||||
((DhServerWorld) serverWorld).changePlayerLevel(player, originLevel, destinationLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message)
|
||||
{
|
||||
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
|
||||
if (serverWorld != null)
|
||||
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
|
||||
{
|
||||
serverWorld.getServerPlayerStateManager().handlePluginMessage(player, message);
|
||||
((DhServerWorld) serverWorld).remotePlayerConnectionHandler.handlePluginMessage(player, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,11 +117,11 @@ public class SharedApi
|
||||
|
||||
public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; }
|
||||
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */
|
||||
public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld instanceof DhClientServerWorld) ? (DhClientServerWorld) currentWorld : null; }
|
||||
public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld != null && DhClientServerWorld.class.isInstance(currentWorld)) ? (DhClientServerWorld) currentWorld : null; }
|
||||
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientWorld} or {@link DhClientServerWorld} */
|
||||
public static IDhClientWorld getIDhClientWorld() { return (currentWorld instanceof IDhClientWorld) ? (IDhClientWorld) currentWorld : null; }
|
||||
public static IDhClientWorld getIDhClientWorld() { return (currentWorld != null && IDhClientWorld.class.isInstance(currentWorld)) ? (IDhClientWorld) currentWorld : null; }
|
||||
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhServerWorld} or {@link DhClientServerWorld} */
|
||||
public static IDhServerWorld getIDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; }
|
||||
public static IDhServerWorld getIDhServerWorld() { return (currentWorld != null && IDhServerWorld.class.isInstance(currentWorld)) ? (IDhServerWorld) currentWorld : null; }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -61,61 +61,236 @@ public class Config
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build();
|
||||
|
||||
|
||||
|
||||
public static class Client
|
||||
private static Config GLOBAL_INSTANCE;
|
||||
public static void initializeGlobalConfig()
|
||||
{
|
||||
public static ConfigEntry<Boolean> quickEnableRendering = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "If true, Distant Horizons will render LODs beyond the vanilla render distance."
|
||||
+ "")
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
|
||||
if (GLOBAL_INSTANCE != null)
|
||||
{
|
||||
throw new IllegalStateException("Cannot reinitialize the Config");
|
||||
}
|
||||
GLOBAL_INSTANCE = new Config();
|
||||
}
|
||||
public static Config getGlobalConfig() { return GLOBAL_INSTANCE; }
|
||||
|
||||
public final QuickSettings quickSettings = new QuickSettings();
|
||||
public final Graphics graphics = new Graphics();
|
||||
|
||||
public class QuickSettings
|
||||
{
|
||||
public final ConfigEntry<Boolean> enableRendering = new ConfigEntry.Builder<Boolean>()
|
||||
.computed(
|
||||
() -> Config.this.debugging.rendererMode.get() != EDhApiRendererMode.DISABLED,
|
||||
enableRendering -> Config.this.debugging.rendererMode.set(enableRendering
|
||||
? EDhApiRendererMode.DEFAULT
|
||||
: EDhApiRendererMode.DISABLED
|
||||
)
|
||||
)
|
||||
.build();
|
||||
|
||||
public static ConfigLinkedEntry quickLodChunkRenderDistance = new ConfigLinkedEntry(Advanced.Graphics.Quality.lodChunkRenderDistanceRadius);
|
||||
|
||||
public static ConfigEntry<EDhApiQualityPreset> qualityPresetSetting = new ConfigEntry.Builder<EDhApiQualityPreset>()
|
||||
.set(EDhApiQualityPreset.MEDIUM) // the default value is set via the listener when accessed
|
||||
.comment(""
|
||||
+ "Changing this setting will modify a number of different settings that will change the \n"
|
||||
+ "visual fidelity of the rendered LODs.\n"
|
||||
+ "\n"
|
||||
+ "Higher settings will improve the graphical quality while increasing GPU and memory use.\n"
|
||||
+ "")
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
|
||||
.addListener(RenderQualityPresetConfigEventHandler.INSTANCE)
|
||||
public final ConfigEntry<Integer> lodChunkRenderDistanceRadius = new ConfigEntry.Builder<Integer>()
|
||||
.linkedTo(() -> Config.this.graphics.quality.lodChunkRenderDistanceRadius)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiThreadPreset> threadPresetSetting = new ConfigEntry.Builder<EDhApiThreadPreset>()
|
||||
public final ConfigEntry<EDhApiQualityPreset> qualityPreset = new ConfigEntry.Builder<EDhApiQualityPreset>()
|
||||
.preset(
|
||||
new ConfigEntry.PresetBuilder<>(EDhApiQualityPreset.CUSTOM)
|
||||
.addConfigEntry(() -> Config.this.graphics.quality.maxHorizontalResolution, map -> {
|
||||
map.put(EDhApiQualityPreset.MINIMUM, EDhApiMaxHorizontalResolution.TWO_BLOCKS);
|
||||
map.put(EDhApiQualityPreset.LOW, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
map.put(EDhApiQualityPreset.MEDIUM, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
map.put(EDhApiQualityPreset.HIGH, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
map.put(EDhApiQualityPreset.EXTREME, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
})
|
||||
.addConfigEntry(() -> Config.this.graphics.quality.verticalQuality, map -> {
|
||||
map.put(EDhApiQualityPreset.MINIMUM, EDhApiVerticalQuality.HEIGHT_MAP);
|
||||
map.put(EDhApiQualityPreset.LOW, EDhApiVerticalQuality.LOW);
|
||||
map.put(EDhApiQualityPreset.MEDIUM, EDhApiVerticalQuality.MEDIUM);
|
||||
map.put(EDhApiQualityPreset.HIGH, EDhApiVerticalQuality.HIGH);
|
||||
map.put(EDhApiQualityPreset.EXTREME, EDhApiVerticalQuality.EXTREME);
|
||||
})
|
||||
.addConfigEntry(() -> Config.this.graphics.quality.horizontalQuality, map -> {
|
||||
map.put(EDhApiQualityPreset.MINIMUM, EDhApiHorizontalQuality.LOWEST);
|
||||
map.put(EDhApiQualityPreset.LOW, EDhApiHorizontalQuality.LOW);
|
||||
map.put(EDhApiQualityPreset.MEDIUM, EDhApiHorizontalQuality.MEDIUM);
|
||||
map.put(EDhApiQualityPreset.HIGH, EDhApiHorizontalQuality.HIGH);
|
||||
map.put(EDhApiQualityPreset.EXTREME, EDhApiHorizontalQuality.EXTREME);
|
||||
})
|
||||
.addConfigEntry(() -> Config.this.graphics.quality.transparency, map -> {
|
||||
map.put(EDhApiQualityPreset.MINIMUM, EDhApiTransparency.DISABLED);
|
||||
map.put(EDhApiQualityPreset.LOW, EDhApiTransparency.DISABLED); // should be fake if/when fake is fixed
|
||||
map.put(EDhApiQualityPreset.MEDIUM, EDhApiTransparency.COMPLETE);
|
||||
map.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
||||
map.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
||||
})
|
||||
.addConfigEntry(() -> Config.this.graphics.quality.ssao.enabled, map -> {
|
||||
map.put(EDhApiQualityPreset.MINIMUM, false);
|
||||
map.put(EDhApiQualityPreset.LOW, false);
|
||||
map.put(EDhApiQualityPreset.MEDIUM, true);
|
||||
map.put(EDhApiQualityPreset.HIGH, true);
|
||||
map.put(EDhApiQualityPreset.EXTREME, true);
|
||||
})
|
||||
)
|
||||
.build();
|
||||
|
||||
public final ConfigEntry<EDhApiThreadPreset> threadPreset = new ConfigEntry.Builder<EDhApiThreadPreset>()
|
||||
.set(EDhApiThreadPreset.LOW_IMPACT) // the default value is set via the listener when accessed
|
||||
.comment(""
|
||||
+ "Changing this setting will modify a number of different settings that will change \n"
|
||||
+ "the load that Distant Horizons is allowed to put on your CPU. \n"
|
||||
+ "\n"
|
||||
+ "Higher options will improve LOD generation and loading speed, \n"
|
||||
+ "but will increase CPU load and may introduce stuttering.\n"
|
||||
+ "\n"
|
||||
+ "Note: on CPUs with 4 cores or less these settings will be less effective \n"
|
||||
+ " and some settings will give similar results. \n"
|
||||
+ "")
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
|
||||
.addListener(ThreadPresetConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigLinkedEntry quickEnableWorldGenerator = new ConfigLinkedEntry(Advanced.WorldGenerator.enableDistantGeneration);
|
||||
public final ConfigLinkedEntry quickEnableWorldGenerator = new ConfigLinkedEntry(_Client.Advanced.WorldGenerator.enableDistantGeneration);
|
||||
|
||||
public static ConfigLinkedEntry quickLodCloudRendering = new ConfigLinkedEntry(Advanced.Graphics.GenericRendering.enableCloudRendering);
|
||||
public final ConfigLinkedEntry quickLodCloudRendering = new ConfigLinkedEntry(_Client.Advanced.Graphics.GenericRendering.enableCloudRendering);
|
||||
|
||||
public static ConfigEntry<Boolean> optionsButton = new ConfigEntry.Builder<Boolean>()
|
||||
public final ConfigEntry<Boolean> showOptionsButton = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
|
||||
.comment("" +
|
||||
"Should Distant Horizon's config button appear in the options screen next to fov slider?")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class Graphics
|
||||
{
|
||||
public final Quality quality = new Quality();
|
||||
|
||||
public static class Quality
|
||||
{
|
||||
public ConfigEntry<Integer> lodChunkRenderDistanceRadius = new ConfigEntry.Builder<Integer>()
|
||||
.setServersideShortName("renderDistanceRadius")
|
||||
.setMinDefaultMax(32, 128, 4096)
|
||||
.comment("" +
|
||||
"The radius of the mod's render distance. (measured in chunks)\n" +
|
||||
"On server changes the distance players will receive real-time updates for, if enabled." +
|
||||
"\n" +
|
||||
"Note for servers:\n" +
|
||||
"This setting does not prevent players from generating farther out.\n" +
|
||||
"If you want to limit performance impact, change rate limits\n" +
|
||||
"and thread count/runtime ratio settings instead.\n" +
|
||||
"It also does not affect the visuals on clients.")
|
||||
.setPerformance(EConfigEntryPerformance.HIGH)
|
||||
.setSide(EConfigEntryRelevantSide.BOTH)
|
||||
.build();
|
||||
|
||||
public ConfigEntry<EDhApiMaxHorizontalResolution> maxHorizontalResolution = new ConfigEntry.Builder<EDhApiMaxHorizontalResolution>()
|
||||
.set(EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.comment(""
|
||||
+ "What is the maximum detail LODs should be drawn at? \n"
|
||||
+ "Higher settings will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ EDhApiMaxHorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.BLOCK + ": render 256 LODs for each Chunk (width of one block). \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EDhApiMaxHorizontalResolution.CHUNK + "\n"
|
||||
+ "Highest Quality: " + EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public ConfigEntry<EDhApiVerticalQuality> verticalQuality = new ConfigEntry.Builder<EDhApiVerticalQuality>()
|
||||
.set(EDhApiVerticalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how well LODs will represent \n"
|
||||
+ "overhangs, caves, floating islands, etc. \n"
|
||||
+ "Higher options will make the world more accurate, but"
|
||||
+ "will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EDhApiVerticalQuality.HEIGHT_MAP + "\n"
|
||||
+ "Highest Quality: " + EDhApiVerticalQuality.EXTREME)
|
||||
.setPerformance(EConfigEntryPerformance.VERY_HIGH)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public ConfigEntry<EDhApiHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EDhApiHorizontalQuality>()
|
||||
.set(EDhApiHorizontalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how quickly LODs decrease in quality the further away they are. \n"
|
||||
+ "Higher settings will render higher quality fake chunks farther away, \n"
|
||||
+ "but will increase memory and GPU usage.")
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public ConfigEntry<EDhApiTransparency> transparency = new ConfigEntry.Builder<EDhApiTransparency>()
|
||||
.set(EDhApiTransparency.COMPLETE)
|
||||
.comment(""
|
||||
+ "How should LOD transparency be handled. \n"
|
||||
+ "\n"
|
||||
+ EDhApiTransparency.COMPLETE + ": LODs will render transparent. \n"
|
||||
+ EDhApiTransparency.FAKE + ": LODs will be opaque, but shaded to match the blocks underneath. \n"
|
||||
+ EDhApiTransparency.DISABLED + ": LODs will be opaque. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
|
||||
public ConfigEntry<EDhApiBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EDhApiBlocksToAvoid>()
|
||||
.set(EDhApiBlocksToAvoid.NON_COLLIDING)
|
||||
.comment(""
|
||||
+ "What blocks shouldn't be rendered as LODs? \n"
|
||||
+ "\n"
|
||||
+ EDhApiBlocksToAvoid.NONE + ": Represent all blocks in the LODs \n"
|
||||
+ EDhApiBlocksToAvoid.NON_COLLIDING + ": Only represent solid blocks in the LODs (tall grass, torches, etc. won't count for a LOD's height) \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public ConfigEntry<Boolean> tintWithAvoidedBlocks = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "Should the blocks underneath avoided blocks gain the color of the avoided block? \n"
|
||||
+ "\n"
|
||||
+ "True: a red flower will tint the grass below it red. \n"
|
||||
+ "False: skipped blocks will not change color of surface below them. "
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
// TODO fixme
|
||||
//public static ConfigEntry<Integer> lodBiomeBlending = new ConfigEntry.Builder<Integer>()
|
||||
// .setMinDefaultMax(0,1,7)
|
||||
// .comment(""
|
||||
// + "This is the same as vanilla Biome Blending settings for Lod area. \n"
|
||||
// + " Note that anything other than '0' will greatly effect Lod building time \n"
|
||||
// + " and increase triangle count. The cost on chunk generation speed is also \n"
|
||||
// + " quite large if set too high.\n"
|
||||
// + "\n"
|
||||
// + " '0' equals to Vanilla Biome Blending of '1x1' or 'OFF', \n"
|
||||
// + " '1' equals to Vanilla Biome Blending of '3x3', \n"
|
||||
// + " '2' equals to Vanilla Biome Blending of '5x5'...")
|
||||
// .build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final Debugging debugging = new Debugging();
|
||||
|
||||
public static class Debugging
|
||||
{
|
||||
public ConfigEntry<EDhApiRendererMode> rendererMode = new ConfigEntry.Builder<EDhApiRendererMode>()
|
||||
.set(EDhApiRendererMode.DEFAULT)
|
||||
.comment(""
|
||||
+ "What renderer is active? \n"
|
||||
+ "\n"
|
||||
+ EDhApiRendererMode.DEFAULT + ": Default lod renderer \n"
|
||||
+ EDhApiRendererMode.DEBUG + ": Debug testing renderer \n"
|
||||
+ EDhApiRendererMode.DISABLED + ": Disable rendering")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class _Client
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
public static ConfigCategory advanced = new ConfigCategory.Builder().set(Advanced.class).build();
|
||||
@@ -146,112 +321,6 @@ public class Config
|
||||
|
||||
public static class Quality
|
||||
{
|
||||
public static ConfigEntry<EDhApiMaxHorizontalResolution> maxHorizontalResolution = new ConfigEntry.Builder<EDhApiMaxHorizontalResolution>()
|
||||
.set(EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.comment(""
|
||||
+ "What is the maximum detail LODs should be drawn at? \n"
|
||||
+ "Higher settings will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ EDhApiMaxHorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.BLOCK + ": render 256 LODs for each Chunk (width of one block). \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EDhApiMaxHorizontalResolution.CHUNK + "\n"
|
||||
+ "Highest Quality: " + EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> lodChunkRenderDistanceRadius = new ConfigEntry.Builder<Integer>()
|
||||
.setServersideShortName("renderDistanceRadius")
|
||||
.setMinDefaultMax(32, 128, 4096)
|
||||
.comment("" +
|
||||
"The radius of the mod's render distance. (measured in chunks)\n" +
|
||||
"On server changes the distance players will receive real-time updates for, if enabled." +
|
||||
"\n" +
|
||||
"Note for servers:\n" +
|
||||
"This setting does not prevent players from generating farther out.\n" +
|
||||
"If you want to limit performance impact, change rate limits\n" +
|
||||
"and thread count/runtime ratio settings instead.\n" +
|
||||
"It also does not affect the visuals on clients.")
|
||||
.setPerformance(EConfigEntryPerformance.HIGH)
|
||||
.setSide(EConfigEntryRelevantSide.BOTH)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiVerticalQuality> verticalQuality = new ConfigEntry.Builder<EDhApiVerticalQuality>()
|
||||
.set(EDhApiVerticalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how well LODs will represent \n"
|
||||
+ "overhangs, caves, floating islands, etc. \n"
|
||||
+ "Higher options will make the world more accurate, but"
|
||||
+ "will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EDhApiVerticalQuality.HEIGHT_MAP + "\n"
|
||||
+ "Highest Quality: " + EDhApiVerticalQuality.EXTREME)
|
||||
.setPerformance(EConfigEntryPerformance.VERY_HIGH)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EDhApiHorizontalQuality>()
|
||||
.set(EDhApiHorizontalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how quickly LODs decrease in quality the further away they are. \n"
|
||||
+ "Higher settings will render higher quality fake chunks farther away, \n"
|
||||
+ "but will increase memory and GPU usage.")
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiTransparency> transparency = new ConfigEntry.Builder<EDhApiTransparency>()
|
||||
.set(EDhApiTransparency.COMPLETE)
|
||||
.comment(""
|
||||
+ "How should LOD transparency be handled. \n"
|
||||
+ "\n"
|
||||
+ EDhApiTransparency.COMPLETE + ": LODs will render transparent. \n"
|
||||
+ EDhApiTransparency.FAKE + ": LODs will be opaque, but shaded to match the blocks underneath. \n"
|
||||
+ EDhApiTransparency.DISABLED + ": LODs will be opaque. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EDhApiBlocksToAvoid>()
|
||||
.set(EDhApiBlocksToAvoid.NON_COLLIDING)
|
||||
.comment(""
|
||||
+ "What blocks shouldn't be rendered as LODs? \n"
|
||||
+ "\n"
|
||||
+ EDhApiBlocksToAvoid.NONE + ": Represent all blocks in the LODs \n"
|
||||
+ EDhApiBlocksToAvoid.NON_COLLIDING + ": Only represent solid blocks in the LODs (tall grass, torches, etc. won't count for a LOD's height) \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> tintWithAvoidedBlocks = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "Should the blocks underneath avoided blocks gain the color of the avoided block? \n"
|
||||
+ "\n"
|
||||
+ "True: a red flower will tint the grass below it red. \n"
|
||||
+ "False: skipped blocks will not change color of surface below them. "
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
// TODO fixme
|
||||
// public static ConfigEntry<Integer> lodBiomeBlending = new ConfigEntry.Builder<Integer>()
|
||||
// .setMinDefaultMax(0,1,7)
|
||||
// .comment(""
|
||||
// + "This is the same as vanilla Biome Blending settings for Lod area. \n"
|
||||
// + " Note that anything other than '0' will greatly effect Lod building time \n"
|
||||
// + " and increase triangle count. The cost on chunk generation speed is also \n"
|
||||
// + " quite large if set too high.\n"
|
||||
// + "\n"
|
||||
// + " '0' equals to Vanilla Biome Blending of '1x1' or 'OFF', \n"
|
||||
// + " '1' equals to Vanilla Biome Blending of '3x3', \n"
|
||||
// + " '2' equals to Vanilla Biome Blending of '5x5'...")
|
||||
// .build();
|
||||
}
|
||||
|
||||
public static class Fog
|
||||
@@ -574,14 +643,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> enableInstancedRendering = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "Can be disabled to use much slower but more compatible direct rendering. \n"
|
||||
+ "Disabling this can be used to fix some crashes on Mac. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class AdvancedGraphics
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.seibel.distanthorizons.core.config;
|
||||
|
||||
public interface IConfigProvider
|
||||
{
|
||||
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class RenderCacheConfigEventHandler
|
||||
// create a new timer task
|
||||
TimerTask timerTask = new TimerTask()
|
||||
{
|
||||
public void run()
|
||||
@Override public void run()
|
||||
{
|
||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ResetConfigEventHandler
|
||||
/** private since we only ever need one handler at a time */
|
||||
private ResetConfigEventHandler()
|
||||
{
|
||||
this.configChangeListener = new ConfigChangeListener<>(Config.Client.ResetConfirmation.resetAllSettings, (resetSettings) -> { doStuff(resetSettings); });
|
||||
this.configChangeListener = new ConfigChangeListener<>(Config.Client.ResetConfirmation.resetAllSettings, (resetSettings) -> { this.doStuff(resetSettings); });
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Handles reading and writing config files.
|
||||
@@ -54,9 +53,6 @@ public class ConfigFileHandling
|
||||
/** This is the object for night-config */
|
||||
private final CommentedFileConfig nightConfig;
|
||||
|
||||
/** prevents readers/writers from overlapping and causing the config file from being duplicated or corrupted */
|
||||
private final ReentrantLock readWriteLock = new ReentrantLock();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
@@ -76,130 +72,28 @@ public class ConfigFileHandling
|
||||
|
||||
|
||||
/** Saves the entire config to the file */
|
||||
public void saveToFile() { this.saveToFile(this.nightConfig); }
|
||||
public void saveToFile()
|
||||
{
|
||||
saveToFile(this.nightConfig);
|
||||
}
|
||||
/** Saves the entire config to the file */
|
||||
public void saveToFile(CommentedFileConfig nightConfig)
|
||||
{
|
||||
try
|
||||
if (!Files.exists(configPath)) // Try to check if the config exists
|
||||
{
|
||||
this.readWriteLock.lock();
|
||||
|
||||
|
||||
|
||||
if (!Files.exists(this.configPath)) // Try to check if the config exists
|
||||
{
|
||||
reCreateFile(this.configPath);
|
||||
}
|
||||
|
||||
|
||||
this.loadNightConfig(nightConfig);
|
||||
|
||||
|
||||
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
|
||||
{
|
||||
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
|
||||
{
|
||||
this.createComment((ConfigEntry<?>) entry, nightConfig);
|
||||
this.saveEntry((ConfigEntry<?>) entry, nightConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
nightConfig.save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If it fails to save, crash game
|
||||
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.readWriteLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the entire config from the file
|
||||
*
|
||||
* @apiNote This overwrites any value currently stored in the config
|
||||
*/
|
||||
public void loadFromFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.readWriteLock.lock();
|
||||
|
||||
int currentCfgVersion = this.configBase.configVersion;
|
||||
try
|
||||
{
|
||||
// Dont load the real `this.nightConfig`, instead create a tempoary one
|
||||
CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
|
||||
tmpNightConfig.load();
|
||||
// Attempt to get the version number
|
||||
currentCfgVersion = (Integer) tmpNightConfig.get("_version");
|
||||
tmpNightConfig.close();
|
||||
}
|
||||
catch (Exception ignored) { }
|
||||
|
||||
if (currentCfgVersion == this.configBase.configVersion)
|
||||
{
|
||||
// handle normally
|
||||
}
|
||||
else if (currentCfgVersion > this.configBase.configVersion)
|
||||
{
|
||||
this.logger.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + this.configBase.configVersion + "]. You may have downgraded the mod and items may have been moved, you have been warned");
|
||||
}
|
||||
else // if (currentCfgVersion < configBase.configVersion)
|
||||
{
|
||||
this.logger.warn(this.configBase.modName + " config is of an older version, currently there is no config updater... so resetting config");
|
||||
try
|
||||
{
|
||||
Files.delete(this.configPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.loadFromFile(this.nightConfig);
|
||||
this.nightConfig.set("_version", this.configBase.configVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.readWriteLock.unlock();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loads the entire config from the file
|
||||
*
|
||||
* @apiNote This overwrites any value currently stored in the config
|
||||
*/
|
||||
private void loadFromFile(CommentedFileConfig nightConfig)
|
||||
{
|
||||
// Attempt to load the file and if it fails then save config to file
|
||||
if (Files.exists(this.configPath))
|
||||
{
|
||||
this.loadNightConfig(nightConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
reCreateFile(this.configPath);
|
||||
reCreateFile(configPath);
|
||||
}
|
||||
|
||||
|
||||
// Load all the entries
|
||||
loadNightConfig(nightConfig);
|
||||
|
||||
|
||||
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
|
||||
{
|
||||
if (ConfigEntry.class.isAssignableFrom(entry.getClass())
|
||||
&& entry.getAppearance().showInFile)
|
||||
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
|
||||
{
|
||||
this.createComment((ConfigEntry<?>) entry, nightConfig);
|
||||
this.loadEntry((ConfigEntry<?>) entry, nightConfig);
|
||||
createComment((ConfigEntry<?>) entry, nightConfig);
|
||||
saveEntry((ConfigEntry<?>) entry, nightConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +105,92 @@ public class ConfigFileHandling
|
||||
catch (Exception e)
|
||||
{
|
||||
// If it fails to save, crash game
|
||||
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e);
|
||||
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the entire config from the file
|
||||
*
|
||||
* @apiNote This overwrites any value currently stored in the config
|
||||
*/
|
||||
public void loadFromFile()
|
||||
{
|
||||
int currentCfgVersion = this.configBase.configVersion;
|
||||
try
|
||||
{
|
||||
// Dont load the real `this.nightConfig`, instead create a tempoary one
|
||||
CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
|
||||
tmpNightConfig.load();
|
||||
// Attempt to get the version number
|
||||
currentCfgVersion = (Integer) tmpNightConfig.get("_version");
|
||||
tmpNightConfig.close();
|
||||
} catch (Exception ignored) { }
|
||||
|
||||
if (currentCfgVersion == this.configBase.configVersion)
|
||||
{
|
||||
// handle normally
|
||||
}
|
||||
else if (currentCfgVersion > this.configBase.configVersion)
|
||||
{
|
||||
this.logger.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + this.configBase.configVersion + "]. You may have downgraded the mod and items may have been moved, you have been warned");
|
||||
}
|
||||
else // if (currentCfgVersion < configBase.configVersion)
|
||||
{
|
||||
this.logger.warn(this.configBase.modName +" config is of an older version, currently there is no config updater... so resetting config");
|
||||
try
|
||||
{
|
||||
Files.delete(this.configPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.loadFromFile(this.nightConfig);
|
||||
this.nightConfig.set("_version", this.configBase.configVersion);
|
||||
}
|
||||
/**
|
||||
* Loads the entire config from the file
|
||||
*
|
||||
* @apiNote This overwrites any value currently stored in the config
|
||||
*/
|
||||
public void loadFromFile(CommentedFileConfig nightConfig)
|
||||
{
|
||||
// Attempt to load the file and if it fails then save config to file
|
||||
if (Files.exists(configPath))
|
||||
{
|
||||
loadNightConfig(nightConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
reCreateFile(configPath);
|
||||
}
|
||||
|
||||
|
||||
// Load all the entries
|
||||
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
|
||||
{
|
||||
if (
|
||||
ConfigEntry.class.isAssignableFrom(entry.getClass()) &&
|
||||
entry.getAppearance().showInFile
|
||||
)
|
||||
{
|
||||
createComment((ConfigEntry<?>) entry, nightConfig);
|
||||
loadEntry((ConfigEntry<?>) entry, nightConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
nightConfig.save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If it fails to save, crash game
|
||||
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
package com.seibel.distanthorizons.core.config.types;
|
||||
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Table;
|
||||
import com.seibel.distanthorizons.core.config.NumberUtil;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||
@@ -28,9 +32,11 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformanc
|
||||
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryRelevantSide;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Use for making the config variables
|
||||
@@ -135,9 +141,9 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
@Override
|
||||
public T get()
|
||||
{
|
||||
if (allowApiOverride && apiValue != null)
|
||||
if (this.allowApiOverride && this.apiValue != null)
|
||||
{
|
||||
return apiValue;
|
||||
return this.apiValue;
|
||||
}
|
||||
|
||||
return super.get();
|
||||
@@ -186,8 +192,14 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
public void clampWithinRange(T min, T max)
|
||||
{
|
||||
byte validness = this.isValid(min, max);
|
||||
if (validness == -1) this.value = (T) NumberUtil.getMinimum(this.value.getClass());
|
||||
if (validness == 1) this.value = (T) NumberUtil.getMaximum(this.value.getClass());
|
||||
if (validness == -1)
|
||||
{
|
||||
this.value = (T) NumberUtil.getMinimum(this.value.getClass());
|
||||
}
|
||||
if (validness == 1)
|
||||
{
|
||||
this.value = (T) NumberUtil.getMaximum(this.value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO is this for command line use?
|
||||
@@ -204,7 +216,7 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
public EConfigEntryRelevantSide getRelevantSide() { return this.relevantSide; }
|
||||
|
||||
/** Fired whenever the config value changes to a new value. */
|
||||
public void addValueChangeListener(Consumer<T> onValueChangeFunc)
|
||||
@Override public void addValueChangeListener(Consumer<T> onValueChangeFunc)
|
||||
{
|
||||
ConfigChangeListener<T> changeListener = new ConfigChangeListener<>(this, onValueChangeFunc);
|
||||
this.addListener(changeListener);
|
||||
@@ -235,7 +247,7 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
* <p> -1 == number too low
|
||||
*/
|
||||
@Override
|
||||
public byte isValid() { return isValid(this.value, this.min, this.max); }
|
||||
public byte isValid() { return this.isValid(this.value, this.min, this.max); }
|
||||
/**
|
||||
* Checks if a new value is valid
|
||||
*
|
||||
@@ -307,88 +319,136 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
}
|
||||
|
||||
/** This should normally not be called since set() automatically calls this */
|
||||
public void save() { configBase.configFileINSTANCE.saveEntry(this); }
|
||||
public void save() { this.configBase.configFileINSTANCE.saveEntry(this); }
|
||||
/** This should normally not be called except for special circumstances */
|
||||
public void load() { configBase.configFileINSTANCE.loadEntry(this); }
|
||||
public void load() { this.configBase.configFileINSTANCE.loadEntry(this); }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(IConfigEntry<?> obj) { return obj.getClass() == ConfigEntry.class && equals((ConfigEntry<?>) obj); }
|
||||
public boolean equals(IConfigEntry<?> obj) { return obj.getClass() == ConfigEntry.class && this.equals((ConfigEntry<?>) obj); }
|
||||
/** Is the value of this equal to another */
|
||||
public boolean equals(ConfigEntry<?> obj)
|
||||
{
|
||||
// Can all of this just be "return this.value.equals(obj.value)"?
|
||||
|
||||
if (Number.class.isAssignableFrom(this.value.getClass()))
|
||||
{
|
||||
return this.value == obj.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.value.equals(obj.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Builder<T> extends AbstractConfigType.Builder<T, Builder<T>>
|
||||
public static class Builder<TValue> extends AbstractConfigType.Builder<TValue, Builder<TValue>>
|
||||
{
|
||||
private String tmpComment = null;
|
||||
private T tmpMin = null;
|
||||
private T tmpMax = null;
|
||||
private TValue tmpMin = null;
|
||||
private TValue tmpMax = null;
|
||||
protected String tmpServersideShortName = null;
|
||||
private boolean tmpUseApiOverwrite = true;
|
||||
private EConfigEntryPerformance tmpPerformance = EConfigEntryPerformance.DONT_SHOW;
|
||||
private EConfigEntryRelevantSide tmpRelevantSide = EConfigEntryRelevantSide.CLIENT;
|
||||
protected ArrayList<IConfigListener> tmpIConfigListener = new ArrayList<>();
|
||||
|
||||
public Builder<T> comment(String newComment)
|
||||
private Supplier<TValue> computedValueGetter;
|
||||
private Consumer<TValue> computedValueSetter;
|
||||
private boolean preventAutoApplyUntilExit;
|
||||
|
||||
public Builder<TValue> comment(String newComment)
|
||||
{
|
||||
this.tmpComment = newComment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<TValue> computed(Supplier<TValue> computedValueGetter, Consumer<TValue> computedValueSetter)
|
||||
{
|
||||
this.computedValueGetter = computedValueGetter;
|
||||
this.computedValueSetter = computedValueSetter;
|
||||
this.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<TValue> linkedTo(DeferredAccessor<TValue> otherConfigAccessor)
|
||||
{
|
||||
return this.computed(
|
||||
otherConfigAccessor::get,
|
||||
otherConfigAccessor::set
|
||||
);
|
||||
}
|
||||
|
||||
public Builder<TValue> preset(PresetBuilder<TValue> presetBuilder)
|
||||
{
|
||||
return this.computed(
|
||||
() -> {
|
||||
List<TValue> values = presetBuilder.presetValues.entrySet().stream()
|
||||
.map(entry -> entry.getValue().inverse().get(entry.getKey().get()))
|
||||
.distinct().limit(2)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (values.size() != 1)
|
||||
{
|
||||
return presetBuilder.customStateValue;
|
||||
}
|
||||
|
||||
return values.get(0);
|
||||
},
|
||||
value -> {
|
||||
for (Map.Entry<DeferredAccessor<Object>, BiMap<TValue, Object>> entry : presetBuilder.presetValues.entrySet())
|
||||
{
|
||||
entry.getKey().set(entry.getValue().get(value));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** Allows most values to be set by 1 setter */
|
||||
public Builder<T> setMinDefaultMax(T newMin, T newDefault, T newMax)
|
||||
public Builder<TValue> setMinDefaultMax(TValue newMin, TValue newDefault, TValue newMax)
|
||||
{
|
||||
this.set(newDefault);
|
||||
this.setMinMax(newMin, newMax);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setMinMax(T newMin, T newMax)
|
||||
public Builder<TValue> setMinMax(TValue newMin, TValue newMax)
|
||||
{
|
||||
this.tmpMin = newMin;
|
||||
this.tmpMax = newMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setMin(T newMin)
|
||||
public Builder<TValue> setMin(TValue newMin)
|
||||
{
|
||||
this.tmpMin = newMin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setMax(T newMax)
|
||||
public Builder<TValue> setMax(TValue newMax)
|
||||
{
|
||||
this.tmpMax = newMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setServersideShortName(String name)
|
||||
public Builder<TValue> setServersideShortName(String name)
|
||||
{
|
||||
this.tmpServersideShortName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setUseApiOverwrite(boolean newUseApiOverwrite)
|
||||
public Builder<TValue> setUseApiOverwrite(boolean newUseApiOverwrite)
|
||||
{
|
||||
this.tmpUseApiOverwrite = newUseApiOverwrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setPerformance(EConfigEntryPerformance newPerformance)
|
||||
public Builder<TValue> setPerformance(EConfigEntryPerformance newPerformance)
|
||||
{
|
||||
this.tmpPerformance = newPerformance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setSide(EConfigEntryRelevantSide relevantSide)
|
||||
public Builder<TValue> setSide(EConfigEntryRelevantSide relevantSide)
|
||||
{
|
||||
this.tmpRelevantSide = relevantSide;
|
||||
return this;
|
||||
@@ -396,25 +456,25 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
|
||||
|
||||
|
||||
public Builder<T> replaceListeners(ArrayList<IConfigListener> newConfigListener)
|
||||
public Builder<TValue> replaceListeners(ArrayList<IConfigListener> newConfigListener)
|
||||
{
|
||||
this.tmpIConfigListener = newConfigListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> addListeners(IConfigListener... newConfigListener)
|
||||
public Builder<TValue> addListeners(IConfigListener... newConfigListener)
|
||||
{
|
||||
this.tmpIConfigListener.addAll(Arrays.asList(newConfigListener));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> addListener(IConfigListener newConfigListener)
|
||||
public Builder<TValue> addListener(IConfigListener newConfigListener)
|
||||
{
|
||||
this.tmpIConfigListener.add(newConfigListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> clearListeners()
|
||||
public Builder<TValue> clearListeners()
|
||||
{
|
||||
this.tmpIConfigListener.clear();
|
||||
return this;
|
||||
@@ -422,7 +482,7 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
|
||||
|
||||
|
||||
public ConfigEntry<T> build()
|
||||
public ConfigEntry<TValue> build()
|
||||
{
|
||||
return new ConfigEntry<>(
|
||||
this.tmpAppearance,
|
||||
@@ -433,4 +493,46 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps deferred accesses to a ConfigEntry. <br>
|
||||
* It replaces the direct uses of ConfigEntry in places where Java runtime should not attempt
|
||||
* to dereference path to said entry, until initialization is done.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DeferredAccessor<T>
|
||||
{
|
||||
ConfigEntry<T> getEntry();
|
||||
|
||||
default T get() { return this.getEntry().get(); }
|
||||
default void set(T value) { this.getEntry().set(value); }
|
||||
|
||||
}
|
||||
|
||||
public static class PresetBuilder<TValue>
|
||||
{
|
||||
public final TValue customStateValue;
|
||||
public final Map<DeferredAccessor<Object>, BiMap<TValue, Object>> presetValues = new HashMap<>();
|
||||
|
||||
public PresetBuilder(TValue customStateValue)
|
||||
{
|
||||
this.customStateValue = customStateValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TControlledEntryValue> PresetBuilder<TValue> addConfigEntry(
|
||||
DeferredAccessor<TControlledEntryValue> controlledEntrySupplier,
|
||||
Consumer<Map<TValue, TControlledEntryValue>> presetValueInitializer
|
||||
)
|
||||
{
|
||||
presetValueInitializer.accept(
|
||||
(Map<TValue, TControlledEntryValue>) this.presetValues.computeIfAbsent(
|
||||
(DeferredAccessor<Object>) controlledEntrySupplier,
|
||||
ignored -> HashBiMap.create()
|
||||
)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.config.types;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance;
|
||||
|
||||
/**
|
||||
* Creates a UI element that copies everything from another element.
|
||||
* This only effects the UI
|
||||
*
|
||||
* @author coolGi
|
||||
*/
|
||||
@Deprecated // FIXME doesn't work with localization
|
||||
public class ConfigLinkedEntry extends AbstractConfigType<AbstractConfigType<?, ?>, ConfigLinkedEntry>
|
||||
{
|
||||
public ConfigLinkedEntry(AbstractConfigType<?, ?> value)
|
||||
{
|
||||
super(EConfigEntryAppearance.ONLY_IN_GUI, value);
|
||||
}
|
||||
|
||||
/** Appearance shouldn't be changed */
|
||||
@Override
|
||||
public void setAppearance(EConfigEntryAppearance newAppearance) { }
|
||||
|
||||
/** Value shouldn't be changed after creation */
|
||||
@Override
|
||||
public void set(AbstractConfigType<?, ?> newValue) { }
|
||||
|
||||
|
||||
public static class Builder extends AbstractConfigType.Builder<AbstractConfigType<?, ?>, Builder>
|
||||
{
|
||||
/** Appearance shouldn't be changed */
|
||||
@Override
|
||||
public Builder setAppearance(EConfigEntryAppearance newAppearance)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigLinkedEntry build()
|
||||
{
|
||||
return new ConfigLinkedEntry(this.tmpValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -923,7 +923,6 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
@Override
|
||||
public String toString() { return DhSectionPos.toString(this.pos); }
|
||||
|
||||
/** Only includes the base data in this object, not the mapping */
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
|
||||
@@ -67,9 +67,13 @@ public final class BufferQuad
|
||||
EDhDirection direction)
|
||||
{
|
||||
if (widthEastWest == 0 || widthNorthSouthOrUpDown == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Size 0 quad!");
|
||||
}
|
||||
if (widthEastWest < 0 || widthNorthSouthOrUpDown < 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Negative sized quad!");
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
@@ -95,7 +99,9 @@ public final class BufferQuad
|
||||
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
||||
{
|
||||
if (this.direction != quad.direction)
|
||||
{
|
||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
||||
}
|
||||
|
||||
if (compareDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
@@ -150,11 +156,15 @@ public final class BufferQuad
|
||||
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
||||
{
|
||||
if (quad.hasError || this.hasError)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// only merge quads that are in the same direction
|
||||
if (this.direction != quad.direction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure these quads share the same perpendicular axis
|
||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y) ||
|
||||
|
||||
@@ -191,14 +191,14 @@ public class LodQuadBuilder
|
||||
public void addQuadDown(short x, short y, short z, short width, short wz, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
|
||||
{
|
||||
BufferQuad quad = new BufferQuad(x, y, z, width, wz, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.DOWN);
|
||||
ArrayList<BufferQuad> qs = (doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||
? transparentQuads[EDhDirection.DOWN.ordinal()] : opaqueQuads[EDhDirection.DOWN.ordinal()];
|
||||
ArrayList<BufferQuad> qs = (this.doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||
? this.transparentQuads[EDhDirection.DOWN.ordinal()] : this.opaqueQuads[EDhDirection.DOWN.ordinal()];
|
||||
if (!qs.isEmpty()
|
||||
&& (qs.get(qs.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||
|| qs.get(qs.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
|
||||
)
|
||||
{
|
||||
premergeCount++;
|
||||
this.premergeCount++;
|
||||
return;
|
||||
}
|
||||
qs.add(quad);
|
||||
@@ -251,7 +251,9 @@ public class LodQuadBuilder
|
||||
private static long mergeQuadsInternal(ArrayList<BufferQuad>[] list, int directionIndex, BufferMergeDirectionEnum mergeDirection)
|
||||
{
|
||||
if (list[directionIndex].size() <= 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
list[directionIndex].sort((objOne, objTwo) -> objOne.compare(objTwo, mergeDirection));
|
||||
|
||||
@@ -423,9 +425,18 @@ public class LodQuadBuilder
|
||||
// 0b01 = positive offset
|
||||
// 0b11 = negative offset
|
||||
// format is: 0b00zzyyxx
|
||||
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11;
|
||||
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100;
|
||||
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000;
|
||||
if (mx != 0)
|
||||
{
|
||||
mirco |= mx > 0 ? 0b01 : 0b11;
|
||||
}
|
||||
if (my != 0)
|
||||
{
|
||||
mirco |= my > 0 ? 0b0100 : 0b1100;
|
||||
}
|
||||
if (mz != 0)
|
||||
{
|
||||
mirco |= mz > 0 ? 0b010000 : 0b110000;
|
||||
}
|
||||
meta |= mirco << 8;
|
||||
|
||||
bb.putShort(meta);
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV1DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV1Repo;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
@@ -65,7 +64,7 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
{
|
||||
try
|
||||
{
|
||||
return new FullDataSourceV1Repo(AbstractDhRepo.DEFAULT_DATABASE_TYPE, new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME));
|
||||
return new FullDataSourceV1Repo("jdbc:sqlite", new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME));
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,6 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
@@ -48,11 +47,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Handles reading/writing {@link FullDataSourceV2}
|
||||
@@ -158,7 +160,7 @@ public class FullDataSourceProviderV2
|
||||
{
|
||||
try
|
||||
{
|
||||
return new FullDataSourceV2Repo(AbstractDhRepo.DEFAULT_DATABASE_TYPE, new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME));
|
||||
return new FullDataSourceV2Repo("jdbc:sqlite", new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME));
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
|
||||
@@ -28,10 +28,8 @@ import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
||||
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
|
||||
import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.level.WorldGenModule;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
@@ -420,13 +418,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
|
||||
|
||||
/** used by external event listeners */
|
||||
@FunctionalInterface
|
||||
public interface IOnWorldGenCompleteListener
|
||||
{
|
||||
boolean shouldDoWorldGen();
|
||||
|
||||
@Nullable
|
||||
DhBlockPos2D getTargetPosForGeneration();
|
||||
|
||||
/** Fired whenever a section has completed generating */
|
||||
void onWorldGenTaskComplete(long pos);
|
||||
|
||||
|
||||
@@ -523,8 +523,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
// getters / setters //
|
||||
//===================//
|
||||
|
||||
public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
||||
public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); }
|
||||
@Override public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
||||
@Override public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); }
|
||||
|
||||
@Override
|
||||
public byte lowestDataDetail() { return this.lowestDataDetail; }
|
||||
@@ -536,7 +536,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
@Override
|
||||
public void setEstimatedTotalTaskCount(int newEstimate) { this.estimatedTotalTaskCount = newEstimate; }
|
||||
|
||||
public void addDebugMenuStringsToList(List<String> messageList) { }
|
||||
@Override public void addDebugMenuStringsToList(List<String> messageList) { }
|
||||
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
// shutdown //
|
||||
//==========//
|
||||
|
||||
public CompletableFuture<Void> startClosingAsync(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
|
||||
@Override public CompletableFuture<Void> startClosingAsync(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
|
||||
{
|
||||
LOGGER.info("Closing world gen queue");
|
||||
this.queueingThread.shutdownNow();
|
||||
@@ -671,13 +671,17 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
{
|
||||
index = 4 * X * X - X - Y;
|
||||
if (X < Y)
|
||||
{
|
||||
index = index - 2 * (X - Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
index = 4 * Y * Y - X - Y;
|
||||
if (X < Y)
|
||||
{
|
||||
index = index + 2 * (X - Y);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandle
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||
import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
@@ -86,7 +85,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
ChunkHashRepo newChunkHashRepo = null;
|
||||
try
|
||||
{
|
||||
newChunkHashRepo = new ChunkHashRepo(AbstractDhRepo.DEFAULT_DATABASE_TYPE, databaseFile);
|
||||
newChunkHashRepo = new ChunkHashRepo("jdbc:sqlite", databaseFile);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
@@ -99,7 +98,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
BeaconBeamRepo newBeaconBeamRepo = null;
|
||||
try
|
||||
{
|
||||
newBeaconBeamRepo = new BeaconBeamRepo(AbstractDhRepo.DEFAULT_DATABASE_TYPE, databaseFile);
|
||||
newBeaconBeamRepo = new BeaconBeamRepo("jdbc:sqlite", databaseFile);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
|
||||
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AbstractDhServerLevel extends AbstractDhLevel implements IDhServerLevel
|
||||
{
|
||||
protected static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConfigBasedLogger NETWORK_LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Logging.logNetworkEvent.get());
|
||||
|
||||
/** 1 Mebibyte minus 576 bytes for other info */
|
||||
public static final int FULL_DATA_SPLIT_SIZE_IN_BYTES = 1_048_000;
|
||||
|
||||
public final ServerLevelModule serverside;
|
||||
protected final IServerLevelWrapper serverLevelWrapper;
|
||||
|
||||
protected final ServerPlayerStateManager serverPlayerStateManager;
|
||||
|
||||
/**
|
||||
* This queue is used for ensuring fair generation speed for each player. <br>
|
||||
* Every tick the first player gets used for centering generation, and then is immediately moved into the back of the queue. <br>
|
||||
* TODO only add players that actually have something to generate
|
||||
*/
|
||||
protected final ConcurrentLinkedQueue<IServerPlayerWrapper> worldGenPlayerCenteringQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final ConcurrentMap<Long, DataSourceRequestGroup> requestGroupByPos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Long, DataSourceRequestGroup> requestGroupByFutureId = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public AbstractDhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, ServerPlayerStateManager serverPlayerStateManager)
|
||||
{
|
||||
this(saveStructure, serverLevelWrapper, serverPlayerStateManager, true);
|
||||
}
|
||||
public AbstractDhServerLevel(
|
||||
AbstractSaveStructure saveStructure,
|
||||
IServerLevelWrapper serverLevelWrapper,
|
||||
ServerPlayerStateManager serverPlayerStateManager,
|
||||
boolean runRepoReliantSetup
|
||||
)
|
||||
{
|
||||
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
|
||||
{
|
||||
LOGGER.warn("unable to create data folder.");
|
||||
}
|
||||
this.serverLevelWrapper = serverLevelWrapper;
|
||||
this.serverside = new ServerLevelModule(this, saveStructure);
|
||||
this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
|
||||
if (runRepoReliantSetup)
|
||||
{
|
||||
this.runRepoReliantSetup();
|
||||
}
|
||||
|
||||
LOGGER.info("Started ${this.getClass().getSimpleName()} for $serverLevelWrapper at $saveStructure.");
|
||||
|
||||
this.serverPlayerStateManager = serverPlayerStateManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// ticks //
|
||||
//=======//
|
||||
|
||||
@Override
|
||||
public void serverTick()
|
||||
{
|
||||
// Send finished data source requests
|
||||
for (Map.Entry<Long, DataSourceRequestGroup> entry : this.requestGroupByPos.entrySet())
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = entry.getValue();
|
||||
|
||||
if (requestGroup.fullDataSource == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NETWORK_LOGGER.debug("[${this.serverLevelWrapper.getDimensionName()}] Fulfilled request group [${entry.getKey()}]");
|
||||
|
||||
// Make this group unavailable for adding into
|
||||
this.requestGroupByPos.remove(entry.getKey());
|
||||
requestGroup.requestRemoveSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null");
|
||||
continue;
|
||||
}
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource);
|
||||
for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values())
|
||||
{
|
||||
this.requestGroupByFutureId.remove(msg.futureId);
|
||||
|
||||
ServerPlayerState serverPlayerState = this.serverPlayerStateManager.getConnectedPlayer(msg.serverPlayer());
|
||||
if (serverPlayerState == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release();
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, msg.getSession()::sendMessage);
|
||||
msg.sendResponse(new FullDataSourceResponseMessage(payload));
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDoWorldGen()
|
||||
{
|
||||
return Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get() && !this.worldGenPlayerCenteringQueue.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DhBlockPos2D getTargetPosForGeneration()
|
||||
{
|
||||
IServerPlayerWrapper firstPlayer = this.worldGenPlayerCenteringQueue.peek();
|
||||
if (firstPlayer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Put first player in back before removing from front, so it can be removed by other thread without blocking
|
||||
// - if it gets removed, remove() below will remove the item we just put instead
|
||||
this.worldGenPlayerCenteringQueue.add(firstPlayer);
|
||||
this.worldGenPlayerCenteringQueue.remove(firstPlayer);
|
||||
|
||||
Vec3d position = firstPlayer.getPosition();
|
||||
return new DhBlockPos2D((int) position.x, (int) position.z);
|
||||
}
|
||||
|
||||
@Override public void worldGenTick()
|
||||
{
|
||||
this.serverside.worldGenModule.worldGenTick();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// network handling //
|
||||
//==================//
|
||||
|
||||
public void registerNetworkHandlers(ServerPlayerState serverPlayerState)
|
||||
{
|
||||
serverPlayerState.networkSession.registerHandler(FullDataSourceRequestMessage.class, (message) ->
|
||||
{
|
||||
if (!this.messagePlayerInThisLevel(message))
|
||||
{
|
||||
// we can't handle players in other levels, don't continue
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this);
|
||||
|
||||
if (message.clientTimestamp == null)
|
||||
{
|
||||
this.queueWorldGenForRequestMessage(serverPlayerState, message, rateLimiterSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.queueLodSyncForRequestMessage(serverPlayerState, message, rateLimiterSet);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
serverPlayerState.networkSession.registerHandler(CancelMessage.class, msg ->
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByFutureId.remove(msg.futureId);
|
||||
if (requestGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If this fails, the group is being removed and completing cancellation is not necessary
|
||||
if (requestGroup.requestRemoveSemaphore.tryAcquire())
|
||||
{
|
||||
// Prevent adding requests in case the group will be removed by this cancellation
|
||||
requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
requestGroup.requestRemoveSemaphore.release();
|
||||
|
||||
serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release();
|
||||
|
||||
FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId);
|
||||
if (requestGroup.requestMessages.isEmpty())
|
||||
{
|
||||
NETWORK_LOGGER.debug("[${this.serverLevelWrapper.getDimensionName()}] Cancelled request group [${DhSectionPos.toString(requestMessage.sectionPos)}].");
|
||||
this.requestGroupByPos.remove(requestMessage.sectionPos);
|
||||
this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestGroup.requestAddSemaphore.release(Short.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
private void queueLodSyncForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet)
|
||||
{
|
||||
if (!serverPlayerState.sessionConfig.getSynchronizeOnLogin())
|
||||
{
|
||||
message.sendResponse(new RequestRejectedException("Operation is disabled in config."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiterSet.syncOnLoginRateLimiter.tryAcquire(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// the client timestamp will be null if we want to retrieve the LOD regardless of when it was last updated
|
||||
long clientTimestamp = (message.clientTimestamp != null) ? message.clientTimestamp : -1;
|
||||
// the server timestamp will be null if no LOD data exists for this position
|
||||
Long serverTimestamp = this.serverside.fullDataFileHandler.getTimestampForPos(message.sectionPos);
|
||||
if (serverTimestamp == null
|
||||
|| serverTimestamp <= clientTimestamp)
|
||||
{
|
||||
// either no data exists to sync, or the client is already up to date
|
||||
rateLimiterSet.syncOnLoginRateLimiter.release();
|
||||
message.sendResponse(new FullDataSourceResponseMessage(null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null");
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverside.fullDataFileHandler.getAsync(message.sectionPos).thenAcceptAsync(fullDataSource ->
|
||||
{
|
||||
rateLimiterSet.syncOnLoginRateLimiter.release();
|
||||
|
||||
FullDataPayload payload = new FullDataPayload(fullDataSource);
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, message.getSession()::sendMessage);
|
||||
message.sendResponse(new FullDataSourceResponseMessage(payload));
|
||||
}, executor);
|
||||
}
|
||||
private void queueWorldGenForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet)
|
||||
{
|
||||
if (!serverPlayerState.sessionConfig.isDistantGenerationEnabled())
|
||||
{
|
||||
message.sendResponse(new RequestRejectedException("Operation is disabled in config."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiterSet.generationRequestRateLimiter.tryAcquire(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByPos.computeIfAbsent(message.sectionPos, pos ->
|
||||
{
|
||||
DataSourceRequestGroup newGroup = new DataSourceRequestGroup();
|
||||
this.tryFulfillDataSourceRequestGroup(newGroup, pos);
|
||||
NETWORK_LOGGER.debug("[${serverLevelWrapper.getDimensionName()}] Created request group for pos [${DhSectionPos.toString(pos)}].");
|
||||
return newGroup;
|
||||
});
|
||||
|
||||
// If this fails, loop until either a permit is acquired or the group is removed to create another one
|
||||
if (!requestGroup.requestAddSemaphore.tryAcquire())
|
||||
{
|
||||
Thread.yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
this.requestGroupByFutureId.put(message.futureId, requestGroup);
|
||||
requestGroup.requestMessages.put(message.futureId, message);
|
||||
requestGroup.requestAddSemaphore.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** May send an error message in response if the message is a {@link AbstractTrackableMessage} */
|
||||
private <T extends AbstractNetworkMessage> boolean messagePlayerInThisLevel(T message)
|
||||
{
|
||||
if (!(message instanceof ILevelRelatedMessage))
|
||||
{
|
||||
LodUtil.assertNotReach("Received message [$message] does not implement [${ILevelRelatedMessage.class.getSimpleName()}]");
|
||||
}
|
||||
|
||||
// Only handle requests for this level
|
||||
if (!((ILevelRelatedMessage) message).isSameLevelAs(this.getServerLevelWrapper()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
||||
|
||||
// Check if the player is in this dimension,
|
||||
// since handling multiple dimensions isn't allowed
|
||||
if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper())
|
||||
{
|
||||
// If the message can be replied to - reply with an error, otherwise just ignore
|
||||
if (message instanceof AbstractTrackableMessage)
|
||||
{
|
||||
((AbstractTrackableMessage) message).sendResponse(
|
||||
new InvalidLevelException(
|
||||
"Generation not allowed. " +
|
||||
"Requested dimension: [${((ILevelRelatedMessage) message).getLevelName()}], " +
|
||||
"player dimension: [${message.getSession().serverPlayer.getLevel().getDimensionName()}], " +
|
||||
"handler dimension: [${this.getLevelWrapper().getDimensionName()}]"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// world gen //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public void onWorldGenTaskComplete(long pos)
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByPos.get(pos);
|
||||
if (requestGroup != null)
|
||||
{
|
||||
this.tryFulfillDataSourceRequestGroup(requestGroup, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryFulfillDataSourceRequestGroup(DataSourceRequestGroup requestGroup, long pos)
|
||||
{
|
||||
this.serverside.fullDataFileHandler.getAsync(pos).thenAccept(fullDataSource ->
|
||||
{
|
||||
if (this.serverside.fullDataFileHandler.isFullyGenerated(fullDataSource.columnGenerationSteps))
|
||||
{
|
||||
requestGroup.fullDataSource = fullDataSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serverside.fullDataFileHandler.queuePositionForRetrieval(pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// player handling //
|
||||
//=================//
|
||||
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.add(serverPlayer); }
|
||||
public void removePlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.remove(serverPlayer); }
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> updateDataSourcesAsync(FullDataSourceV2 data)
|
||||
{
|
||||
if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get())
|
||||
{
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
LOGGER.warn("Unable to send FullDataPartialUpdateMessage - getNetworkCompressionExecutor() is null");
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
FullDataPayload payload = new FullDataPayload(data);
|
||||
for (ServerPlayerState serverPlayerState : this.serverPlayerStateManager.getConnectedPlayers())
|
||||
{
|
||||
if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
||||
int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
||||
if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance()
|
||||
&& distanceFromPlayer <= serverPlayerState.sessionConfig.getRenderDistanceRadius())
|
||||
{
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, serverPlayerState.networkSession::sendMessage);
|
||||
serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload));
|
||||
}
|
||||
}
|
||||
}, executor);
|
||||
|
||||
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public int getMinY() { return this.getLevelWrapper().getMinHeight(); }
|
||||
|
||||
@Override
|
||||
public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevelWrapper; }
|
||||
|
||||
@Override
|
||||
public ILevelWrapper getLevelWrapper() { return this.getServerLevelWrapper(); }
|
||||
|
||||
@Override
|
||||
public FullDataSourceProviderV2 getFullDataProvider() { return this.serverside.fullDataFileHandler; }
|
||||
|
||||
@Override
|
||||
public AbstractSaveStructure getSaveStructure() { return this.serverside.saveStructure; }
|
||||
|
||||
@Override
|
||||
public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); }
|
||||
|
||||
|
||||
|
||||
//==========//
|
||||
// shutdown //
|
||||
//==========//
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
super.close();
|
||||
this.serverside.close();
|
||||
LOGGER.info("Closed DHLevel for [" + this.getLevelWrapper() + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
private static class DataSourceRequestGroup
|
||||
{
|
||||
public final ConcurrentMap<Long, FullDataSourceRequestMessage> requestMessages = new ConcurrentHashMap<>();
|
||||
|
||||
@CheckForNull
|
||||
public FullDataSourceV2 fullDataSource;
|
||||
|
||||
/**
|
||||
* These two Semaphores are used to prevent all threads from locking on the group after it being fulfilled,
|
||||
* as opposed to ReentrantReadWriteLocks which would allow the locking thread continue using it anyway. <br>
|
||||
* Short.MAX_VALUE is chosen as a large enough number so non-exclusive accesses never block each other.
|
||||
*/
|
||||
public final Semaphore requestAddSemaphore = new Semaphore(Short.MAX_VALUE, true);
|
||||
/** @see DataSourceRequestGroup#requestAddSemaphore */
|
||||
public final Semaphore requestRemoveSemaphore = new Semaphore(Short.MAX_VALUE, true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,9 +126,9 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
|
||||
|
||||
boolean isBuffersDirty = false;
|
||||
EDhApiDebugRendering newDebugRendering = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
if (newDebugRendering != lastDebugRendering)
|
||||
if (newDebugRendering != this.lastDebugRendering)
|
||||
{
|
||||
lastDebugRendering = newDebugRendering;
|
||||
this.lastDebugRendering = newDebugRendering;
|
||||
isBuffersDirty = true;
|
||||
}
|
||||
if (isBuffersDirty)
|
||||
@@ -225,7 +225,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
|
||||
}
|
||||
}
|
||||
|
||||
public void close()
|
||||
@Override public void close()
|
||||
{
|
||||
// shutdown the renderer
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
|
||||
@@ -52,7 +52,6 @@ import javax.annotation.CheckForNull;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/** The level used when connected to a server */
|
||||
@@ -109,7 +108,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
|
||||
this.dataFileHandler = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride, this.syncOnLoginRequestQueue);
|
||||
this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration);
|
||||
this.worldGenModule = new WorldGenModule(this, this.dataFileHandler, () -> new WorldGenState(this, networkState));
|
||||
this.worldGenModule = new WorldGenModule(this);
|
||||
|
||||
this.clientside = new ClientLevelModule(this);
|
||||
|
||||
@@ -172,7 +171,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDoWorldGen()
|
||||
public void worldGenTick()
|
||||
{
|
||||
ClientNetworkState networkState = this.networkState;
|
||||
|
||||
@@ -183,23 +182,33 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper;
|
||||
}
|
||||
|
||||
return isClientUsable
|
||||
boolean shouldDoWorldGen = isClientUsable
|
||||
&& networkState.sessionConfig.isDistantGenerationEnabled()
|
||||
&& isAllowedDimension
|
||||
&& this.clientside.isRendering();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DhBlockPos2D getTargetPosForGeneration()
|
||||
{
|
||||
return new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void worldGenTick()
|
||||
{
|
||||
this.worldGenModule.worldGenTick();
|
||||
|
||||
boolean isWorldGenRunning = this.worldGenModule.isWorldGenRunning();
|
||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||
{
|
||||
// start world gen
|
||||
this.worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, networkState));
|
||||
|
||||
// populate the queue based on the current rendering tree
|
||||
ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get();
|
||||
renderState.quadtree.leafNodeIterator().forEachRemaining(node -> {
|
||||
this.dataFileHandler.getAsync(node.sectionPos);
|
||||
});
|
||||
}
|
||||
else if (!shouldDoWorldGen && isWorldGenRunning)
|
||||
{
|
||||
// stop world gen
|
||||
this.worldGenModule.stopWorldGen(this.dataFileHandler);
|
||||
}
|
||||
|
||||
if (this.worldGenModule.isWorldGenRunning())
|
||||
{
|
||||
this.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,44 +22,59 @@ package com.seibel.distanthorizons.core.level;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/** The level used on a singleplayer world */
|
||||
public class DhClientServerLevel extends AbstractDhServerLevel implements IDhClientLevel
|
||||
public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLevel, IDhServerLevel
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
public final ServerLevelModule serverside;
|
||||
public final ClientLevelModule clientside;
|
||||
|
||||
private final IServerLevelWrapper serverLevelWrapper;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, ServerPlayerStateManager serverPlayerStateManager)
|
||||
public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper)
|
||||
{
|
||||
super(saveStructure, serverLevelWrapper, serverPlayerStateManager, false);
|
||||
|
||||
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
|
||||
{
|
||||
LOGGER.warn("unable to create data folder.");
|
||||
}
|
||||
this.serverLevelWrapper = serverLevelWrapper;
|
||||
this.serverLevelWrapper.setParentLevel(this);
|
||||
this.serverside = new ServerLevelModule(this, saveStructure);
|
||||
this.clientside = new ClientLevelModule(this);
|
||||
this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
|
||||
this.runRepoReliantSetup();
|
||||
|
||||
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +94,34 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
||||
public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
|
||||
{ this.clientside.renderDeferred(renderEventParam, profiler); }
|
||||
|
||||
@Override
|
||||
public void serverTick() { }
|
||||
|
||||
@Override
|
||||
public void worldGenTick()
|
||||
{
|
||||
this.serverside.worldGeneratorEnabledConfig.pollNewValue(); // if not called the get() line below may not
|
||||
boolean shouldDoWorldGen = this.serverside.worldGeneratorEnabledConfig.get() && this.clientside.isRendering();
|
||||
boolean isWorldGenRunning = this.serverside.worldGenModule.isWorldGenRunning();
|
||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||
{
|
||||
// start world gen
|
||||
this.serverside.worldGenModule.startWorldGen(this.serverside.fullDataFileHandler, new ServerLevelModule.WorldGenState(this));
|
||||
}
|
||||
else if (!shouldDoWorldGen && isWorldGenRunning)
|
||||
{
|
||||
// stop world gen
|
||||
this.serverside.worldGenModule.stopWorldGen(this.serverside.fullDataFileHandler);
|
||||
}
|
||||
|
||||
if (isWorldGenRunning)
|
||||
{
|
||||
this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// render //
|
||||
//========//
|
||||
@@ -114,14 +157,28 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
||||
public void clearRenderCache() { this.clientside.clearRenderCache(); }
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> updateDataSourcesAsync(FullDataSourceV2 data)
|
||||
public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevelWrapper; }
|
||||
@Override
|
||||
public ILevelWrapper getLevelWrapper() { return this.getServerLevelWrapper(); }
|
||||
|
||||
@Override
|
||||
public FullDataSourceProviderV2 getFullDataProvider() { return this.serverside.fullDataFileHandler; }
|
||||
|
||||
@Override
|
||||
public AbstractSaveStructure getSaveStructure()
|
||||
{
|
||||
return CompletableFuture.allOf(
|
||||
super.updateDataSourcesAsync(data),
|
||||
this.clientside.updateDataSourcesAsync(data)
|
||||
);
|
||||
return this.serverside.saveStructure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> updateDataSourcesAsync(FullDataSourceV2 data) { return this.clientside.updateDataSourcesAsync(data); }
|
||||
|
||||
@Override
|
||||
public int getMinY() { return this.getLevelWrapper().getMinHeight(); }
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
@@ -190,8 +247,6 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
||||
@Override
|
||||
public void onWorldGenTaskComplete(long pos)
|
||||
{
|
||||
super.onWorldGenTaskComplete(pos);
|
||||
|
||||
DebugRenderer.makeParticle(
|
||||
new DebugRenderer.BoxParticle(
|
||||
new DebugRenderer.Box(pos, 128f, 156f, 0.09f, Color.red.darker()),
|
||||
|
||||
@@ -19,23 +19,86 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||
import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException;
|
||||
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
|
||||
import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class DhServerLevel extends AbstractDhServerLevel
|
||||
public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConfigBasedLogger NETWORK_LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Logging.logNetworkEvent.get());
|
||||
|
||||
/** 1 Mebibyte minus 576 bytes for other info */
|
||||
public static final int FULL_DATA_SPLIT_SIZE_IN_BYTES = 1_048_000;
|
||||
|
||||
public final ServerLevelModule serverside;
|
||||
private final IServerLevelWrapper serverLevelWrapper;
|
||||
|
||||
private final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
|
||||
|
||||
/**
|
||||
* This queue is used for ensuring fair generation speed for each player. <br>
|
||||
* Every tick the first player gets used for centering generation, and then is immediately moved into the back of the queue. <br>
|
||||
* TODO only add players that actually have something to generate
|
||||
*/
|
||||
private final ConcurrentLinkedQueue<IServerPlayerWrapper> worldGenPlayerCenteringQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final ConcurrentMap<Long, DataSourceRequestGroup> requestGroupByPos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Long, DataSourceRequestGroup> requestGroupByFutureId = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, ServerPlayerStateManager serverPlayerStateManager)
|
||||
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler)
|
||||
{
|
||||
super(saveStructure, serverLevelWrapper, serverPlayerStateManager);
|
||||
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
|
||||
{
|
||||
LOGGER.warn("unable to create data folder.");
|
||||
}
|
||||
this.serverLevelWrapper = serverLevelWrapper;
|
||||
this.serverside = new ServerLevelModule(this, saveStructure);
|
||||
this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
|
||||
this.runRepoReliantSetup();
|
||||
|
||||
LOGGER.info("Started DHLevel for ["+serverLevelWrapper+"] at ["+saveStructure+"].");
|
||||
|
||||
this.remotePlayerConnectionHandler = remotePlayerConnectionHandler;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +108,355 @@ public class DhServerLevel extends AbstractDhServerLevel
|
||||
//=======//
|
||||
|
||||
@Override
|
||||
public boolean shouldDoWorldGen()
|
||||
public void serverTick()
|
||||
{
|
||||
return true; //todo;
|
||||
// Send finished data source requests
|
||||
for (Map.Entry<Long, DataSourceRequestGroup> entry : this.requestGroupByPos.entrySet())
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = entry.getValue();
|
||||
|
||||
if (requestGroup.fullDataSource == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Fulfilled request group ["+entry.getKey()+"]");
|
||||
|
||||
// Make this group unavailable for adding into
|
||||
this.requestGroupByPos.remove(entry.getKey());
|
||||
requestGroup.requestRemoveSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null");
|
||||
continue;
|
||||
}
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource);
|
||||
for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values())
|
||||
{
|
||||
this.requestGroupByFutureId.remove(msg.futureId);
|
||||
|
||||
ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer());
|
||||
if (serverPlayerState == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release();
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, msg.getSession()::sendMessage);
|
||||
msg.sendResponse(new FullDataSourceResponseMessage(payload));
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void worldGenTick()
|
||||
{
|
||||
boolean shouldDoWorldGen = true; //todo;
|
||||
boolean isWorldGenRunning = this.serverside.worldGenModule.isWorldGenRunning();
|
||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||
{
|
||||
// start world gen
|
||||
this.serverside.worldGenModule.startWorldGen(this.serverside.fullDataFileHandler, new ServerLevelModule.WorldGenState(this));
|
||||
}
|
||||
else if (!shouldDoWorldGen && isWorldGenRunning)
|
||||
{
|
||||
// stop world gen
|
||||
this.serverside.worldGenModule.stopWorldGen(this.serverside.fullDataFileHandler);
|
||||
}
|
||||
|
||||
if (this.serverside.worldGenModule.isWorldGenRunning())
|
||||
{
|
||||
IServerPlayerWrapper firstPlayer = this.worldGenPlayerCenteringQueue.peek();
|
||||
if (firstPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Put first player in back before removing from front, so it can be removed by other thread without blocking
|
||||
// - if it gets removed, remove() below will remove the item we just put instead
|
||||
this.worldGenPlayerCenteringQueue.add(firstPlayer);
|
||||
this.worldGenPlayerCenteringQueue.remove(firstPlayer);
|
||||
|
||||
Vec3d position = firstPlayer.getPosition();
|
||||
this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// network handling //
|
||||
//==================//
|
||||
|
||||
public void registerNetworkHandlers(ServerPlayerState serverPlayerState)
|
||||
{
|
||||
serverPlayerState.networkSession.registerHandler(FullDataSourceRequestMessage.class, (message) ->
|
||||
{
|
||||
if (!this.messagePlayerInThisLevel(message))
|
||||
{
|
||||
// we can't handle players in other levels, don't continue
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this);
|
||||
|
||||
if (message.clientTimestamp == null)
|
||||
{
|
||||
this.queueWorldGenForRequestMessage(serverPlayerState, message, rateLimiterSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.queueLodSyncForRequestMessage(serverPlayerState, message, rateLimiterSet);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
serverPlayerState.networkSession.registerHandler(CancelMessage.class, msg ->
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByFutureId.remove(msg.futureId);
|
||||
if (requestGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If this fails, the group is being removed and completing cancellation is not necessary
|
||||
if (requestGroup.requestRemoveSemaphore.tryAcquire())
|
||||
{
|
||||
// Prevent adding requests in case the group will be removed by this cancellation
|
||||
requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
requestGroup.requestRemoveSemaphore.release();
|
||||
|
||||
serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release();
|
||||
|
||||
FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId);
|
||||
if (requestGroup.requestMessages.isEmpty())
|
||||
{
|
||||
NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"].");
|
||||
this.requestGroupByPos.remove(requestMessage.sectionPos);
|
||||
this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestGroup.requestAddSemaphore.release(Short.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
private void queueLodSyncForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet)
|
||||
{
|
||||
if (!serverPlayerState.sessionConfig.getSynchronizeOnLogin())
|
||||
{
|
||||
message.sendResponse(new RequestRejectedException("Operation is disabled in config."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiterSet.syncOnLoginRateLimiter.tryAcquire(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// the client timestamp will be null if we want to retrieve the LOD regardless of when it was last updated
|
||||
long clientTimestamp = (message.clientTimestamp != null) ? message.clientTimestamp : -1;
|
||||
// the server timestamp will be null if no LOD data exists for this position
|
||||
Long serverTimestamp = this.serverside.fullDataFileHandler.getTimestampForPos(message.sectionPos);
|
||||
if (serverTimestamp == null
|
||||
|| serverTimestamp <= clientTimestamp)
|
||||
{
|
||||
// either no data exists to sync, or the client is already up to date
|
||||
rateLimiterSet.syncOnLoginRateLimiter.release();
|
||||
message.sendResponse(new FullDataSourceResponseMessage(null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null");
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverside.fullDataFileHandler.getAsync(message.sectionPos).thenAcceptAsync(fullDataSource ->
|
||||
{
|
||||
rateLimiterSet.syncOnLoginRateLimiter.release();
|
||||
|
||||
FullDataPayload payload = new FullDataPayload(fullDataSource);
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, message.getSession()::sendMessage);
|
||||
message.sendResponse(new FullDataSourceResponseMessage(payload));
|
||||
}, executor);
|
||||
}
|
||||
private void queueWorldGenForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet)
|
||||
{
|
||||
if (!serverPlayerState.sessionConfig.isDistantGenerationEnabled())
|
||||
{
|
||||
message.sendResponse(new RequestRejectedException("Operation is disabled in config."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiterSet.generationRequestRateLimiter.tryAcquire(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByPos.computeIfAbsent(message.sectionPos, pos ->
|
||||
{
|
||||
DataSourceRequestGroup newGroup = new DataSourceRequestGroup();
|
||||
this.tryFulfillDataSourceRequestGroup(newGroup, pos);
|
||||
NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"].");
|
||||
return newGroup;
|
||||
});
|
||||
|
||||
// If this fails, loop until either a permit is acquired or the group is removed to create another one
|
||||
if (!requestGroup.requestAddSemaphore.tryAcquire())
|
||||
{
|
||||
Thread.yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
this.requestGroupByFutureId.put(message.futureId, requestGroup);
|
||||
requestGroup.requestMessages.put(message.futureId, message);
|
||||
requestGroup.requestAddSemaphore.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** May send an error message in response if the message is a {@link AbstractTrackableMessage} */
|
||||
private <T extends AbstractNetworkMessage> boolean messagePlayerInThisLevel(T message)
|
||||
{
|
||||
if (!(message instanceof ILevelRelatedMessage))
|
||||
{
|
||||
LodUtil.assertNotReach("Received message ["+ILevelRelatedMessage.class.getSimpleName()+"] does not implement ["+message.getClass().getSimpleName()+"]");
|
||||
}
|
||||
|
||||
// Only handle requests for this level
|
||||
if (!((ILevelRelatedMessage) message).isSameLevelAs(this.getServerLevelWrapper()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
||||
|
||||
// Check if the player is in this dimension,
|
||||
// since handling multiple dimensions isn't allowed
|
||||
if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper())
|
||||
{
|
||||
// If the message can be replied to - reply with an error, otherwise just ignore
|
||||
if (message instanceof AbstractTrackableMessage)
|
||||
{
|
||||
((AbstractTrackableMessage) message).sendResponse(
|
||||
new InvalidLevelException(
|
||||
"Generation not allowed. " +
|
||||
"Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " +
|
||||
"player dimension: ["+message.getSession().serverPlayer.getLevel().getDimensionName()+"], " +
|
||||
"handler dimension: ["+this.getLevelWrapper().getDimensionName()+"]"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// world gen //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public void onWorldGenTaskComplete(long pos)
|
||||
{
|
||||
DataSourceRequestGroup requestGroup = this.requestGroupByPos.get(pos);
|
||||
if (requestGroup != null)
|
||||
{
|
||||
this.tryFulfillDataSourceRequestGroup(requestGroup, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryFulfillDataSourceRequestGroup(DataSourceRequestGroup requestGroup, long pos)
|
||||
{
|
||||
this.serverside.fullDataFileHandler.getAsync(pos).thenAccept(fullDataSource ->
|
||||
{
|
||||
if (this.serverside.fullDataFileHandler.isFullyGenerated(fullDataSource.columnGenerationSteps))
|
||||
{
|
||||
requestGroup.fullDataSource = fullDataSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serverside.fullDataFileHandler.queuePositionForRetrieval(pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// player handling //
|
||||
//=================//
|
||||
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.add(serverPlayer); }
|
||||
public void removePlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.remove(serverPlayer); }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> updateDataSourcesAsync(FullDataSourceV2 data)
|
||||
{
|
||||
if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get())
|
||||
{
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
LOGGER.warn("Unable to send FullDataPartialUpdateMessage - getNetworkCompressionExecutor() is null");
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
FullDataPayload payload = new FullDataPayload(data);
|
||||
for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers())
|
||||
{
|
||||
if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
||||
int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
||||
if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance()
|
||||
&& distanceFromPlayer <= serverPlayerState.sessionConfig.getRenderDistanceRadius())
|
||||
{
|
||||
payload.splitAndSend(FULL_DATA_SPLIT_SIZE_IN_BYTES, serverPlayerState.networkSession::sendMessage);
|
||||
serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload));
|
||||
}
|
||||
}
|
||||
}, executor);
|
||||
|
||||
|
||||
return this.getFullDataProvider().updateDataSourceAsync(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +465,24 @@ public class DhServerLevel extends AbstractDhServerLevel
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public int getMinY() { return this.getLevelWrapper().getMinHeight(); }
|
||||
|
||||
@Override
|
||||
public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevelWrapper; }
|
||||
|
||||
@Override
|
||||
public ILevelWrapper getLevelWrapper() { return this.getServerLevelWrapper(); }
|
||||
|
||||
@Override
|
||||
public FullDataSourceProviderV2 getFullDataProvider() { return this.serverside.fullDataFileHandler; }
|
||||
|
||||
@Override
|
||||
public AbstractSaveStructure getSaveStructure() { return this.serverside.saveStructure; }
|
||||
|
||||
@Override
|
||||
public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public GenericObjectRenderer getGenericRenderer()
|
||||
{
|
||||
@@ -78,7 +505,8 @@ public class DhServerLevel extends AbstractDhServerLevel
|
||||
@Override
|
||||
public void addDebugMenuStringsToList(List<String> messageList)
|
||||
{
|
||||
messageList.add("[${this.serverLevelWrapper.getDimensionName()}]");
|
||||
String dimName = this.serverLevelWrapper.getDimensionName();
|
||||
messageList.add("["+dimName+"]");
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +520,33 @@ public class DhServerLevel extends AbstractDhServerLevel
|
||||
{
|
||||
super.close();
|
||||
this.serverside.close();
|
||||
LOGGER.info("Closed DHLevel for [${this.getLevelWrapper()}].");
|
||||
LOGGER.info("Closed DHLevel for ["+this.getLevelWrapper()+"].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
private static class DataSourceRequestGroup
|
||||
{
|
||||
public final ConcurrentMap<Long, FullDataSourceRequestMessage> requestMessages = new ConcurrentHashMap<>();
|
||||
|
||||
@CheckForNull
|
||||
public FullDataSourceV2 fullDataSource;
|
||||
|
||||
/**
|
||||
* These semaphores prevent a given thread from accidentally locking on the same group
|
||||
* multiple times, as the semaphore is tied to the given thread. <br>
|
||||
* Reentrant Lock isn't used since it would allow the thread to lock on the same group. <br>
|
||||
* the Short.MAX_VALUE is just a very large number that should be larger than the number of
|
||||
* threads we'll have.
|
||||
*/
|
||||
public final Semaphore requestAddSemaphore = new Semaphore(Short.MAX_VALUE, true);
|
||||
/** @see DataSourceRequestGroup#requestAddSemaphore */
|
||||
public final Semaphore requestRemoveSemaphore = new Semaphore(Short.MAX_VALUE, true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,10 @@ public class ServerLevelModule implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private final IDhServerLevel parentServerLevel;
|
||||
public final IDhServerLevel parentServerLevel;
|
||||
public final AbstractSaveStructure saveStructure;
|
||||
public final GeneratedFullDataSourceProvider fullDataFileHandler;
|
||||
public final AppliedConfigState<Boolean> worldGeneratorEnabledConfig;
|
||||
|
||||
public final WorldGenModule worldGenModule;
|
||||
|
||||
@@ -51,7 +52,8 @@ public class ServerLevelModule implements AutoCloseable
|
||||
this.parentServerLevel = parentServerLevel;
|
||||
this.saveStructure = saveStructure;
|
||||
this.fullDataFileHandler = new GeneratedFullDataSourceProvider(parentServerLevel, saveStructure);
|
||||
this.worldGenModule = new WorldGenModule(this.parentServerLevel, this.fullDataFileHandler, () -> new ServerLevelModule.WorldGenState(this.parentServerLevel));
|
||||
this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration);
|
||||
this.worldGenModule = new WorldGenModule(this.parentServerLevel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,15 +25,11 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Handles both single-player/server-side world gen and client side LOD requests.
|
||||
@@ -45,9 +41,6 @@ public class WorldGenModule implements Closeable
|
||||
|
||||
private final GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener;
|
||||
|
||||
private final GeneratedFullDataSourceProvider dataSourceProvider;
|
||||
private final Supplier<? extends AbstractWorldGenState> worldGenStateSupplier;
|
||||
|
||||
private final AtomicReference<AbstractWorldGenState> worldGenStateRef = new AtomicReference<>();
|
||||
|
||||
|
||||
@@ -56,15 +49,9 @@ public class WorldGenModule implements Closeable
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public WorldGenModule(
|
||||
GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener,
|
||||
GeneratedFullDataSourceProvider dataSourceProvider,
|
||||
Supplier<? extends AbstractWorldGenState> worldGenStateSupplier
|
||||
)
|
||||
public WorldGenModule(GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener)
|
||||
{
|
||||
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
|
||||
this.dataSourceProvider = dataSourceProvider;
|
||||
this.worldGenStateSupplier = worldGenStateSupplier;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,33 +95,13 @@ public class WorldGenModule implements Closeable
|
||||
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
|
||||
}
|
||||
|
||||
public void worldGenTick()
|
||||
/** @param targetPosForGeneration the position that world generation should be centered around */
|
||||
public void worldGenTick(DhBlockPos2D targetPosForGeneration)
|
||||
{
|
||||
boolean shouldDoWorldGen = this.onWorldGenCompleteListener.shouldDoWorldGen();
|
||||
|
||||
boolean isWorldGenRunning = this.isWorldGenRunning();
|
||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
|
||||
if (worldGenState != null)
|
||||
{
|
||||
// start world gen
|
||||
this.startWorldGen(this.dataSourceProvider, this.worldGenStateSupplier.get());
|
||||
}
|
||||
else if (!shouldDoWorldGen && isWorldGenRunning)
|
||||
{
|
||||
// stop world gen
|
||||
this.stopWorldGen(this.dataSourceProvider);
|
||||
}
|
||||
|
||||
if (this.isWorldGenRunning())
|
||||
{
|
||||
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
|
||||
if (worldGenState != null)
|
||||
{
|
||||
DhBlockPos2D targetPosForGeneration = this.onWorldGenCompleteListener.getTargetPosForGeneration();
|
||||
if (targetPosForGeneration != null)
|
||||
{
|
||||
worldGenState.startGenerationQueueAndSetTargetPos(targetPosForGeneration);
|
||||
}
|
||||
}
|
||||
worldGenState.startGenerationQueueAndSetTargetPos(targetPosForGeneration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.seibel.distanthorizons.core.multiplayer.server;
|
||||
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -8,12 +9,11 @@ import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ServerPlayerStateManager
|
||||
public class RemotePlayerConnectionHandler
|
||||
{
|
||||
private final ConcurrentMap<IServerPlayerWrapper, ServerPlayerState> connectedPlayerStateByPlayerWrapper = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<IServerPlayerWrapper, MessageQueueState> messageQueueByPlayerWrapper = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<IServerPlayerWrapper, Queue<AbstractNetworkMessage>> messageQueueByPlayerWrapper = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,19 @@ public class ServerPlayerStateManager
|
||||
{
|
||||
ServerPlayerState playerState = new ServerPlayerState(serverPlayer);
|
||||
this.connectedPlayerStateByPlayerWrapper.put(serverPlayer, playerState);
|
||||
|
||||
Queue<AbstractNetworkMessage> queuedMessages = this.messageQueueByPlayerWrapper.get(serverPlayer);
|
||||
if (queuedMessages != null)
|
||||
{
|
||||
NetworkSession networkSession = playerState.networkSession;
|
||||
for (AbstractNetworkMessage message : queuedMessages)
|
||||
{
|
||||
networkSession.tryHandleMessage(message);
|
||||
}
|
||||
|
||||
this.messageQueueByPlayerWrapper.remove(serverPlayer);
|
||||
}
|
||||
|
||||
return playerState;
|
||||
}
|
||||
|
||||
@@ -45,30 +58,14 @@ public class ServerPlayerStateManager
|
||||
|
||||
public void handlePluginMessage(IServerPlayerWrapper player, AbstractNetworkMessage message)
|
||||
{
|
||||
MessageQueueState messageQueue = this.messageQueueByPlayerWrapper.computeIfAbsent(player, k -> new MessageQueueState());
|
||||
messageQueue.messageQueue.add(message);
|
||||
|
||||
ServerPlayerState playerState = this.connectedPlayerStateByPlayerWrapper.get(player);
|
||||
if (playerState != null)
|
||||
{
|
||||
this.handlePluginMessagesFromQueue(playerState, messageQueue);
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePluginMessagesFromQueue(ServerPlayerState playerState)
|
||||
{
|
||||
MessageQueueState messageQueue = this.messageQueueByPlayerWrapper.computeIfAbsent(playerState.getServerPlayer(), k -> new MessageQueueState());
|
||||
this.handlePluginMessagesFromQueue(playerState, messageQueue);
|
||||
}
|
||||
|
||||
private void handlePluginMessagesFromQueue(ServerPlayerState playerState, MessageQueueState messageQueueState)
|
||||
{
|
||||
while (!messageQueueState.messageQueue.isEmpty() && messageQueueState.isBeingDrained.compareAndSet(false, true))
|
||||
{
|
||||
AbstractNetworkMessage message = messageQueueState.messageQueue.poll();
|
||||
playerState.networkSession.tryHandleMessage(message);
|
||||
|
||||
messageQueueState.isBeingDrained.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.messageQueueByPlayerWrapper.computeIfAbsent(player, k -> new ConcurrentLinkedQueue<>()).add(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +80,6 @@ public class ServerPlayerStateManager
|
||||
public Iterable<ServerPlayerState> getConnectedPlayers() { return this.connectedPlayerStateByPlayerWrapper.values(); }
|
||||
|
||||
|
||||
private static class MessageQueueState
|
||||
{
|
||||
public final Queue<AbstractNetworkMessage> messageQueue = new ConcurrentLinkedQueue<>();
|
||||
public final AtomicBoolean isBeingDrained = new AtomicBoolean();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.seibel.distanthorizons.core.multiplayer.server;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
|
||||
import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
||||
@@ -33,8 +33,8 @@ public class ServerPlayerState implements Closeable
|
||||
@NotNull
|
||||
public final SessionConfig sessionConfig = new SessionConfig();
|
||||
|
||||
private final ConcurrentHashMap<AbstractDhServerLevel, RateLimiterSet> rateLimiterSets = new ConcurrentHashMap<>();
|
||||
public RateLimiterSet getRateLimiterSet(AbstractDhServerLevel level) { return this.rateLimiterSets.computeIfAbsent(level, ignored -> new RateLimiterSet()); }
|
||||
private final ConcurrentHashMap<DhServerLevel, RateLimiterSet> rateLimiterSets = new ConcurrentHashMap<>();
|
||||
public RateLimiterSet getRateLimiterSet(DhServerLevel level) { return this.rateLimiterSets.computeIfAbsent(level, ignored -> new RateLimiterSet()); }
|
||||
public void clearRateLimiterSets() { this.rateLimiterSets.clear(); }
|
||||
|
||||
|
||||
|
||||
@@ -76,19 +76,19 @@ public class LodFogConfig
|
||||
private LodFogConfig(EDhApiFogDrawMode fogDrawMode)
|
||||
{
|
||||
// TODO: Move these out of here
|
||||
earthCurveRatio = Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get();
|
||||
this.earthCurveRatio = Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get();
|
||||
|
||||
noiseEnable = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get();
|
||||
noiseSteps = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get();
|
||||
noiseIntensity = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue();
|
||||
noiseDropoff = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get();
|
||||
this.noiseEnable = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get();
|
||||
this.noiseSteps = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get();
|
||||
this.noiseIntensity = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue();
|
||||
this.noiseDropoff = Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get();
|
||||
|
||||
|
||||
if (fogDrawMode != EDhApiFogDrawMode.FOG_DISABLED)
|
||||
{
|
||||
// fog should be drawn
|
||||
|
||||
farFogSetting = new FogSettings(
|
||||
this.farFogSetting = new FogSettings(
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogStart.get(),
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogEnd.get(),
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMin.get(),
|
||||
@@ -97,20 +97,20 @@ public class LodFogConfig
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogFalloff.get()
|
||||
);
|
||||
|
||||
heightFogMixMode = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode.get();
|
||||
if (heightFogMixMode == EDhApiHeightFogMixMode.IGNORE_HEIGHT || heightFogMixMode == EDhApiHeightFogMixMode.BASIC)
|
||||
this.heightFogMixMode = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode.get();
|
||||
if (this.heightFogMixMode == EDhApiHeightFogMixMode.IGNORE_HEIGHT || this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC)
|
||||
{
|
||||
// basic fog mixing
|
||||
|
||||
heightFogSetting = null;
|
||||
heightFogMode = null;
|
||||
heightFogHeight = 0.f;
|
||||
this.heightFogSetting = null;
|
||||
this.heightFogMode = null;
|
||||
this.heightFogHeight = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// advanced fog mixing
|
||||
|
||||
heightFogSetting = new FogSettings(
|
||||
this.heightFogSetting = new FogSettings(
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogDensity.get(),
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogEnd.get(),
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMin.get(),
|
||||
@@ -119,15 +119,15 @@ public class LodFogConfig
|
||||
Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogFalloff.get()
|
||||
);
|
||||
|
||||
heightFogMode = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode.get();
|
||||
this.heightFogMode = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode.get();
|
||||
|
||||
if (heightFogMode.basedOnCamera)
|
||||
if (this.heightFogMode.basedOnCamera)
|
||||
{
|
||||
heightFogHeight = 0.f;
|
||||
this.heightFogHeight = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
heightFogHeight = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogBaseHeight.get().floatValue();
|
||||
this.heightFogHeight = Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogBaseHeight.get().floatValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,11 +135,11 @@ public class LodFogConfig
|
||||
{
|
||||
// fog disabled
|
||||
|
||||
farFogSetting = null;
|
||||
heightFogMixMode = null;
|
||||
heightFogMode = null;
|
||||
heightFogSetting = null;
|
||||
heightFogHeight = 0.f;
|
||||
this.farFogSetting = null;
|
||||
this.heightFogMixMode = null;
|
||||
this.heightFogMode = null;
|
||||
this.heightFogSetting = null;
|
||||
this.heightFogHeight = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class LodFogConfig
|
||||
"} \n");
|
||||
|
||||
|
||||
if (farFogSetting == null)
|
||||
if (this.farFogSetting == null)
|
||||
{
|
||||
str.append("\n" +
|
||||
"float getFarFogThickness(float dist) { return 0.0; } \n" +
|
||||
@@ -195,7 +195,7 @@ public class LodFogConfig
|
||||
str.append("" +
|
||||
"float getFarFogThickness(float dist) \n" +
|
||||
"{ \n" +
|
||||
getFarFogMethod(farFogSetting.fogType) + "\n" +
|
||||
getFarFogMethod(this.farFogSetting.fogType) + "\n" +
|
||||
"} \n");
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ public class LodFogConfig
|
||||
str.append("" +
|
||||
"float getHeightFogThickness(float dist) \n" +
|
||||
"{ \n" +
|
||||
(heightFogSetting != null ? getHeightFogMethod(heightFogSetting.fogType) : " return 0.0;") + "\n" +
|
||||
(this.heightFogSetting != null ? getHeightFogMethod(this.heightFogSetting.fogType) : " return 0.0;") + "\n" +
|
||||
"} \n");
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ public class LodFogConfig
|
||||
str.append("" +
|
||||
"float calculateHeightFogDepth(float vertical, float realY) \n" +
|
||||
"{ \n" +
|
||||
(heightFogSetting != null ? getHeightDepthMethod(heightFogMode, heightFogHeight) : " return 0.0;") + "\n" +
|
||||
(this.heightFogSetting != null ? getHeightDepthMethod(this.heightFogMode, this.heightFogHeight) : " return 0.0;") + "\n" +
|
||||
"} \n");
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ public class LodFogConfig
|
||||
str.append("" +
|
||||
"float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" +
|
||||
"{ \n" +
|
||||
" return " + (heightFogMixMode == EDhApiHeightFogMixMode.BASIC ?
|
||||
" return " + (this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC ?
|
||||
"(dist - uNearFogStart)/(1.0 - uNearFogStart);" :
|
||||
"(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") +
|
||||
"} \n");
|
||||
@@ -228,7 +228,7 @@ public class LodFogConfig
|
||||
str.append("" +
|
||||
"float mixFogThickness(float near, float far, float height) \n" +
|
||||
"{ \n" +
|
||||
getMixFogLine(heightFogMixMode) + "\n" +
|
||||
getMixFogLine(this.heightFogMixMode) + "\n" +
|
||||
"} \n");
|
||||
}
|
||||
}
|
||||
@@ -398,23 +398,27 @@ public class LodFogConfig
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
}
|
||||
if (o == null || this.getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LodFogConfig that = (LodFogConfig) o;
|
||||
return Float.compare(that.heightFogHeight, heightFogHeight) == 0 &&
|
||||
Objects.equals(farFogSetting, that.farFogSetting) &&
|
||||
Objects.equals(heightFogSetting, that.heightFogSetting) && heightFogMixMode == that.heightFogMixMode &&
|
||||
heightFogMode == that.heightFogMode
|
||||
return Float.compare(that.heightFogHeight, this.heightFogHeight) == 0 &&
|
||||
Objects.equals(this.farFogSetting, that.farFogSetting) &&
|
||||
Objects.equals(this.heightFogSetting, that.heightFogSetting) && this.heightFogMixMode == that.heightFogMixMode &&
|
||||
this.heightFogMode == that.heightFogMode
|
||||
// TODO: Move these out of here
|
||||
&& earthCurveRatio == that.earthCurveRatio
|
||||
&& noiseEnable == that.noiseEnable && noiseSteps == that.noiseSteps && noiseIntensity == that.noiseIntensity && noiseDropoff == that.noiseDropoff;
|
||||
&& this.earthCurveRatio == that.earthCurveRatio
|
||||
&& this.noiseEnable == that.noiseEnable && this.noiseSteps == that.noiseSteps && this.noiseIntensity == that.noiseIntensity && this.noiseDropoff == that.noiseDropoff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(farFogSetting, heightFogSetting, heightFogMixMode, heightFogMode, heightFogHeight, earthCurveRatio, noiseEnable, noiseSteps, noiseIntensity, noiseDropoff);
|
||||
return Objects.hash(this.farFogSetting, this.heightFogSetting, this.heightFogMixMode, this.heightFogMode, this.heightFogHeight, this.earthCurveRatio, this.noiseEnable, this.noiseSteps, this.noiseIntensity, this.noiseDropoff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -130,8 +130,12 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (this.uEarthRadius != -1) this.setUniform(this.uEarthRadius,
|
||||
if (this.uEarthRadius != -1)
|
||||
{
|
||||
this.setUniform(this.uEarthRadius,
|
||||
/*6371KM*/ 6371000.0f / Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Noise Uniforms
|
||||
@@ -185,7 +189,10 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
|
||||
// setUniform(skyLightUniform, skyLight);
|
||||
this.setUniform(this.uLightMap, 0); // TODO this should probably be passed in
|
||||
|
||||
if (this.uWorldYOffset != -1) this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset);
|
||||
if (this.uWorldYOffset != -1)
|
||||
{
|
||||
this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset);
|
||||
}
|
||||
|
||||
// Debug
|
||||
this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
|
||||
|
||||
@@ -845,15 +845,23 @@ public class LodRenderer
|
||||
}
|
||||
|
||||
if (this.quadIBO != null)
|
||||
{
|
||||
this.quadIBO.destroyAsync();
|
||||
}
|
||||
|
||||
// Delete framebuffer, color texture, and depth texture
|
||||
if (this.framebuffer != null && !this.usingMcFrameBuffer)
|
||||
{
|
||||
this.framebuffer.destroy();
|
||||
}
|
||||
if (this.nullableColorTexture != null)
|
||||
{
|
||||
this.nullableColorTexture.destroy();
|
||||
}
|
||||
if (this.depthTexture != null)
|
||||
{
|
||||
this.depthTexture.destroy();
|
||||
}
|
||||
|
||||
EVENT_LOGGER.info("Renderer Cleanup Complete");
|
||||
});
|
||||
|
||||
@@ -23,11 +23,13 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
@@ -195,15 +197,6 @@ public class CloudRenderHandler
|
||||
|
||||
// this color is changed at render time based on the level time
|
||||
Color color = new Color(255,255,255,255);
|
||||
if (DEBUG_BORDER_COLORS)
|
||||
{
|
||||
// equals is included so the boarder is 2 blocks wide, making it easier to see
|
||||
if (x <= 1) { color = Color.RED; }
|
||||
else if (x >= textureWidth - 2) { color = Color.GREEN; }
|
||||
if (z <= 1) { color = Color.BLUE; }
|
||||
else if (z >= textureWidth - 2) { color = Color.BLACK; }
|
||||
}
|
||||
|
||||
DhApiRenderableBox box = new DhApiRenderableBox(
|
||||
new DhApiVec3d(minXBlockPos, 0, minZBlockPos),
|
||||
new DhApiVec3d(maxXBlockPos, CLOUD_BOX_THICKNESS, maxZBlockPos),
|
||||
@@ -270,7 +263,7 @@ public class CloudRenderHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.renderer.getInstancedRenderingAvailable())
|
||||
if (!this.renderer.getUseInstancedRendering())
|
||||
{
|
||||
if (!this.disabledWarningLogged)
|
||||
{
|
||||
@@ -290,15 +283,20 @@ public class CloudRenderHandler
|
||||
// FIXME transparency sorting makes having transparent clouds impossible
|
||||
// maybe someday we could add the option to cull individual faces? a single bit for each direction should be enough
|
||||
|
||||
// if debug colors are enabled don't change them
|
||||
if (!DEBUG_BORDER_COLORS)
|
||||
// cloud color changes based on the time of day and weather so we need to get it from the level
|
||||
Color cloudColor = this.level.getClientLevelWrapper().getCloudColor(renderParam.partialTicks);
|
||||
if (DEBUG_BORDER_COLORS)
|
||||
{
|
||||
// cloud color changes based on the time of day and weather so we need to get it from the level
|
||||
Color cloudColor = this.level.getClientLevelWrapper().getCloudColor(renderParam.partialTicks);
|
||||
for (DhApiRenderableBox box : boxGroup)
|
||||
{
|
||||
box.color = cloudColor;
|
||||
}
|
||||
// equals is included so the board is 2 blocks wide, it makes it easier to see
|
||||
if (cloudParams.instanceOffsetX <= 1) { cloudColor = Color.RED; }
|
||||
else if (cloudParams.instanceOffsetX >= cloudParams.textureWidth - 2) { cloudColor = Color.GREEN; }
|
||||
if (cloudParams.instanceOffsetZ <= 1) { cloudColor = Color.BLUE; }
|
||||
else if (cloudParams.instanceOffsetZ >= cloudParams.textureWidth - 2) { cloudColor = Color.BLACK; }
|
||||
}
|
||||
|
||||
for (DhApiRenderableBox box : boxGroup)
|
||||
{
|
||||
box.color = cloudColor;
|
||||
}
|
||||
boxGroup.triggerBoxChange();
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.jar.EPlatform;
|
||||
@@ -45,12 +44,14 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.ARBInstancedArrays;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
@@ -58,6 +59,7 @@ import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -85,12 +87,11 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
// rendering setup
|
||||
private boolean init = false;
|
||||
|
||||
private IDhApiGenericObjectShaderProgram instancedShaderProgram;
|
||||
private IDhApiGenericObjectShaderProgram directShaderProgram;
|
||||
private IDhApiGenericObjectShaderProgram shaderProgram;
|
||||
private GLVertexBuffer boxVertexBuffer;
|
||||
private GLElementBuffer boxIndexBuffer;
|
||||
|
||||
private boolean instancedRenderingAvailable;
|
||||
private boolean useInstancedRendering;
|
||||
private boolean vertexAttribDivisorSupported;
|
||||
private boolean instancedArraysSupported;
|
||||
|
||||
@@ -185,8 +186,8 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
|
||||
this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported;
|
||||
this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported;
|
||||
this.instancedRenderingAvailable = this.vertexAttribDivisorSupported || this.instancedArraysSupported;
|
||||
if (!this.instancedRenderingAvailable)
|
||||
this.useInstancedRendering = this.vertexAttribDivisorSupported || this.instancedArraysSupported;
|
||||
if (!this.useInstancedRendering)
|
||||
{
|
||||
LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow and some effects may be disabled.");
|
||||
}
|
||||
@@ -195,7 +196,8 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
boolean isMac = (EPlatform.get() == EPlatform.MACOS);
|
||||
if (isMac && SODIUM != null)
|
||||
{
|
||||
LOGGER.warn("There have been reports of instanced rendering causing crashes on macOS when Sodium is present. Instanced rendering can be disabled via the DH config.");
|
||||
this.useInstancedRendering = false;
|
||||
LOGGER.warn("Instanced rendering is broken on Mac when Sodium is present, falling back to direct rendering. Generic object rendering will be slow and some effects may be disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +207,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
// startup the renderer //
|
||||
//======================//
|
||||
|
||||
this.instancedShaderProgram = new GenericObjectShaderProgram(true);
|
||||
this.directShaderProgram = new GenericObjectShaderProgram(false);
|
||||
this.shaderProgram = new GenericObjectShaderProgram(this.useInstancedRendering);
|
||||
|
||||
this.createBuffers();
|
||||
|
||||
@@ -391,9 +392,6 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
GLState glState = new GLState();
|
||||
this.init();
|
||||
|
||||
boolean useInstancedRendering = this.instancedRenderingAvailable
|
||||
&& Config.Client.Advanced.Graphics.GenericRendering.enableInstancedRendering.get();
|
||||
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam);
|
||||
|
||||
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
|
||||
@@ -403,7 +401,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
|
||||
GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
IDhApiGenericObjectShaderProgram shaderProgram = useInstancedRendering ? this.instancedShaderProgram : this.directShaderProgram;
|
||||
IDhApiGenericObjectShaderProgram shaderProgram = this.shaderProgram;
|
||||
IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class);
|
||||
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
|
||||
{
|
||||
@@ -439,7 +437,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
profiler.popPush("rendering");
|
||||
profiler.push(boxGroup.getResourceLocationNamespace());
|
||||
profiler.push(boxGroup.getResourceLocationPath());
|
||||
if (useInstancedRendering)
|
||||
if (this.useInstancedRendering)
|
||||
{
|
||||
this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos);
|
||||
}
|
||||
@@ -595,14 +593,14 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
//=========//
|
||||
|
||||
/** @throws IllegalStateException if {@link #init()} function hasn't been called yet */
|
||||
public boolean getInstancedRenderingAvailable() throws IllegalStateException
|
||||
public boolean getUseInstancedRendering() throws IllegalStateException
|
||||
{
|
||||
if (!this.init)
|
||||
{
|
||||
throw new IllegalStateException("GL initialization hasn't been completed.");
|
||||
}
|
||||
|
||||
return this.instancedRenderingAvailable;
|
||||
return this.useInstancedRendering;
|
||||
}
|
||||
|
||||
|
||||
@@ -614,26 +612,21 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
|
||||
public String getVboRenderDebugMenuString()
|
||||
{
|
||||
// get counts
|
||||
int totalGroupCount = this.boxGroupById.size();
|
||||
int totalBoxCount = 0;
|
||||
|
||||
int activeGroupCount = 0;
|
||||
int activeBoxCount = 0;
|
||||
|
||||
int totalCount = this.boxGroupById.size();
|
||||
int activeCount = 0;
|
||||
for (long key : this.boxGroupById.keySet())
|
||||
{
|
||||
RenderableBoxGroup renderGroup = this.boxGroupById.get(key);
|
||||
if (renderGroup.active)
|
||||
{
|
||||
activeGroupCount++;
|
||||
activeBoxCount += renderGroup.size();
|
||||
activeCount++;
|
||||
}
|
||||
totalBoxCount += renderGroup.size();
|
||||
}
|
||||
|
||||
|
||||
return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " +
|
||||
"Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount);
|
||||
String totalCountText = F3Screen.NUMBER_FORMAT.format(totalCount);
|
||||
String activeCountText = F3Screen.NUMBER_FORMAT.format(activeCount);
|
||||
return LodUtil.formatLog("Generic Obj Count: " + activeCountText + "/" + totalCountText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,14 +115,32 @@ public class FogShader extends AbstractShaderRenderer
|
||||
int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
// Fog
|
||||
if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0);
|
||||
if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks));
|
||||
if (this.uFullFogMode != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0);
|
||||
}
|
||||
if (this.uFogColor != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks));
|
||||
}
|
||||
|
||||
float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance;
|
||||
if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart);
|
||||
if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f);
|
||||
if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
|
||||
if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
|
||||
if (this.uNearFogStart != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uNearFogStart, nearFogStart);
|
||||
}
|
||||
if (this.uNearFogLength != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uNearFogLength, 0.0f);
|
||||
}
|
||||
if (this.uFogScale != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
|
||||
}
|
||||
if (this.uFogVerticalScale != -1)
|
||||
{
|
||||
this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
|
||||
}
|
||||
}
|
||||
private Color getFogColor(float partialTicks)
|
||||
{
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.google.common.base.MoreObjects;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
@@ -80,16 +79,14 @@ public class FullDataSourceV2DTO implements IBaseDTO<Long>, INetworkObject
|
||||
|
||||
public static FullDataSourceV2DTO CreateFromDataSource(FullDataSourceV2 dataSource, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
byte[] dataPointByteArray = writeDataSourceDataArrayToBlob(dataSource.dataPoints, compressionModeEnum);
|
||||
CheckedByteArray checkedDataPointArray = writeDataSourceDataArrayToBlob(dataSource.dataPoints, compressionModeEnum);
|
||||
byte[] compressedWorldGenStepByteArray = writeGenerationStepsToBlob(dataSource.columnGenerationSteps, compressionModeEnum);
|
||||
byte[] compressedWorldCompressionModeByteArray = writeWorldCompressionModeToBlob(dataSource.columnWorldCompressionMode, compressionModeEnum);
|
||||
byte[] mappingByteArray = writeDataMappingToBlob(dataSource.mapping, compressionModeEnum);
|
||||
|
||||
int checksum = (dataSource.mapping.hashCode() * 4217) + dataSource.hashCode();
|
||||
|
||||
return new FullDataSourceV2DTO(
|
||||
dataSource.getPos(),
|
||||
checksum, compressedWorldGenStepByteArray, compressedWorldCompressionModeByteArray, FullDataSourceV2.DATA_FORMAT_VERSION, compressionModeEnum.value, dataPointByteArray,
|
||||
dataSource.getPos(),
|
||||
checkedDataPointArray.checksum, compressedWorldGenStepByteArray, compressedWorldCompressionModeByteArray, FullDataSourceV2.DATA_FORMAT_VERSION, compressionModeEnum.value, checkedDataPointArray.byteArray,
|
||||
dataSource.lastModifiedUnixDateTime, dataSource.createdUnixDateTime,
|
||||
mappingByteArray, dataSource.applyToParent,
|
||||
dataSource.levelMinY
|
||||
@@ -207,14 +204,16 @@ public class FullDataSourceV2DTO implements IBaseDTO<Long>, INetworkObject
|
||||
// (de)serializing //
|
||||
//=================//
|
||||
|
||||
private static byte[] writeDataSourceDataArrayToBlob(LongArrayList[] dataArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
private static CheckedByteArray writeDataSourceDataArrayToBlob(LongArrayList[] dataArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
// write the outputs to a stream to prep for writing to the database
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
// the order of these streams is important, otherwise the checksum won't be calculated
|
||||
CheckedOutputStream checkedOut = new CheckedOutputStream(byteArrayOutputStream, new Adler32());
|
||||
// normally a DhStream should be the topmost stream to prevent closing the stream accidentally,
|
||||
// but since this stream will be closed immediately after writing anyway, it won't be an issue
|
||||
DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum);
|
||||
DhDataOutputStream compressedOut = new DhDataOutputStream(checkedOut, compressionModeEnum);
|
||||
|
||||
|
||||
// write the data
|
||||
@@ -239,9 +238,10 @@ public class FullDataSourceV2DTO implements IBaseDTO<Long>, INetworkObject
|
||||
|
||||
// generate the checksum
|
||||
compressedOut.flush();
|
||||
int checksum = (int) checkedOut.getChecksum().getValue();
|
||||
byteArrayOutputStream.close();
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
return new CheckedByteArray(checksum, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
private static LongArrayList[] readBlobToDataSourceDataArray(byte[] compressedDataByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
@@ -452,4 +452,22 @@ public class FullDataSourceV2DTO implements IBaseDTO<Long>, INetworkObject
|
||||
|
||||
public EDhApiDataCompressionMode getCompressionMode() throws IllegalArgumentException { return EDhApiDataCompressionMode.getFromValue(this.compressionModeValue); }
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
private static class CheckedByteArray
|
||||
{
|
||||
public final int checksum;
|
||||
public final byte[] byteArray;
|
||||
|
||||
public CheckedByteArray(int checksum, byte[] byteArray)
|
||||
{
|
||||
this.checksum = checksum;
|
||||
this.byteArray = byteArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,17 +40,13 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
public abstract class AbstractDhRepo<TKey, TDTO extends IBaseDTO<TKey>> implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final String DEFAULT_DATABASE_TYPE = "jdbc:sqlite";
|
||||
/** a value of 0 means there's no timeout */
|
||||
public static final int TIMEOUT_SECONDS = 0;
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConcurrentHashMap<String, Connection> CONNECTIONS_BY_CONNECTION_STRING = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<AbstractDhRepo<?, ?>, String> ACTIVE_CONNECTION_STRINGS_BY_REPO = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
private final String connectionString;
|
||||
private final Connection connection;
|
||||
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.seibel.distanthorizons.core.world;
|
||||
|
||||
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
|
||||
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public abstract class AbstractDhServerWorld<TDhServerLevel extends AbstractDhServerLevel> extends AbstractDhWorld implements IDhServerWorld
|
||||
{
|
||||
protected final HashMap<ILevelWrapper, TDhServerLevel> dhLevelByLevelWrapper = new HashMap<>();
|
||||
public final LocalSaveStructure saveStructure = new LocalSaveStructure();
|
||||
|
||||
private final ServerPlayerStateManager serverPlayerStateManager;
|
||||
|
||||
public AbstractDhServerWorld(EWorldEnvironment worldEnvironment)
|
||||
{
|
||||
super(worldEnvironment);
|
||||
this.serverPlayerStateManager = new ServerPlayerStateManager();
|
||||
}
|
||||
|
||||
|
||||
//=================//
|
||||
// player handling //
|
||||
//=================//
|
||||
|
||||
|
||||
@Override
|
||||
public ServerPlayerStateManager getServerPlayerStateManager()
|
||||
{
|
||||
return this.serverPlayerStateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
ServerPlayerState playerState = this.serverPlayerStateManager.registerJoinedPlayer(serverPlayer);
|
||||
this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer);
|
||||
|
||||
for (TDhServerLevel level : (Iterable<? extends TDhServerLevel>) this.dhLevelByLevelWrapper.values().stream().distinct()::iterator)
|
||||
{
|
||||
level.registerNetworkHandlers(playerState);
|
||||
}
|
||||
|
||||
this.serverPlayerStateManager.handlePluginMessagesFromQueue(playerState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer);
|
||||
this.serverPlayerStateManager.unregisterLeftPlayer(serverPlayer);
|
||||
|
||||
// If player's left, session is already closed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
|
||||
{
|
||||
this.getLevel(destinationLevel).addPlayer(player);
|
||||
this.getLevel(originLevel).removePlayer(player);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handling //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public TDhServerLevel getLevel(@NotNull ILevelWrapper wrapper) { return this.dhLevelByLevelWrapper.get(wrapper); }
|
||||
@Override
|
||||
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.dhLevelByLevelWrapper.values(); }
|
||||
@Override
|
||||
public int getLoadedLevelCount() { return this.dhLevelByLevelWrapper.size(); }
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public void serverTick() { this.dhLevelByLevelWrapper.values().forEach(TDhServerLevel::serverTick); }
|
||||
|
||||
@Override
|
||||
public void worldGenTick() { this.dhLevelByLevelWrapper.values().forEach(TDhServerLevel::worldGenTick); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
for (TDhServerLevel level : this.dhLevelByLevelWrapper.values())
|
||||
{
|
||||
LOGGER.info("Unloading level [" + level.getLevelWrapper().getDimensionName() + "].");
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
}
|
||||
|
||||
this.dhLevelByLevelWrapper.clear();
|
||||
LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.world;
|
||||
|
||||
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.level.DhClientServerLevel;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.EventLoop;
|
||||
@@ -29,14 +31,15 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLevel> implements IDhClientWorld
|
||||
public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWorld, IDhServerWorld
|
||||
{
|
||||
private final Set<DhClientServerLevel> dhLevels = Collections.synchronizedSet(new HashSet<>());
|
||||
private final HashMap<ILevelWrapper, DhClientServerLevel> levelWrapperByDhLevel = new HashMap<>();
|
||||
private final HashSet<DhClientServerLevel> dhLevels = new HashSet<>();
|
||||
public final LocalSaveStructure saveStructure = new LocalSaveStructure();
|
||||
|
||||
public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client Server World Ticker Thread", 2);
|
||||
public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); //TODO: Rate-limit the loop
|
||||
@@ -64,18 +67,18 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
{
|
||||
if (wrapper instanceof IServerLevelWrapper)
|
||||
{
|
||||
return this.dhLevelByLevelWrapper.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
{
|
||||
File levelFile = this.saveStructure.getLevelFolder(levelWrapper);
|
||||
LodUtil.assertTrue(levelFile != null);
|
||||
DhClientServerLevel level = new DhClientServerLevel(this.saveStructure, (IServerLevelWrapper) levelWrapper, this.getServerPlayerStateManager());
|
||||
DhClientServerLevel level = new DhClientServerLevel(this.saveStructure, (IServerLevelWrapper) levelWrapper);
|
||||
this.dhLevels.add(level);
|
||||
return level;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.dhLevelByLevelWrapper.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
{
|
||||
IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) levelWrapper;
|
||||
IServerLevelWrapper serverLevelWrapper = clientLevelWrapper.tryGetServerSideWrapper();
|
||||
@@ -86,7 +89,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
}
|
||||
|
||||
|
||||
DhClientServerLevel level = this.dhLevelByLevelWrapper.get(serverLevelWrapper);
|
||||
DhClientServerLevel level = this.levelWrapperByDhLevel.get(serverLevelWrapper);
|
||||
if (level == null)
|
||||
{
|
||||
return null;
|
||||
@@ -99,17 +102,25 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhClientServerLevel getLevel(@NotNull ILevelWrapper wrapper) { return this.levelWrapperByDhLevel.get(wrapper); }
|
||||
|
||||
@Override
|
||||
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.dhLevels; }
|
||||
@Override
|
||||
public int getLoadedLevelCount() { return this.dhLevels.size(); }
|
||||
|
||||
@Override
|
||||
public void unloadLevel(@NotNull ILevelWrapper wrapper)
|
||||
{
|
||||
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
||||
if (this.levelWrapperByDhLevel.containsKey(wrapper))
|
||||
{
|
||||
if (wrapper instanceof IServerLevelWrapper)
|
||||
{
|
||||
LOGGER.info("Unloading level " + this.dhLevelByLevelWrapper.get(wrapper));
|
||||
LOGGER.info("Unloading level " + this.levelWrapperByDhLevel.get(wrapper));
|
||||
wrapper.onUnload();
|
||||
|
||||
DhClientServerLevel clientServerLevel = this.dhLevelByLevelWrapper.remove(wrapper);
|
||||
DhClientServerLevel clientServerLevel = this.levelWrapperByDhLevel.remove(wrapper);
|
||||
clientServerLevel.close();
|
||||
this.dhLevels.remove(clientServerLevel);
|
||||
}
|
||||
@@ -118,7 +129,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
// If the level wrapper is a Client Level Wrapper, then that means the client side leaves the level,
|
||||
// but note that the server side still has the level loaded. So, we don't want to unload the level,
|
||||
// we just want to stop rendering it.
|
||||
this.dhLevelByLevelWrapper.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere.
|
||||
this.levelWrapperByDhLevel.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,12 +140,16 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
this.dhLevels.forEach(DhClientServerLevel::clientTick);
|
||||
}
|
||||
|
||||
@Override public void clientTick()
|
||||
public void clientTick()
|
||||
{
|
||||
//LOGGER.info("Client world tick");
|
||||
this.eventLoop.tick();
|
||||
}
|
||||
|
||||
public void serverTick() { this.dhLevels.forEach(DhClientServerLevel::serverTick); }
|
||||
|
||||
public void worldGenTick() { this.dhLevels.forEach(DhClientServerLevel::worldGenTick); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
@@ -145,25 +160,25 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
@Override
|
||||
public synchronized void close()
|
||||
{
|
||||
synchronized (this.dhLevels)
|
||||
// clear dhLevels to prevent concurrent modification errors
|
||||
HashSet<DhClientServerLevel> levelsToClose = new HashSet<>(this.dhLevels);
|
||||
this.dhLevels.clear();
|
||||
// close each level
|
||||
for (DhClientServerLevel level : levelsToClose)
|
||||
{
|
||||
// close each level
|
||||
for (DhClientServerLevel level : this.dhLevels)
|
||||
LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionName());
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionName());
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
}
|
||||
|
||||
this.dhLevelByLevelWrapper.clear();
|
||||
this.levelWrapperByDhLevel.clear();
|
||||
this.eventLoop.close();
|
||||
LOGGER.info("Closed DhWorld of type " + this.environment);
|
||||
}
|
||||
|
||||
@@ -19,16 +19,29 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.world;
|
||||
|
||||
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
|
||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||
public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
|
||||
{
|
||||
private final HashMap<IServerLevelWrapper, DhServerLevel> levels;
|
||||
public final LocalSaveStructure saveStructure;
|
||||
|
||||
public final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
@@ -36,10 +49,48 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||
public DhServerWorld()
|
||||
{
|
||||
super(EWorldEnvironment.Server_Only);
|
||||
|
||||
this.saveStructure = new LocalSaveStructure();
|
||||
this.levels = new HashMap<>();
|
||||
|
||||
this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler();
|
||||
|
||||
LOGGER.info("Started ["+DhServerWorld.class.getSimpleName()+"] of type ["+this.environment+"].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// player handling //
|
||||
//=================//
|
||||
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
ServerPlayerState playerState = this.remotePlayerConnectionHandler.registerJoinedPlayer(serverPlayer);
|
||||
this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer);
|
||||
|
||||
for (DhServerLevel level : this.levels.values())
|
||||
{
|
||||
level.registerNetworkHandlers(playerState);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer);
|
||||
this.remotePlayerConnectionHandler.unregisterLeftPlayer(serverPlayer);
|
||||
|
||||
// If player's left, session is already closed
|
||||
}
|
||||
|
||||
public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
|
||||
{
|
||||
this.getLevel(destinationLevel).addPlayer(player);
|
||||
this.getLevel(originLevel).removePlayer(player);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handling //
|
||||
//================//
|
||||
@@ -52,14 +103,30 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.dhLevelByLevelWrapper.computeIfAbsent(wrapper, (serverLevelWrapper) ->
|
||||
return this.levels.computeIfAbsent((IServerLevelWrapper) wrapper, (serverLevelWrapper) ->
|
||||
{
|
||||
File levelFile = this.saveStructure.getLevelFolder(wrapper);
|
||||
LodUtil.assertTrue(levelFile != null);
|
||||
return new DhServerLevel(this.saveStructure, (IServerLevelWrapper) serverLevelWrapper, this.getServerPlayerStateManager());
|
||||
return new DhServerLevel(this.saveStructure, serverLevelWrapper, this.remotePlayerConnectionHandler);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhServerLevel getLevel(@NotNull ILevelWrapper wrapper)
|
||||
{
|
||||
if (!(wrapper instanceof IServerLevelWrapper))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.levels.get(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.levels.values(); }
|
||||
@Override
|
||||
public int getLoadedLevelCount() { return this.levels.size(); }
|
||||
|
||||
@Override
|
||||
public void unloadLevel(@NotNull ILevelWrapper wrapper)
|
||||
{
|
||||
@@ -68,12 +135,51 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
||||
if (this.levels.containsKey(wrapper))
|
||||
{
|
||||
LOGGER.info("Unloading level {} ", this.dhLevelByLevelWrapper.get(wrapper));
|
||||
LOGGER.info("Unloading level {} ", this.levels.get(wrapper));
|
||||
wrapper.onUnload();
|
||||
this.dhLevelByLevelWrapper.remove(wrapper).close();
|
||||
this.levels.remove(wrapper).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); }
|
||||
|
||||
@Override
|
||||
public void worldGenTick() { this.levels.values().forEach(DhServerLevel::worldGenTick); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
for (DhServerLevel level : this.levels.values())
|
||||
{
|
||||
LOGGER.info("Unloading level [" + level.getLevelWrapper().getDimensionName() + "].");
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
}
|
||||
|
||||
this.levels.clear();
|
||||
LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,18 +20,11 @@
|
||||
package com.seibel.distanthorizons.core.world;
|
||||
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
|
||||
/** Used both for dedicated server and singleplayer worlds */
|
||||
public interface IDhServerWorld extends IDhWorld
|
||||
{
|
||||
ServerPlayerStateManager getServerPlayerStateManager();
|
||||
void addPlayer(IServerPlayerWrapper serverPlayer);
|
||||
void removePlayer(IServerPlayerWrapper serverPlayer);
|
||||
void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel);
|
||||
void serverTick();
|
||||
|
||||
default IDhServerLevel getOrLoadServerLevel(ILevelWrapper levelWrapper) { return (IDhServerLevel) this.getOrLoadLevel(levelWrapper); }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 11 KiB |
@@ -330,10 +330,6 @@
|
||||
"Enable Cloud Rendering",
|
||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering.@tooltip":
|
||||
"If true LOD clouds will be rendered.",
|
||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableInstancedRendering":
|
||||
"Enable Instanced Rendering",
|
||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableInstancedRendering.@tooltip":
|
||||
"Can be disabled to use much slower but more compatible direct rendering. \nDisabling this can be used to fix some crashes on Mac.",
|
||||
|
||||
|
||||
"distanthorizons.config.client.advanced.worldGenerator":
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 573 B |
@@ -1,372 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tests;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
public class FullDataChecksumStabilityTest
|
||||
{
|
||||
public static String DB_FILE_NAME_PREFIX = "DistantHorizonsTest-";
|
||||
public static String DB_FILE_NAME_SUFFIX = ".sqlite";
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// testing methods //
|
||||
//=================//
|
||||
|
||||
@Test
|
||||
public void testIdenticalDataSources()
|
||||
{
|
||||
System.out.println("--- FullDataChecksumStabilityTest - Identical data sources ---");
|
||||
|
||||
try
|
||||
{
|
||||
int checksum1, checksum2;
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("First database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum1 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("Second database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum2 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(checksum1, checksum2);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentMappingOrder()
|
||||
{
|
||||
System.out.println("--- FullDataChecksumStabilityTest - Different mapping order ---");
|
||||
|
||||
try
|
||||
{
|
||||
int checksum1, checksum2;
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("First database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum1 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("Second database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum2 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(checksum1, checksum2);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraMappingEntries()
|
||||
{
|
||||
System.out.println("--- FullDataChecksumStabilityTest - Different mapping order ---");
|
||||
|
||||
try
|
||||
{
|
||||
int checksum1, checksum2;
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("First database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("3"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum1 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
File uncompressedDatabaseFile = File.createTempFile(DB_FILE_NAME_PREFIX, DB_FILE_NAME_SUFFIX);
|
||||
System.out.println("Second database location: " + uncompressedDatabaseFile.getAbsolutePath());
|
||||
Assert.assertTrue(uncompressedDatabaseFile.exists());
|
||||
|
||||
try (FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile))
|
||||
{
|
||||
FullDataSourceV2 uncompressedDataSource = FullDataSourceV2.createEmpty(0);
|
||||
|
||||
int mappingEntryId1 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("1"), new TestBlockStateWrapper());
|
||||
int mappingEntryId2 = uncompressedDataSource.mapping.addIfNotPresentAndGetId(new TestBiomeWrapper("2"), new TestBlockStateWrapper());
|
||||
|
||||
uncompressedDataSource.setSingleColumn(
|
||||
new LongArrayList(new long[]{
|
||||
FullDataPointUtil.encode(mappingEntryId1, 1, 0, (byte) 15, (byte) 15),
|
||||
FullDataPointUtil.encode(mappingEntryId2, 1, 1, (byte) 15, (byte) 15),
|
||||
}),
|
||||
0, 0,
|
||||
EDhApiWorldGenerationStep.FEATURES,
|
||||
EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS
|
||||
);
|
||||
|
||||
uncompressedRepo.save(FullDataSourceV2DTO.CreateFromDataSource(uncompressedDataSource, EDhApiDataCompressionMode.UNCOMPRESSED));
|
||||
|
||||
checksum2 = uncompressedRepo.getByKey(0L).dataChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(checksum1, checksum2);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestBiomeWrapper implements IBiomeWrapper
|
||||
{
|
||||
private final String name;
|
||||
private TestBiomeWrapper(String nameSuffix)
|
||||
{
|
||||
this.name = "distanthorizons:testbiome" + nameSuffix;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
@Override
|
||||
public String getSerialString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
@Override
|
||||
public Object getWrappedMcObject()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestBlockStateWrapper implements IBlockStateWrapper
|
||||
{
|
||||
@Override
|
||||
public String getSerialString()
|
||||
{
|
||||
return "BLOCKSTATE";
|
||||
}
|
||||
@Override
|
||||
public int getOpacity()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getLightEmission()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public byte getMaterialId()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public boolean isBeaconBlock()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isBeaconTintBlock()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isBeaconBaseBlock()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Color getMapColor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Color getBeaconTintColor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean isAir()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isSolid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean isLiquid()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Object getWrappedMcObject()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||