diff --git a/mod/build.gradle b/mod/build.gradle index 6a77e35..2acfe9c 100644 --- a/mod/build.gradle +++ b/mod/build.gradle @@ -19,7 +19,7 @@ apply plugin: 'net.minecraftforge.gradle' apply plugin: 'idea' apply plugin: 'maven-publish' -ext.modversion = "4.4.1" +ext.modversion = "5.0.0" ext.mcversion = "1.15.2" ext.forgeversion = "31.1.0" diff --git a/mod/src/main/java/zsawyer/mods/mumblelink/mumble/UpdateData.java b/mod/src/main/java/zsawyer/mods/mumblelink/mumble/UpdateData.java index 48a574c..f1d96a9 100644 --- a/mod/src/main/java/zsawyer/mods/mumblelink/mumble/UpdateData.java +++ b/mod/src/main/java/zsawyer/mods/mumblelink/mumble/UpdateData.java @@ -54,6 +54,13 @@ public class UpdateData { NativeUpdateErrorHandler errorHandler; private int uiTick = 0; + private int dimensionHeightOffset = 0; + private int dimensionOffsetUpdateCounter = 0; + + // First prime number higher than (2^16 + 512) / 13 + final int DIMENSION_OFFSET_SEED = 5081; + final int DIMENSION_OFFSET_UPDATE_RATE = 101; + public UpdateData(LinkAPILibrary mumbleLink, NativeUpdateErrorHandler errorHandler) { this.mumbleLink = mumbleLink; @@ -177,6 +184,8 @@ public void set(Minecraft game) { Float.parseFloat(Double.toString(topDirection.y * fCameraTopY))}; + handleDimensionHeightOffset(game); + // Identifier which uniquely identifies a certain player in a // context (e.g. the ingame Name). identity = generateIdentity(game, @@ -233,4 +242,82 @@ private Vec3d getTopVec(Minecraft game) { return new Vec3d((double) (f2 * f3), (double) f4, (double) (f1 * f3)); } + + private void handleDimensionHeightOffset(Minecraft game) { + // Don't recalculate this on every Mumble update. + // Dimension changes don't happen very often so a second or 2 of delay should not matter. + if (dimensionOffsetUpdateCounter <= 0) { + calculateDimensionHeightOffset(game); + dimensionOffsetUpdateCounter = DIMENSION_OFFSET_UPDATE_RATE; + } + + dimensionOffsetUpdateCounter--; + + // Offset the height-coordinate based on the dimension + fAvatarPosition[2] += dimensionHeightOffset; + fCameraPosition[2] += dimensionHeightOffset; + } + + private void calculateDimensionHeightOffset(Minecraft game) { + /* + * Since the Link API coordinates are stored in floats, and those only have 23 bits of precision, at coordinates + * around 2^16 meters, the positional audio precision is already reduced to centimeters and becomes increasingly + * less accurate after that. At 2^23 a float can not contain more precise location data than meters. This is why + * a maximum y-offset around 2^16 (65536) was chosen. + * Now there is no way to fit a 256 y-range for each possible dimension id in the range of -65536 to 65536, so + * instead the three vanilla dimensions, Nether, Overworld and End get their own reserved "Mumble-space" of 256 + * meters high and the rest of the dimensions are distributed over the space in between with the realistic chance + * of overlapping positional audio spaces, but it is what it is... + * + * The distribution is as follows: + * null worlds + * |---------| + * 2^16 2^16+256 + * |-----------------|----------------------|------------|------------------------|------------| + * -(2^16)-512 -(2^16)-256 0 256 2^16+512 2^16+768 + * Nether Negative ID Mod dims Overworld Positive ID Mod dims End + */ + + int maxHeightOffset = 65536; + int heightOfADimension = 256; + + if (game.world == null) { + // If we don't know what to do, just use a non-vanilla dimension offset so the "Mumble space" won't overlap + // with the Overworld or End. + dimensionHeightOffset = maxHeightOffset; + return; + } + + int dimID = game.world.dimension.getType().getId(); + + switch (dimID) { + case 0: + // Overworld + dimensionHeightOffset = 0; + return; + case -1: + // Nether + dimensionHeightOffset = -1 * maxHeightOffset - (heightOfADimension * 2); + return; + case 1: + // End + dimensionHeightOffset = maxHeightOffset + (heightOfADimension * 2); + return; + default: + // In all other cases, use the distribution function. + break; + } + + // Min_INT * Min_ INT < Max_LONG + // -(2^31) * -(2^31) = 2^62 < 2^63-1 + long rawHeightOffset = Math.abs((long) DIMENSION_OFFSET_SEED * (long) dimID); + + // Transform the offset to be distributed between 256 and 2^16 + 256 + dimensionHeightOffset = (int) ((rawHeightOffset % maxHeightOffset) + heightOfADimension); + + // Transform the offset to be distributed between -(2^16) - 256 and -256 + if (dimID < 0) { + dimensionHeightOffset *= -1; + } + } }