mirror of
https://gitlab.com/distant-horizons-team/distant-horizons-server-plugin.git
synced 2026-11-05 17:02:33 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0004539b41 | ||
|
|
9ef0f09dd2 | ||
|
|
79cb5aa302 | ||
|
|
5e9760c3d1 | ||
|
|
637636b5e8 | ||
|
|
0ffe620f92 | ||
|
|
2bcf9c7ca9 | ||
|
|
222221f9dc | ||
|
|
d086458546 | ||
|
|
12b1580535 | ||
|
|
81326b415d | ||
|
|
328684cf1d | ||
|
|
d89cfbfec5 | ||
|
|
413e2a6e70 | ||
|
|
ee385ea5af | ||
|
|
37db471c4f | ||
|
|
3e9165b7a3 | ||
|
|
da893ec1f0 | ||
|
|
320c04aeb7 | ||
|
|
fb8f98c52f | ||
|
|
99cee46ae5 | ||
|
|
a05ee01b8b |
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'no.jckf.dhs'
|
||||
version = '0.13.1'
|
||||
version = '0.14.0'
|
||||
description = 'Distant Horizons Support'
|
||||
|
||||
java {
|
||||
|
||||
@@ -24,9 +24,9 @@ import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import no.jckf.dhsupport.core.configuration.WorldConfiguration;
|
||||
import no.jckf.dhsupport.core.scheduling.Scheduler;
|
||||
import no.jckf.dhsupport.core.world.MaterialRecord;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Beacon;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
@@ -58,7 +58,14 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
|
||||
protected boolean trustHeightMap = true;
|
||||
|
||||
protected Map<String, ChunkSnapshot> chunks = new HashMap<>();
|
||||
private Integer sectionX;
|
||||
private Integer sectionZ;
|
||||
|
||||
private final ChunkSnapshot[] chunks = new ChunkSnapshot[16];
|
||||
|
||||
protected EnumMap<Material, MaterialRecord> materialCache = new EnumMap<>(Material.class);
|
||||
|
||||
protected Map<String, Map<String, String>> propertyCache = new HashMap<>();
|
||||
|
||||
protected UnsafeValues unsafeValues;
|
||||
|
||||
@@ -147,13 +154,26 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
String key = chunkX + "x" + chunkZ;
|
||||
int sectionX = Coordinates.chunkToSection(chunkX);
|
||||
int sectionZ = Coordinates.chunkToSection(chunkZ);
|
||||
|
||||
if (this.chunks.containsKey(key)) {
|
||||
return this.chunks.get(key);
|
||||
if (this.sectionX == null) {
|
||||
this.sectionX = sectionX;
|
||||
this.sectionZ = sectionZ;
|
||||
} else if (this.sectionX != sectionX || this.sectionZ != sectionZ) {
|
||||
throw new IllegalStateException("We appear to be leaking world interface instances across LOD sections");
|
||||
}
|
||||
|
||||
ChunkSnapshot chunk;
|
||||
int baseChunkX = Coordinates.sectionToChunk(sectionX);
|
||||
int baseChunkZ = Coordinates.sectionToChunk(sectionZ);
|
||||
|
||||
int index = (chunkX - baseChunkX) + ((chunkZ - baseChunkZ) << 2);
|
||||
|
||||
ChunkSnapshot chunk = this.chunks[index];
|
||||
|
||||
if (chunk != null) {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
Scheduler scheduler = this.plugin.getDhSupport().getScheduler();
|
||||
|
||||
@@ -165,11 +185,16 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
).join();
|
||||
}
|
||||
|
||||
this.chunks.put(key, chunk);
|
||||
this.chunks[index] = chunk;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public boolean isInsideSection(int x, int z)
|
||||
{
|
||||
return Coordinates.blockToSection(x) == this.sectionX && Coordinates.blockToSection(z) == this.sectionZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId()
|
||||
{
|
||||
@@ -503,7 +528,7 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
}
|
||||
|
||||
if (this.isDummyMode()) {
|
||||
return Collections.max(this.getDummyColumn().keySet());
|
||||
return this.getDummyColumn().isEmpty() ? this.getMinY() : Collections.max(this.getDummyColumn().keySet());
|
||||
}
|
||||
|
||||
return this.getChunk(x, z).getHighestBlockYAt(Coordinates.blockToChunkRelative(x), Coordinates.blockToChunkRelative(z));
|
||||
@@ -540,45 +565,98 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
public String getMaterialAt(int x, int y, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return this.getDummyMaterialAt(y);
|
||||
return Utils.namespaceKey(this.getDummyMaterialAt(y));
|
||||
}
|
||||
|
||||
String material = this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getKey().toString();
|
||||
return this.getMaterialRecordAt(x, y, z).toString();
|
||||
}
|
||||
|
||||
return Utils.namespaceKey(this.config.getString(DhsConfig.MATERIAL_MAP + "." + Utils.unNamespaceKey(material), material));
|
||||
public MaterialRecord getMaterialRecordAt(int x, int y, int z)
|
||||
{
|
||||
return this.materialCache.computeIfAbsent(
|
||||
this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)),
|
||||
(mat) -> {
|
||||
String key = Utils.unNamespaceKey(mat.getKey().toString());
|
||||
String mapped = this.config.getString(DhsConfig.MATERIAL_MAP + '.' + key, key);
|
||||
|
||||
return new MaterialRecord(
|
||||
Utils.namespaceKey(mapped == null ? key : mapped),
|
||||
!key.equals(mapped)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBlockStateAsStringAt(int x, int y, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return "";
|
||||
MaterialRecord materialRecord = this.getMaterialRecordAt(x, y, z);
|
||||
|
||||
if (this.isDummyMode() || materialRecord.isMapped()) {
|
||||
return materialRecord.toString();
|
||||
}
|
||||
|
||||
return this.getChunk(x, z).getBlockData(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getAsString();
|
||||
Map<String, String> properties = this.getBlockPropertiesAt(x, y, z);
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
return materialRecord.toString();
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append(materialRecord.toString())
|
||||
.append('[');
|
||||
|
||||
for (Map.Entry<String, String> property : properties.entrySet()) {
|
||||
builder.append(property.getKey())
|
||||
.append('=')
|
||||
.append(property.getValue())
|
||||
.append(',');
|
||||
}
|
||||
|
||||
builder.setLength(builder.length() - 1);
|
||||
builder.append(']');
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getBlockPropertiesAt(int x, int y, int z)
|
||||
{
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
if (this.isDummyMode()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String dataString = this.getBlockStateAsStringAt(x, y, z);
|
||||
String dataString = this.getChunk(x, z).getBlockData(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getAsString();
|
||||
|
||||
int kvStart = dataString.indexOf("[");
|
||||
Map<String, String> cached = this.propertyCache.get(dataString);
|
||||
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
int kvStart = dataString.indexOf('[');
|
||||
|
||||
if (kvStart == -1) {
|
||||
return properties;
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String[] kvStrings = dataString.substring(kvStart + 1, dataString.length() - 1).split(",");
|
||||
|
||||
Arrays.sort(kvStrings);
|
||||
|
||||
Map<String, String> properties = new LinkedHashMap<>(kvStrings.length);
|
||||
|
||||
for (String kvString : kvStrings) {
|
||||
String[] kv = kvString.split("=", 2);
|
||||
|
||||
properties.put(kv[0], kv[1]);
|
||||
}
|
||||
|
||||
properties = Collections.unmodifiableMap(properties);
|
||||
|
||||
this.propertyCache.put(dataString, properties);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -615,15 +693,63 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getMaterialAt(x, y, z).equals("minecraft:beacon")) {
|
||||
if (!this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).equals(Material.BEACON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.plugin.getDhSupport().getScheduler()
|
||||
.runOnRegionThread(this.world.getUID(), x, z, () ->
|
||||
((Beacon) this.world.getBlockAt(x, y, z).getState()).getTier() > 0
|
||||
)
|
||||
.join();
|
||||
return this.hasTierOneBeaconBase(x, y, z) && this.hasClearBeaconBeamPath(x, y, z);
|
||||
}
|
||||
|
||||
public boolean hasTierOneBeaconBase(int x, int y, int z)
|
||||
{
|
||||
int baseY = y - 1;
|
||||
|
||||
if (baseY < this.getMinY()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int checkX = x - 1; checkX <= x + 1; checkX++) {
|
||||
for (int checkZ = z - 1; checkZ <= z + 1; checkZ++) {
|
||||
if (this.isInsideSection(checkX, checkZ) && !this.isBeaconBaseBlock(this.getMaterialAt(checkX, baseY, checkZ))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isBeaconBaseBlock(String material)
|
||||
{
|
||||
switch (material) {
|
||||
case "minecraft:iron_block":
|
||||
case "minecraft:gold_block":
|
||||
case "minecraft:diamond_block":
|
||||
case "minecraft:emerald_block":
|
||||
case "minecraft:netherite_block":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasClearBeaconBeamPath(int x, int y, int z)
|
||||
{
|
||||
for (int checkY = y + 1; checkY < this.getMaxY(); checkY++) {
|
||||
String material = this.getMaterialAt(x, checkY, z);
|
||||
|
||||
switch (material) {
|
||||
case "minecraft:air":
|
||||
case "minecraft:cave_air":
|
||||
case "minecraft:void_air":
|
||||
case "minecraft:glass":
|
||||
continue;
|
||||
default:
|
||||
return material.endsWith("_stained_glass");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,6 +30,7 @@ import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.MultiLineChart;
|
||||
import org.bstats.charts.SingleLineChart;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
@@ -65,9 +66,11 @@ public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
|
||||
}
|
||||
|
||||
this.getLogger().info("I am DHS " + pluginVersion + ".");
|
||||
String gameVersion = this.getGameVersion();
|
||||
|
||||
this.dhSupport = new DhSupport(pluginVersion, this.getServer().getVersion());
|
||||
this.getLogger().info("I am DHS " + pluginVersion + " running on " + this.getServer().getName() + " for Minecraft " + gameVersion + ".");
|
||||
|
||||
this.dhSupport = new DhSupport(pluginVersion, gameVersion);
|
||||
this.dhSupport.setLogger(this.getLogger());
|
||||
this.dhSupport.setDataDirectory(this.getDataFolder().getAbsolutePath());
|
||||
|
||||
@@ -161,6 +164,21 @@ public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getGameVersion()
|
||||
{
|
||||
try {
|
||||
Class<? extends Server> serverClass = this.getServer().getClass();
|
||||
|
||||
serverClass.getMethod("getMinecraftVersion");
|
||||
|
||||
return this.getServer().getMinecraftVersion();
|
||||
} catch (NoSuchMethodException exception) {
|
||||
// If we're here, that probably means we're not running Paper or any Paper fork.
|
||||
}
|
||||
|
||||
return this.getServer().getBukkitVersion().split("-", 2)[0];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DhSupport getDhSupport()
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlayerHandler implements Listener
|
||||
@@ -51,7 +52,7 @@ public class PlayerHandler implements Listener
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String message : dhSupport.getConfig().getStringList(DhsConfig.AD_MESSAGE, new ArrayList<>())) {
|
||||
for (String message : dhSupport.getConfig().getStringList(DhsConfig.AD_MESSAGE, Collections.emptyList())) {
|
||||
player.sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
package no.jckf.dhsupport.core;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
// Block -> chunk -> section -> region
|
||||
public class Coordinates
|
||||
{
|
||||
@@ -101,4 +103,39 @@ public class Coordinates
|
||||
{
|
||||
return value & 0x0F;
|
||||
}
|
||||
|
||||
// PACK/UNPACK
|
||||
|
||||
public static long pack(int x, int z)
|
||||
{
|
||||
return ((long) x & 0xffffffffL)
|
||||
| (((long) z & 0xffffffffL) << 32);
|
||||
}
|
||||
|
||||
public static int unpackX(long packed)
|
||||
{
|
||||
return (int) packed;
|
||||
}
|
||||
|
||||
public static int unpackZ(long packed)
|
||||
{
|
||||
return (int) (packed >> 32);
|
||||
}
|
||||
|
||||
// ???
|
||||
|
||||
public static void foreachChunkInSection(int sectionX, int sectionZ, BiConsumer<Integer, Integer> consumer)
|
||||
{
|
||||
int worldX = sectionToBlock(sectionX);
|
||||
int worldZ = sectionToBlock(sectionZ);
|
||||
|
||||
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
|
||||
for (int zMultiplier = 0; zMultiplier < 4; zMultiplier++) {
|
||||
consumer.accept(
|
||||
worldX + 16 * xMultiplier,
|
||||
worldZ + 16 * zMultiplier
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,24 +400,19 @@ public class DhSupport implements Configurable
|
||||
|
||||
boolean generateNewChunks = world.getConfig().getBool(DhsConfig.GENERATE_NEW_CHUNKS, true);
|
||||
|
||||
Map<String, CompletableFuture<Boolean>> loads = new HashMap<>();
|
||||
Map<Long, CompletableFuture<Boolean>> loads = new HashMap<>();
|
||||
|
||||
if (manageChunks) {
|
||||
// Load all the chunks we need for this request.
|
||||
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
|
||||
for (int zMultiplier = 0; zMultiplier < 4; zMultiplier++) {
|
||||
int chunkX = worldX + 16 * xMultiplier;
|
||||
int chunkZ = worldZ + 16 * zMultiplier;
|
||||
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
if (generateNewChunks) {
|
||||
loads.put(worldX + "x" + worldZ, world.loadOrGenerateChunkAsync(chunkX, chunkZ));
|
||||
} else {
|
||||
loads.put(worldX + "x" + worldZ, world.loadChunkAsync(chunkX, chunkZ));
|
||||
}
|
||||
Coordinates.foreachChunkInSection(position.getX(), position.getZ(), (chunkX, chunkZ) -> {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
if (generateNewChunks) {
|
||||
loads.put(Coordinates.pack(chunkX, chunkZ), world.loadOrGenerateChunkAsync(chunkX, chunkZ));
|
||||
} else {
|
||||
loads.put(Coordinates.pack(chunkX, chunkZ), world.loadChunkAsync(chunkX, chunkZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for chunk loads, then...
|
||||
@@ -436,52 +431,36 @@ public class DhSupport implements Configurable
|
||||
.anyMatch(Predicate.isEqual(false));
|
||||
|
||||
if (loadRejected) {
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (String key : loads.keySet()) {
|
||||
String[] xz = key.split("x", 2);
|
||||
|
||||
world.discardChunk(
|
||||
Integer.parseInt(xz[0]),
|
||||
Integer.parseInt(xz[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// If no dummy chunk is configured, reject the build request.
|
||||
if (world.getConfig().getStringList(DhsConfig.DUMMY_CHUNK).isEmpty()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
// Use dummy chunks instead of real world data.
|
||||
world.setDummyMode(true);
|
||||
} else {
|
||||
// Prime chunk data in world interface.
|
||||
Coordinates.foreachChunkInSection(position.getX(), position.getZ(), (chunkX, chunkZ) -> {
|
||||
world.getMaterialAt(chunkX, world.getSeaLevel(), chunkZ);
|
||||
});
|
||||
}
|
||||
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (long packed : loads.keySet()) {
|
||||
world.discardChunk(
|
||||
Coordinates.unpackX(packed),
|
||||
Coordinates.unpackZ(packed)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// No LOD was found. Start building a new one.
|
||||
CompletableFuture<Lod> lodFuture = this.queueBuilder(worldId, position, this.getBuilder(world, position));
|
||||
|
||||
// Combine the LOD and beacons and save the result in the database.
|
||||
return lodFuture.thenApply((lod) -> {
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (String key : loads.keySet()) {
|
||||
String[] xz = key.split("x", 2);
|
||||
|
||||
world.discardChunk(
|
||||
Integer.parseInt(xz[0]),
|
||||
Integer.parseInt(xz[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
Encoder lodEncoder = new Encoder();
|
||||
Encoder lodEncoder = new Encoder(8192);
|
||||
lod.encode(lodEncoder);
|
||||
|
||||
Encoder beaconEncoder = new Encoder();
|
||||
Encoder beaconEncoder = new Encoder(16 + lod.getBeacons().size() * 16);
|
||||
beaconEncoder.writeCollection(lod.getBeacons());
|
||||
|
||||
this.generationTracker.ping();
|
||||
@@ -592,7 +571,7 @@ public class DhSupport implements Configurable
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!playerConfig.getBool(DhsConfig.DISTANT_GENERATION_ENABLED) || !playerConfig.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED)) {
|
||||
if (!playerConfig.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -627,11 +606,11 @@ public class DhSupport implements Configurable
|
||||
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
|
||||
chunkResponse.setBufferId(myBufferId);
|
||||
chunkResponse.setIsFirst(chunkNo == 0);
|
||||
chunkResponse.setData(Arrays.copyOfRange(
|
||||
chunkResponse.setData(
|
||||
data,
|
||||
LodHandler.CHUNK_SIZE * chunkNo,
|
||||
Math.min(LodHandler.CHUNK_SIZE * chunkNo + LodHandler.CHUNK_SIZE, data.length)
|
||||
));
|
||||
Math.min(LodHandler.CHUNK_SIZE, data.length - LodHandler.CHUNK_SIZE * chunkNo)
|
||||
);
|
||||
|
||||
this.pluginMessageHandler.sendPluginMessage(player.getUniqueId(), chunkResponse);
|
||||
}
|
||||
@@ -697,7 +676,7 @@ public class DhSupport implements Configurable
|
||||
{
|
||||
WorldInterface world = this.getWorldInterface(worldId);
|
||||
|
||||
List<SectionPosition> positions = new ArrayList<>();
|
||||
List<SectionPosition> positions = new ArrayList<>(concurrency);
|
||||
|
||||
for (int i = 0; i < concurrency; i++) {
|
||||
SectionPosition position = new SectionPosition();
|
||||
@@ -713,7 +692,7 @@ public class DhSupport implements Configurable
|
||||
.toArray(CompletableFuture[]::new)
|
||||
)
|
||||
.thenCompose((foo) -> {
|
||||
List<CompletableFuture<?>> chains = new ArrayList<>();
|
||||
List<CompletableFuture<?>> chains = new ArrayList<>(positions.size());
|
||||
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public class PreGenerator implements Runnable
|
||||
int[][] directions = {{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }};
|
||||
int dirIndex = 0;
|
||||
|
||||
List<CompletableFuture<LodModel>> requests = new ArrayList<>();
|
||||
List<CompletableFuture<LodModel>> requests = new ArrayList<>(rateLimit);
|
||||
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
@@ -21,11 +21,16 @@ package no.jckf.dhsupport.core;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Utils
|
||||
{
|
||||
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final Map<String, String> NAMESPACE_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, String> UNNAMESPACE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public static String bytesToHex(byte[] bytes)
|
||||
{
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
@@ -115,9 +120,12 @@ public class Utils
|
||||
return builder.substring(0, builder.length() - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated For awareness: This method caches results by key only.
|
||||
*/
|
||||
public static String namespaceKey(String key, String defaultNamespace)
|
||||
{
|
||||
return key.contains(":") ? key : defaultNamespace + ":" + key;
|
||||
return NAMESPACE_CACHE.computeIfAbsent(key, (k) -> k.contains(":") ? k : defaultNamespace + ":" + k);
|
||||
}
|
||||
|
||||
public static String namespaceKey(String key)
|
||||
@@ -125,9 +133,12 @@ public class Utils
|
||||
return namespaceKey(key, "minecraft");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated For awareness: This method caches results by key only.
|
||||
*/
|
||||
public static String unNamespaceKey(String key, String namespace)
|
||||
{
|
||||
return key.startsWith(namespace + ":") ? key.substring(namespace.length() + 1) : key;
|
||||
return UNNAMESPACE_CACHE.computeIfAbsent(key, (k) -> k.startsWith(namespace + ":") ? k.substring(namespace.length() + 1) : k);
|
||||
}
|
||||
|
||||
public static String unNamespaceKey(String key)
|
||||
|
||||
@@ -110,7 +110,7 @@ public class Decoder
|
||||
{
|
||||
int size = this.readInt();
|
||||
|
||||
List<T> items = new ArrayList<>();
|
||||
List<T> items = new ArrayList<>(Math.min(size, 128));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
T item = this.readObject(className);
|
||||
|
||||
@@ -33,11 +33,21 @@ public class Encoder
|
||||
this.output = ByteStreams.newDataOutput();
|
||||
}
|
||||
|
||||
public Encoder(int size)
|
||||
{
|
||||
this.output = ByteStreams.newDataOutput(size);
|
||||
}
|
||||
|
||||
public void write(byte[] data)
|
||||
{
|
||||
this.output.write(data);
|
||||
}
|
||||
|
||||
public void write(byte[] data, int offset, int length)
|
||||
{
|
||||
this.output.write(data, offset, length);
|
||||
}
|
||||
|
||||
public void writeByte(byte value)
|
||||
{
|
||||
this.output.writeByte(value);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -35,6 +36,11 @@ public class Configuration
|
||||
this.variables.clear();
|
||||
}
|
||||
|
||||
public boolean has(String key)
|
||||
{
|
||||
return this.variables.containsKey(key);
|
||||
}
|
||||
|
||||
public void set(String key, @Nullable Object value)
|
||||
{
|
||||
if (value instanceof String stringValue) {
|
||||
@@ -106,7 +112,7 @@ public class Configuration
|
||||
|
||||
public List<String> getStringList(String key)
|
||||
{
|
||||
return this.getStringList(key, new ArrayList<>());
|
||||
return this.getStringList(key, Collections.emptyList());
|
||||
}
|
||||
|
||||
public int increment(String key)
|
||||
|
||||
@@ -42,6 +42,10 @@ public class Lod extends DataObject
|
||||
|
||||
public static int width = 64;
|
||||
|
||||
protected static volatile byte[] compressedGenerationStep;
|
||||
|
||||
protected static volatile byte[] compressedWorldCompressionType;
|
||||
|
||||
protected WorldInterface worldInterface;
|
||||
|
||||
protected SectionPosition position;
|
||||
@@ -63,7 +67,7 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeData(Encoder encoder)
|
||||
{
|
||||
Encoder toCompress = new Encoder();
|
||||
Encoder toCompress = new Encoder(8192);
|
||||
|
||||
for (List<DataPoint> column : this.columns) {
|
||||
toCompress.writeShort(column.size());
|
||||
@@ -89,10 +93,15 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeColumnGenerationStep(Encoder encoder)
|
||||
{
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width];
|
||||
Arrays.fill(bytesToCompress, (byte) GenerationStep.LIGHT.value);
|
||||
byte[] compressed = Lod.compressedGenerationStep;
|
||||
|
||||
byte[] compressed = this.compress(bytesToCompress);
|
||||
if (compressed == null) {
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width];
|
||||
Arrays.fill(bytesToCompress, (byte) GenerationStep.LIGHT.value);
|
||||
|
||||
compressed = this.compress(bytesToCompress);
|
||||
Lod.compressedGenerationStep = compressed;
|
||||
}
|
||||
|
||||
encoder.writeInt(compressed.length);
|
||||
encoder.write(compressed);
|
||||
@@ -100,9 +109,14 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeWorldCompressionType(Encoder encoder)
|
||||
{
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width]; // All zeroes means WorldCompressionType.STRICT
|
||||
byte[] compressed = Lod.compressedWorldCompressionType;
|
||||
|
||||
byte[] compressed = this.compress(bytesToCompress);
|
||||
if (compressed == null) {
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width]; // All zeroes means WorldCompressionType.STRICT
|
||||
|
||||
compressed = this.compress(bytesToCompress);
|
||||
Lod.compressedWorldCompressionType = compressed;
|
||||
}
|
||||
|
||||
encoder.writeInt(compressed.length);
|
||||
encoder.write(compressed);
|
||||
@@ -110,7 +124,7 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeMappings(Encoder encoder)
|
||||
{
|
||||
Encoder toCompress = new Encoder();
|
||||
Encoder toCompress = new Encoder(1024);
|
||||
|
||||
toCompress.writeInt(this.idMappings.size());
|
||||
|
||||
@@ -155,7 +169,11 @@ public class Lod extends DataObject
|
||||
ByteArrayOutputStream compressedStream = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
OutputStream compressorStream = new XZOutputStream(compressedStream, new LZMA2Options(3), XZ.CHECK_CRC64);
|
||||
LZMA2Options options = new LZMA2Options(3);
|
||||
|
||||
options.setDictSize(Math.max(LZMA2Options.DICT_SIZE_MIN, Math.min(uncompressedData.length, 1 << 20)));
|
||||
|
||||
OutputStream compressorStream = new XZOutputStream(compressedStream, options, XZ.CHECK_CRC64);
|
||||
|
||||
compressorStream.write(uncompressedData);
|
||||
compressorStream.flush();
|
||||
|
||||
@@ -30,7 +30,6 @@ import no.jckf.dhsupport.core.message.plugin.FullDataSourceResponseMessage;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LodHandler
|
||||
@@ -174,11 +173,11 @@ public class LodHandler
|
||||
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
|
||||
chunkResponse.setBufferId(myBufferId);
|
||||
chunkResponse.setIsFirst(chunkNo == 0);
|
||||
chunkResponse.setData(Arrays.copyOfRange(
|
||||
chunkResponse.setData(
|
||||
data,
|
||||
CHUNK_SIZE * chunkNo,
|
||||
Math.min(CHUNK_SIZE * chunkNo + CHUNK_SIZE, data.length)
|
||||
));
|
||||
Math.min(CHUNK_SIZE, data.length - CHUNK_SIZE * chunkNo)
|
||||
);
|
||||
|
||||
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), chunkResponse);
|
||||
}
|
||||
|
||||
@@ -22,12 +22,17 @@ import no.jckf.dhsupport.core.DhSupport;
|
||||
import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import no.jckf.dhsupport.core.message.plugin.LevelInitMessage;
|
||||
import no.jckf.dhsupport.core.message.plugin.LevelInitRequestMessage;
|
||||
import no.jckf.dhsupport.core.message.plugin.RemotePlayerConfigMessage;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerConfigHandler
|
||||
{
|
||||
public final static String USAGE_PERMISSION = "distant_horizons.worlds.%s";
|
||||
@@ -44,14 +49,28 @@ public class PlayerConfigHandler
|
||||
|
||||
public void register()
|
||||
{
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(RemotePlayerConfigMessage.class, (configMessage) -> {
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(LevelInitRequestMessage.class, (initRequestMessage) -> {
|
||||
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
|
||||
Player player = Bukkit.getPlayer(configMessage.getSender());
|
||||
Player player = Bukkit.getPlayer(initRequestMessage.getSender());
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(player.getWorld().getUID());
|
||||
NamespacedKey worldKey = NamespacedKey.fromString(initRequestMessage.getWorldKey());
|
||||
|
||||
String permission = USAGE_PERMISSION.formatted(world.getName());
|
||||
boolean dhUseIsAllowed = !player.isPermissionSet(permission) || player.hasPermission(permission);
|
||||
// TODO: This too 👎
|
||||
UUID worldUuid = null;
|
||||
|
||||
for (World world : Bukkit.getServer().getWorlds()) {
|
||||
if (world.getKey().equals(worldKey)) {
|
||||
worldUuid = world.getUID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (worldUuid == null) {
|
||||
this.dhSupport.warning("Player " + player.getName() + " requested init for unknown world " + worldKey + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(worldUuid);
|
||||
|
||||
Configuration dhsConfig = world.getConfig();
|
||||
|
||||
@@ -62,13 +81,24 @@ public class PlayerConfigHandler
|
||||
levelKey = levelKeyPrefix + levelKey;
|
||||
}
|
||||
|
||||
//this.dhSupport.info("Received DH config for " + player.getName() + " in " + levelKey);
|
||||
|
||||
LevelInitMessage levelInitResponse = new LevelInitMessage();
|
||||
levelInitResponse.setNamespacedKey(worldKey.toString());
|
||||
levelInitResponse.setServerKey(dhsConfig.getString(DhsConfig.SERVER_KEY, ""));
|
||||
levelInitResponse.setLevelKey(levelKey);
|
||||
levelInitResponse.setTime(System.currentTimeMillis());
|
||||
this.pluginMessageHandler.sendPluginMessage(configMessage.getSender(), levelInitResponse);
|
||||
this.pluginMessageHandler.sendPluginMessage(initRequestMessage.getSender(), levelInitResponse);
|
||||
});
|
||||
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(RemotePlayerConfigMessage.class, (configMessage) -> {
|
||||
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
|
||||
Player player = Bukkit.getPlayer(configMessage.getSender());
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(player.getWorld().getUID());
|
||||
|
||||
String permission = USAGE_PERMISSION.formatted(world.getName());
|
||||
boolean dhUseIsAllowed = !player.isPermissionSet(permission) || player.hasPermission(permission);
|
||||
|
||||
Configuration dhsConfig = world.getConfig();
|
||||
|
||||
Configuration clientConfig = configMessage.toConfiguration();
|
||||
|
||||
@@ -92,6 +122,10 @@ public class PlayerConfigHandler
|
||||
}
|
||||
}
|
||||
|
||||
if (key.equals(DhsConfig.REAL_TIME_UPDATES_ENABLED) && !dhUseIsAllowed) {
|
||||
keepValue = false;
|
||||
}
|
||||
|
||||
// TODO: This is ugly. Move to comparator closures like in DH.
|
||||
if (key.equals(DhsConfig.BORDER_CENTER_X)) {
|
||||
keepValue = world.getWorldBorderX();
|
||||
|
||||
@@ -38,7 +38,7 @@ public class PluginMessageHandler
|
||||
|
||||
public final String pluginChannel = "distant_horizons:msg";
|
||||
|
||||
public final short protocolVersion = 13;
|
||||
public final short protocolVersion = 15;
|
||||
|
||||
private EventBus<PluginMessage> eventBus;
|
||||
|
||||
@@ -53,13 +53,14 @@ public class PluginMessageHandler
|
||||
this.messageTypeRegistry.registerMessageType(0, null);
|
||||
this.messageTypeRegistry.registerMessageType(1, CloseReasonMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(2, LevelInitMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(3, RemotePlayerConfigMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(4, CancelMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(5, ExceptionMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(6, FullDataSourceRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(7, FullDataSourceResponseMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(8, FullDataPartialUpdateMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(9, FullDataChunkMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(3, LevelInitRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(4, RemotePlayerConfigMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(5, CancelMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(6, ExceptionMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(7, FullDataSourceRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(8, FullDataSourceResponseMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(9, FullDataPartialUpdateMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(10, FullDataChunkMessage.class);
|
||||
}
|
||||
|
||||
public void onEnable()
|
||||
@@ -153,19 +154,7 @@ public class PluginMessageHandler
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
|
||||
try {
|
||||
Encoder encoder = new Encoder();
|
||||
message.encode(encoder);
|
||||
data = encoder.toByteArray();
|
||||
} catch (Exception exception) {
|
||||
this.dhSupport.warning("Failed to encode " + message.getClass().getSimpleName());
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
Encoder encoder = new Encoder();
|
||||
Encoder encoder = new Encoder(message.bufferSize());
|
||||
|
||||
encoder.writeShort(this.protocolVersion);
|
||||
encoder.writeShort(messageTypeId);
|
||||
@@ -174,7 +163,13 @@ public class PluginMessageHandler
|
||||
encoder.writeInt(((TrackablePluginMessage) message).getTracker());
|
||||
}
|
||||
|
||||
encoder.write(data);
|
||||
try {
|
||||
message.encode(encoder);
|
||||
} catch (Exception exception) {
|
||||
this.dhSupport.warning("Failed to encode " + message.getClass().getSimpleName());
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] fullMessage = encoder.toByteArray();
|
||||
|
||||
|
||||
@@ -56,12 +56,12 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
boolean includeNonCollidingTopLayer = this.worldInterface.getConfig().getBool(DhsConfig.INCLUDE_NON_COLLIDING_TOP_LAYER, true);
|
||||
boolean sampleBiomes3d = this.worldInterface.getConfig().getBool(DhsConfig.SAMPLE_BIOMES_3D, false);
|
||||
|
||||
List<IdMapping> idMappings = new ArrayList<>();
|
||||
Map<String, Integer> mapMap = new HashMap<>();
|
||||
List<IdMapping> idMappings = new ArrayList<>(64);
|
||||
Map<String, Map<String, Integer>> mapMap = new HashMap<>();
|
||||
|
||||
List<List<DataPoint>> columns = new ArrayList<>();
|
||||
List<List<DataPoint>> columns = new ArrayList<>(Lod.width * Lod.width);
|
||||
|
||||
List<Beacon> beacons = new ArrayList<>();
|
||||
List<Beacon> beacons = new ArrayList<>(8);
|
||||
|
||||
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
|
||||
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
|
||||
@@ -100,7 +100,7 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
}
|
||||
|
||||
List<DataPoint> column = new ArrayList<>();
|
||||
List<DataPoint> column = new ArrayList<>(8);
|
||||
|
||||
@Nullable
|
||||
DataPoint previous = null;
|
||||
@@ -158,15 +158,18 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
biome = this.worldInterface.getBiomeAt(worldX, highWorldY, worldZ);
|
||||
}
|
||||
|
||||
String compositeKey = biome + "|" + material;
|
||||
Map<String, Integer> biomeMap = mapMap.get(biome);
|
||||
if (biomeMap == null) {
|
||||
biomeMap = new HashMap<>();
|
||||
mapMap.put(biome, biomeMap);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer id = mapMap.get(compositeKey);
|
||||
Integer id = biomeMap.get(material);
|
||||
|
||||
if (id == null) {
|
||||
idMappings.add(new IdMapping(biome, material, null));
|
||||
id = idMappings.size() - 1;
|
||||
mapMap.put(compositeKey, id);
|
||||
biomeMap.put(material, id);
|
||||
}
|
||||
|
||||
DataPoint point;
|
||||
|
||||
@@ -52,12 +52,12 @@ public class FullBuilder extends LodBuilder
|
||||
boolean performUnderglowHack = this.worldInterface.getConfig().getBool(DhsConfig.PERFORM_UNDERGLOW_HACK, false);
|
||||
boolean sampleBiomes3d = this.worldInterface.getConfig().getBool(DhsConfig.SAMPLE_BIOMES_3D, false);
|
||||
|
||||
List<IdMapping> idMappings = new ArrayList<>();
|
||||
Map<String, Integer> mapMap = new HashMap<>();
|
||||
List<IdMapping> idMappings = new ArrayList<>(64);
|
||||
Map<String, Map<String, Map<String, Integer>>> mapMap = new HashMap<>();
|
||||
|
||||
List<List<DataPoint>> columns = new ArrayList<>();
|
||||
List<List<DataPoint>> columns = new ArrayList<>(Lod.width * Lod.width);
|
||||
|
||||
List<Beacon> beacons = new ArrayList<>();
|
||||
List<Beacon> beacons = new ArrayList<>(8);
|
||||
|
||||
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
|
||||
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
|
||||
@@ -96,7 +96,7 @@ public class FullBuilder extends LodBuilder
|
||||
biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
}
|
||||
|
||||
List<DataPoint> column = new ArrayList<>();
|
||||
List<DataPoint> column = new ArrayList<>(32);
|
||||
|
||||
@Nullable
|
||||
DataPoint previous = null;
|
||||
@@ -138,15 +138,26 @@ public class FullBuilder extends LodBuilder
|
||||
biome = this.worldInterface.getBiomeAt(worldX, highWorldY, worldZ);
|
||||
}
|
||||
|
||||
String compositeKey = biome + "|" + material + "|" + this.worldInterface.getBlockStateAsStringAt(worldX, highWorldY, worldZ);
|
||||
Map<String, Map<String, Integer>> biomeMap = mapMap.get(biome);
|
||||
if (biomeMap == null) {
|
||||
biomeMap = new HashMap<>();
|
||||
mapMap.put(biome, biomeMap);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer id = mapMap.get(compositeKey);
|
||||
Map<String, Integer> materialMap = biomeMap.get(material);
|
||||
if (materialMap == null) {
|
||||
materialMap = new HashMap<>();
|
||||
biomeMap.put(material, materialMap);
|
||||
}
|
||||
|
||||
String blockState = this.worldInterface.getBlockStateAsStringAt(worldX, highWorldY, worldZ);
|
||||
|
||||
Integer id = materialMap.get(blockState);
|
||||
|
||||
if (id == null) {
|
||||
idMappings.add(new IdMapping(biome, material, this.worldInterface.getBlockPropertiesAt(worldX, highWorldY, worldZ)));
|
||||
id = idMappings.size() - 1;
|
||||
mapMap.put(compositeKey, id);
|
||||
materialMap.put(blockState, id);
|
||||
}
|
||||
|
||||
DataPoint point;
|
||||
|
||||
@@ -25,6 +25,11 @@ import no.jckf.dhsupport.core.bytestream.Encoder;
|
||||
|
||||
public abstract class Message implements Encodable, Decodable
|
||||
{
|
||||
public int bufferSize()
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -26,6 +26,10 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
|
||||
protected byte[] data;
|
||||
|
||||
protected int offset;
|
||||
|
||||
protected int length;
|
||||
|
||||
protected boolean isFirst = true;
|
||||
|
||||
public void setBufferId(int bufferId)
|
||||
@@ -38,9 +42,11 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
return this.bufferId;
|
||||
}
|
||||
|
||||
public void setData(byte[] data)
|
||||
public void setData(byte[] data, int offset, int length)
|
||||
{
|
||||
this.data = data;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte[] getData()
|
||||
@@ -58,12 +64,18 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
return this.isFirst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return 32 + this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
encoder.writeInt(this.bufferId);
|
||||
encoder.writeInt(this.data.length);
|
||||
encoder.write(this.data);
|
||||
encoder.writeInt(this.length);
|
||||
encoder.write(this.data, this.offset, this.length);
|
||||
encoder.writeBoolean(this.isFirst);
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,12 @@ public class FullDataPartialUpdateMessage extends PluginMessage
|
||||
return this.beacons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return 32 + this.beacons.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,12 @@ public class FullDataSourceResponseMessage extends TrackablePluginMessage
|
||||
return this.beacons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return this.bufferId == null ? super.bufferSize() : 16 + this.beacons.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
|
||||
@@ -21,14 +21,26 @@ package no.jckf.dhsupport.core.message.plugin;
|
||||
import no.jckf.dhsupport.core.bytestream.Decoder;
|
||||
import no.jckf.dhsupport.core.bytestream.Encoder;
|
||||
|
||||
public class LevelInitMessage extends PluginMessage
|
||||
public class LevelInitMessage extends PluginMessage // TODO: Should probably eventually be a TrackablePluginMessage
|
||||
{
|
||||
protected String namespacedKey;
|
||||
|
||||
protected String serverKey;
|
||||
|
||||
protected String levelKey;
|
||||
|
||||
protected long time;
|
||||
|
||||
public void setNamespacedKey(String namespacedKey)
|
||||
{
|
||||
this.namespacedKey = namespacedKey;
|
||||
}
|
||||
|
||||
public String getNamespacedKey()
|
||||
{
|
||||
return namespacedKey;
|
||||
}
|
||||
|
||||
public void setServerKey(String serverKey)
|
||||
{
|
||||
this.serverKey = serverKey;
|
||||
@@ -62,6 +74,7 @@ public class LevelInitMessage extends PluginMessage
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
encoder.writeShortString(this.namespacedKey);
|
||||
encoder.writeShortString(this.serverKey);
|
||||
encoder.writeShortString(this.levelKey);
|
||||
encoder.writeLong(this.time);
|
||||
@@ -70,6 +83,7 @@ public class LevelInitMessage extends PluginMessage
|
||||
@Override
|
||||
public void decode(Decoder decoder)
|
||||
{
|
||||
this.namespacedKey = decoder.readShortString();
|
||||
this.serverKey = decoder.readShortString();
|
||||
this.levelKey = decoder.readShortString();
|
||||
this.time = decoder.readLong();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.message.plugin;
|
||||
|
||||
import no.jckf.dhsupport.core.bytestream.Decoder;
|
||||
|
||||
public class LevelInitRequestMessage extends PluginMessage
|
||||
{
|
||||
protected String worldKey;
|
||||
|
||||
public String getWorldKey()
|
||||
{
|
||||
return worldKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(Decoder decoder)
|
||||
{
|
||||
this.worldKey = decoder.readShortString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.world;
|
||||
|
||||
public class MaterialRecord
|
||||
{
|
||||
protected final String key;
|
||||
|
||||
protected final boolean isMapped;
|
||||
|
||||
public MaterialRecord(String key, boolean mapped)
|
||||
{
|
||||
this.key = key;
|
||||
this.isMapped = mapped;
|
||||
}
|
||||
|
||||
public boolean isMapped()
|
||||
{
|
||||
return this.isMapped;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
@@ -85,8 +85,16 @@ public interface WorldInterface extends Configurable
|
||||
|
||||
String getMaterialAt(int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get the string representation of the block state at the given coordinates.
|
||||
* Block state properties are ordered in ascending order.
|
||||
*/
|
||||
String getBlockStateAsStringAt(int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get block properties as key/value pairs.
|
||||
* Ordered in ascending order.
|
||||
*/
|
||||
Map<String, String> getBlockPropertiesAt(int x, int y, int z);
|
||||
|
||||
byte getBlockLightAt(int x, int y, int z);
|
||||
|
||||
Reference in new issue
Block a user