├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.yml │ └── bug_report.yml └── PULL_REQUEST_TEMPLATE │ ├── bug_fixes.md │ └── bug_fixes.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── assets │ │ │ └── circumnavigate │ │ │ │ ├── lang │ │ │ │ └── en_us.json │ │ │ │ └── icon.png │ │ ├── compat.mixins.json │ │ ├── fabric.mod.json │ │ └── minecraft.mixins.json │ └── java │ │ └── com │ │ └── fexl │ │ └── circumnavigate │ │ ├── injected │ │ ├── IsClientSideInjector.java │ │ ├── DimensionTransformerInjector.java │ │ └── ServerPlayerInjector.java │ │ ├── accessors │ │ ├── LevelAccessor.java │ │ ├── TransformerAccessor.java │ │ └── WorldWrappingSettingsAccessor.java │ │ ├── CircumnavigateServer.java │ │ ├── storage │ │ ├── DebugInfo.java │ │ └── TransformerRequests.java │ │ ├── mixin │ │ ├── light │ │ │ ├── LightEngineAccessor.java │ │ │ ├── BlockLightEngineMixin.java │ │ │ └── SkyLightEngineMixin.java │ │ ├── chunk │ │ │ ├── PositionedAccessorMixin.java │ │ │ ├── ServerChunkCacheMixin.java │ │ │ ├── TrackedEntityMixin.java │ │ │ ├── ChunkStatusTasksMixin.java │ │ │ ├── SectionTrackerMixin.java │ │ │ ├── ChunkTrackingView$PositionedMixin.java │ │ │ ├── ChunkMapMixin.java │ │ │ └── ChunkTrackerMixin.java │ │ ├── block │ │ │ ├── blocks │ │ │ │ ├── RailBlockMixin.java │ │ │ │ ├── BaseRailBlockMixin.java │ │ │ │ ├── RailStateMixin.java │ │ │ │ ├── fluid │ │ │ │ │ └── FlowingFluidMixin.java │ │ │ │ ├── piston │ │ │ │ │ ├── PistonBaseBlockMixin.java │ │ │ │ │ ├── PistonStructureResolverMixin.java │ │ │ │ │ └── PistonHeadBlockMixin.java │ │ │ │ ├── DoubleBlockCombinerMixin.java │ │ │ │ ├── SignalGetterMixin.java │ │ │ │ └── NetherPortalBlockMixin.java │ │ │ ├── NeighborUpdatorMixin.java │ │ │ ├── VibrationSystemMixin.java │ │ │ └── ExplosionMixin.java │ │ ├── debug │ │ │ ├── SharedConstantsMixin.java │ │ │ ├── ServerLevelMixin.java │ │ │ ├── PathMixin.java │ │ │ └── DebugPacketsMixin.java │ │ ├── interfaceInjects │ │ │ ├── WorldGenRegionInjectorMixin.java │ │ │ ├── PathNavigationRegionInjectorMixin.java │ │ │ ├── ChunkTrackerInjectorMixin.java │ │ │ ├── DimensionTransformerInjectorMixin.java │ │ │ └── PlayerClientInjectorMixin.java │ │ ├── worldInit │ │ │ ├── PathNavigationRegionMixin.java │ │ │ ├── WorldGenRegionMixin.java │ │ │ ├── MinecraftServerMixin.java │ │ │ ├── PrimaryLevelDataMixin.java │ │ │ ├── ServerLevelMixin.java │ │ │ └── PlayerListMixin.java │ │ ├── entity │ │ │ ├── collisions │ │ │ │ ├── HitResultMixin.java │ │ │ │ ├── BlockCollisionsMixin.java │ │ │ │ └── LevelMixin.java │ │ │ ├── entities │ │ │ │ ├── vehicle │ │ │ │ │ └── AbstractMinecartMixin.java │ │ │ │ └── FishingHookMixin.java │ │ │ ├── PlayerMixin.java │ │ │ ├── ServerPlayerMixin.java │ │ │ ├── LivingEntityMixin.java │ │ │ └── EntityMixin.java │ │ ├── packet │ │ │ ├── ServerGamePacketListenerImplMixin$1.java │ │ │ ├── ServerConfigurationPacketListenerImplMixin.java │ │ │ ├── ConnectionMixin.java │ │ │ └── PlayerChunkSenderMixin.java │ │ ├── worldgen │ │ │ ├── other │ │ │ │ ├── densityFunctions │ │ │ │ │ ├── DensityFunctions$ShiftNoiseMixin.java │ │ │ │ │ ├── DensityFunctions$NoiseMixin.java │ │ │ │ │ ├── DensityFunction$NoiseHolderMixin.java │ │ │ │ │ ├── DensityFunctions$ShiftedNoiseMixin.java │ │ │ │ │ └── DensityFunctions$WeirdScaledSamplerMixin.java │ │ │ │ ├── NoiseBasedStateProviderMixin.java │ │ │ │ └── DualNoiseProviderMixin.java │ │ │ ├── NormalNoiseMixin.java │ │ │ ├── PerlinSimplexNoiseMixin.java │ │ │ ├── PerlinNoiseMixin.java │ │ │ ├── ImprovedNoiseMixin.java │ │ │ ├── SimplexNoiseMixin.java │ │ │ └── BlendedNoiseMixin.java │ │ ├── gameevent │ │ │ ├── EuclideanGameEventListenerRegistryMixin.java │ │ │ └── GameEventDispatcherMixin.java │ │ ├── propagators │ │ │ └── chunkTracker │ │ │ │ ├── providers │ │ │ │ ├── PoiManager$DistanceTracker.java │ │ │ │ └── ChunkMap$DistanceManagerMixin.java │ │ │ │ └── senders │ │ │ │ └── DistanceManagerMixin.java │ │ └── ServerToClientListener.java │ │ ├── processing │ │ ├── EntityHitResultWrapped.java │ │ ├── AABBWrapped.java │ │ ├── Cursor3DWrapped.java │ │ ├── BlockHitResultWrapped.java │ │ └── Vec3iWrapped.java │ │ ├── options │ │ ├── WrappingOptions.java │ │ ├── WorldWrappingSettings.java │ │ └── DimensionWrappingSettings.java │ │ ├── core │ │ ├── CoordinateConstants.java │ │ ├── FakeCoordinateTransformers.java │ │ ├── BasicPositionOperations.java │ │ └── CoordinateTransformers.java │ │ ├── network │ │ └── packet │ │ │ ├── DimensionWrappingRequest.java │ │ │ ├── ChunkLoadingLevelsPayload.java │ │ │ └── DimensionWrappingPayload.java │ │ ├── compat │ │ └── CompatMixinConfigPlugin.java │ │ └── Circumnavigate.java └── client │ ├── resources │ ├── Circumnavigate Curvature Shader.zip │ ├── compat-client.mixins.json │ └── minecraft-client.mixins.json │ └── java │ └── com │ └── fexl │ └── circumnavigate │ ├── client │ └── storage │ │ ├── DebugVariables.java │ │ └── TransformersStorage.java │ ├── mixin │ └── client │ │ ├── worldInit │ │ ├── ConnectionMixin.java │ │ ├── CreateWorldScreenMixin.java │ │ ├── ClientLevelMixin.java │ │ └── WorldTabMixin.java │ │ ├── OptionsMixin.java │ │ ├── SectionOcclusionGraphMixin.java │ │ ├── debug │ │ ├── DebugRendererMixin.java │ │ ├── DebugScreenOverlayMixin.java │ │ └── KeyboardHandlerMixin.java │ │ └── ClientToServerListener.java │ ├── compat │ └── mixin │ │ └── client │ │ └── iris │ │ └── CommonUniformsMixin.java │ └── CircumnavigateClient.java ├── settings.gradle ├── .gitattributes ├── gradle.properties ├── versions └── 1.21.1 │ └── gradle.properties ├── .gitignore ├── README.md ├── gradlew.bat └── CODE_OF_CONDUCT.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: famrofexl 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FamroFexl/Circumnavigate/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/circumnavigate/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "options.use_internal_curvature_shader": "Curvature Shader" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/circumnavigate/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FamroFexl/Circumnavigate/HEAD/src/main/resources/assets/circumnavigate/icon.png -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bug_fixes.md: -------------------------------------------------------------------------------- 1 | ### Bug Fixes 2 | Describe the bug fixed: 3 | 4 | Related Issues: 5 | 6 | How has the fixed been tested and validated?: -------------------------------------------------------------------------------- /src/client/resources/Circumnavigate Curvature Shader.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FamroFexl/Circumnavigate/HEAD/src/client/resources/Circumnavigate Curvature Shader.zip -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/injected/IsClientSideInjector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.injected; 6 | 7 | public interface IsClientSideInjector { 8 | boolean isClientSide(); 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/accessors/LevelAccessor.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.accessors; 2 | 3 | import net.minecraft.server.level.ServerLevel; 4 | 5 | public interface LevelAccessor { 6 | public ServerLevel getLevel(); 7 | 8 | public void setLevel(ServerLevel level); 9 | } 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Java 2 | org.gradle.jvmargs=-Xmx2G 3 | org.gradle.parallel=true 4 | 5 | # Mod Properties 6 | mod_version=0.6.1 7 | mod_group=com.fexl 8 | mod_id=circumnavigate 9 | mod_name=Circumnavigate 10 | mod_stage=alpha 11 | 12 | #Manifold preprocessor for multi-version development 13 | manifold_preprocessor_version = 2024.1.54 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Request a feature 3 | title: "[Feature Request]: " 4 | labels: "enhancement" 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Suggestion 9 | description: A clear and concise description of what you would like to add. 10 | validations: 11 | required: true -------------------------------------------------------------------------------- /src/main/resources/compat.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.fexl.circumnavigate.compat.mixin", 4 | "plugin": "com.fexl.circumnavigate.compat.CompatMixinConfigPlugin", 5 | "refmap": "circumnavigate.refmap.json", 6 | "compatibilityLevel": "JAVA_21", 7 | "mixins": [ 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/CircumnavigateServer.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate; 4 | 5 | import net.fabricmc.api.DedicatedServerModInitializer; 6 | 7 | public class CircumnavigateServer implements DedicatedServerModInitializer { 8 | @Override 9 | public void onInitializeServer() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/storage/DebugInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.storage; 6 | 7 | import net.minecraft.world.level.ChunkPos; 8 | 9 | import java.util.HashMap; 10 | 11 | public class DebugInfo { 12 | public static HashMap chunkLoadingLevels = new HashMap<>(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/accessors/TransformerAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.accessors; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | 9 | public interface TransformerAccessor { 10 | DimensionTransformer getTransformer(); 11 | void setTransformer(DimensionTransformer transformer); 12 | } 13 | -------------------------------------------------------------------------------- /src/client/resources/compat-client.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.fexl.circumnavigate.compat.mixin.client", 4 | "plugin": "com.fexl.circumnavigate.compat.CompatMixinConfigPlugin", 5 | "refmap": "circumnavigate.refmap.json", 6 | "compatibilityLevel": "JAVA_21", 7 | "client": [ 8 | "iris.CommonUniformsMixin" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /versions/1.21.1/gradle.properties: -------------------------------------------------------------------------------- 1 | #Java 2 | java_version = 21 3 | 4 | #Minecraft 5 | minecraft_version=1.21.1 6 | parchment_version = 1.21:2024.11.10 7 | 8 | #Fabric 9 | fabric_loader_version=0.16.9 10 | fabric_api_version=0.107.0+1.21.1 11 | 12 | sodium_version = mc1.21.1-0.6.7-fabric 13 | 14 | #Iris 15 | iris_shaders_version = 1.8.1+1.21.1-fabric 16 | antlr4_runtime_version = 4.13.1 17 | glsl_transformer_version = 2.0.1 18 | anarres_jcpp_version = 1.4.14 -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/injected/DimensionTransformerInjector.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.injected; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | 7 | public interface DimensionTransformerInjector { 8 | default DimensionTransformer getTransformer() { 9 | return null; 10 | } 11 | 12 | default void setTransformer(DimensionTransformer transformer) { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/light/LightEngineAccessor.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.light; 2 | 3 | import net.minecraft.world.level.chunk.LightChunkGetter; 4 | import net.minecraft.world.level.lighting.LightEngine; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(LightEngine.class) 9 | public interface LightEngineAccessor { 10 | @Accessor LightChunkGetter getChunkSource(); 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | lib/ 8 | 9 | # eclipse 10 | 11 | *.launch 12 | 13 | #Manifold run config 14 | build.properties 15 | 16 | # idea 17 | 18 | .idea/ 19 | *.iml 20 | *.ipr 21 | *.iws 22 | 23 | # vscode 24 | 25 | .settings/ 26 | .vscode/ 27 | bin/ 28 | .classpath 29 | .project 30 | 31 | # macos 32 | 33 | *.DS_Store 34 | 35 | # fabric 36 | 37 | run/ 38 | 39 | # java 40 | 41 | hs_err_*.log 42 | replay_*.log 43 | *.hprof 44 | *.jfr 45 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/client/storage/DebugVariables.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.client.storage; 6 | 7 | public class DebugVariables { 8 | public static boolean renderNeighborUpdates = false; 9 | public static boolean renderStructurePieces = false; 10 | public static boolean renderPathfinding = false; 11 | public static boolean renderLightDebug = false; 12 | public static boolean renderChunkInfo = false; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/PositionedAccessorMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.chunk; 4 | 5 | import net.minecraft.server.level.ChunkTrackingView.Positioned; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | @Mixin(Positioned.class) 10 | public interface PositionedAccessorMixin { 11 | @Invoker("squareIntersects") boolean squareIntersectsAM(Positioned other); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/processing/EntityHitResultWrapped.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.processing; 6 | 7 | import net.minecraft.world.phys.EntityHitResult; 8 | 9 | public class EntityHitResultWrapped extends EntityHitResult { 10 | public EntityHitResultWrapped(EntityHitResult result) { 11 | super(result.getEntity(), result.getEntity().level().getTransformer().Vector3D.wrapToBounds(result.getLocation())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/accessors/WorldWrappingSettingsAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.accessors; 10 | 11 | import com.fexl.circumnavigate.options.WorldWrappingSettings; 12 | 13 | public interface WorldWrappingSettingsAccessor { 14 | void setWorldWrappingSettings(WorldWrappingSettings settings); 15 | WorldWrappingSettings getWorldWrappingSettings(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/storage/TransformerRequests.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.storage; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerLevel; 8 | 9 | /** 10 | * Storage for context propagation down call stacks. 11 | */ 12 | public class TransformerRequests { 13 | public static DimensionTransformer chunkMapTransformer; 14 | public static MinecraftServer server = null; 15 | public static ServerLevel noiseLevel; 16 | } 17 | -------------------------------------------------------------------------------- /src/client/resources/minecraft-client.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.fexl.circumnavigate.mixin.client", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "ClientToServerListener", 7 | "OptionsMixin", 8 | "SectionOcclusionGraphMixin", 9 | "debug.ChunkBorderRendererMixin", 10 | "debug.DebugRendererMixin", 11 | "debug.DebugScreenOverlayMixin", 12 | "debug.KeyboardHandlerMixin", 13 | "worldInit.ClientLevelMixin", 14 | "worldInit.ConnectionMixin", 15 | "worldInit.CreateWorldScreenMixin", 16 | "worldInit.WorldTabMixin" 17 | ], 18 | "injectors": { 19 | "defaultRequire": 1 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/options/WrappingOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.options; 6 | 7 | import com.mojang.serialization.Codec; 8 | import com.mojang.serialization.MapCodec; 9 | import com.mojang.serialization.codecs.RecordCodecBuilder; 10 | 11 | public record WrappingOptions(int version) { 12 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( 13 | instance -> instance.group( 14 | Codec.INT.fieldOf("Version").forGetter(WrappingOptions::version) 15 | ) 16 | .apply(instance, instance.stable(WrappingOptions::new)) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/processing/AABBWrapped.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.processing; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import net.minecraft.world.phys.AABB; 9 | import net.minecraft.world.phys.Vec3; 10 | 11 | public class AABBWrapped extends AABB { 12 | final DimensionTransformer transformer; 13 | public AABBWrapped(AABB aabb, DimensionTransformer transformer) { 14 | super(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ); 15 | this.transformer = transformer; 16 | } 17 | 18 | @Override 19 | public double distanceToSqr(Vec3 vec) { 20 | return transformer.distanceToSqrWrappedCoord(this, vec); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/injected/ServerPlayerInjector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.injected; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.world.level.ChunkPos; 9 | import net.minecraft.world.phys.Vec3; 10 | 11 | public interface ServerPlayerInjector { 12 | default double getClientX() { return 0; } 13 | default double getClientZ() { return 0; } 14 | 15 | default ChunkPos getClientChunk() { return ChunkPos.ZERO; } 16 | 17 | default BlockPos getClientBlock() { return BlockPos.ZERO; } 18 | 19 | default Vec3 getClientPosition() { return Vec3.ZERO; } 20 | 21 | default void setClientX(double clientX) {}; 22 | default void setClientZ(double clientZ) {}; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/RailBlockMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.block.blocks; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.level.Level; 6 | import net.minecraft.world.level.block.RailBlock; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 10 | 11 | @Mixin(RailBlock.class) 12 | public class RailBlockMixin { 13 | 14 | @ModifyVariable(method = "updateState", at = @At("HEAD"), index = 3, argsOnly = true) 15 | public BlockPos wrapBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 16 | return level.getTransformer().onlyServerSide().Block.wrapToBounds(blockPos); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/BaseRailBlockMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.block.blocks; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.level.Level; 6 | import net.minecraft.world.level.block.BaseRailBlock; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 10 | 11 | @Mixin(BaseRailBlock.class) 12 | public class BaseRailBlockMixin { 13 | 14 | @ModifyVariable(method = "neighborChanged", at = @At("HEAD"), index = 3, argsOnly = true) 15 | public BlockPos wrapBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 16 | return level.getTransformer().onlyServerSide().Block.wrapToBounds(blockPos); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/debug/SharedConstantsMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.debug; 6 | 7 | import com.fexl.circumnavigate.Circumnavigate; 8 | import net.minecraft.SharedConstants; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(SharedConstants.class) 16 | public class SharedConstantsMixin { 17 | @Shadow public static boolean IS_RUNNING_IN_IDE; 18 | 19 | @Inject(method = "", at = @At("TAIL")) 20 | private static void clinit(CallbackInfo ci) { 21 | if(!Circumnavigate.DEV_MODE) return; 22 | 23 | IS_RUNNING_IN_IDE = true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/RailStateMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.block.blocks; 2 | 3 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 4 | import com.llamalad7.mixinextras.sugar.Local; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.world.level.Level; 7 | import net.minecraft.world.level.block.RailState; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 11 | 12 | @Mixin(RailState.class) 13 | public class RailStateMixin { 14 | 15 | @ModifyVariable(method = "", at = @At("HEAD"), argsOnly = true, index = 2) 16 | private static BlockPos wrapBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 17 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/fluid/FlowingFluidMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.block.blocks.fluid; 2 | 3 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 4 | import com.llamalad7.mixinextras.sugar.Local; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.world.level.Level; 7 | import net.minecraft.world.level.material.FlowingFluid; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 11 | 12 | @Mixin(FlowingFluid.class) 13 | public abstract class FlowingFluidMixin { 14 | @ModifyVariable(method = "spread", at = @At("HEAD"), argsOnly = true, index = 2) 15 | public BlockPos wrapSpread(BlockPos blockPos, @Local(argsOnly = true) Level level) { 16 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/interfaceInjects/WorldGenRegionInjectorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.interfaceInjects; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.fexl.circumnavigate.injected.DimensionTransformerInjector; 9 | import net.minecraft.server.level.WorldGenRegion; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | 13 | @Mixin(WorldGenRegion.class) 14 | public class WorldGenRegionInjectorMixin implements DimensionTransformerInjector { 15 | @Unique 16 | private DimensionTransformer transformer = null; 17 | 18 | @Override 19 | public DimensionTransformer getTransformer() { 20 | return transformer; 21 | } 22 | 23 | @Override 24 | public void setTransformer(DimensionTransformer transformer) { 25 | this.transformer = transformer; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/processing/Cursor3DWrapped.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.processing; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import net.minecraft.core.Cursor3D; 9 | 10 | /** 11 | * Wraps outputs by manipulating them via a transformer. 12 | */ 13 | public class Cursor3DWrapped extends Cursor3D { 14 | final DimensionTransformer transformer; 15 | 16 | public Cursor3DWrapped(int originX, int originY, int originZ, int endX, int endY, int endZ, DimensionTransformer transformer) { 17 | super(originX, originY, originZ, endX, endY, endZ); 18 | this.transformer = transformer; 19 | } 20 | 21 | @Override 22 | public int nextX() { 23 | return transformer.Coord.X.wrapToBounds(super.nextX()); 24 | } 25 | 26 | @Override 27 | public int nextZ() { 28 | return transformer.Coord.Z.wrapToBounds(super.nextZ()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/interfaceInjects/PathNavigationRegionInjectorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.interfaceInjects; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.fexl.circumnavigate.injected.DimensionTransformerInjector; 9 | import net.minecraft.world.level.PathNavigationRegion; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | 13 | @Mixin(PathNavigationRegion.class) 14 | public class PathNavigationRegionInjectorMixin implements DimensionTransformerInjector { 15 | @Unique 16 | private DimensionTransformer transformer = null; 17 | 18 | @Override 19 | public DimensionTransformer getTransformer() { 20 | return transformer; 21 | } 22 | 23 | @Override 24 | public void setTransformer(DimensionTransformer transformer) { 25 | this.transformer = transformer; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/worldInit/ConnectionMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client.worldInit; 4 | 5 | import com.fexl.circumnavigate.client.storage.TransformersStorage; 6 | import net.minecraft.network.Connection; 7 | import net.minecraft.network.chat.Component; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(Connection.class) 14 | public class ConnectionMixin { 15 | /** 16 | * Set the world transformers to null, so they can be adjusted for the next world. 17 | */ 18 | @Inject(method = "disconnect(Lnet/minecraft/network/chat/Component;)V", at = @At("HEAD")) 19 | public void disconnect(Component message, CallbackInfo ci) { 20 | TransformersStorage.clearTransformers(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/debug/ServerLevelMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.debug; 2 | 3 | import net.minecraft.network.protocol.game.DebugPackets; 4 | import net.minecraft.server.level.ServerLevel; 5 | import net.minecraft.world.level.WorldGenLevel; 6 | import net.minecraft.world.level.chunk.ChunkAccess; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(ServerLevel.class) 13 | public class ServerLevelMixin { 14 | //Send structure starts on init, not just on generation 15 | @Inject(method = "onStructureStartsAvailable", at = @At("HEAD")) 16 | public void sendStructurePacket(ChunkAccess chunk, CallbackInfo ci) { 17 | chunk.getAllStarts().forEach((structure, start) -> { 18 | DebugPackets.sendStructurePacket((WorldGenLevel) this, start); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/piston/PistonBaseBlockMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks.piston; 6 | 7 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.world.level.Level; 11 | import net.minecraft.world.level.block.piston.PistonBaseBlock; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 15 | 16 | @Mixin(PistonBaseBlock.class) 17 | public class PistonBaseBlockMixin { 18 | @ModifyVariable(method = "triggerEvent", at = @At("HEAD"), index = 3, argsOnly = true) 19 | public BlockPos modifyBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 20 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/PathNavigationRegionMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldInit; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.PathNavigationRegion; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(PathNavigationRegion.class) 17 | public class PathNavigationRegionMixin { 18 | @Inject(method = "", at = @At("RETURN")) 19 | public void init(Level level, BlockPos centerPos, BlockPos offsetPos, CallbackInfo ci) { 20 | PathNavigationRegion thiz = (PathNavigationRegion) (Object) this; 21 | 22 | thiz.setTransformer(level.getTransformer().onlyServerSide()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/core/CoordinateConstants.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.core; 2 | 3 | import net.minecraft.world.level.ChunkPos; 4 | import net.minecraft.world.level.chunk.LevelChunkSection; 5 | 6 | public class CoordinateConstants { 7 | /** The width of a single chunk. */ 8 | public static final int CHUNK_WIDTH = LevelChunkSection.SECTION_WIDTH; 9 | 10 | /** A value well-beyond vanilla chunk limits used to disable WorldTransformers. */ 11 | public static final int DISABLING_CHUNK_POS = ChunkPos.getX(ChunkPos.INVALID_CHUNK_POS)+10000; 12 | 13 | /** The minimum chunk buffer required around a view distance when constrained to the level size. */ 14 | public static final int MIN_VIEW_DISTANCE_BUFFER = 3; 15 | 16 | /** The minimum level size which still allows the minimum default 2-chunk view distance. */ 17 | public static final int MIN_LEVEL_WIDTH = 10; 18 | 19 | /** The minimum acceptable render distance */ 20 | public static final int MIN_RENDER_DISTANCE = 2; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/ServerChunkCacheMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.chunk; 6 | 7 | import net.minecraft.server.level.ServerChunkCache; 8 | import net.minecraft.server.level.ServerLevel; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | 13 | @Mixin(ServerChunkCache.class) 14 | public class ServerChunkCacheMixin { 15 | @Shadow @Final ServerLevel level; 16 | 17 | //TODO: Very slow, but optimal injection 18 | /** 19 | @ModifyVariable(method = "getChunkNow", at = @At("HEAD"), argsOnly = true, index = 1) 20 | public int modifyX(int chunkX) { 21 | return level.getTransformer().Chunk.X.wrapToBounds(chunkX); 22 | } 23 | 24 | @ModifyVariable(method = "getChunkNow", at = @At("HEAD"), argsOnly = true, index = 2) 25 | public int modifyZ(int chunkZ) { 26 | return level.getTransformer().Chunk.Z.wrapToBounds(chunkZ); 27 | }**/ 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/network/packet/DimensionWrappingRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.network.packet; 6 | 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 10 | 11 | public record DimensionWrappingRequest() implements CustomPacketPayload { 12 | public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(DimensionWrappingRequest::write, DimensionWrappingRequest::new); 13 | public static final CustomPacketPayload.Type TYPE = CustomPacketPayload.createType("debug/circumnavigate/wrapping_data_request"); 14 | 15 | private DimensionWrappingRequest(FriendlyByteBuf buffer) { 16 | this(); 17 | } 18 | 19 | private void write(FriendlyByteBuf buffer) { 20 | } 21 | 22 | @Override 23 | public CustomPacketPayload.Type type() { 24 | return TYPE; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/options/WorldWrappingSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.options; 6 | 7 | import com.mojang.serialization.Codec; 8 | import com.mojang.serialization.codecs.RecordCodecBuilder; 9 | import net.minecraft.core.registries.Registries; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.world.level.Level; 12 | import net.minecraft.world.level.dimension.LevelStem; 13 | 14 | import java.util.Map; 15 | 16 | public record WorldWrappingSettings(WrappingOptions options, Map, DimensionWrappingSettings> dimensions) { 17 | public static final Codec CODEC = RecordCodecBuilder.create( 18 | instance -> instance.group(WrappingOptions.CODEC.forGetter(WorldWrappingSettings::options), Codec.unboundedMap(ResourceKey.codec(Registries.DIMENSION), DimensionWrappingSettings.CODEC).fieldOf("dimensions").forGetter(WorldWrappingSettings::dimensions)) 19 | .apply(instance, instance.stable(WorldWrappingSettings::new)) 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/piston/PistonStructureResolverMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks.piston; 6 | 7 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.block.piston.PistonStructureResolver; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 16 | 17 | @Mixin(PistonStructureResolver.class) 18 | public class PistonStructureResolverMixin { 19 | @Final @Shadow private Level level; 20 | 21 | @ModifyVariable(method = "", at = @At("TAIL"), index = 2, argsOnly = true) 22 | private BlockPos modifyBlockPos(BlockPos blockPos) { 23 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/collisions/HitResultMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.entity.collisions; 2 | 3 | import net.minecraft.world.entity.Entity; 4 | import net.minecraft.world.phys.HitResult; 5 | import net.minecraft.world.phys.Vec3; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | @Mixin(HitResult.class) 14 | public class HitResultMixin { 15 | @Final @Shadow protected Vec3 location; 16 | @Inject(method = "distanceTo", at = @At("HEAD"), cancellable = true) 17 | public void wrapDistanceSquared(Entity entity, CallbackInfoReturnable cir) { 18 | cir.setReturnValue(entity.level().getTransformer().onlyServerSide().Coord.sqrDistToBounds(entity.getX(), entity.getY(), entity.getZ(), location.x, location.y, location.z)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/interfaceInjects/ChunkTrackerInjectorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.interfaceInjects; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.fexl.circumnavigate.injected.DimensionTransformerInjector; 9 | import net.minecraft.server.level.ChunkTracker; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | 13 | /** 14 | * Injects a transformer and accessor methods in ChunkTracker so it can be used in all subclasses instantiated from DistanceManager. 15 | */ 16 | @Mixin(ChunkTracker.class) 17 | public class ChunkTrackerInjectorMixin implements DimensionTransformerInjector { 18 | @Unique 19 | private DimensionTransformer transformer = null; 20 | 21 | @Override 22 | public DimensionTransformer getTransformer() { 23 | return transformer; 24 | } 25 | 26 | @Override 27 | public void setTransformer(DimensionTransformer transformer) { 28 | this.transformer = transformer; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/entities/vehicle/AbstractMinecartMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity.entities.vehicle; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.world.entity.vehicle.AbstractMinecart; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 13 | 14 | @Mixin(AbstractMinecart.class) 15 | public abstract class AbstractMinecartMixin { 16 | @Unique 17 | AbstractMinecart thiz = (AbstractMinecart) (Object) this; 18 | 19 | @ModifyVariable(method = "moveAlongTrack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/vehicle/AbstractMinecart;setPos(DDD)V", shift = At.Shift.AFTER), index = 1, argsOnly = true) 20 | public BlockPos wrapBlockPos(BlockPos blockPos) { 21 | return thiz.level().getTransformer().onlyServerSide().Block.unwrapFromBounds(thiz.blockPosition(), blockPos); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/WorldGenRegionMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldInit; 6 | 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.server.level.WorldGenRegion; 9 | import net.minecraft.util.StaticCache2D; 10 | import net.minecraft.world.level.chunk.ChunkAccess; 11 | import net.minecraft.world.level.chunk.status.ChunkStep; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import java.util.List; 18 | 19 | @Mixin(WorldGenRegion.class) 20 | public class WorldGenRegionMixin { 21 | @Inject(method = "", at = @At("RETURN")) 22 | public void init(ServerLevel level, StaticCache2D cache, ChunkStep generatingStep, ChunkAccess center, CallbackInfo ci) { 23 | WorldGenRegion thiz = (WorldGenRegion) (Object) this; 24 | thiz.setTransformer(level.getTransformer()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/packet/ServerGamePacketListenerImplMixin$1.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.packet; 6 | 7 | import net.minecraft.world.InteractionHand; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.phys.Vec3; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.ModifyArg; 13 | 14 | @Mixin(targets = "net.minecraft.server.network.ServerGamePacketListenerImpl$1") 15 | public class ServerGamePacketListenerImplMixin$1 { 16 | @ModifyArg(method = "method_33898", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;interactAt(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;"), index = 1) 17 | private static Vec3 wrapVec(Player player, Vec3 vec, InteractionHand interactionHand) { 18 | return player.level().getTransformer().Vector3D.wrapToBounds(vec); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/interfaceInjects/DimensionTransformerInjectorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* SPDX-License-Identifier: AGPL-3.0-only */ 6 | 7 | package com.fexl.circumnavigate.mixin.interfaceInjects; 8 | 9 | import com.fexl.circumnavigate.core.DimensionTransformer; 10 | import com.fexl.circumnavigate.injected.DimensionTransformerInjector; 11 | import net.minecraft.world.level.Level; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Unique; 14 | 15 | /** 16 | * Injects a transformer and accessor methods into Level instances. This means transformers are stored on a per-level basis. 17 | */ 18 | @Mixin(Level.class) 19 | public class DimensionTransformerInjectorMixin implements DimensionTransformerInjector { 20 | @Unique 21 | private DimensionTransformer transformer = null; 22 | 23 | @Override 24 | public DimensionTransformer getTransformer() { 25 | return transformer; 26 | } 27 | 28 | @Override 29 | public void setTransformer(DimensionTransformer transformer) { 30 | this.transformer = transformer; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/DoubleBlockCombinerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks; 6 | 7 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.world.level.LevelAccessor; 12 | import net.minecraft.world.level.block.DoubleBlockCombiner; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 16 | 17 | @Mixin(DoubleBlockCombiner.class) 18 | public class DoubleBlockCombinerMixin { 19 | @ModifyVariable(method = "combineWithNeigbour", argsOnly = true, at = @At("HEAD"), index = 6) 20 | private static BlockPos modifyBlockPos(BlockPos blockPos, @Local(argsOnly = true)LevelAccessor level) { 21 | if(level instanceof ServerLevel serverLevel) { 22 | return new BlockPosWrapped(blockPos, serverLevel.getTransformer()); 23 | } 24 | return blockPos; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/TrackedEntityMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.chunk; 6 | 7 | import com.llamalad7.mixinextras.sugar.Local; 8 | import net.minecraft.server.level.ChunkMap; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.world.phys.Vec3; 11 | 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Redirect; 15 | 16 | @Mixin(ChunkMap.TrackedEntity.class) 17 | public abstract class TrackedEntityMixin { 18 | /** 19 | * Updates an entity's player tracking based on the player's distance. Modified to support wrapped distances. 20 | */ 21 | @Redirect(method = "updatePlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;subtract(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;")) 22 | public Vec3 unwrapVec(Vec3 playerPos, Vec3 entityPos, @Local(argsOnly = true) ServerPlayer player) { 23 | return playerPos.subtract(player.level().getTransformer().Vector3D.unwrapFromBounds(playerPos, entityPos)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/densityFunctions/DensityFunctions$ShiftNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other.densityFunctions; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.world.level.levelgen.DensityFunction; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(targets = "net.minecraft.world.level.levelgen.DensityFunctions$ShiftNoise") 16 | public interface DensityFunctions$ShiftNoiseMixin { 17 | @Shadow DensityFunction.NoiseHolder offsetNoise(); 18 | 19 | @Inject(method = "compute", at = @At("HEAD"), cancellable = true) 20 | default void compute(double x, double y, double z, CallbackInfoReturnable cir) { 21 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 22 | cir.setReturnValue(offsetNoise().getValue(x, y * 0.25, z) * 4.0); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/piston/PistonHeadBlockMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks.piston; 6 | 7 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.world.level.Level; 11 | import net.minecraft.world.level.block.piston.PistonHeadBlock; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 15 | 16 | @Mixin(PistonHeadBlock.class) 17 | public class PistonHeadBlockMixin { 18 | @ModifyVariable(method = "playerWillDestroy", at = @At("HEAD"), index = 2, argsOnly = true) 19 | public BlockPos modifyBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 20 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 21 | } 22 | 23 | @ModifyVariable(method = "onRemove", at = @At("HEAD"), index = 3, argsOnly = true) 24 | public BlockPos modifyBlockPos2(BlockPos blockPos, @Local(argsOnly = true) Level level) { 25 | return new BlockPosWrapped(blockPos, level.getTransformer().onlyServerSide()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/OptionsMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client; 4 | 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.OptionInstance; 7 | import net.minecraft.client.Options; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(Options.class) 16 | public abstract class OptionsMixin { 17 | @Shadow private int serverRenderDistance; 18 | @Shadow @Final private OptionInstance renderDistance; 19 | 20 | @Inject(method = "getEffectiveRenderDistance", at = @At("HEAD"), cancellable = true) 21 | public void getEffectiveRenderDistance(CallbackInfoReturnable cir) { 22 | if(Minecraft.getInstance().level == null) return; 23 | 24 | int renderDistance = serverRenderDistance > 0 ? Math.min(this.renderDistance.get(), serverRenderDistance) : this.renderDistance.get(); 25 | cir.setReturnValue(Minecraft.getInstance().level.getTransformer().limitViewDistance(renderDistance)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/client/storage/TransformersStorage.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.client.storage; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import net.minecraft.resources.ResourceKey; 7 | import net.minecraft.world.level.Level; 8 | 9 | import java.util.HashMap; 10 | 11 | /** 12 | * Stores a list of level and transformer pairs, corresponding to the levels/dimensions on the server. 13 | */ 14 | public class TransformersStorage { 15 | private static HashMap, DimensionTransformer> transformers = new HashMap<>(); 16 | 17 | /** 18 | * Returns the transformer for the requested level/dimension 19 | */ 20 | public static DimensionTransformer getTransformer(ResourceKey levelKey) { 21 | //No transformer packet received. No wrapping on server. 22 | DimensionTransformer transformer = transformers.get(levelKey); 23 | if(transformer == null) { 24 | return DimensionTransformer.DISABLED; 25 | } 26 | return transformer; 27 | } 28 | 29 | public static void setTransformer(ResourceKey levelKey, DimensionTransformer transformer) { 30 | transformers.put(levelKey, transformer); 31 | } 32 | 33 | public static void clearTransformers() { 34 | transformers.clear(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/SectionOcclusionGraphMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import com.fexl.circumnavigate.storage.TransformerRequests; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.renderer.SectionOcclusionGraph; 9 | import net.minecraft.core.BlockPos; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(SectionOcclusionGraph.class) 16 | public class SectionOcclusionGraphMixin { 17 | /** 18 | * ChunkTrackingView is mainly used server-side. This is the only usage of it client-side. Because the server and client do not handle chunks in the same way, they cannot use the same code. 19 | */ 20 | @Inject(method = "isInViewDistance", at = @At("HEAD")) 21 | public void isInViewDistance(BlockPos pos, BlockPos origin, CallbackInfoReturnable cir) { 22 | TransformerRequests.chunkMapTransformer = Minecraft.getInstance().level != null ? Minecraft.getInstance().level.getTransformer() : DimensionTransformer.DISABLED; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/gameevent/EuclideanGameEventListenerRegistryMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.gameevent; 6 | 7 | import com.fexl.circumnavigate.processing.Vec3iWrapped; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.core.Vec3i; 11 | import net.minecraft.server.level.ServerLevel; 12 | import net.minecraft.world.level.gameevent.EuclideanGameEventListenerRegistry; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Redirect; 16 | 17 | import java.util.Optional; 18 | 19 | @Mixin(EuclideanGameEventListenerRegistry.class) 20 | public class EuclideanGameEventListenerRegistryMixin { 21 | /** 22 | * Wrap distance check for vibrations etc. 23 | */ 24 | @Redirect(method = "getPostableListenerPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;distSqr(Lnet/minecraft/core/Vec3i;)D")) 25 | private static double modifySqr(BlockPos instance, Vec3i vec3i, @Local(argsOnly = true) ServerLevel level) { 26 | return new Vec3iWrapped(instance.getX(), instance.getY(), instance.getZ(), level.getTransformer().onlyServerSide()).distSqr(vec3i); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/NoiseBasedStateProviderMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.world.level.levelgen.feature.stateproviders.NoiseBasedStateProvider; 10 | import net.minecraft.world.level.levelgen.synth.NormalNoise; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(NoiseBasedStateProvider.class) 19 | public class NoiseBasedStateProviderMixin { 20 | @Shadow @Final protected NormalNoise noise; 21 | 22 | @Inject(method = "getNoiseValue", at = @At("HEAD"), cancellable = true) 23 | public void getNoiseValue(BlockPos pos, double delta, CallbackInfoReturnable cir) { 24 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 25 | cir.setReturnValue(this.noise.getValue((double)pos.getX(), (double)pos.getY() * delta, (double)pos.getZ())); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/PlayerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity; 6 | 7 | import com.fexl.circumnavigate.processing.AABBWrapped; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.phys.AABB; 10 | import net.minecraft.world.phys.Vec3; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | @Mixin(Player.class) 16 | public class PlayerMixin { 17 | Player thiz = (Player) (Object) this; 18 | @Redirect(method = "canInteractWithEntity(Lnet/minecraft/world/phys/AABB;D)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/AABB;distanceToSqr(Lnet/minecraft/world/phys/Vec3;)D")) 19 | public double canInteractWithEntity(AABB instance, Vec3 vec) { 20 | return new AABBWrapped(instance, thiz.level().getTransformer().onlyServerSide()).distanceToSqr(vec); 21 | } 22 | 23 | @Redirect(method = "canInteractWithBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/AABB;distanceToSqr(Lnet/minecraft/world/phys/Vec3;)D")) 24 | public double canInteractWithBlock(AABB instance, Vec3 vec) { 25 | return new AABBWrapped(instance, thiz.level().getTransformer().onlyServerSide()).distanceToSqr(vec); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/NeighborUpdatorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block; 6 | 7 | import com.llamalad7.mixinextras.sugar.Local; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.world.level.Level; 11 | import net.minecraft.world.level.LevelAccessor; 12 | import net.minecraft.world.level.redstone.NeighborUpdater; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 16 | 17 | @Mixin(NeighborUpdater.class) 18 | public interface NeighborUpdatorMixin { 19 | @ModifyVariable(method = "executeUpdate", at = @At("HEAD"), index = 2, argsOnly = true) 20 | private static BlockPos wrapBlockPos(BlockPos blockPos, @Local(argsOnly = true) Level level) { 21 | return level.getTransformer().onlyServerSide().Block.wrapToBounds(blockPos); 22 | } 23 | 24 | @ModifyVariable(method = "executeShapeUpdate", at = @At("HEAD"), index = 3, argsOnly = true) 25 | private static BlockPos wrapBlockPos(BlockPos blockPos, @Local(argsOnly = true) LevelAccessor level) { 26 | if(level instanceof ServerLevel serverLevel) { 27 | return serverLevel.getTransformer().Block.wrapToBounds(blockPos); 28 | } 29 | return blockPos; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/densityFunctions/DensityFunctions$NoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other.densityFunctions; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.world.level.levelgen.DensityFunction; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(targets = "net.minecraft.world.level.levelgen.DensityFunctions$Noise") 17 | public class DensityFunctions$NoiseMixin { 18 | @Shadow @Final private DensityFunction.NoiseHolder noise; 19 | @Shadow @Final private double xzScale; 20 | @Shadow @Final private double yScale; 21 | 22 | @Inject(method = "compute", at = @At("HEAD"), cancellable = true) 23 | public void compute(DensityFunction.FunctionContext context, CallbackInfoReturnable cir) { 24 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 25 | cir.setReturnValue(this.noise.getValue(context.blockX(), (double)context.blockY() * this.yScale, context.blockZ())); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bug_fixes.yml: -------------------------------------------------------------------------------- 1 | name: Bug Fixes 2 | description: Fix problems with Circumnavigate 3 | title: "[Bug]: " 4 | labels: "bug" 5 | body: 6 | - type: input 7 | attributes: 8 | label: Describe the bug fixed 9 | description: What fixes were implemented? 10 | placeholder: "Entity coordinates weren't being wrapped correctly, so I modified several methods in `Entity.class` to get it working." 11 | validations: 12 | required: true 13 | - type: input 14 | attributes: 15 | label: Related Issues 16 | description: What existing issues does this bug fix resolve/affect? 17 | placeholder: "This issue resolves some issues with #6." 18 | validations: 19 | required: false 20 | - type: input 21 | attributes: 22 | label: Testing 23 | description: How has the fixed been tested and validated? 24 | placeholder: "I checked the entities' position by printing out their coordinates server-side whenever they moved, and made sure their position changed to the wrapped bounds." 25 | validations: 26 | required: true 27 | - type: input 28 | attributes: 29 | label: Additional Information 30 | description: What additional context or data surrounding this issue would you consider useful to add? How did you overcome problems that might present themselves in the future? 31 | validations: 32 | required: false 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/DualNoiseProviderMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.world.level.levelgen.feature.stateproviders.DualNoiseProvider; 10 | import net.minecraft.world.level.levelgen.synth.NormalNoise; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(DualNoiseProvider.class) 19 | public class DualNoiseProviderMixin { 20 | @Shadow @Final private float slowScale; 21 | @Shadow @Final private NormalNoise slowNoise; 22 | 23 | @Inject(method = "getSlowNoiseValue", at = @At("HEAD"), cancellable = true) 24 | public void getSlowNoiseValue(BlockPos pos, CallbackInfoReturnable cir) { 25 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 26 | cir.setReturnValue(this.slowNoise.getValue((double)((float)pos.getX()), (double)((float)pos.getY() * this.slowScale), (double)((float)pos.getZ()))); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/ServerPlayerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.level.portal.DimensionTransition; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.spongepowered.asm.mixin.Debug; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 20 | @Debug(export = true) 21 | @Mixin(ServerPlayer.class) 22 | public abstract class ServerPlayerMixin { 23 | ServerPlayer thiz = (ServerPlayer) (Object) this; 24 | 25 | @Shadow 26 | public abstract ServerLevel serverLevel(); 27 | 28 | /** 29 | * Wrap bed block distance check. 30 | */ 31 | @ModifyVariable(method = "isReachableBedBlock", at = @At("HEAD"), index = 1, argsOnly = true) 32 | public BlockPos modifyBlockPos(BlockPos blockPos) { 33 | return this.serverLevel().getTransformer().Block.unwrapFromBounds(thiz.blockPosition(), blockPos); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/packet/ServerConfigurationPacketListenerImplMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.packet; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.fexl.circumnavigate.network.packet.DimensionWrappingPayload; 9 | import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | @Mixin(ServerConfigurationPacketListenerImpl.class) 18 | public class ServerConfigurationPacketListenerImplMixin { 19 | ServerConfigurationPacketListenerImpl thiz = (ServerConfigurationPacketListenerImpl) (Object) this; 20 | @Inject(method = "startConfiguration", at = @At("HEAD")) 21 | public void startConfiguration(CallbackInfo ci) { 22 | for(ServerLevel level : thiz.server.getAllLevels()) { 23 | if (level.getTransformer().equals(DimensionTransformer.DISABLED)) { 24 | continue; 25 | } 26 | ServerConfigurationNetworking.send(thiz, new DimensionWrappingPayload(level.dimension(), level.getTransformer().wrappingSettings)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/VibrationSystemMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block; 6 | 7 | import com.llamalad7.mixinextras.sugar.Local; 8 | import net.minecraft.core.Holder; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.util.Mth; 11 | import net.minecraft.world.level.gameevent.GameEvent; 12 | import net.minecraft.world.level.gameevent.vibrations.VibrationInfo; 13 | import net.minecraft.world.level.gameevent.vibrations.VibrationSystem; 14 | import net.minecraft.world.phys.Vec3; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.ModifyArg; 18 | 19 | @Mixin(VibrationSystem.Listener.class) 20 | public abstract class VibrationSystemMixin { 21 | /** 22 | * Wrap distance calculation for vibration travel distance. 23 | */ 24 | @ModifyArg(method = "scheduleVibration", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/gameevent/vibrations/VibrationInfo;(Lnet/minecraft/core/Holder;FLnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)V")) 25 | public float schedule(float distance, @Local(argsOnly = true) ServerLevel level, @Local(argsOnly = true, ordinal = 0) Vec3 pos, @Local(argsOnly = true, ordinal = 1) Vec3 sensorPos) { 26 | return Mth.sqrt((float)level.getTransformer().Vector3D.sqrDistToBounds(pos, sensorPos)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/propagators/chunkTracker/providers/PoiManager$DistanceTracker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.propagators.chunkTracker.providers; 6 | 7 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 8 | import com.fexl.circumnavigate.core.DimensionTransformer; 9 | import com.fexl.circumnavigate.mixin.chunk.SectionTrackerMixin; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.world.entity.ai.village.poi.PoiManager; 12 | import net.minecraft.world.entity.ai.village.poi.PoiSection; 13 | import net.minecraft.world.level.chunk.storage.SectionStorage; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | @Mixin(targets = "net.minecraft.world.entity.ai.village.poi.PoiManager$DistanceTracker") 20 | public class PoiManager$DistanceTracker extends SectionTrackerMixin { 21 | @Inject(method = "", at = @At("TAIL")) 22 | public void init(PoiManager poiManager, CallbackInfo ci) { 23 | //This is only initialized in ChunkMap with a ServerLevel, which is why we can assume it is a ServerLevel 24 | DimensionTransformer transformer = ((ServerLevel)((SectionStorage) poiManager).levelHeightAccessor).getTransformer(); 25 | ((TransformerAccessor) this).setTransformer(transformer); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Say farewell to immersion-breaking world borders, and greetings to wrapped worlds! This mod visually and functionally tiles a finite world so that you can walk across from one border to the other.** 2 |
3 | 4 | **Please note this is an ALPHA. No stable or expected functionality or future support is guaranteed.** 5 | 6 | ## Tutorial:
7 | Watch [this video](https://www.youtube.com/watch?v=bmkUSeLEE7Y) or read the [wiki](https://github.com/FamroFexl/Circumnavigate/wiki) for setup information. 8 | 9 | ## Updates: 10 | **Current Functionality:** 11 | - Wrapped chunk sending and loading 12 | - World wrapping settings GUI 13 | - Wrapped block destruction/placement 14 | - Wrapped player positioning (for proper block collision and interaction distance) 15 | - Entity Handling (teleportation, hitboxes, spawning, riding) 16 | - Vanilla client support 17 | - Redstone/block update handling (redstone, explosions, light updates, fluids, pistons) 18 | - Stopping over-bounds chunk generation and storage 19 | - Proper nether limits and portal handling 20 | - Overworld wrapped world generation 21 | - Nether wrapped world generation 22 | 23 | **Future Updates (🔴 Alpha):** (not exclusive) 24 | - Entity handling (pathing) 25 | - Wrapped structure and feature generation 26 | 27 | **Future Updates (🟠 Beta/ 🟢 Release):** 28 | - Wrapped coordinate support in commands? 29 | - Wrapped chunk shifting? 30 | - Map item support? 31 | - Curvature shader? 32 | - Developer API? 33 | - Datapack coordinate wrapping? 34 | 35 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/compat/mixin/client/iris/CommonUniformsMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.compat.mixin.client.iris; 2 | 3 | import com.fexl.circumnavigate.core.DimensionTransformer; 4 | import net.irisshaders.iris.gl.uniform.UniformHolder; 5 | import net.irisshaders.iris.gl.uniform.UniformUpdateFrequency; 6 | import net.irisshaders.iris.shaderpack.properties.PackDirectives; 7 | import net.irisshaders.iris.uniforms.CommonUniforms; 8 | import net.irisshaders.iris.uniforms.FrameUpdateNotifier; 9 | import net.minecraft.client.Minecraft; 10 | import org.joml.Vector2i; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(CommonUniforms.class) 17 | public class CommonUniformsMixin { 18 | @Inject(method = "generalCommonUniforms", at = @At("HEAD"), remap = false) 19 | private static void general(UniformHolder uniforms, FrameUpdateNotifier updateNotifier, PackDirectives directives, CallbackInfo ci) { 20 | uniforms.uniform2i(UniformUpdateFrequency.PER_FRAME, "dimensionBounds", CommonUniformsMixin::getDimensionBounds); 21 | } 22 | 23 | private static Vector2i getDimensionBounds() { 24 | DimensionTransformer transformer = Minecraft.getInstance().level != null ? Minecraft.getInstance().level.getTransformer() : DimensionTransformer.DISABLED; 25 | return new Vector2i(transformer.xWidth, transformer.zWidth); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/densityFunctions/DensityFunction$NoiseHolderMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other.densityFunctions; 6 | 7 | import net.minecraft.core.Holder; 8 | import net.minecraft.world.level.levelgen.synth.NormalNoise; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | 14 | import java.util.function.Function; 15 | 16 | @Mixin(targets = "net.minecraft.world.level.levelgen.DensityFunction$NoiseHolder") 17 | public class DensityFunction$NoiseHolderMixin { 18 | @Shadow @Final private Holder noiseData; 19 | @Shadow @Final private @Nullable NormalNoise noise; 20 | 21 | private long lastTime =0; 22 | 23 | /** 24 | public double getValue(double x, double y, double z) { 25 | if(this.noise == null) { 26 | return 0.0; 27 | } 28 | else { 29 | return this.noise.getValue(x, y, z); 30 | } 31 | }**/ 32 | 33 | /** 34 | 35 | double xMul = 1; 36 | double zMul = 1; 37 | double xAdd = 0; 38 | double zAdd = 0; 39 | 40 | @Override 41 | public void setXMul(double xMul) { 42 | this.xMul = xMul; 43 | } 44 | 45 | @Override 46 | public void setZMul(double zMul) { 47 | this.zMul = zMul; 48 | } 49 | 50 | @Override 51 | public void setXAdd(double xAdd) { 52 | this.xAdd = xAdd; 53 | } 54 | 55 | @Override 56 | public void setZAdd(double zAdd) { 57 | this.zAdd = zAdd; 58 | }**/ 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/densityFunctions/DensityFunctions$ShiftedNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other.densityFunctions; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.world.level.levelgen.DensityFunction; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(targets = "net.minecraft.world.level.levelgen.DensityFunctions$ShiftedNoise") 17 | public class DensityFunctions$ShiftedNoiseMixin { 18 | @Shadow @Final private DensityFunction shiftX; 19 | @Shadow @Final private DensityFunction shiftY; 20 | @Shadow @Final private DensityFunction shiftZ; 21 | @Shadow @Final private double xzScale; 22 | @Shadow @Final private double yScale; 23 | @Shadow @Final private DensityFunction.NoiseHolder noise; 24 | 25 | @Inject(method = "compute", at = @At("HEAD"), cancellable = true) 26 | public void compute(DensityFunction.FunctionContext context, CallbackInfoReturnable cir) { 27 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 28 | cir.setReturnValue(this.noise.getValue(context.blockX(), context.blockY() * this.yScale + this.shiftY.compute(context), context.blockZ())); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.mixin.worldInit; 10 | 11 | import com.fexl.circumnavigate.storage.TransformerRequests; 12 | import com.mojang.datafixers.DataFixer; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.Services; 15 | import net.minecraft.server.WorldStem; 16 | import net.minecraft.server.level.progress.ChunkProgressListenerFactory; 17 | import net.minecraft.server.packs.repository.PackRepository; 18 | import net.minecraft.world.level.storage.LevelStorageSource; 19 | import org.spongepowered.asm.mixin.Mixin; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | import java.net.Proxy; 25 | 26 | @Mixin(MinecraftServer.class) 27 | public class MinecraftServerMixin { 28 | //Add universal codebase access to the MinecraftServer instance, similar to the Minecraft client class. DedicatedServer instances have no direct references associated with them. 29 | @Inject(method = "", at = @At("RETURN")) 30 | public void init(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory, CallbackInfo ci) { 31 | TransformerRequests.server = (MinecraftServer) (Object) this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report Problems with Circumnavigate 3 | title: "[Bug]: " 4 | labels: "bug" 5 | body: 6 | - type: input 7 | attributes: 8 | label: Describe the Bug 9 | description: A clear and concise description of what the bug is. 10 | validations: 11 | required: true 12 | - type: input 13 | attributes: 14 | label: Mod version(s) affected 15 | description: On what version(s) of the mod did this problem occur? 16 | placeholder: "1.0, 1.0.1, 1.1.3, etc." 17 | validations: 18 | required: true 19 | - type: dropdown 20 | attributes: 21 | label: Mod loader(s) used 22 | description: What Minecraft modloader(s) did you use? 23 | multiple: false 24 | options: 25 | - Fabric 26 | validations: 27 | required: true 28 | - type: input 29 | attributes: 30 | label: Minecraft version(s) used 31 | description: What version(s) of Minecraft did this bug occur in? 32 | placeholder: "1.19.4, 1.20.4, 1.21, etc." 33 | validations: 34 | required: true 35 | - type: input 36 | attributes: 37 | label: Other mods used 38 | description: What other mods were running at the same time? 39 | placeholder: "Optifine, Sodium, JourneyMap, etc." 40 | - type: textarea 41 | attributes: 42 | label: To Reproduce (If applicable) 43 | description: What actions and conditions were required to make the bug occur? 44 | - type: textarea 45 | attributes: 46 | label: Additional Information 47 | description: Other context you think might be useful. -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/propagators/chunkTracker/providers/ChunkMap$DistanceManagerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | /* 10 | * SPDX-License-Identifier: AGPL-3.0-only 11 | */ 12 | 13 | /* 14 | * SPDX-License-Identifier: AGPL-3.0-only 15 | */ 16 | 17 | package com.fexl.circumnavigate.mixin.propagators.chunkTracker.providers; 18 | 19 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 20 | import com.fexl.circumnavigate.mixin.propagators.chunkTracker.senders.DistanceManagerMixin; 21 | import net.minecraft.server.level.ChunkMap; 22 | import org.spongepowered.asm.mixin.*; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 26 | 27 | import java.util.concurrent.Executor; 28 | 29 | /** 30 | * Pass to {@link DistanceManagerMixin} 31 | */ 32 | @Mixin(ChunkMap.DistanceManager.class) 33 | public class ChunkMap$DistanceManagerMixin extends DistanceManagerMixin { 34 | @Inject(method = "", at = @At("TAIL")) 35 | public void init(ChunkMap chunkMap, Executor dispatcher, Executor mainThreadExecutor, CallbackInfo ci) { 36 | //Assigns a WorldTransformer to the DistanceManager (this is the only place it is used) 37 | ((TransformerAccessor) this).setTransformer(chunkMap.level.getTransformer()); 38 | 39 | //Initializes WorldTransformers for ChunkTracker instances since they couldn't be assigned in the superclasses' constructor. 40 | this.assignTransformers(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/other/densityFunctions/DensityFunctions$WeirdScaledSamplerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen.other.densityFunctions; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.world.level.levelgen.DensityFunction; 9 | import net.minecraft.world.level.levelgen.DensityFunctions; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | @Mixin(targets = "net.minecraft.world.level.levelgen.DensityFunctions$WeirdScaledSampler") 18 | public class DensityFunctions$WeirdScaledSamplerMixin { 19 | @Shadow @Final private DensityFunction input; 20 | @Shadow @Final private DensityFunction.NoiseHolder noise; 21 | @Shadow @Final private DensityFunctions.WeirdScaledSampler.RarityValueMapper rarityValueMapper; 22 | 23 | @Inject(method = "transform", at = @At("HEAD"), cancellable = true) 24 | public void transform(DensityFunction.FunctionContext context, double value, CallbackInfoReturnable cir) { 25 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 26 | double d = this.rarityValueMapper.mapper.get(value); 27 | cir.setReturnValue(Math.abs(this.noise.getValue(context.blockX(), (double)context.blockY() / d, context.blockZ()))); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/ExplosionMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.world.entity.Entity; 9 | import net.minecraft.world.level.Explosion; 10 | import net.minecraft.world.level.Level; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Redirect; 16 | 17 | @Mixin(Explosion.class) 18 | public class ExplosionMixin { 19 | @Final @Shadow private Level level; 20 | @Final @Shadow private double x; 21 | @Final @Shadow private double z; 22 | 23 | @Redirect(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;containing(DDD)Lnet/minecraft/core/BlockPos;")) 24 | public BlockPos wrapBlockPos(double x, double y, double z) { 25 | return level.getTransformer().onlyServerSide().Block.wrapToBounds(BlockPos.containing(x, y, z)); 26 | } 27 | 28 | @Redirect(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getX()D")) 29 | public double unwrapEntityX(Entity instance) { 30 | return level.getTransformer().onlyServerSide().Coord.X.unwrapFromBounds(instance.getX(), x); 31 | } 32 | 33 | @Redirect(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getZ()D")) 34 | public double unwrapEntityZ(Entity instance) { 35 | return level.getTransformer().onlyServerSide().Coord.Z.unwrapFromBounds(instance.getZ(), z); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/interfaceInjects/PlayerClientInjectorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.interfaceInjects; 6 | 7 | import com.fexl.circumnavigate.injected.ServerPlayerInjector; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.util.Mth; 11 | import net.minecraft.world.level.ChunkPos; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Unique; 15 | 16 | @Mixin(ServerPlayer.class) 17 | public class PlayerClientInjectorMixin implements ServerPlayerInjector { 18 | @Unique 19 | private double clientX = ((ServerPlayer)(Object)this).getX(); 20 | @Unique 21 | private double clientZ = ((ServerPlayer)(Object)this).getZ(); 22 | 23 | @Override 24 | public double getClientX() { return clientX; } 25 | 26 | @Override 27 | public double getClientZ() { 28 | return clientZ; 29 | } 30 | 31 | @Override 32 | public ChunkPos getClientChunk() { return new ChunkPos((int) (clientX / 16), (int) (clientZ / 16)); } 33 | 34 | @Override 35 | public BlockPos getClientBlock() { return new BlockPos(Mth.floor(clientX), ((ServerPlayer)(Object)this).blockPosition().getY(), Mth.floor(clientZ)); } 36 | 37 | @Override 38 | public Vec3 getClientPosition() { return new Vec3(clientX, ((ServerPlayer)(Object)this).position().y, clientZ); } 39 | 40 | @Override 41 | public void setClientX(double clientX) { 42 | this.clientX = clientX; 43 | } 44 | 45 | @Override 46 | public void setClientZ(double clientZ) { 47 | this.clientZ = clientZ; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/entities/FishingHookMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity.entities; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 9 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.entity.projectile.FishingHook; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | 16 | @Mixin(FishingHook.class) 17 | public abstract class FishingHookMixin { 18 | FishingHook thiz = (FishingHook) (Object) this; 19 | 20 | // Fix knockback miscalculation 21 | @WrapOperation(method = "pullEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V")) 22 | public void wrapDelta(Entity instance, Vec3 deltaMovement, Operation original) { 23 | Entity owner = thiz.getOwner(); 24 | DimensionTransformer transformer = instance.level().getTransformer().onlyServerSide(); 25 | double deltaX = transformer.Coord.X.deltaFromBounds(instance.getX(), owner.getX()); 26 | double deltaY = owner.getY() - instance.getY(); // Cant use y directly from deltaMovement since its scaled by vanilla 27 | double deltaZ = transformer.Coord.Z.deltaFromBounds(instance.getZ(), owner.getZ()); 28 | 29 | Vec3 newDeltaMovement = new Vec3(deltaX, deltaY, deltaZ).scale(0.1); 30 | original.call(instance, instance.getDeltaMovement().add(newDeltaMovement)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/ChunkStatusTasksMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.chunk; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.server.level.GenerationChunkHolder; 9 | import net.minecraft.util.StaticCache2D; 10 | import net.minecraft.world.level.chunk.ChunkAccess; 11 | import net.minecraft.world.level.chunk.status.ChunkStep; 12 | import net.minecraft.world.level.chunk.status.WorldGenContext; 13 | 14 | import net.minecraft.world.level.chunk.status.ChunkStatusTasks; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | @Mixin(ChunkStatusTasks.class) 23 | public class ChunkStatusTasksMixin { 24 | /** 25 | * Cancels chunk generation beyond wrapping bounds for all generation steps. 26 | */ 27 | @Inject(method = {"generateStructureStarts", "generateStructureReferences", "generateBiomes", "generateNoise", "generateSurface", "generateCarvers", "generateFeatures", "generateSpawn"}, at = @At("HEAD"), cancellable = true) 28 | private static void targetAll(WorldGenContext worldGenContext, ChunkStep step, StaticCache2D cache, ChunkAccess chunk, CallbackInfoReturnable> cir) { 29 | //Set the noise level for world transformers 30 | TransformerRequests.noiseLevel = worldGenContext.level(); 31 | 32 | if(worldGenContext.level().getTransformer().Chunk.isOverBounds(chunk.getPos())) cir.setReturnValue(CompletableFuture.completedFuture(chunk)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/SignalGetterMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks; 6 | 7 | import com.fexl.circumnavigate.processing.BlockPosWrapped; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.world.level.SignalGetter; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 14 | 15 | @Mixin(SignalGetter.class) 16 | public interface SignalGetterMixin { 17 | 18 | @ModifyVariable(method = "getControlInputSignal", at = @At("HEAD"), index = 1, argsOnly = true) 19 | default BlockPos modifyBlockPos(BlockPos blockPos) { 20 | SignalGetter thiz = (SignalGetter) (Object) this; 21 | if(thiz instanceof ServerLevel serverLevel) { 22 | return new BlockPosWrapped(blockPos, serverLevel.getTransformer()); 23 | } 24 | return blockPos; 25 | } 26 | 27 | @ModifyVariable(method = "getSignal", at = @At("HEAD"), index = 1, argsOnly = true) 28 | default BlockPos modifyBlockPos2(BlockPos blockPos) { 29 | SignalGetter thiz = (SignalGetter) (Object) this; 30 | if(thiz instanceof ServerLevel serverLevel) { 31 | return new BlockPosWrapped(blockPos, serverLevel.getTransformer()); 32 | } 33 | return blockPos; 34 | } 35 | 36 | @ModifyVariable(method = "hasNeighborSignal", at = @At("HEAD"), index = 1, argsOnly = true) 37 | default BlockPos modifyBlockPos3(BlockPos blockPos) { 38 | SignalGetter thiz = (SignalGetter) (Object) this; 39 | if(thiz instanceof ServerLevel serverLevel) { 40 | return new BlockPosWrapped(blockPos, serverLevel.getTransformer()); 41 | } 42 | return blockPos; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/light/BlockLightEngineMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.light; 2 | 3 | import com.fexl.circumnavigate.core.DimensionTransformer; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.server.level.ServerChunkCache; 7 | import net.minecraft.world.level.BlockGetter; 8 | import net.minecraft.world.level.lighting.BlockLightEngine; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | @Mixin(BlockLightEngine.class) 16 | public class BlockLightEngineMixin { 17 | @Unique 18 | BlockGetter level = ((LightEngineAccessor) this).getChunkSource().getLevel(); 19 | 20 | @ModifyVariable(method = "propagateIncrease", at = @At("HEAD"), index = 1, argsOnly = true) 21 | public long wrapBlockPosLong(long pos) { 22 | if(level instanceof ServerChunkCache cache) { 23 | DimensionTransformer transformer = cache.getLevel().getTransformer(); 24 | return transformer.Block.wrapToBounds(pos); 25 | } 26 | 27 | return pos; 28 | } 29 | 30 | @Redirect(method = {"propagateIncrease", "propagateDecrease"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;offset(JLnet/minecraft/core/Direction;)J")) 31 | public long wrapBlockPosOffsets(long pos, Direction direction) { 32 | if(level instanceof ServerChunkCache cache) { 33 | DimensionTransformer transformer = cache.getLevel().getTransformer(); 34 | return transformer.Block.wrapToBounds(BlockPos.offset(pos, direction)); 35 | } 36 | 37 | return BlockPos.offset(pos, direction); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/compat/CompatMixinConfigPlugin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.compat; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 6 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 7 | 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class CompatMixinConfigPlugin implements IMixinConfigPlugin { 12 | @Override 13 | public void onLoad(String mixinPackage) { 14 | 15 | } 16 | 17 | @Override 18 | public String getRefMapperConfig() { 19 | return "circumnavigate.refmap.json"; 20 | } 21 | 22 | @Override 23 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 24 | if(mixinClassName.contains("com.fexl.circumnavigate.compat.mixin")) { 25 | String[] elements = mixinClassName.split("\\."); 26 | String modid; 27 | if(elements[5].equals("client")) 28 | modid = elements[6]; 29 | else 30 | modid = elements[5]; 31 | return FabricLoader.getInstance().isModLoaded(modid); 32 | } 33 | return false; 34 | } 35 | 36 | @Override 37 | public void acceptTargets(Set myTargets, Set otherTargets) { 38 | 39 | } 40 | 41 | @Override 42 | public List getMixins() { 43 | return List.of(); 44 | } 45 | 46 | @Override 47 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 48 | 49 | } 50 | 51 | @Override 52 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/light/SkyLightEngineMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.light; 2 | 3 | import com.fexl.circumnavigate.core.DimensionTransformer; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.server.level.ServerChunkCache; 7 | import net.minecraft.world.level.BlockGetter; 8 | import net.minecraft.world.level.lighting.SkyLightEngine; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | @Mixin(SkyLightEngine.class) 16 | public class SkyLightEngineMixin { 17 | @Unique 18 | BlockGetter level = ((LightEngineAccessor) this).getChunkSource().getLevel(); 19 | 20 | @ModifyVariable(method = {"propagateIncrease", "propagateDecrease"}, at = @At("HEAD"), index = 1, argsOnly = true) 21 | public long wrapBlockPosLong(long pos) { 22 | if(level instanceof ServerChunkCache cache) { 23 | DimensionTransformer transformer = cache.getLevel().getTransformer(); 24 | return transformer.Block.wrapToBounds(pos); 25 | } 26 | 27 | return pos; 28 | } 29 | 30 | @Redirect(method = {"propagateIncrease", "propagateDecrease"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;offset(JLnet/minecraft/core/Direction;)J")) 31 | public long wrapBlockPosOffsets(long pos, Direction direction) { 32 | if(level instanceof ServerChunkCache cache) { 33 | DimensionTransformer transformer = cache.getLevel().getTransformer(); 34 | return transformer.Block.wrapToBounds(BlockPos.offset(pos, direction)); 35 | } 36 | 37 | return BlockPos.offset(pos, direction); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/Circumnavigate.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate; 4 | 5 | import com.fexl.circumnavigate.core.CoordinateConstants; 6 | import com.fexl.circumnavigate.network.packet.DimensionWrappingPayload; 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.fabricmc.api.ModInitializer; 9 | 10 | import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; 11 | import net.minecraft.server.MinecraftServer; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.apache.logging.log4j.LogManager; 14 | import org.apache.logging.log4j.Logger; 15 | 16 | public class Circumnavigate implements ModInitializer { 17 | public static final String MOD_ID = "circumnavigate"; 18 | public static final Logger LOGGER = LogManager.getLogger(StringUtils.capitalize(MOD_ID)); 19 | 20 | public static final boolean DEV_MODE = System.getProperty("env", "").equals("dev"); 21 | 22 | public static int tickCount = 0; 23 | @Override 24 | public void onInitialize() { 25 | PayloadTypeRegistry.configurationS2C().register(DimensionWrappingPayload.TYPE, DimensionWrappingPayload.STREAM_CODEC); 26 | //PayloadTypeRegistry.playS2C().register(ChunkLoadingLevelsPayload.TYPE, ChunkLoadingLevelsPayload.STREAM_CODEC); 27 | 28 | /** 29 | ServerTickEvents.END_SERVER_TICK.register((server -> { 30 | if (tickCount++ >= 10) { 31 | server.getAllLevels().forEach((serverLevel -> { 32 | for(ServerPlayer player : serverLevel.players()) { 33 | ServerPlayNetworking.send(player, new ChunkLoadingLevelsPayload(DebugInfo.chunkLoadingLevels)); 34 | } 35 | })); 36 | DebugInfo.chunkLoadingLevels = new HashMap<>(); 37 | tickCount = 0; 38 | 39 | // LOGGER.info("Sent chunk loading levels to all players"); 40 | } 41 | }));**/ 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/debug/PathMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.debug; 6 | 7 | import com.fexl.circumnavigate.Circumnavigate; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.world.level.pathfinder.Node; 11 | import net.minecraft.world.level.pathfinder.Path; 12 | import net.minecraft.world.level.pathfinder.Path.DebugData; 13 | import net.minecraft.world.level.pathfinder.Target; 14 | import org.jetbrains.annotations.Nullable; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Mutable; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | import java.util.List; 24 | import java.util.Set; 25 | 26 | @Mixin(Path.class) 27 | public class PathMixin { 28 | @Shadow @Mutable @Nullable private Path.DebugData debugData; 29 | 30 | @Shadow @Final private List nodes; 31 | 32 | @Shadow @Final private BlockPos target; 33 | 34 | @Inject(method ="writeToStream", at = @At("HEAD")) 35 | private void writeToStream(FriendlyByteBuf buffer, CallbackInfo ci) { 36 | if(!Circumnavigate.DEV_MODE) return; 37 | 38 | Node[] nodes1 = nodes.stream().filter(node -> !node.closed).toArray(Node[]::new); 39 | Node[] nodes2 = nodes.stream().filter(node -> node.closed).toArray(Node[]::new); 40 | this.debugData = new DebugData(nodes.stream().filter(node -> !node.closed).toArray(Node[]::new), nodes.stream().filter(node -> node.closed).toArray(Node[]::new), Set.of(new Target(target.getX(), target.getY(), target.getZ()))); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/core/FakeCoordinateTransformers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.core; 6 | 7 | public class FakeCoordinateTransformers extends CoordinateTransformers { 8 | public FakeCoordinateTransformers() { 9 | super(-CoordinateConstants.DISABLING_CHUNK_POS, CoordinateConstants.DISABLING_CHUNK_POS); 10 | 11 | this.Coord = new CoordMethods(); 12 | this.Chunk = new ChunkMethods(); 13 | } 14 | 15 | public class CoordMethods extends CoordinateTransformers.CoordMethods { 16 | public final int domainLength = CoordinateConstants.DISABLING_CHUNK_POS*2 * 16; 17 | 18 | @Override 19 | public Double wrapToBounds(Double coord) { 20 | return coord; 21 | } 22 | 23 | public Integer wrapToBounds(Integer coord) { 24 | return wrapToBounds(coord.doubleValue()).intValue(); 25 | } 26 | 27 | @Override 28 | public Double unwrapFromBounds(Double refCoord, Double wrappedCoord) { 29 | return wrappedCoord; 30 | } 31 | 32 | public Integer unwrapFromBounds(Integer refCoord, Integer wrappedCoord) { 33 | return unwrapFromBounds(refCoord.doubleValue(), wrappedCoord.doubleValue()).intValue(); 34 | } 35 | 36 | @Override 37 | public boolean isOverBounds(Double coord) { 38 | return false; 39 | } 40 | 41 | public boolean isOverBounds(Integer coord) { 42 | return isOverBounds(coord.doubleValue()); 43 | } 44 | 45 | public Double deltaFromBounds(Double fromCoord, Double toCoord) { 46 | double toCoordUnwrapped = unwrapFromBounds(fromCoord, toCoord); 47 | 48 | return toCoordUnwrapped - fromCoord; 49 | } 50 | 51 | public Double sqrDistToBounds(Double dist) { 52 | return dist * dist; 53 | } 54 | 55 | public Integer sqrDistToBounds(Integer dist) { 56 | return sqrDistToBounds(dist.doubleValue()).intValue(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/debug/DebugRendererMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.client.debug; 6 | 7 | import static com.fexl.circumnavigate.client.storage.DebugVariables.*; 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import net.minecraft.client.renderer.MultiBufferSource; 10 | import net.minecraft.client.renderer.debug.*; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | @Mixin(DebugRenderer.class) 19 | public class DebugRendererMixin { 20 | @Shadow @Final public DebugRenderer.SimpleDebugRenderer neighborsUpdateRenderer; 21 | @Shadow @Final public StructureRenderer structureRenderer; 22 | @Shadow @Final public PathfindingRenderer pathfindingRenderer; 23 | @Shadow @Final public DebugRenderer.SimpleDebugRenderer lightDebugRenderer; 24 | @Shadow @Final public DebugRenderer.SimpleDebugRenderer chunkRenderer; 25 | 26 | @Inject(method = "render", at = @At("HEAD")) 27 | public void render(PoseStack poseStack, MultiBufferSource.BufferSource bufferSource, double camX, double camY, double camZ, CallbackInfo ci) { 28 | if(renderNeighborUpdates) neighborsUpdateRenderer.render(poseStack, bufferSource, camX, camY, camZ); 29 | if(renderStructurePieces) structureRenderer.render(poseStack, bufferSource, camX, camY, camZ); 30 | if(renderPathfinding) pathfindingRenderer.render(poseStack, bufferSource, camX, camY, camZ); 31 | if(renderLightDebug) lightDebugRenderer.render(poseStack, bufferSource, camX, camY, camZ); 32 | if(renderChunkInfo) chunkRenderer.render(poseStack, bufferSource, camX, camY, camZ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/processing/BlockHitResultWrapped.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.processing; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.core.Direction; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.phys.BlockHitResult; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | public class BlockHitResultWrapped extends BlockHitResult { 16 | final DimensionTransformer transformer; 17 | 18 | private final Vec3 location; 19 | private final Direction direction; 20 | private final boolean miss; 21 | private final boolean inside; 22 | 23 | private BlockHitResultWrapped(boolean miss, Vec3 location, Direction direction, BlockPos blockPos, boolean inside, DimensionTransformer transformer) { 24 | super(transformer.Vector3D.wrapToBounds(location), direction, transformer.Block.wrapToBounds(blockPos), inside); 25 | this.miss = miss; 26 | this.location = location; 27 | this.direction = direction; 28 | this.inside = inside; 29 | this.transformer = transformer; 30 | } 31 | 32 | public BlockHitResultWrapped(BlockHitResult result, DimensionTransformer transformer) { 33 | this(result.getType() == Type.MISS, result.getLocation(), result.getDirection(), result.getBlockPos(), result.isInside(), transformer); 34 | } 35 | 36 | @Override 37 | public @NotNull BlockHitResultWrapped withPosition(BlockPos pos) { 38 | return new BlockHitResultWrapped(this.miss, this.location, this.direction, pos, this.inside, this.transformer); 39 | } 40 | 41 | @Override 42 | public double distanceTo(Entity entity) { 43 | return this.transformer.Coord.sqrDistToBounds(entity.getX(), entity.getY(), entity.getZ(), this.location.x, this.location.y, this.location.z); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/worldInit/CreateWorldScreenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.client.worldInit; 6 | 7 | import com.fexl.circumnavigate.accessors.WorldWrappingSettingsAccessor; 8 | import com.fexl.circumnavigate.options.WorldWrappingSettings; 9 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 10 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 11 | import com.mojang.serialization.Lifecycle; 12 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 13 | import net.minecraft.world.level.LevelSettings; 14 | import net.minecraft.world.level.levelgen.WorldOptions; 15 | import net.minecraft.world.level.storage.PrimaryLevelData; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | 19 | @Mixin(CreateWorldScreen.class) 20 | public class CreateWorldScreenMixin implements WorldWrappingSettingsAccessor { 21 | @WrapOperation(method = "createNewWorld", at = @At(value = "NEW", target = "Lnet/minecraft/world/level/storage/PrimaryLevelData;")) 22 | public PrimaryLevelData createNewWorld(LevelSettings settings, WorldOptions worldOptions, PrimaryLevelData.SpecialWorldProperty specialWorldProperty, Lifecycle worldGenSettingsLifecycle, Operation original) { 23 | PrimaryLevelData newData = original.call(settings, worldOptions, specialWorldProperty, worldGenSettingsLifecycle); 24 | ((WorldWrappingSettingsAccessor) (Object) newData).setWorldWrappingSettings(this.settings); 25 | 26 | return newData; 27 | } 28 | 29 | WorldWrappingSettings settings = null; 30 | 31 | @Override 32 | public void setWorldWrappingSettings(WorldWrappingSettings settings) { 33 | this.settings = settings; 34 | } 35 | 36 | @Override 37 | public WorldWrappingSettings getWorldWrappingSettings() { 38 | return settings; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/NormalNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.mixin.worldgen; 10 | 11 | import com.fexl.circumnavigate.storage.TransformerRequests; 12 | import net.minecraft.util.RandomSource; 13 | import net.minecraft.world.level.levelgen.synth.NormalNoise; 14 | import net.minecraft.world.level.levelgen.synth.PerlinNoise; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 22 | 23 | @Mixin(NormalNoise.class) 24 | public class NormalNoiseMixin { 25 | @Shadow @Final private double valueFactor; 26 | @Shadow @Final private PerlinNoise first; 27 | @Shadow @Final private PerlinNoise second; 28 | 29 | @Shadow @Final private double maxValue; 30 | private final double xWidth = 256.0; 31 | private final double zWidth = 256.0; 32 | 33 | private long lastTime = 0; 34 | 35 | private long source; 36 | @Inject(method = "", at = @At("TAIL")) 37 | public void init(RandomSource random, NormalNoise.NoiseParameters parameters, boolean useLegacyNetherBiome, CallbackInfo ci) { 38 | source = random.nextLong(); 39 | } 40 | 41 | @Inject(method = "getValue", at = @At("HEAD"), cancellable = true) 42 | public void getValue(double x, double y, double z, CallbackInfoReturnable cir) { 43 | if(TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 44 | cir.setReturnValue((this.first.getValue(x, y, z) + this.second.getValue(x, y, z)) * this.valueFactor); 45 | } 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/network/packet/ChunkLoadingLevelsPayload.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.network.packet; 6 | 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 10 | import net.minecraft.world.level.ChunkPos; 11 | 12 | import java.util.HashMap; 13 | 14 | public class ChunkLoadingLevelsPayload implements CustomPacketPayload { 15 | public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ChunkLoadingLevelsPayload::write, ChunkLoadingLevelsPayload::new); 16 | public static final CustomPacketPayload.Type TYPE = CustomPacketPayload.createType("debug/circumnavigate/chunk_loading_levels"); 17 | 18 | private final HashMap levels; 19 | 20 | public ChunkLoadingLevelsPayload(HashMap levels) { 21 | this.levels = levels; 22 | } 23 | 24 | private ChunkLoadingLevelsPayload(FriendlyByteBuf buffer) { 25 | this.levels = decodeLevels(buffer); 26 | } 27 | 28 | private void write(FriendlyByteBuf buffer) { 29 | buffer.writeInt(levels.size()); 30 | levels.keySet().forEach((chunkPos -> { 31 | buffer.writeChunkPos(chunkPos); 32 | buffer.writeInt(levels.get(chunkPos)); 33 | })); 34 | } 35 | 36 | private HashMap decodeLevels(FriendlyByteBuf buffer) { 37 | int size = buffer.readInt(); 38 | 39 | HashMap levels = new HashMap<>(); 40 | 41 | for(int i = 0; i < size; i++) { 42 | levels.put(buffer.readChunkPos(), buffer.readInt()); 43 | } 44 | 45 | return levels; 46 | } 47 | 48 | public HashMap getLevels() { 49 | return levels; 50 | } 51 | 52 | @Override 53 | public CustomPacketPayload.Type type() { 54 | return TYPE; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/core/BasicPositionOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.core; 6 | 7 | /** 8 | * Basic Position transformations and checks. 9 | *
{@code wrapToBounds} and {@code unwrapFromBounds} transform Positions between coordinate spaces using Position wrapping and unwrapping. 10 | *
{@code isOverBounds} checks if the Position exceeds a Bounds. 11 | * @param The type of Position the transformations and checks will be used for. 12 | */ 13 | public class BasicPositionOperations { 14 | /** 15 | * Wraps a Position around a Bounds until it is limited/constrained to the Bounds or falls/fits within the Bounds. 16 | * @param pos The Position to be Wrapped to the Bounds. 17 | * @return {@code wrappedPos} The Wrapped Position. 18 | */ 19 | public T wrapToBounds(T pos) { 20 | throw new UnsupportedOperationException("The " + pos.getClass().getSimpleName() + " type has no wrapping method!"); 21 | } 22 | 23 | /** 24 | * Unwraps a Wrapped Position relative to another Position. 25 | * @param refPos The reference Position to be relative to. 26 | * @param wrappedPos A Wrapped Position. 27 | * @return An unwrapped position 28 | */ 29 | public T unwrapFromBounds(T refPos, T wrappedPos) { 30 | throw new UnsupportedOperationException("The \"" + wrappedPos.getClass().getSimpleName() + "\" type has no unwrapping method!"); 31 | } 32 | 33 | /** 34 | * Checks if a Position is over/beyond a Bounds. 35 | * @param pos The Position to check. 36 | * @return {@code true} if the Position is over/beyond the Bounds. 37 | */ 38 | public boolean isOverBounds(T pos) { 39 | throw new UnsupportedOperationException("The \"" + pos.getClass().getSimpleName() + "\" type has no over-bounds checking method!"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/network/packet/DimensionWrappingPayload.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.network.packet; 4 | 5 | import com.fexl.circumnavigate.options.DimensionWrappingSettings; 6 | import net.minecraft.core.registries.Registries; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.world.level.Level; 12 | 13 | /** 14 | * Sends clients dimension wrapping data. 15 | */ 16 | public record DimensionWrappingPayload(ResourceKey levelKey, DimensionWrappingSettings wrappingSettings) implements CustomPacketPayload { 17 | public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(DimensionWrappingPayload::write, DimensionWrappingPayload::new); 18 | public static final CustomPacketPayload.Type TYPE = CustomPacketPayload.createType("debug/circumnavigate/wrapping_data"); 19 | 20 | private DimensionWrappingPayload(FriendlyByteBuf buffer) { 21 | this(buffer.readResourceKey(Registries.DIMENSION), new DimensionWrappingSettings(buffer.readInt(), buffer.readInt(), buffer.readInt(), buffer.readInt(), buffer.readEnum(DimensionWrappingSettings.Axis.class), buffer.readInt(), false)); 22 | } 23 | 24 | private void write(FriendlyByteBuf buffer) { 25 | buffer.writeResourceKey(levelKey); 26 | buffer.writeInt(wrappingSettings.xChunkBoundMin()); 27 | buffer.writeInt(wrappingSettings.xChunkBoundMax()); 28 | buffer.writeInt(wrappingSettings.zChunkBoundMin()); 29 | buffer.writeInt(wrappingSettings.zChunkBoundMax()); 30 | buffer.writeEnum(wrappingSettings.shiftAxis()); 31 | buffer.writeInt(wrappingSettings.shiftAmount()); 32 | } 33 | 34 | @Override 35 | public CustomPacketPayload.Type type() { 36 | return TYPE; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/worldInit/ClientLevelMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client.worldInit; 4 | 5 | import com.fexl.circumnavigate.client.storage.TransformersStorage; 6 | import com.fexl.circumnavigate.core.DimensionTransformer; 7 | import net.minecraft.client.multiplayer.ClientLevel; 8 | import net.minecraft.client.multiplayer.ClientPacketListener; 9 | import net.minecraft.client.renderer.LevelRenderer; 10 | import net.minecraft.core.Holder; 11 | import net.minecraft.resources.ResourceKey; 12 | import net.minecraft.world.level.Level; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | import com.llamalad7.mixinextras.sugar.Local; 19 | 20 | import java.util.function.Supplier; 21 | 22 | @Mixin(ClientLevel.class) 23 | public class ClientLevelMixin { 24 | ClientLevel thiz = (ClientLevel) (Object) this; 25 | 26 | /** 27 | * Set the dimension transformer for the ClientLevel depending on the requested dimension. 28 | */ 29 | @Inject(method = "", at = @At("TAIL")) 30 | public void init(ClientPacketListener connection, ClientLevel.ClientLevelData clientLevelData, ResourceKey dimension, Holder dimensionType, int viewDistance, int serverSimulationDistance, Supplier profiler, LevelRenderer levelRenderer, boolean isDebug, long biomeZoomSeed, CallbackInfo ci) { 31 | thiz.setTransformer(TransformersStorage.getTransformer(dimension)); 32 | } 33 | 34 | @ModifyVariable(method = "", at = @At("HEAD"), index = 5, argsOnly = true) 35 | private static int changeViewDistance(int viewDistance, @Local ResourceKey dimension) { 36 | DimensionTransformer transformer = TransformersStorage.getTransformer(dimension); 37 | return transformer.limitViewDistance(viewDistance); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/options/DimensionWrappingSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.options; 6 | 7 | import com.mojang.serialization.Codec; 8 | import com.mojang.serialization.DataResult; 9 | import com.mojang.serialization.codecs.RecordCodecBuilder; 10 | 11 | public record DimensionWrappingSettings(int xChunkBoundMin, int xChunkBoundMax, int zChunkBoundMin, int zChunkBoundMax, Axis shiftAxis, int shiftAmount, boolean useWrappedWorldGen) { 12 | public static final Codec CODEC = RecordCodecBuilder.create( 13 | instance -> instance.group( 14 | Codec.INT.fieldOf("XChunkBoundMin").forGetter(DimensionWrappingSettings::xChunkBoundMin), 15 | Codec.INT.fieldOf("XChunkBoundMax").forGetter(DimensionWrappingSettings::xChunkBoundMax), 16 | Codec.INT.fieldOf("ZChunkBoundMin").forGetter(DimensionWrappingSettings::zChunkBoundMin), 17 | Codec.INT.fieldOf("ZChunkBoundMax").forGetter(DimensionWrappingSettings::zChunkBoundMax), 18 | Axis.CODEC.fieldOf("ShiftAxis").forGetter(DimensionWrappingSettings::shiftAxis), 19 | Codec.INT.fieldOf("ShiftAmount").forGetter(DimensionWrappingSettings::shiftAmount), 20 | Codec.BOOL.fieldOf("UseWrappedWorldGen").forGetter(DimensionWrappingSettings::useWrappedWorldGen) 21 | ) 22 | .apply(instance, instance.stable(DimensionWrappingSettings::new)) 23 | ); 24 | 25 | public DimensionWrappingSettings(int bounds) { 26 | this(-Math.abs(bounds), Math.abs(bounds), -Math.abs(bounds), Math.abs(bounds), Axis.X, 0, false); 27 | } 28 | 29 | public enum Axis { 30 | X, 31 | Z; 32 | 33 | public static final Codec CODEC = Codec.STRING.comapFlatMap( 34 | axis -> { 35 | try { 36 | return DataResult.success(DimensionWrappingSettings.Axis.valueOf(axis)); 37 | } catch (IllegalArgumentException e) { 38 | return DataResult.error(() -> "\"" + axis + "\" is not an axis!"); 39 | } 40 | }, 41 | DimensionWrappingSettings.Axis::toString 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/SectionTrackerMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.chunk; 4 | 5 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 6 | import com.fexl.circumnavigate.core.DimensionTransformer; 7 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 8 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 9 | import com.llamalad7.mixinextras.sugar.Local; 10 | import net.minecraft.server.level.SectionTracker; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | 14 | //Transformer is available after the initializer 15 | @Mixin(SectionTracker.class) 16 | public class SectionTrackerMixin implements TransformerAccessor { 17 | SectionTracker thiz = (SectionTracker) (Object) this; 18 | 19 | /** 20 | * Updates loading levels of adjacent chunk sections so they are ready when needed. Modified to include wrapped sections. 21 | */ 22 | @WrapOperation(method = "checkNeighborsAfterUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/SectionPos;offset(JIII)J")) 23 | private long wrapChunkPos(long pos, int x, int y, int z, Operation original, @Local(argsOnly = true) int level, @Local(argsOnly = true) boolean isDecreasing) { 24 | DimensionTransformer transformer = getTransformer(); 25 | 26 | int wrappedX = transformer.Chunk.X.wrapToBounds(x); 27 | int wrappedZ = transformer.Chunk.Z.wrapToBounds(z); 28 | long chunkLong = original.call(pos, wrappedX, y, wrappedZ); 29 | 30 | if (chunkLong != pos) { 31 | SectionTracker thiz = (SectionTracker) (Object) this; 32 | thiz.checkNeighbor(pos, chunkLong, level, isDecreasing); 33 | } 34 | 35 | return original.call(pos, x, y, z); 36 | } 37 | 38 | DimensionTransformer transformer; 39 | 40 | @Override 41 | public DimensionTransformer getTransformer() { 42 | return this.transformer; 43 | } 44 | 45 | @Override 46 | public void setTransformer(DimensionTransformer transformer) { 47 | this.transformer = transformer; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${mod_id}", 4 | "version": "${mod_version}", 5 | "name": "${mod_name}", 6 | "description": "Finite, Tiled, Seamless World Wrapping", 7 | "authors": [ 8 | "Famro Fexl", 9 | "Skidam" 10 | ], 11 | "contact": { 12 | "homepage": "https://modrinth.com/project/circumnavigate", 13 | "sources": "https://github.com/FamroFexl/Circumnavigate", 14 | "issues": "https://github.com/FamroFexl/Circumnavigate/issues" 15 | }, 16 | "license": "AGPL-3.0", 17 | "icon": "assets/circumnavigate/icon.png", 18 | "environment": "*", 19 | "entrypoints": { 20 | "main": [ 21 | "com.fexl.circumnavigate.Circumnavigate" 22 | ], 23 | "client": [ 24 | "com.fexl.circumnavigate.CircumnavigateClient" 25 | ], 26 | "server": [ 27 | "com.fexl.circumnavigate.CircumnavigateServer" 28 | ] 29 | }, 30 | "accessWidener": "circumnavigate.accesswidener", 31 | "custom": { 32 | "loom:injected_interfaces": { 33 | "net/minecraft/class_3222": ["com/fexl/circumnavigate/injected/ServerPlayerInjector"], 34 | "net/minecraft/class_1937": ["com/fexl/circumnavigate/injected/DimensionTransformerInjector"], 35 | "net/minecraft/class_1922": ["com/fexl/circumnavigate/injected/DimensionTransformerInjector", "com/fexl/circumnavigate/injected/IsClientSideInjector"], 36 | "net/minecraft/class_1924": ["com/fexl/circumnavigate/injected/DimensionTransformerInjector"], 37 | "net/minecraft/class_3196": ["com/fexl/circumnavigate/injected/DimensionTransformerInjector"] 38 | } 39 | }, 40 | "mixins": [ 41 | "minecraft.mixins.json", 42 | "compat.mixins.json", 43 | { 44 | "config": "minecraft-client.mixins.json", 45 | "environment": "client" 46 | }, 47 | { 48 | "config": "compat-client.mixins.json", 49 | "environment": "client" 50 | } 51 | ], 52 | "depends": { 53 | "fabricloader": ">=${fabric_loader_version}", 54 | "minecraft": "${mc_version}", 55 | "java": ">=${java_version}", 56 | "fabric-api": ">=${fabric_api_version}" 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/propagators/chunkTracker/senders/DistanceManagerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | /* 10 | * SPDX-License-Identifier: AGPL-3.0-only 11 | */ 12 | 13 | package com.fexl.circumnavigate.mixin.propagators.chunkTracker.senders; 14 | 15 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 16 | import com.fexl.circumnavigate.core.DimensionTransformer; 17 | import net.minecraft.server.level.DistanceManager; 18 | import net.minecraft.server.level.TickingTracker; 19 | import org.spongepowered.asm.mixin.*; 20 | 21 | /** 22 | * Pass to {@link com.fexl.circumnavigate.mixin.chunk.ChunkTrackerMixin} instances 23 | */ 24 | @Mixin(DistanceManager.class) 25 | public abstract class DistanceManagerMixin implements TransformerAccessor { 26 | @Mutable @Shadow @Final private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter; 27 | @Mutable @Shadow @Final private DistanceManager.PlayerTicketTracker playerTicketManager; 28 | @Mutable @Shadow @Final private DistanceManager.ChunkTicketTracker ticketTracker; 29 | @Mutable @Shadow @Final private TickingTracker tickingTicketsTracker; 30 | 31 | /** 32 | * Sets the transformers for the trackers. This cannot be called before DistanceManager's constructor because the child class ChunkMap.DistanceManager needs to call its super method (DistanceManager's constructor) before it can assign a transformer to the class. 33 | */ 34 | @Unique 35 | public void assignTransformers() { 36 | this.naturalSpawnChunkCounter.setTransformer(this.getTransformer()); 37 | this.playerTicketManager.setTransformer(this.getTransformer()); 38 | this.ticketTracker.setTransformer(this.getTransformer()); 39 | this.tickingTicketsTracker.setTransformer(this.getTransformer()); 40 | } 41 | 42 | DimensionTransformer transformer; 43 | 44 | @Override 45 | public DimensionTransformer getTransformer() { 46 | return this.transformer; 47 | } 48 | 49 | @Override 50 | public void setTransformer(DimensionTransformer transformer) { 51 | this.transformer = transformer; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/PerlinSimplexNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import it.unimi.dsi.fastutil.ints.IntSortedSet; 9 | import net.minecraft.util.RandomSource; 10 | import net.minecraft.world.level.levelgen.synth.PerlinSimplexNoise; 11 | import net.minecraft.world.level.levelgen.synth.SimplexNoise; 12 | import org.spongepowered.asm.mixin.Final; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 19 | 20 | @Mixin(PerlinSimplexNoise.class) 21 | public class PerlinSimplexNoiseMixin { 22 | @Shadow @Final private SimplexNoise[] noiseLevels; 23 | @Shadow @Final private double highestFreqValueFactor; 24 | @Shadow @Final private double highestFreqInputFactor; 25 | 26 | private final double xWidth = 256.0; 27 | private final double zWidth = 256.0; 28 | 29 | private long source; 30 | @Inject(method = "(Lnet/minecraft/util/RandomSource;Lit/unimi/dsi/fastutil/ints/IntSortedSet;)V", at = @At("TAIL")) 31 | public void init(RandomSource random, IntSortedSet octaves, CallbackInfo ci) { 32 | source = random.nextLong(); 33 | } 34 | 35 | @Inject(method = "getValue", at= @At("HEAD"), cancellable = true) 36 | public void getValue(double x, double y, boolean useNoiseOffsets, CallbackInfoReturnable cir) { 37 | if(!TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 38 | return; 39 | } 40 | 41 | double d = 0.0; 42 | double e = this.highestFreqInputFactor; 43 | double f = this.highestFreqValueFactor; 44 | 45 | for (SimplexNoise simplexNoise : this.noiseLevels) { 46 | if (simplexNoise != null) { 47 | double finalE = e; 48 | d += simplexNoise.getValue(x, y) * f; 49 | } 50 | 51 | e /= 2.0; 52 | f *= 2.0; 53 | } 54 | 55 | cir.setReturnValue(d); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/debug/DebugScreenOverlayMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client.debug; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import com.llamalad7.mixinextras.sugar.Local; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.components.DebugScreenOverlay; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.core.SectionPos; 11 | import net.minecraft.world.level.ChunkPos; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | import java.util.List; 19 | import java.util.Locale; 20 | 21 | @Mixin(DebugScreenOverlay.class) 22 | public class DebugScreenOverlayMixin { 23 | @Shadow Minecraft minecraft; 24 | 25 | 26 | @Inject(method = "getGameInformation()Ljava/util/List;", at = @At("RETURN")) 27 | public void getGameInformation(CallbackInfoReturnable> cir, @Local BlockPos blockPos, @Local ChunkPos chunkPos, @Local List list) { 28 | DimensionTransformer transformer = minecraft.level.getTransformer(); 29 | if(!transformer.isWrapped()) 30 | return; 31 | 32 | int addPos; 33 | if(list.get(9).equals("")) 34 | addPos = 13; 35 | else 36 | addPos = 12; 37 | 38 | //Provides the server-side chunk & block position if the player is past the range where they are identical 39 | if(transformer.Coord.X.isOverBounds(blockPos.getX()) || transformer.Coord.Z.isOverBounds(blockPos.getZ())) { 40 | list.add(addPos++, String.format(Locale.ROOT, "Actual Block: %d %d %d", transformer.Coord.X.wrapToBounds(blockPos.getX()), blockPos.getY(), transformer.Coord.Z.wrapToBounds(blockPos.getZ()))); 41 | list.add(addPos++, String.format(Locale.ROOT, "Actual Chunk: %d %d %d", transformer.Chunk.X.wrapToBounds(chunkPos.x), SectionPos.blockToSectionCoord((int)blockPos.getY()), transformer.Chunk.Z.wrapToBounds(chunkPos.z))); 42 | } 43 | //Shows the wrapping info 44 | list.add(addPos, String.format(Locale.ROOT, transformer.toString())); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/block/blocks/NetherPortalBlockMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.block.blocks; 6 | 7 | import net.minecraft.core.BlockPos; 8 | import net.minecraft.resources.ResourceKey; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.level.Level; 12 | import net.minecraft.world.level.block.NetherPortalBlock; 13 | import net.minecraft.world.level.border.WorldBorder; 14 | import net.minecraft.world.level.portal.DimensionTransition; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 20 | 21 | @Mixin(NetherPortalBlock.class) 22 | public abstract class NetherPortalBlockMixin { 23 | @Shadow protected abstract DimensionTransition getExitPortal(ServerLevel level, Entity entity, BlockPos pos, BlockPos exitPos, boolean isNether, WorldBorder worldBorder); 24 | 25 | /** 26 | * Adjust portal scaling to dimension scaling. 27 | */ 28 | @Inject(method = "getPortalDestination", at = @At("HEAD"), cancellable = true) 29 | public void getPortalDestination(ServerLevel level, Entity entity, BlockPos pos, CallbackInfoReturnable cir) { 30 | ResourceKey resourceKey = level.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER; 31 | ServerLevel serverLevel = level.getServer().getLevel(resourceKey); 32 | if (serverLevel == null) { 33 | cir.setReturnValue(null); 34 | } else { 35 | boolean bl = serverLevel.dimension() == Level.NETHER; 36 | WorldBorder worldBorder = serverLevel.getWorldBorder(); 37 | double x = ((double) serverLevel.getTransformer().xWidth) / ((double) level.getTransformer().xWidth); 38 | double z = ((double) serverLevel.getTransformer().zWidth) / ((double) level.getTransformer().zWidth); 39 | BlockPos blockPos = worldBorder.clampToBounds(entity.getX() * x, entity.getY(), entity.getZ() * z); 40 | cir.setReturnValue(this.getExitPortal(serverLevel, entity, pos, blockPos, bl, worldBorder)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/worldInit/WorldTabMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client.worldInit; 4 | 5 | import com.fexl.circumnavigate.client.gui.screens.WorldWrappingSettingsScreen; 6 | import com.llamalad7.mixinextras.sugar.Local; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.components.CycleButton; 9 | import net.minecraft.client.gui.components.StringWidget; 10 | import net.minecraft.client.gui.components.Tooltip; 11 | import net.minecraft.client.gui.layouts.LayoutSettings; 12 | import net.minecraft.client.gui.layouts.GridLayout; 13 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 14 | import net.minecraft.network.chat.Component; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 19 | 20 | @Mixin(targets = "net.minecraft.client.gui.screens.worldselection.CreateWorldScreen$WorldTab") 21 | public class WorldTabMixin { 22 | /** 23 | * Adds a "Wrap World" button to the WorldTab, which opens up a wrappingSettings screen. 24 | */ 25 | @Inject(method = "(Lnet/minecraft/client/gui/screens/worldselection/CreateWorldScreen;)V", at = @At("TAIL")) 26 | public void gameTab(CreateWorldScreen createWorldScreen, CallbackInfo ci, @Local GridLayout.RowHelper rowHelper) { 27 | CycleButton.Builder wrappingButton = CycleButton.onOffBuilder(false); 28 | Tooltip tooltip = Tooltip.create(Component.literal("Wrap the world along boundaries.")); 29 | wrappingButton.withTooltip(boolean_ -> tooltip); 30 | wrappingButton.displayOnlyValue(); 31 | rowHelper.addChild(new StringWidget(Component.literal("Wrap World"), Minecraft.getInstance().font), new LayoutSettings.LayoutSettingsImpl().alignVerticallyMiddle()); 32 | rowHelper.addChild(wrappingButton.create(0, 0, 44, 20, (Component) Component.empty(), (cycleButton, state) -> { 33 | if(cycleButton.getValue()) { 34 | Minecraft.getInstance().setScreen(new WorldWrappingSettingsScreen(createWorldScreen, cycleButton)); 35 | } 36 | }), new LayoutSettings.LayoutSettingsImpl().alignHorizontallyRight()); 37 | } 38 | 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/PrimaryLevelDataMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.worldInit; 4 | 5 | import com.fexl.circumnavigate.accessors.WorldWrappingSettingsAccessor; 6 | import com.fexl.circumnavigate.options.WorldWrappingSettings; 7 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import com.mojang.serialization.Dynamic; 10 | import net.minecraft.core.RegistryAccess; 11 | import net.minecraft.nbt.CompoundTag; 12 | import net.minecraft.nbt.NbtOps; 13 | import net.minecraft.world.level.storage.PrimaryLevelData; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.util.Optional; 20 | 21 | @SuppressWarnings("deprecation") 22 | @Mixin(PrimaryLevelData.class) 23 | public class PrimaryLevelDataMixin implements WorldWrappingSettingsAccessor { 24 | /** 25 | * Inject wrappingSettings into the level.dat. 26 | */ 27 | @Inject(method = "setTagData(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/nbt/CompoundTag;)V", at = @At(value = "TAIL")) 28 | private void injectSaveData(RegistryAccess registry, CompoundTag nbt, CompoundTag playerNBT, CallbackInfo ci) { 29 | if(settings != null) { 30 | nbt.put("WrappingSettings", WorldWrappingSettings.CODEC.encodeStart(NbtOps.INSTANCE, settings).getOrThrow()); 31 | } 32 | } 33 | 34 | /** 35 | * Retrieve the wrappingSettings save data from the level.dat on world selection. 36 | */ 37 | @ModifyReturnValue(method = "parse", at = @At("RETURN")) 38 | private static PrimaryLevelData parse(PrimaryLevelData original, @Local(argsOnly = true) Dynamic tag) { 39 | Optional worldWrappingSettings = WorldWrappingSettings.CODEC.parse(tag.get("WrappingSettings").orElseEmptyMap()).result(); 40 | ((WorldWrappingSettingsAccessor) (Object) original).setWorldWrappingSettings(worldWrappingSettings.orElse(null)); 41 | 42 | return original; 43 | } 44 | 45 | WorldWrappingSettings settings = null; 46 | 47 | @Override 48 | public void setWorldWrappingSettings(WorldWrappingSettings settings) { 49 | this.settings = settings; 50 | } 51 | 52 | @Override 53 | public WorldWrappingSettings getWorldWrappingSettings() { 54 | return settings; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/gameevent/GameEventDispatcherMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.gameevent; 6 | 7 | import com.llamalad7.mixinextras.sugar.Local; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.core.Holder; 10 | import net.minecraft.core.SectionPos; 11 | import net.minecraft.network.protocol.game.DebugPackets; 12 | import net.minecraft.server.level.ServerChunkCache; 13 | import net.minecraft.server.level.ServerLevel; 14 | import net.minecraft.world.level.chunk.ChunkAccess; 15 | import net.minecraft.world.level.chunk.LevelChunk; 16 | import net.minecraft.world.level.gameevent.GameEvent; 17 | import net.minecraft.world.level.gameevent.GameEventDispatcher; 18 | import net.minecraft.world.level.gameevent.GameEventListener; 19 | import net.minecraft.world.level.gameevent.GameEventListenerRegistry; 20 | import net.minecraft.world.phys.Vec3; 21 | import org.spongepowered.asm.mixin.Final; 22 | import org.spongepowered.asm.mixin.Mixin; 23 | import org.spongepowered.asm.mixin.Shadow; 24 | import org.spongepowered.asm.mixin.injection.At; 25 | import org.spongepowered.asm.mixin.injection.ModifyArg; 26 | import org.spongepowered.asm.mixin.injection.Redirect; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | @Mixin(GameEventDispatcher.class) 32 | public abstract class GameEventDispatcherMixin { 33 | @Shadow @Final private ServerLevel level; 34 | 35 | /** 36 | * Wrap distance calculation for game events dependent on minimum distances. 37 | */ 38 | @ModifyArg(method = "method_45492", index = 4, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/gameevent/GameEvent$ListenerInfo;(Lnet/minecraft/core/Holder;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/level/gameevent/GameEvent$Context;Lnet/minecraft/world/level/gameevent/GameEventListener;Lnet/minecraft/world/phys/Vec3;)V")) 39 | public Vec3 modifyDist(Vec3 listener, @Local(argsOnly = true, ordinal = 0) Vec3 event) { 40 | return level.getTransformer().Vector3D.unwrapFromBounds(event, listener); 41 | } 42 | 43 | @Redirect(method = "post", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getChunkNow(II)Lnet/minecraft/world/level/chunk/LevelChunk;")) 44 | public LevelChunk postChunkMix(ServerChunkCache instance, int chunkX, int chunkZ) { 45 | return this.level.getChunkSource().getChunkNow(level.getTransformer().Chunk.X.wrapToBounds(chunkX), level.getTransformer().Chunk.Z.wrapToBounds(chunkZ)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.entity; 2 | 3 | import com.fexl.circumnavigate.core.DimensionTransformer; 4 | import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 5 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 6 | import net.minecraft.world.damagesource.DamageSource; 7 | import net.minecraft.world.entity.Entity; 8 | import net.minecraft.world.entity.LivingEntity; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | // Fix knockback miscalculation 16 | @Mixin(LivingEntity.class) 17 | public abstract class LivingEntityMixin { 18 | 19 | @Unique private double deltaX; 20 | @Unique private double deltaZ; 21 | 22 | @Inject(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")) 23 | public void wrapDelta(DamageSource source, float amount, CallbackInfoReturnable cir) { 24 | Entity thiz = (Entity) (Object) this; 25 | Entity enemy = source.getEntity(); 26 | DimensionTransformer transformer = enemy.level().getTransformer().onlyServerSide(); 27 | deltaX = transformer.Coord.X.deltaFromBounds(thiz.getX(), enemy.getX()); 28 | deltaZ = transformer.Coord.Z.deltaFromBounds(thiz.getZ(), enemy.getZ()); 29 | 30 | // Vanilla parity code 31 | while (deltaX * deltaX + deltaZ * deltaZ < 1.0E-4) { 32 | deltaX = (Math.random() - Math.random()) * 0.01; 33 | deltaZ = (Math.random() - Math.random()) * 0.01; 34 | } 35 | } 36 | 37 | @WrapMethod(method = "knockback") 38 | public void wrapDistance1(double strength, double x, double z, Operation original) { 39 | // In case this method would be called from somewhere else 40 | if (deltaX != 0 || deltaZ != 0) { 41 | x = deltaX; 42 | z = deltaZ; 43 | } 44 | 45 | original.call(strength, x, z); 46 | } 47 | 48 | @WrapMethod(method = "indicateDamage") 49 | public void wrapDistance2(double x, double z, Operation original) { 50 | // In case this method would be called from somewhere else 51 | if (deltaX != 0 || deltaZ != 0) { 52 | x = deltaX; 53 | z = deltaZ; 54 | } 55 | 56 | original.call(x, z); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/PerlinNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.mixin.worldgen; 10 | 11 | import com.fexl.circumnavigate.storage.TransformerRequests; 12 | import com.mojang.datafixers.util.Pair; 13 | import it.unimi.dsi.fastutil.doubles.DoubleList; 14 | import net.minecraft.util.RandomSource; 15 | import net.minecraft.world.level.levelgen.synth.ImprovedNoise; 16 | import net.minecraft.world.level.levelgen.synth.PerlinNoise; 17 | import org.spongepowered.asm.mixin.Final; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.Shadow; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 24 | 25 | @Mixin(PerlinNoise.class) 26 | public class PerlinNoiseMixin { 27 | 28 | @Shadow @Final private ImprovedNoise[] noiseLevels; 29 | @Shadow @Final private int firstOctave; 30 | @Shadow @Final private DoubleList amplitudes; 31 | @Shadow @Final private double lowestFreqValueFactor; 32 | @Shadow @Final private double lowestFreqInputFactor; 33 | @Shadow @Final private double maxValue; 34 | 35 | private final double xWidth = 256.0; 36 | private final double zWidth = 256.0; 37 | 38 | //PerlinNoise thiz = (PerlinNoise) (Object) this; 39 | 40 | private long source; 41 | 42 | @Inject(method = "", at = @At("TAIL")) 43 | public void init(RandomSource random, Pair octavesAndAmplitudes, boolean useNewFactory, CallbackInfo ci) { 44 | source = random.nextLong(); 45 | } 46 | 47 | @Inject(method = "getValue(DDDDDZ)D", at = @At("HEAD"), cancellable = true) 48 | public void getValue(double x, double y, double z, double yScale, double yMax, boolean useFixedY, CallbackInfoReturnable cir) { 49 | if(!TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 50 | return; 51 | } 52 | 53 | 54 | double d = 0.0; 55 | double e = this.lowestFreqInputFactor; 56 | double f = this.lowestFreqValueFactor; 57 | 58 | for (int i = 0; i < this.noiseLevels.length; i++) { 59 | ImprovedNoise improvedNoise = this.noiseLevels[i]; 60 | if (improvedNoise != null) { 61 | double g = improvedNoise.noise(x, useFixedY ? -improvedNoise.yo : PerlinNoise.wrap(y * e), z, yScale * e, yMax * e); 62 | d += this.amplitudes.getDouble(i) * g * f; 63 | } 64 | 65 | e *= 2.0; 66 | f /= 2.0; 67 | } 68 | 69 | cir.setReturnValue(d); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/packet/ConnectionMixin.java: -------------------------------------------------------------------------------- 1 | package com.fexl.circumnavigate.mixin.packet; 2 | 3 | import com.fexl.circumnavigate.processing.PacketTransformer; 4 | import com.fexl.circumnavigate.storage.TransformerRequests; 5 | import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 6 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 7 | import net.minecraft.network.Connection; 8 | import net.minecraft.network.PacketListener; 9 | import net.minecraft.network.PacketSendListener; 10 | import net.minecraft.network.protocol.Packet; 11 | import net.minecraft.server.level.ServerPlayer; 12 | import net.minecraft.server.network.ServerCommonPacketListenerImpl; 13 | import org.jetbrains.annotations.Nullable; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Unique; 16 | 17 | @Mixin(Connection.class) 18 | public abstract class ConnectionMixin { 19 | 20 | @Unique 21 | Connection thiz = (Connection) (Object) this; 22 | 23 | /** 24 | * Processes incoming packets from a client. 25 | */ 26 | @WrapMethod(method = "genericsFtw") 27 | private static void packetReceive(Packet packet, PacketListener listener, Operation original) { 28 | if(listener instanceof ServerCommonPacketListenerImpl commonPacketListener) { 29 | ServerPlayer player = getPlayerByConnection(commonPacketListener.connection); 30 | if(player == null) { 31 | original.call(packet, listener); 32 | return; 33 | } 34 | 35 | original.call(PacketTransformer.process(packet, player), listener); 36 | } 37 | else original.call(packet, listener); 38 | } 39 | 40 | /** 41 | * Processes outgoing packets to a client. 42 | */ 43 | @WrapMethod(method = "send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;Z)V") 44 | public void packetSend(Packet packet, @Nullable PacketSendListener listener, boolean flush, Operation original) { 45 | ServerPlayer player = getPlayerByConnection(thiz); 46 | if(player == null) { 47 | original.call(packet, listener, flush); 48 | return; 49 | } 50 | 51 | original.call(PacketTransformer.process(packet, player), listener, flush); 52 | } 53 | 54 | @Unique 55 | private static ServerPlayer getPlayerByConnection(Connection connection) { 56 | for(ServerPlayer player : TransformerRequests.server.getPlayerList().getPlayers()) { 57 | if(player.connection.connection.equals(connection) ) { 58 | return player; 59 | } 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/packet/PlayerChunkSenderMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.packet; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.google.common.collect.Comparators; 9 | import it.unimi.dsi.fastutil.longs.LongSet; 10 | import net.minecraft.server.level.ChunkMap; 11 | import net.minecraft.server.network.PlayerChunkSender; 12 | import net.minecraft.util.Mth; 13 | import net.minecraft.world.level.ChunkPos; 14 | import net.minecraft.world.level.chunk.LevelChunk; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 21 | 22 | import java.util.Comparator; 23 | import java.util.List; 24 | import java.util.Objects; 25 | 26 | @SuppressWarnings({"unused", "unchecked", "deprecation"}) 27 | @Mixin(PlayerChunkSender.class) 28 | public class PlayerChunkSenderMixin { 29 | @Shadow 30 | private float batchQuota; 31 | @Final 32 | @Shadow private boolean memoryConnection; 33 | @Final @Shadow private LongSet pendingChunks; 34 | 35 | /** 36 | * Collect chunks to send, prioritizing closer chunks first. Modified to include wrapped chunks as closer. 37 | */ 38 | @Inject(method = "collectChunksToSend", at = @At("HEAD"), cancellable = true) 39 | private void wrapChunkPosPriority(ChunkMap chunkMap, ChunkPos chunkPos, CallbackInfoReturnable> cir) { 40 | DimensionTransformer transformer = chunkMap.level.getTransformer(); 41 | cir.cancel(); 42 | 43 | int i = Mth.floor(this.batchQuota); 44 | List list; 45 | if (!this.memoryConnection && this.pendingChunks.size() > i) { 46 | //list = ((List)this.pendingChunks.stream().collect(Comparators.least(i, Comparator.comparingInt(chunkPos::distanceSquared)))) 47 | list = ((List)this.pendingChunks.stream().collect(Comparators.least(i, Comparator.comparingInt(compare -> transformer.Chunk.sqrDistToBounds(chunkPos.toLong(), compare))))) 48 | .stream() 49 | .mapToLong(longValue -> (long) longValue) 50 | .mapToObj(chunkMap::getChunkToSend) 51 | .filter(Objects::nonNull) 52 | .toList(); 53 | } else { 54 | list = this.pendingChunks 55 | .longStream() 56 | .mapToObj(chunkMap::getChunkToSend) 57 | .filter(Objects::nonNull) 58 | //.sorted(Comparator.comparingInt(levelChunkx -> chunkPos.distanceSquared(levelChunkx.getPos()))) 59 | .sorted(Comparator.comparingInt(levelChunkx -> transformer.Chunk.sqrDistToBounds(levelChunkx.getPos(), chunkPos))) 60 | .toList(); 61 | } 62 | 63 | for (LevelChunk levelChunk : list) { 64 | this.pendingChunks.remove(levelChunk.getPos().toLong()); 65 | } 66 | 67 | cir.setReturnValue(list); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/collisions/BlockCollisionsMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity.collisions; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import com.google.common.collect.AbstractIterator; 9 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 10 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 11 | import net.minecraft.core.BlockPos; 12 | import net.minecraft.server.level.ServerLevel; 13 | import net.minecraft.world.entity.Entity; 14 | import net.minecraft.world.level.*; 15 | import net.minecraft.world.phys.AABB; 16 | import net.minecraft.world.phys.shapes.VoxelShape; 17 | import org.spongepowered.asm.mixin.*; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | 22 | import java.util.function.BiFunction; 23 | 24 | @Mixin(BlockCollisions.class) 25 | public abstract class BlockCollisionsMixin extends AbstractIterator { 26 | @Unique 27 | ServerLevel serverLevel; 28 | 29 | @Mutable @Shadow @Final private AABB box; 30 | 31 | /** 32 | * Provides the serverLevel, if it exists. 33 | */ 34 | @Inject(method = "", at = @At("RETURN")) 35 | private void wrap3DCursor(CollisionGetter collisionGetter, Entity entity, AABB box, boolean onlySuffocatingBlocks, BiFunction resultProvider, CallbackInfo ci) { 36 | serverLevel = null; 37 | if(collisionGetter instanceof ServerLevel level) serverLevel = level; 38 | } 39 | 40 | /** 41 | * Wraps thee BlockCollisions.getChunk() method. 42 | */ 43 | @WrapOperation(method = "computeNext", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/BlockCollisions;getChunk(II)Lnet/minecraft/world/level/BlockGetter;")) 44 | public BlockGetter getChunk(BlockCollisions instance, int x, int z, Operation original) { 45 | if(serverLevel == null) return original.call(instance, x, z); 46 | DimensionTransformer transformer = serverLevel.getTransformer(); 47 | return original.call(instance, (int) transformer.Coord.X.wrapToBounds(x), (int) transformer.Coord.Z.wrapToBounds(z)); 48 | } 49 | 50 | /** 51 | * Wraps the MutableBlockPos.set() method. 52 | */ 53 | @WrapOperation(method = "computeNext", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos$MutableBlockPos;set(III)Lnet/minecraft/core/BlockPos$MutableBlockPos;")) 54 | public BlockPos.MutableBlockPos setPos(BlockPos.MutableBlockPos instance, int x, int y, int z, Operation original) { 55 | if(serverLevel == null) return original.call(instance, x, y, z); 56 | DimensionTransformer transformer = serverLevel.getTransformer(); 57 | return original.call(instance, (int) transformer.Coord.X.wrapToBounds(x), y, (int) transformer.Coord.Z.wrapToBounds(z)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/debug/DebugPacketsMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.debug; 6 | 7 | import com.fexl.circumnavigate.Circumnavigate; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 10 | import net.minecraft.network.protocol.common.custom.NeighborUpdatesDebugPayload; 11 | import net.minecraft.network.protocol.common.custom.PathfindingDebugPayload; 12 | import net.minecraft.network.protocol.common.custom.StructuresDebugPayload; 13 | import net.minecraft.network.protocol.game.DebugPackets; 14 | import net.minecraft.server.level.ServerLevel; 15 | import net.minecraft.world.entity.Mob; 16 | import net.minecraft.world.level.Level; 17 | import net.minecraft.world.level.WorldGenLevel; 18 | import net.minecraft.world.level.levelgen.structure.StructurePiece; 19 | import net.minecraft.world.level.levelgen.structure.StructureStart; 20 | import net.minecraft.world.level.pathfinder.Path; 21 | import org.jetbrains.annotations.Nullable; 22 | import org.spongepowered.asm.mixin.Mixin; 23 | import org.spongepowered.asm.mixin.Shadow; 24 | import org.spongepowered.asm.mixin.injection.At; 25 | import org.spongepowered.asm.mixin.injection.Inject; 26 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | @Mixin(DebugPackets.class) 32 | public abstract class DebugPacketsMixin { 33 | @Shadow private static void sendPacketToAllPlayers(ServerLevel level, CustomPacketPayload payload) {}; 34 | 35 | @Inject(method = "sendNeighborsUpdatePacket", at = @At("HEAD")) 36 | private static void sendNeighborsUpdatePacket(Level level, BlockPos pos, CallbackInfo ci) { 37 | if(!Circumnavigate.DEV_MODE) return; 38 | 39 | sendPacketToAllPlayers((ServerLevel) level, new NeighborUpdatesDebugPayload(level.getGameTime(), pos)); 40 | } 41 | 42 | @Inject(method = "sendPathFindingPacket", at = @At("HEAD")) 43 | private static void sendPathFindingPacket(Level level, Mob mob, @Nullable Path path, float maxDistanceToWaypoint, CallbackInfo ci) { 44 | if(!Circumnavigate.DEV_MODE) return; 45 | 46 | if(path != null) sendPacketToAllPlayers((ServerLevel) level, new PathfindingDebugPayload(mob.getId(), path, maxDistanceToWaypoint)); 47 | } 48 | 49 | @Inject(method = "sendStructurePacket", at = @At("HEAD")) 50 | private static void sendStructurePacket(WorldGenLevel level, StructureStart structureStart, CallbackInfo ci) { 51 | if(!Circumnavigate.DEV_MODE) return; 52 | 53 | List pieces = new ArrayList<>(); 54 | 55 | structureStart.getPieces().forEach(structurePiece -> pieces.add(new StructuresDebugPayload.PieceInfo(structurePiece.getBoundingBox(), pieces.isEmpty()))); 56 | 57 | sendPacketToAllPlayers(level.getLevel(), new StructuresDebugPayload(level.getLevel().dimension(), structureStart.getBoundingBox(), pieces)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/CircumnavigateClient.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate; 4 | 5 | import com.fexl.circumnavigate.client.storage.TransformersStorage; 6 | import com.fexl.circumnavigate.core.DimensionTransformer; 7 | import com.fexl.circumnavigate.network.packet.DimensionWrappingPayload; 8 | import net.fabricmc.api.ClientModInitializer; 9 | import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking; 10 | import net.fabricmc.loader.api.FabricLoader; 11 | import net.minecraft.world.level.ChunkPos; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.nio.file.StandardCopyOption; 18 | import java.util.HashMap; 19 | 20 | import static com.fexl.circumnavigate.Circumnavigate.LOGGER; 21 | 22 | public class CircumnavigateClient implements ClientModInitializer { 23 | public static HashMap chunkLoadingLevels = new HashMap<>(); 24 | 25 | @Override 26 | public void onInitializeClient() { 27 | //Read an incoming transformer from the server. Received during server configuration. 28 | ClientConfigurationNetworking.registerGlobalReceiver(DimensionWrappingPayload.TYPE, (((payload, context) -> { 29 | TransformersStorage.setTransformer(payload.levelKey(), new DimensionTransformer(payload.wrappingSettings(), true)); 30 | }))); 31 | 32 | copyShader(); 33 | 34 | /** 35 | ClientPlayNetworking.registerGlobalReceiver(ChunkLoadingLevelsPayload.TYPE, (((payload, context) -> { 36 | HashMap newLevels = payload.getLevels(); 37 | for(ChunkPos serverPos : newLevels.keySet()) { 38 | chunkLoadingLevels.put(serverPos, newLevels.get(serverPos)); 39 | } 40 | })));**/ 41 | } 42 | 43 | private void copyShader() { 44 | //Check if Iris is installed 45 | if(!FabricLoader.getInstance().isModLoaded("iris")) return; 46 | 47 | String shaderName = "Circumnavigate Curvature Shader.zip"; 48 | 49 | Path shaderDir = FabricLoader.getInstance().getGameDir().resolve("shaderpacks"); 50 | Path shaderPath = shaderDir.resolve(shaderName); 51 | 52 | //Only copy the shader over if it doesn't exist 53 | if(shaderPath.toFile().exists()) { 54 | LOGGER.info("Default shader already exists in \"shaderpacks\", skipping..."); 55 | return; 56 | } 57 | 58 | //Create "shaderpacks" directory if it doesn't exist 59 | try { 60 | if(!Files.exists(shaderDir)) Files.createDirectory(shaderDir); 61 | } catch (IOException e) { 62 | 63 | LOGGER.error("Couldn't create a \"shaderpacks\" folder!"); 64 | } 65 | 66 | //Copy default shader if it doesn't exist. 67 | try (InputStream in = getClass().getResourceAsStream("/" + shaderName)){ 68 | Files.copy(in, shaderPath, StandardCopyOption.REPLACE_EXISTING); 69 | LOGGER.info("Copied default shader to \"shaderpacks\"!"); 70 | } catch (IOException e) { 71 | LOGGER.error("Couldn't copy the default shader to \"shaderpacks\"!"); 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/ServerLevelMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.worldInit; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import com.fexl.circumnavigate.accessors.WorldWrappingSettingsAccessor; 7 | import com.fexl.circumnavigate.options.WorldWrappingSettings; 8 | import com.fexl.circumnavigate.storage.TransformerRequests; 9 | import net.minecraft.resources.ResourceKey; 10 | import net.minecraft.server.MinecraftServer; 11 | import net.minecraft.server.level.ServerLevel; 12 | import net.minecraft.server.level.progress.ChunkProgressListener; 13 | import net.minecraft.world.RandomSequences; 14 | import net.minecraft.world.level.Level; 15 | import net.minecraft.world.level.dimension.LevelStem; 16 | import net.minecraft.world.level.storage.DerivedLevelData; 17 | import net.minecraft.world.level.storage.LevelStorageSource; 18 | import net.minecraft.world.level.storage.PrimaryLevelData; 19 | import net.minecraft.world.level.storage.ServerLevelData; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.injection.At; 22 | import org.spongepowered.asm.mixin.injection.Inject; 23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 24 | 25 | import java.util.List; 26 | import java.util.concurrent.Executor; 27 | 28 | @Mixin(ServerLevel.class) 29 | public class ServerLevelMixin { 30 | /** 31 | * Set the wrapping settings for each level when it is created as quickly as possible. 32 | */ 33 | @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/LevelStem;generator()Lnet/minecraft/world/level/chunk/ChunkGenerator;")) 34 | public void init(MinecraftServer server, Executor dispatcher, LevelStorageSource.LevelStorageAccess levelStorageAccess, ServerLevelData serverLevelData, ResourceKey dimension, LevelStem levelStem, ChunkProgressListener progressListener, boolean isDebug, long biomeZoomSeed, List customSpawners, boolean tickTime, RandomSequences randomSequences, CallbackInfo ci) { 35 | ServerLevel thiz = (ServerLevel) (Object) this; 36 | 37 | //Get world wrapping settings from PrimaryLevelData 38 | WorldWrappingSettings settings = null; 39 | if(serverLevelData instanceof PrimaryLevelData primaryLevelData) { 40 | settings = ((WorldWrappingSettingsAccessor) (Object) primaryLevelData).getWorldWrappingSettings(); 41 | } 42 | else if(serverLevelData instanceof DerivedLevelData derivedLevelData) { 43 | settings = ((WorldWrappingSettingsAccessor) (Object) derivedLevelData.wrapped).getWorldWrappingSettings(); 44 | } 45 | 46 | //Assign transformers from world wrapping settings 47 | if(settings != null && settings.dimensions().containsKey(dimension)) { 48 | thiz.setTransformer(new DimensionTransformer(settings.dimensions().get(dimension), false)); 49 | } 50 | else { 51 | thiz.setTransformer(DimensionTransformer.DISABLED); 52 | } 53 | 54 | //Set the initial noise level so it can be used when objects are set up. 55 | TransformerRequests.noiseLevel = thiz; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/ImprovedNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.mixin.worldgen; 10 | 11 | import com.fexl.circumnavigate.core.DimensionTransformer; 12 | import com.fexl.circumnavigate.processing.worldgen.OpenSimplex2S; 13 | import com.fexl.circumnavigate.storage.TransformerRequests; 14 | import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 15 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 16 | import net.minecraft.util.Mth; 17 | import net.minecraft.util.RandomSource; 18 | import net.minecraft.world.level.levelgen.synth.ImprovedNoise; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Shadow; 22 | import org.spongepowered.asm.mixin.injection.At; 23 | import org.spongepowered.asm.mixin.injection.Inject; 24 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 25 | 26 | import java.util.function.Function; 27 | 28 | @Mixin(ImprovedNoise.class) 29 | public class ImprovedNoiseMixin { 30 | ImprovedNoise thiz = (ImprovedNoise) (Object) this; 31 | //private final int seed = 2497518; 32 | //private final long randomSource = new WorldgenRandom(new LegacyRandomSource(seed)).nextLong(); 33 | 34 | private static long lastTime = 0; 35 | 36 | @Final @Shadow private byte[] p; 37 | @Final @Shadow public double xo; 38 | @Final @Shadow public double yo; 39 | @Final @Shadow public double zo; 40 | 41 | private long source; 42 | 43 | @Inject(method = "", at = @At("TAIL")) 44 | public void init(RandomSource random, CallbackInfo ci) { 45 | source = random.nextLong(); 46 | } 47 | 48 | @WrapMethod(method = "noise(DDDDD)D") 49 | public double noise(double x, double y, double z, double yScale, double yMax, Operation original) { 50 | DimensionTransformer transformer = TransformerRequests.noiseLevel.getTransformer(); 51 | 52 | if(!transformer.wrappingSettings.useWrappedWorldGen()) { 53 | return original.call(x, y, z, yScale, yMax); 54 | } 55 | 56 | int intY = Mth.floor(y); 57 | double deltaY = y - intY; 58 | 59 | double n; 60 | if (yScale != 0.0) { 61 | double m; 62 | if (yMax >= 0.0 && yMax < deltaY) { 63 | m = yMax; 64 | } else { 65 | m = deltaY; 66 | } 67 | 68 | n = (double)Mth.floor(m / yScale + 1.0E-7F) * yScale; 69 | } else { 70 | n = 0.0; 71 | } 72 | 73 | //double xa = ((x - xAdd) / xMul) / (xWidth); 74 | //double za = ((z - zAdd) / zMul) / (zWidth); 75 | double xa = (x + transformer.wrappingSettings.xChunkBoundMin()*16) / (transformer.xWidth*16); 76 | double za = (z + transformer.wrappingSettings.zChunkBoundMin()*16) / (transformer.zWidth*16); 77 | 78 | double rxa = xa * 2.0 * Math.PI; 79 | double rza = za * 2.0 * Math.PI; 80 | 81 | double noise4 = OpenSimplex2S.noise4_Fallback(source, Math.sin(rxa), Math.cos(rxa), Math.sin(rza), Math.cos(rza)); 82 | double noise1 = OpenSimplex2S.noise2(source, 0, y - n); 83 | return (noise4 + noise1)/2.0; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/ClientToServerListener.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.client; 4 | 5 | import com.mojang.logging.LogUtils; 6 | import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; 7 | import net.minecraft.network.protocol.Packet; 8 | import net.minecraft.network.protocol.game.ClientGamePacketListener; 9 | import net.minecraft.network.protocol.game.ServerGamePacketListener; 10 | import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; 11 | import org.slf4j.Logger; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import java.lang.reflect.ParameterizedType; 18 | import java.lang.reflect.Type; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | /** 23 | * For testing only 24 | */ 25 | @Mixin(ClientCommonPacketListenerImpl.class) 26 | public class ClientToServerListener { 27 | private static final Logger LOGGER = LogUtils.getLogger(); 28 | 29 | String previousLine; 30 | String currentLine; 31 | int lineCount = 1; 32 | 33 | @Inject(method = "send", at = @At("HEAD")) 34 | public void send(Packet packet, CallbackInfo ci) { 35 | //if(isGamePacket(packet.getClass())) 36 | Class clazz = packet.getClass(); 37 | //if(!isInExceptions(clazz.getSimpleName())) 38 | //return; 39 | if(true) 40 | return; 41 | String log = "C->S: "; 42 | if(clazz.getEnclosingClass() != null) 43 | log += clazz.getEnclosingClass().getSimpleName() + "." + clazz.getSimpleName(); 44 | else 45 | log += clazz.getSimpleName(); 46 | /** 47 | if(packet instanceof ServerboundMovePlayerPacket.Pos) { 48 | ServerboundMovePlayerPacket.Pos posUpdate = (ServerboundMovePlayerPacket.Pos) packet; 49 | log += " " + posUpdate.getX(-1) + ", " + posUpdate.getZ(-1); 50 | }**/ 51 | 52 | currentLine = log; 53 | 54 | //If the current line equals the previous line 55 | if(currentLine.equals(previousLine)) { 56 | //Increment the line count 57 | lineCount++; 58 | } 59 | //Not equal to the previous line 60 | else { 61 | //LOGGER.info(previousLine + ((lineCount != 1) ? (" {" + lineCount + "}") : "")); 62 | lineCount = 1; 63 | previousLine = currentLine; 64 | } 65 | } 66 | 67 | public boolean isInExceptions(String name) { 68 | List stringList = Arrays.asList("ServerboundPlayerActionPacket", "ServerboundSwingPacket"); 69 | return stringList.contains(name); 70 | } 71 | 72 | public boolean isGamePacket(Class clazz) { 73 | Type[] interfaces = clazz.getGenericInterfaces(); 74 | for(Type iface : interfaces) { 75 | if (iface instanceof ParameterizedType) { 76 | ParameterizedType paramType = (ParameterizedType) iface; 77 | if (paramType.getRawType().equals(Packet.class)) { 78 | Type[] typeArgs = paramType.getActualTypeArguments(); 79 | if (typeArgs.length == 1 && typeArgs[0].equals(ClientGamePacketListener.class)) { 80 | return true; 81 | } 82 | } 83 | } 84 | } 85 | return false; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/ChunkTrackingView$PositionedMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.chunk; 4 | 5 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 6 | import com.fexl.circumnavigate.core.DimensionTransformer; 7 | import com.google.common.annotations.VisibleForTesting; 8 | import net.minecraft.server.level.ChunkTrackingView.Positioned; 9 | import net.minecraft.world.level.ChunkPos; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Overwrite; 12 | import org.spongepowered.asm.mixin.Unique; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | import java.util.function.Consumer; 19 | 20 | @Mixin(Positioned.class) 21 | public abstract class ChunkTrackingView$PositionedMixin implements TransformerAccessor { 22 | 23 | @Unique private Positioned thiz = (Positioned) (Object) this; 24 | /** 25 | * Modified to ensure when the player is on a chunk boundary, and login, they will get the correct chunks from both sides of the world. This method is only used on login. 26 | * 27 | * @author Famro Fexl 28 | * @reason Wrapped Worlds require including wrapped chunks in calculations. 29 | */ 30 | @Overwrite 31 | public void forEach(Consumer action) { 32 | for (int x = thiz.minX(); x <= thiz.maxX(); x++) { 33 | for (int z = thiz.minZ(); z <= thiz.maxZ(); z++) { 34 | 35 | int wrappedX = transformer.Chunk.X.wrapToBounds(x); 36 | int wrappedZ = transformer.Chunk.Z.wrapToBounds(z); 37 | 38 | if (((Positioned)(Object)this).contains(wrappedX, wrappedZ)) { 39 | action.accept(new ChunkPos(wrappedX, wrappedZ)); 40 | } 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * Modified to ensure chunks aren't unnecessarily sent or dropped when the player is teleported across the world bounds. 47 | * 48 | * @author Famro Fexl 49 | * @reason Intersections must be wrapped in a Wrapped World. 50 | */ 51 | @VisibleForTesting 52 | @Overwrite 53 | public boolean squareIntersects(Positioned other) { 54 | boolean xIntersects = (thiz.minX() <= other.maxX() && thiz.maxX() >= other.minX()) || 55 | (thiz.minX() + transformer.xWidth <= other.maxX() && thiz.maxX() + transformer.xWidth >= other.minX()) || 56 | (thiz.minX() <= other.maxX() + transformer.xWidth && thiz.maxX() >= other.minX() + transformer.xWidth); 57 | 58 | boolean zIntersects = (thiz.minZ() <= other.maxZ() && thiz.maxZ() >= other.minZ()) || 59 | (thiz.minZ() + transformer.zWidth <= other.maxZ() && thiz.maxZ() + transformer.zWidth >= other.minZ()) || 60 | (thiz.minZ() <= other.maxZ() + transformer.zWidth && thiz.maxZ() >= other.minZ() + transformer.zWidth); 61 | 62 | return xIntersects && zIntersects; 63 | } 64 | 65 | DimensionTransformer transformer; 66 | 67 | @Override 68 | public DimensionTransformer getTransformer() { 69 | return this.transformer; 70 | } 71 | 72 | @Override 73 | public void setTransformer(DimensionTransformer transformer) { 74 | this.transformer = transformer; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/processing/Vec3iWrapped.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.processing; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import net.minecraft.core.Direction; 9 | import net.minecraft.core.Vec3i; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class Vec3iWrapped extends Vec3i { 13 | final DimensionTransformer transformer; 14 | 15 | public Vec3iWrapped(int x, int y, int z, DimensionTransformer transformer) { 16 | super(transformer.Coord.X.wrapToBounds(x), y, transformer.Coord.Z.wrapToBounds(z)); 17 | this.transformer = transformer; 18 | } 19 | 20 | @Override 21 | protected @NotNull Vec3iWrapped setX(int x) { 22 | return (Vec3iWrapped) super.setX(transformer.Coord.X.wrapToBounds(x)); 23 | } 24 | 25 | @Override 26 | protected @NotNull Vec3iWrapped setZ(int z) { 27 | return (Vec3iWrapped) super.setZ(transformer.Coord.Z.wrapToBounds(z)); 28 | } 29 | 30 | @Override 31 | public @NotNull Vec3iWrapped offset(int dx, int dy, int dz) { 32 | return dx == 0 && dy == 0 && dz == 0 ? this : new Vec3iWrapped(this.getX() + dx, this.getY() + dy, this.getZ() + dz, this.transformer); 33 | } 34 | 35 | @Override 36 | public @NotNull Vec3iWrapped multiply(int scalar) { 37 | if (scalar == 1) { 38 | return this; 39 | } else { 40 | return scalar == 0 ? (Vec3iWrapped) ZERO : new Vec3iWrapped(this.getX() * scalar, this.getY() * scalar, this.getZ() * scalar, this.transformer); 41 | } 42 | } 43 | 44 | @Override 45 | public @NotNull Vec3iWrapped relative(Direction direction, int distance) { 46 | return distance == 0 47 | ? this 48 | : new Vec3iWrapped(this.getX() + direction.getStepX() * distance, this.getY() + direction.getStepY() * distance, this.getZ() + direction.getStepZ() * distance, this.transformer); 49 | } 50 | 51 | @Override 52 | public @NotNull Vec3iWrapped relative(Direction.Axis axis, int amount) { 53 | if (amount == 0) { 54 | return this; 55 | } else { 56 | int i = axis == Direction.Axis.X ? amount : 0; 57 | int j = axis == Direction.Axis.Y ? amount : 0; 58 | int k = axis == Direction.Axis.Z ? amount : 0; 59 | return new Vec3iWrapped(this.getX() + i, this.getY() + j, this.getZ() + k, this.transformer); 60 | } 61 | } 62 | 63 | @Override 64 | public @NotNull Vec3iWrapped cross(Vec3i vector) { 65 | return new Vec3iWrapped( 66 | this.getY() * vector.getZ() - this.getZ() * vector.getY(), 67 | this.getZ() * vector.getX() - this.getX() * vector.getZ(), 68 | this.getX() * vector.getY() - this.getY() * vector.getX(), 69 | this.transformer 70 | ); 71 | } 72 | 73 | @Override 74 | public double distToCenterSqr(double x, double y, double z) { 75 | return transformer.Coord.sqrDistToBounds(x, y, z, this.getX() + 0.5, this.getY() + 0.5, this.getZ() + 0.5); 76 | } 77 | 78 | @Override 79 | public double distToLowCornerSqr(double x, double y, double z) { 80 | return transformer.Coord.sqrDistToBounds(x, y, z, this.getX(), this.getY(), this.getZ()); 81 | } 82 | 83 | @Override 84 | public int distManhattan(Vec3i vector) { 85 | float f = transformer.Coord.X.wrapToBounds(Math.abs(vector.getX() - this.getX())); 86 | float g = (float)Math.abs(vector.getY() - this.getY()); 87 | float h = transformer.Coord.Z.wrapToBounds(Math.abs(vector.getZ() - this.getZ())); 88 | return (int)(f + g + h); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/ChunkMapMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.chunk; 4 | 5 | import com.fexl.circumnavigate.accessors.TransformerAccessor; 6 | import com.fexl.circumnavigate.core.DimensionTransformer; 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.core.SectionPos; 9 | import net.minecraft.server.level.*; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.level.ChunkPos; 12 | import org.spongepowered.asm.mixin.*; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.Redirect; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 18 | 19 | @Mixin(ChunkMap.class) 20 | public abstract class ChunkMapMixin { 21 | 22 | @Final @Shadow public ServerLevel level; 23 | 24 | /** 25 | * Players will track wrapped chunks as part of their tracking view. 26 | */ 27 | @Inject(method = "isChunkTracked", at = @At("HEAD"), cancellable = true) 28 | public void isChunkTracked(ServerPlayer player, int x, int z, CallbackInfoReturnable cir) { 29 | DimensionTransformer transformer = player.serverLevel().getTransformer(); 30 | 31 | //Stores the serverLevel for usage further down the call chain where it was not passed. 32 | TransformerRequests.chunkMapTransformer = transformer; 33 | 34 | cir.setReturnValue(player.getChunkTrackingView().contains(x, z) && !player.connection.chunkSender.isPending(ChunkPos.asLong(transformer.Chunk.X.unwrapFromBounds(player.getClientChunk().x, x), transformer.Chunk.Z.unwrapFromBounds(player.getClientChunk().z, z)))); 35 | } 36 | 37 | /** 38 | * Gives ChunkTrackingView.Positioned instances a WorldTransformer when they are created (this is the only place they are created) 39 | */ 40 | @Redirect(method = "updateChunkTracking", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkTrackingView;of(Lnet/minecraft/world/level/ChunkPos;I)Lnet/minecraft/server/level/ChunkTrackingView;")) 41 | public ChunkTrackingView setChunkTransformer(ChunkPos center, int viewDistance) { 42 | ChunkTrackingView.Positioned newView = (ChunkTrackingView.Positioned) ChunkTrackingView.of(center, viewDistance); 43 | ((TransformerAccessor) (Object) newView).setTransformer(level.getTransformer()); 44 | return newView; 45 | } 46 | 47 | /** 48 | * Stores the serverLevel for usage further down the call chain where it was not passed. 49 | */ 50 | @Inject(method = "applyChunkTrackingView", at = @At("HEAD")) 51 | public void captureLevel(ServerPlayer player, ChunkTrackingView chunkTrackingView, CallbackInfo ci) { 52 | TransformerRequests.chunkMapTransformer = player.serverLevel().getTransformer(); 53 | } 54 | 55 | /** 56 | * Support wrapped distances as closest Euclidean distance. 57 | */ 58 | @Inject(method = "euclideanDistanceSquared", at = @At("HEAD"), cancellable = true) 59 | private static void euclideanDistanceSquared(ChunkPos chunkPos, Entity entity, CallbackInfoReturnable cir) { 60 | double d = SectionPos.sectionToBlockCoord(chunkPos.x, 8); 61 | double e = SectionPos.sectionToBlockCoord(chunkPos.z, 8); 62 | cir.setReturnValue(entity.level().getTransformer().Coord.sqrDistToBounds(entity.getX(), 0, entity.getZ(), d, 0, e)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/collisions/LevelMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity.collisions; 6 | 7 | import com.fexl.circumnavigate.core.DimensionTransformer; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.util.AbortableIterationConsumer; 10 | import net.minecraft.util.profiling.ProfilerFiller; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.boss.EnderDragonPart; 13 | import net.minecraft.world.entity.boss.enderdragon.EnderDragon; 14 | import net.minecraft.world.level.Level; 15 | import net.minecraft.world.level.block.Block; 16 | import net.minecraft.world.level.entity.EntityTypeTest; 17 | import net.minecraft.world.level.entity.LevelEntityGetter; 18 | import net.minecraft.world.phys.AABB; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Shadow; 22 | import org.spongepowered.asm.mixin.Unique; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 26 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 27 | 28 | import java.util.List; 29 | import java.util.function.Predicate; 30 | 31 | @Mixin(Level.class) 32 | public abstract class LevelMixin { 33 | @Shadow protected abstract LevelEntityGetter getEntities(); 34 | @Shadow public abstract ProfilerFiller getProfiler(); 35 | 36 | Level thiz = (Level) (Object) this; 37 | 38 | @ModifyVariable(method = {"getBlockState", "getFluidState", "setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;II)Z", "removeBlock", "destroyBlock"}, at = @At("HEAD"), argsOnly = true, index = 1) 39 | public BlockPos modifyBlockPos(BlockPos blockPos) { 40 | return thiz.getTransformer().onlyServerSide().Block.wrapToBounds(blockPos); 41 | } 42 | 43 | /** 44 | * Returns all entities within the bounds of a wrapped bounding box. 45 | */ 46 | @Inject(method = "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;Ljava/util/List;I)V", at = @At("HEAD"), cancellable = true) 47 | public void getEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate, List output, int maxResults, CallbackInfo ci) { 48 | if(thiz.isClientSide) return; 49 | ci.cancel(); 50 | DimensionTransformer transformer = thiz.getTransformer(); 51 | 52 | this.getProfiler().incrementCounter("getEntities"); 53 | List boxes = transformer.AABoundingBox.splitAcrossBounds(bounds); 54 | for(AABB box : boxes) { 55 | this.getEntities().get(entityTypeTest, box, entity -> { 56 | if (predicate.test(entity)) { 57 | output.add(entity); 58 | if (output.size() >= maxResults) { 59 | return AbortableIterationConsumer.Continuation.ABORT; 60 | } 61 | } 62 | 63 | if (entity instanceof EnderDragon enderDragon) { 64 | for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { 65 | T entity2 = entityTypeTest.tryCast(enderDragonPart); 66 | if (entity2 != null && predicate.test(entity2)) { 67 | output.add(entity2); 68 | if (output.size() >= maxResults) { 69 | return AbortableIterationConsumer.Continuation.ABORT; 70 | } 71 | } 72 | } 73 | } 74 | 75 | return AbortableIterationConsumer.Continuation.CONTINUE; 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/chunk/ChunkTrackerMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.chunk; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 7 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 8 | import com.llamalad7.mixinextras.sugar.Local; 9 | import net.minecraft.server.level.ChunkTracker; 10 | import net.minecraft.server.level.DistanceManager; 11 | import net.minecraft.world.level.ChunkPos; 12 | import net.minecraft.world.level.lighting.DynamicGraphMinFixedPoint; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.injection.*; 16 | 17 | //Transformer is available after the initializer 18 | @Mixin(ChunkTracker.class) 19 | public abstract class ChunkTrackerMixin extends DynamicGraphMinFixedPoint { 20 | ChunkTracker thiz = (ChunkTracker) (Object) this; 21 | 22 | protected ChunkTrackerMixin(int firstQueuedLevel, int width, int height) { 23 | super(firstQueuedLevel, width, height); 24 | } 25 | 26 | @Shadow protected abstract int computeLevelFromNeighbor(long startPos, long endPos, int startLevel); 27 | 28 | /** 29 | * Modifies ChunkPos to use wrapped chunks 30 | */ 31 | @WrapOperation(method = "getComputedLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkTracker;computeLevelFromNeighbor(JJI)I")) 32 | private int getComputedLevel(ChunkTracker instance, long startPos, long endPos, int startLevel, Operation original, @Local(argsOnly = true, ordinal = 1) long excludedSourcePos) { 33 | //This class cannot use this transformation because it will not unload the chunks at save. However, the intended effect of these transformations still applies. 34 | if(thiz instanceof DistanceManager.ChunkTicketTracker) return original.call(instance, startPos, endPos, startLevel); 35 | 36 | int originalRet = original.call(instance, startPos, endPos, startLevel); 37 | 38 | DimensionTransformer transformer = thiz.getTransformer(); 39 | 40 | ChunkPos chunkPos = new ChunkPos(startPos); 41 | ChunkPos wrappedChunkPos = transformer.Chunk.wrapToBounds(chunkPos); 42 | long wrappedPos = wrappedChunkPos.toLong(); 43 | if (wrappedPos == endPos) { 44 | wrappedPos = ChunkPos.INVALID_CHUNK_POS; 45 | } 46 | 47 | if (wrappedPos != excludedSourcePos) { 48 | int wrappedRet = this.computeLevelFromNeighbor(wrappedPos, endPos, getLevel(wrappedPos)); 49 | 50 | // return the lower of the two levels 51 | return Math.min(originalRet, wrappedRet); 52 | } 53 | 54 | return originalRet; 55 | } 56 | 57 | /** 58 | * Updates loading levels of adjacent chunks so they are ready when needed. Modified to include wrapped chunks. 59 | */ 60 | @WrapOperation(method = "checkNeighborsAfterUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ChunkPos;asLong(II)J")) 61 | private long wrapChunkPos(int x, int z, Operation original, @Local(argsOnly = true) long pos, @Local(argsOnly = true) int level, @Local(argsOnly = true) boolean isDecreasing) { 62 | DimensionTransformer transformer = thiz.getTransformer(); 63 | 64 | int wrappedX = transformer.Chunk.X.wrapToBounds(x); 65 | int wrappedZ = transformer.Chunk.Z.wrapToBounds(z); 66 | long chunkLong = original.call(wrappedX, wrappedZ); 67 | 68 | if (chunkLong != pos) { 69 | thiz.checkNeighbor(pos, chunkLong, level, isDecreasing); 70 | } 71 | 72 | return original.call(x, z); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/entity/EntityMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.entity; 6 | 7 | import net.minecraft.util.Mth; 8 | import net.minecraft.world.entity.Entity; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.phys.AABB; 11 | import net.minecraft.world.phys.Vec3; 12 | import net.minecraft.world.phys.shapes.BooleanOp; 13 | import net.minecraft.world.phys.shapes.Shapes; 14 | import net.minecraft.world.phys.shapes.VoxelShape; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 20 | import org.spongepowered.asm.mixin.injection.Redirect; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 22 | 23 | @Mixin(Entity.class) 24 | public abstract class EntityMixin { 25 | @Shadow private Level level; 26 | 27 | Entity thiz = (Entity) (Object) this; 28 | 29 | /** 30 | * Modifies the inputted X position of the entity to be within the wrapping bounds 31 | */ 32 | @ModifyVariable(method = "setPosRaw", at = @At("HEAD"), ordinal = 0, argsOnly = true) 33 | public double wrapX(double x) { 34 | return level.getTransformer().onlyServerSide().Coord.X.wrapToBounds(x); 35 | } 36 | 37 | /** 38 | * Modifies the inputted Z position of the entity to be within the wrapping bounds 39 | */ 40 | @ModifyVariable(method = "setPosRaw", at = @At("HEAD"), ordinal = 2, argsOnly = true) 41 | public double wrapZ(double z) { 42 | return level.getTransformer().onlyServerSide().Coord.Z.wrapToBounds(z); 43 | } 44 | 45 | /** 46 | * Checks if an entity is colliding with a block. Modified to support wrapped worlds. 47 | */ 48 | @Redirect(method = "isColliding", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/shapes/Shapes;joinIsNotEmpty(Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/BooleanOp;)Z")) 49 | public boolean wrapAABB(VoxelShape shape1, VoxelShape shape2, BooleanOp resultOperator) { 50 | AABB empty = new AABB(0, 0, 0, 0, 0, 0); 51 | VoxelShape result = Shapes.create(level.getTransformer().onlyServerSide().AABoundingBox.unwrapFromBounds(shape1.isEmpty() ? empty : shape1.bounds(), shape2.isEmpty() ? empty : shape2.bounds())); 52 | return Shapes.joinIsNotEmpty(shape1, result, resultOperator); 53 | } 54 | 55 | @Inject(method = "distanceTo", at = @At("HEAD"), cancellable = true) 56 | public void wrapDistanceSquared1(Entity entity, CallbackInfoReturnable cir) { 57 | cir.setReturnValue(Mth.sqrt((float)level.getTransformer().onlyServerSide().Coord.sqrDistToBounds(entity.getX(), entity.getY(), entity.getZ(), thiz.getX(), thiz.getY(), thiz.getZ()))); 58 | } 59 | 60 | @Inject(method = "distanceToSqr(DDD)D", at = @At("HEAD"), cancellable = true) 61 | public void wrapDistanceSquared2(double x, double y, double z, CallbackInfoReturnable cir) { 62 | cir.setReturnValue(level.getTransformer().onlyServerSide().Coord.sqrDistToBounds(x, y, z, thiz.getX(), thiz.getY(), thiz.getZ())); 63 | } 64 | 65 | @Inject(method = "distanceToSqr(Lnet/minecraft/world/phys/Vec3;)D", at = @At("HEAD"), cancellable = true) 66 | public void wrapDistanceSquared3(Vec3 vec, CallbackInfoReturnable cir) { 67 | cir.setReturnValue(level.getTransformer().onlyServerSide().Vector3D.sqrDistToBounds(vec, new Vec3(thiz.getX(), thiz.getY(), thiz.getZ()))); 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/SimplexNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | /* 6 | * SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package com.fexl.circumnavigate.mixin.worldgen; 10 | 11 | import com.fexl.circumnavigate.core.DimensionTransformer; 12 | import com.fexl.circumnavigate.processing.worldgen.OpenSimplex2S; 13 | import com.fexl.circumnavigate.storage.TransformerRequests; 14 | import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 15 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 16 | import net.minecraft.util.RandomSource; 17 | import net.minecraft.world.level.levelgen.synth.SimplexNoise; 18 | import org.spongepowered.asm.mixin.Final; 19 | import org.spongepowered.asm.mixin.Mixin; 20 | import org.spongepowered.asm.mixin.Shadow; 21 | import org.spongepowered.asm.mixin.injection.At; 22 | import org.spongepowered.asm.mixin.injection.Inject; 23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 24 | 25 | import java.util.function.Function; 26 | 27 | @Mixin(SimplexNoise.class) 28 | public abstract class SimplexNoiseMixin { 29 | @Final @Shadow public double xo; 30 | @Final @Shadow public double yo; 31 | @Final @Shadow public double zo; 32 | @Final @Shadow private int[] p; 33 | @Final @Shadow private static double SQRT_3; 34 | @Final @Shadow private static double F2; 35 | @Final @Shadow private static double G2; 36 | @Final @Shadow abstract public int p(int index); 37 | @Final @Shadow abstract public double getCornerNoise3D(int gradientIndex, double x, double y, double z, double offset); 38 | 39 | private long source; 40 | 41 | @Inject(method = "", at = @At("TAIL")) 42 | public void init(RandomSource random, CallbackInfo ci) { 43 | source = random.nextLong(); 44 | } 45 | 46 | @WrapMethod(method = "getValue(DD)D") 47 | public double getValue(double x, double y, Operation original) { 48 | DimensionTransformer transformer = TransformerRequests.noiseLevel.getTransformer(); 49 | 50 | if(!transformer.wrappingSettings.useWrappedWorldGen()) { 51 | return original.call(x, y); 52 | } 53 | 54 | //double xa = ((x - xAdd) / xMul) / (xWidth); 55 | //double za = ((y - zAdd) / zMul) / (zWidth); 56 | double xa = (x + transformer.wrappingSettings.xChunkBoundMin()*16) / (transformer.xWidth*16); 57 | double za = (y + transformer.wrappingSettings.zChunkBoundMin()*16) / (transformer.zWidth*16); 58 | 59 | double rxa = xa * 2.0 * Math.PI; 60 | double rza = za * 2.0 * Math.PI; 61 | 62 | return OpenSimplex2S.noise4_Fallback(source, Math.sin(rxa), Math.cos(rxa), Math.sin(rza), Math.cos(rza)); 63 | } 64 | 65 | @WrapMethod(method = "getValue(DDD)D") 66 | public double getValue(double x, double y, double z, Operation original) { 67 | DimensionTransformer transformer = TransformerRequests.noiseLevel.getTransformer(); 68 | 69 | if(!transformer.wrappingSettings.useWrappedWorldGen()) { 70 | return original.call(x, y, z); 71 | } 72 | 73 | //double xa = ((x - xAdd) / xMul) / (xWidth); 74 | //double za = ((z - zAdd) / zMul) / (zWidth); 75 | double xa = (x + transformer.wrappingSettings.xChunkBoundMin()*16) / (transformer.xWidth*16); 76 | double za = (y + transformer.wrappingSettings.zChunkBoundMin()*16) / (transformer.zWidth*16); 77 | 78 | double rxa = xa * 2.0 * Math.PI; 79 | double rza = za * 2.0 * Math.PI; 80 | 81 | double noise4 = OpenSimplex2S.noise4_Fallback(source, Math.sin(rxa), Math.cos(rxa), Math.sin(rza), Math.cos(rza)); 82 | double noise1 = OpenSimplex2S.noise2(source, 0, y); 83 | return (noise4 + noise1)/2.0; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/resources/minecraft.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.fexl.circumnavigate.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "mixins": [ 6 | "ServerToClientListener", 7 | "block.ExplosionMixin", 8 | "block.NeighborUpdatorMixin", 9 | "block.VibrationSystemMixin", 10 | "block.blocks.BaseRailBlockMixin", 11 | "block.blocks.DoubleBlockCombinerMixin", 12 | "block.blocks.NetherPortalBlockMixin", 13 | "block.blocks.RailBlockMixin", 14 | "block.blocks.RailStateMixin", 15 | "block.blocks.SignalGetterMixin", 16 | "block.blocks.fluid.FlowingFluidMixin", 17 | "block.blocks.piston.PistonBaseBlockMixin", 18 | "block.blocks.piston.PistonHeadBlockMixin", 19 | "block.blocks.piston.PistonStructureResolverMixin", 20 | "chunk.ChunkMapMixin", 21 | "chunk.ChunkStatusTasksMixin", 22 | "chunk.ChunkTrackerMixin", 23 | "chunk.ChunkTrackingView$PositionedMixin", 24 | "chunk.ChunkTrackingViewMixin", 25 | "chunk.PositionedAccessorMixin", 26 | "chunk.SectionTrackerMixin", 27 | "chunk.ServerChunkCacheMixin", 28 | "chunk.TrackedEntityMixin", 29 | "debug.DebugPacketsMixin", 30 | "debug.PathMixin", 31 | "debug.ServerLevelMixin", 32 | "debug.SharedConstantsMixin", 33 | "entity.EntityMixin", 34 | "entity.LivingEntityMixin", 35 | "entity.PlayerMixin", 36 | "entity.ServerEntityMixin", 37 | "entity.ServerPlayerMixin", 38 | "entity.collisions.BlockCollisionsMixin", 39 | "entity.collisions.EntityGetterMixin", 40 | "entity.collisions.HitResultMixin", 41 | "entity.collisions.LevelMixin", 42 | "entity.entities.FishingHookMixin", 43 | "entity.entities.vehicle.AbstractMinecartMixin", 44 | "gameevent.EuclideanGameEventListenerRegistryMixin", 45 | "gameevent.GameEventDispatcherMixin", 46 | "interfaceInjects.ChunkTrackerInjectorMixin", 47 | "interfaceInjects.DimensionTransformerInjectorMixin", 48 | "interfaceInjects.PathNavigationRegionInjectorMixin", 49 | "interfaceInjects.PlayerClientInjectorMixin", 50 | "interfaceInjects.WorldGenRegionInjectorMixin", 51 | "light.BlockLightEngineMixin", 52 | "light.LightEngineAccessor", 53 | "light.SkyLightEngineMixin", 54 | "packet.ConnectionMixin", 55 | "packet.PlayerChunkSenderMixin", 56 | "packet.ServerConfigurationPacketListenerImplMixin", 57 | "packet.ServerGamePacketListenerImplMixin", 58 | "packet.ServerGamePacketListenerImplMixin$1", 59 | "propagators.chunkTracker.providers.ChunkMap$DistanceManagerMixin", 60 | "propagators.chunkTracker.providers.PoiManager$DistanceTracker", 61 | "propagators.chunkTracker.senders.DistanceManagerMixin", 62 | "worldgen.BlendedNoiseMixin", 63 | "worldgen.ImprovedNoiseMixin", 64 | "worldgen.NormalNoiseMixin", 65 | "worldgen.PerlinNoiseMixin", 66 | "worldgen.PerlinSimplexNoiseMixin", 67 | "worldgen.SimplexNoiseMixin", 68 | "worldgen.other.DualNoiseProviderMixin", 69 | "worldgen.other.NoiseBasedStateProviderMixin", 70 | "worldgen.other.SurfaceSystemMixin", 71 | "worldgen.other.densityFunctions.DensityFunction$NoiseHolderMixin", 72 | "worldgen.other.densityFunctions.DensityFunctions$NoiseMixin", 73 | "worldgen.other.densityFunctions.DensityFunctions$ShiftedNoiseMixin", 74 | "worldgen.other.densityFunctions.DensityFunctions$ShiftNoiseMixin", 75 | "worldgen.other.densityFunctions.DensityFunctions$WeirdScaledSamplerMixin", 76 | "worldInit.MinecraftServerMixin", 77 | "worldInit.PathNavigationRegionMixin", 78 | "worldInit.PlayerListMixin", 79 | "worldInit.PrimaryLevelDataMixin", 80 | "worldInit.ServerLevelMixin", 81 | "worldInit.WorldGenRegionMixin" 82 | ], 83 | "server": [ 84 | ], 85 | "injectors": { 86 | "defaultRequire": 1 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/ServerToClientListener.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin; 4 | 5 | import com.mojang.logging.LogUtils; 6 | import net.minecraft.network.PacketSendListener; 7 | import net.minecraft.network.protocol.Packet; 8 | import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; 9 | import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; 10 | import net.minecraft.network.protocol.game.ServerGamePacketListener; 11 | import net.minecraft.server.network.ServerCommonPacketListenerImpl; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.slf4j.Logger; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.lang.reflect.ParameterizedType; 20 | import java.lang.reflect.Type; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | /** 25 | * For testing only 26 | */ 27 | @Mixin(ServerCommonPacketListenerImpl.class) 28 | public class ServerToClientListener { 29 | private static final Logger LOGGER = LogUtils.getLogger(); 30 | 31 | String previousLine; 32 | String currentLine; 33 | int lineCount = 1; 34 | 35 | @Inject(method = "send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;)V", at = @At("HEAD")) 36 | public void sendWithListener(Packet packet, @Nullable PacketSendListener listener, CallbackInfo ci) { 37 | Class clazz = packet.getClass(); 38 | //if(!isInExceptions(clazz.getSimpleName())) return; 39 | String log = "S->C: "; 40 | 41 | if(clazz.getEnclosingClass() != null) 42 | log += clazz.getEnclosingClass().getSimpleName() + "." + clazz.getSimpleName(); 43 | else 44 | log += clazz.getSimpleName(); 45 | log += " "; 46 | 47 | currentLine = log; 48 | 49 | //If the current line equals the previous line 50 | if(currentLine.equals(previousLine)) { 51 | //Increment the line count 52 | lineCount++; 53 | } 54 | //Not equal to the previous line 55 | else { 56 | //LOGGER.info(previousLine + ((lineCount != 1) ? (" {" + lineCount + "}") : "")); 57 | lineCount = 1; 58 | previousLine = currentLine; 59 | } 60 | } 61 | 62 | 63 | /** 64 | @Inject(method = "send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;)V", at = @At("HEAD")) 65 | public void send(Packet packet, @Nullable PacketSendListener listener, CallbackInfo ci) { 66 | //if(isGamePacket(packet.getClass())) 67 | System.out.println("S->C (2): " + packet.getClass().getSimpleName()); 68 | }**/ 69 | 70 | public boolean isGamePacket(Class clazz) { 71 | Type[] interfaces = clazz.getGenericInterfaces(); 72 | for(Type iface : interfaces) { 73 | if (iface instanceof ParameterizedType) { 74 | ParameterizedType paramType = (ParameterizedType) iface; 75 | if (paramType.getRawType().equals(Packet.class)) { 76 | Type[] typeArgs = paramType.getActualTypeArguments(); 77 | if (typeArgs.length == 1 && typeArgs[0].equals(ServerGamePacketListener.class)) { 78 | return true; 79 | } 80 | } 81 | } 82 | } 83 | return false; 84 | } 85 | 86 | public boolean isntInExceptions(String name) { 87 | List stringList = Arrays.asList("ClientboundSetEntityMotionPacket", "ClientboundTeleportEntityPacket", "ClientboundMoveEntityPacket", "Pos", "Rot", "PosRot", "ClientboundRotateHeadPacket"); 88 | return stringList.contains(name); 89 | } 90 | 91 | public boolean isInExceptions(String name) { 92 | List stringList = Arrays.asList("ClientboundBlockUpdatePacket", "ClientboundBlockChangedAckPacket"); 93 | return stringList.contains(name); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/client/java/com/fexl/circumnavigate/mixin/client/debug/KeyboardHandlerMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.client.debug; 6 | 7 | import static com.fexl.circumnavigate.client.storage.DebugVariables.*; 8 | 9 | import com.fexl.circumnavigate.Circumnavigate; 10 | import com.mojang.blaze3d.platform.InputConstants; 11 | import net.minecraft.client.KeyboardHandler; 12 | import net.minecraft.client.Minecraft; 13 | import org.lwjgl.glfw.GLFW; 14 | import org.spongepowered.asm.mixin.Final; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.Unique; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 22 | 23 | @Mixin(KeyboardHandler.class) 24 | public abstract class KeyboardHandlerMixin { 25 | @Shadow protected abstract boolean handleChunkDebugKeys(int keyCode); 26 | @Shadow @Final private Minecraft minecraft; 27 | 28 | @Shadow protected abstract void debugFeedback(String message, Object... args); 29 | 30 | /** 31 | * Bind discarded chunk debug keys to the F3 input event. 32 | *

33 | * E: SectionPath 34 | * L: SmartCull (this key disables tick metrics) 35 | * U: Capture Frustrum (F3+Shift+U to disable) 36 | * V: SectionVisibility 37 | * W: WireFrame (does nothing) 38 | */ 39 | @Inject(method = "handleDebugKeys", at = @At("HEAD"), cancellable = true) 40 | public void handleDebugKeys(int key, CallbackInfoReturnable cir) { 41 | if(!Circumnavigate.DEV_MODE) return; 42 | 43 | if(handleChunkDebugKeys(key)) cir.setReturnValue(true); 44 | } 45 | 46 | /** 47 | * Add an F6 input event for handling previously disabled debug renders. 48 | */ 49 | @Inject(method = "keyPress", at = @At("HEAD"), cancellable = true) 50 | public void keyPress(long windowPointer, int key, int scanCode, int action, int modifiers, CallbackInfo ci) { 51 | if(!Circumnavigate.DEV_MODE) return; 52 | 53 | if (windowPointer == minecraft.getWindow().getWindow()) { 54 | boolean F6_DOWN = InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_F6); 55 | 56 | if(F6_DOWN && action != 0) { 57 | handleMoreDebugKeys(key); 58 | ci.cancel(); 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Add additional keys to handle disabled debug renders to an F6 input event. 65 | *

66 | * N: NeighborUpdates 67 | * S: StructurePieces 68 | * P: PathFinding 69 | * L: LightingLevels 70 | * C: ChunkInfo 71 | */ 72 | @Unique 73 | private boolean handleMoreDebugKeys(int key) { 74 | switch(key) { 75 | case GLFW.GLFW_KEY_N: 76 | renderNeighborUpdates = !renderNeighborUpdates; 77 | this.debugFeedback("NeighborUpdates: {0}", renderNeighborUpdates ? "shown" : "hidden"); 78 | break; 79 | case GLFW.GLFW_KEY_S: 80 | renderStructurePieces = !renderStructurePieces; 81 | this.debugFeedback("StructurePieces: {0}", renderStructurePieces ? "shown" : "hidden"); 82 | break; 83 | case GLFW.GLFW_KEY_P: 84 | renderPathfinding = !renderPathfinding; 85 | this.debugFeedback("PathFinding: {0}", renderPathfinding ? "shown" : "hidden"); 86 | break; 87 | case GLFW.GLFW_KEY_L: 88 | renderLightDebug = !renderLightDebug; 89 | this.debugFeedback("LightingLevels: {0}", renderLightDebug ? "shown" : "hidden"); 90 | break; 91 | case GLFW.GLFW_KEY_C: 92 | renderChunkInfo = !renderChunkInfo; 93 | this.debugFeedback("ChunkInfo: {0}", renderChunkInfo ? "shown" : "hidden"); 94 | break; 95 | default: 96 | return false; 97 | } 98 | return true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Contributor Covenant Code of Conduct 2 | # Our Pledge 3 | ___ 4 | We as participants pledge to love, serve, and honor each other to the best of our ability. We will not disparage or bully those who are inferior, and we will tolerate those we oppose. 5 | 6 | # Our Standards 7 | What we are for: 8 | - Practicing charity on others 9 | - Respecting others worldviews 10 | - Giving and gracefully accepting constructive feedback 11 | - Reconciling with those you have harmed 12 | - Humbly taking responsibility for your mistakes 13 | 14 | What we are against: 15 | - Personal attacks 16 | - Inappropriate content 17 | - Bullying and harassment 18 | - Unprofessional conduct 19 | - Hateful discrimination 20 | 21 | # Observations 22 | We expect that all discussions and actions taking place within project spaces are focused on the project. We will not accommodate topics, conversations, or comments which do not pertain to Circumnavigate, such as social, political, or economic observations. 23 | 24 | While we try to include everyone, we will not provide unreasonable accommodations at our own expense, and will expect that participants adapt their expectations accordingly. We recognize that not everyone can or should be included. 25 | 26 | # Enforcement Responsibilities 27 | Project leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 28 | 29 | Project leaders have the right and responsibility to remove, edit, or reject contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 30 | 31 | # Scope 32 | This Code of Conduct applies within all project spaces, and also applies when an individual is interacting with project participants, including members, contributors, representatives, and users. 33 | 34 | # Enforcement 35 | Instances of unacceptable behavior should be reported to the community leaders responsible for enforcement at Contact method. All complaints will be reviewed and investigated promptly and fairly. 36 | 37 | All community leaders must respect the privacy and security of the reporter of any incident. 38 | 39 | # Enforcement Guidelines 40 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 41 | 42 | ### 1. Correction 43 | Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 44 | 45 | Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 46 | 47 | ### 2. Warning 48 | Community Impact: A violation through a single incident or series of actions. 49 | 50 | Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 51 | 52 | ### 3. Temporary Ban 53 | Community Impact: A serious violation of community standards, including sustained inappropriate behavior. 54 | Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 55 | 56 | ### 4. Permanent Ban 57 | Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 58 | 59 | Consequence: A permanent ban from any sort of public interaction within the community. -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/core/CoordinateTransformers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.core; 6 | 7 | public class CoordinateTransformers { 8 | public final int lowerChunkBounds; 9 | public final int upperChunkBounds; 10 | 11 | public CoordMethods Coord; 12 | public ChunkMethods Chunk; 13 | 14 | public CoordinateTransformers(int lowerChunkBounds, int upperChunkBounds) { 15 | this.lowerChunkBounds = lowerChunkBounds; 16 | this.upperChunkBounds = upperChunkBounds; 17 | 18 | Coord = new CoordMethods(); 19 | Chunk = new ChunkMethods(); 20 | } 21 | 22 | public class CoordMethods extends BasicPositionOperations { 23 | public final int domainLength = Math.abs(upperChunkBounds - lowerChunkBounds) * CoordinateConstants.CHUNK_WIDTH; 24 | 25 | @Override 26 | public Double wrapToBounds(Double coord) { 27 | //Short-circuit 28 | if(!isOverBounds(coord)) return coord; 29 | 30 | double domainStart = lowerChunkBounds * CoordinateConstants.CHUNK_WIDTH; 31 | double wrappedCoord = (coord - domainStart) % domainLength; 32 | 33 | // If wrappedCoord is negative, adjust it by adding domainLength 34 | if (wrappedCoord < 0) { 35 | wrappedCoord += domainLength; 36 | } 37 | 38 | return domainStart + wrappedCoord; 39 | } 40 | 41 | public Integer wrapToBounds(Integer coord) { 42 | return wrapToBounds(coord.doubleValue()).intValue(); 43 | } 44 | 45 | @Override 46 | public Double unwrapFromBounds(Double refCoord, Double wrappedCoord) { 47 | double wrappedRefCoord = wrapToBounds(refCoord); 48 | 49 | double diff = wrappedCoord - wrappedRefCoord; 50 | 51 | double unwrappedCoord = refCoord + diff; 52 | 53 | // Adjust to ensure the unwrapped coordinate is correct 54 | if (unwrappedCoord < refCoord - (double) domainLength / 2) { 55 | unwrappedCoord += domainLength; 56 | } 57 | if (unwrappedCoord > refCoord + (double) domainLength / 2) { 58 | unwrappedCoord -= domainLength; 59 | } 60 | 61 | return unwrappedCoord; 62 | } 63 | 64 | public Integer unwrapFromBounds(Integer refCoord, Integer wrappedCoord) { 65 | return unwrapFromBounds(refCoord.doubleValue(), wrappedCoord.doubleValue()).intValue(); 66 | } 67 | 68 | @Override 69 | public boolean isOverBounds(Double coord) { 70 | return coord >= upperChunkBounds * CoordinateConstants.CHUNK_WIDTH || coord < lowerChunkBounds * CoordinateConstants.CHUNK_WIDTH; 71 | } 72 | 73 | public boolean isOverBounds(Integer coord) { 74 | return isOverBounds(coord.doubleValue()); 75 | } 76 | 77 | public Double deltaFromBounds(Double fromCoord, Double toCoord) { 78 | double toCoordUnwrapped = unwrapFromBounds(fromCoord, toCoord); 79 | 80 | return toCoordUnwrapped - fromCoord; 81 | } 82 | 83 | public Double sqrDistToBounds(Double dist) { 84 | if(dist > upperChunkBounds * CoordinateConstants.CHUNK_WIDTH) { 85 | dist -= Coord.domainLength; 86 | } 87 | else if (dist < lowerChunkBounds * CoordinateConstants.CHUNK_WIDTH) { 88 | dist += Coord.domainLength; 89 | } 90 | 91 | return dist * dist; 92 | } 93 | 94 | public Integer sqrDistToBounds(Integer dist) { 95 | return sqrDistToBounds(dist.doubleValue()).intValue(); 96 | } 97 | } 98 | 99 | public class ChunkMethods extends BasicPositionOperations { 100 | public final int domainLength = Coord.domainLength/16; 101 | 102 | @Override 103 | public Integer wrapToBounds(Integer chunkCoord) { 104 | return Coord.wrapToBounds(chunkCoord*16)/16; 105 | } 106 | 107 | @Override 108 | public Integer unwrapFromBounds(Integer refChunkCoord, Integer wrappedChunkCoord) { 109 | return Coord.unwrapFromBounds(refChunkCoord*16, wrappedChunkCoord*16)/16; 110 | } 111 | 112 | @Override 113 | public boolean isOverBounds(Integer chunkCoord) { 114 | return Coord.isOverBounds(chunkCoord*16); 115 | } 116 | 117 | public int sqrDistToBounds(int chunkDist) { 118 | return Coord.sqrDistToBounds(chunkDist*16)/(16*16); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldInit/PlayerListMixin.java: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: AGPL-3.0-only */ 2 | 3 | package com.fexl.circumnavigate.mixin.worldInit; 4 | 5 | import com.fexl.circumnavigate.core.DimensionTransformer; 6 | import com.llamalad7.mixinextras.sugar.Local; 7 | import net.minecraft.network.Connection; 8 | import net.minecraft.network.protocol.Packet; 9 | import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; 10 | import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; 11 | import net.minecraft.server.MinecraftServer; 12 | import net.minecraft.server.level.ServerLevel; 13 | import net.minecraft.server.level.ServerPlayer; 14 | import net.minecraft.server.network.CommonListenerCookie; 15 | import net.minecraft.server.players.PlayerList; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.Redirect; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | @Mixin(PlayerList.class) 24 | public abstract class PlayerListMixin { 25 | @Shadow public abstract MinecraftServer getServer(); 26 | @Shadow public abstract void broadcastAll(Packet packet); 27 | @Shadow private int viewDistance; 28 | @Shadow private int simulationDistance; 29 | 30 | /** 31 | * Initializes the player's client-side positioning so they can be used for unwrapping operations. 32 | */ 33 | @Inject(method = "placeNewPlayer", at = @At("TAIL")) 34 | public void placeNewPlayer2(Connection connection, ServerPlayer player, CommonListenerCookie cookie, CallbackInfo ci) { 35 | player.setClientX(player.getX()); 36 | player.setClientZ(player.getZ()); 37 | } 38 | 39 | /** 40 | * Modifies the server's per-level view distance to align with client expectations. 41 | */ 42 | @Inject(method = "setViewDistance", at = @At("HEAD"), cancellable = true) 43 | public void setViewDistance(int viewDistance, CallbackInfo ci) { 44 | ci.cancel(); 45 | 46 | this.viewDistance = viewDistance; 47 | this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); 48 | 49 | for (ServerLevel serverLevel : this.getServer().getAllLevels()) { 50 | if (serverLevel != null) { 51 | DimensionTransformer levelTransformer = serverLevel.getTransformer(); 52 | serverLevel.getChunkSource().setViewDistance(levelTransformer.limitViewDistance(viewDistance)); 53 | 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Modifies the server's per-level simulation distance to align with client expectations. 60 | */ 61 | @Inject(method = "setSimulationDistance", at = @At("HEAD"), cancellable = true) 62 | public void setSimulationDistance(int simulationDistance, CallbackInfo ci) { 63 | ci.cancel(); 64 | 65 | this.simulationDistance = simulationDistance; 66 | this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance)); 67 | 68 | for (ServerLevel serverLevel : this.getServer().getAllLevels()) { 69 | if (serverLevel != null) { 70 | DimensionTransformer levelTransformer = serverLevel.getTransformer(); 71 | serverLevel.getChunkSource().setSimulationDistance(levelTransformer.limitViewDistance(simulationDistance)); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Wraps the X for the ServerPlayer so it can apply to them 78 | */ 79 | @Redirect(method = "broadcast", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getX()D")) 80 | public double broadcast_X(ServerPlayer instance, @Local(ordinal = 0, argsOnly = true) double x) { 81 | return instance.serverLevel().getTransformer().Coord.X.unwrapFromBounds(x, instance.getX()); 82 | } 83 | 84 | /** 85 | * Wraps the Z for the ServerPlayer so it can apply to them 86 | */ 87 | @Redirect(method = "broadcast", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getZ()D")) 88 | public double broadcast_Z(ServerPlayer instance, @Local(ordinal = 2, argsOnly = true) double z) { 89 | return instance.serverLevel().getTransformer().Coord.Z.unwrapFromBounds(z, instance.getZ()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/fexl/circumnavigate/mixin/worldgen/BlendedNoiseMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: AGPL-3.0-only 3 | */ 4 | 5 | package com.fexl.circumnavigate.mixin.worldgen; 6 | 7 | import com.fexl.circumnavigate.storage.TransformerRequests; 8 | import net.minecraft.util.Mth; 9 | import net.minecraft.util.RandomSource; 10 | import net.minecraft.world.level.levelgen.DensityFunction; 11 | import net.minecraft.world.level.levelgen.synth.BlendedNoise; 12 | import net.minecraft.world.level.levelgen.synth.ImprovedNoise; 13 | import net.minecraft.world.level.levelgen.synth.PerlinNoise; 14 | import org.spongepowered.asm.mixin.Final; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 21 | 22 | @Mixin(BlendedNoise.class) 23 | public class BlendedNoiseMixin { 24 | @Shadow @Final private PerlinNoise minLimitNoise; 25 | @Shadow @Final private PerlinNoise maxLimitNoise; 26 | @Shadow @Final private PerlinNoise mainNoise; 27 | @Shadow @Final private double xzMultiplier; 28 | @Shadow @Final private double yMultiplier; 29 | @Shadow @Final private double xzFactor; 30 | @Shadow @Final private double yFactor; 31 | @Shadow @Final private double smearScaleMultiplier; 32 | @Shadow @Final private double maxValue; 33 | @Shadow @Final private double xzScale; 34 | @Shadow @Final private double yScale; 35 | 36 | private final double xWidth = 256.0; 37 | private final double zWidth = 256.0; 38 | 39 | private long source; 40 | 41 | private long lastTime = 0; 42 | 43 | @Inject(method = "(Lnet/minecraft/util/RandomSource;DDDDD)V", at = @At("TAIL")) 44 | public void init(RandomSource random, double xzScale, double yScale, double xzFactor, double yFactor, double smearScaleMultiplier, CallbackInfo ci) { 45 | source = random.nextLong(); 46 | } 47 | 48 | 49 | @Inject(method = "compute", at = @At("HEAD"), cancellable = true) 50 | public void compute(DensityFunction.FunctionContext context, CallbackInfoReturnable cir) { 51 | if(!TransformerRequests.noiseLevel.getTransformer().wrappingSettings.useWrappedWorldGen()) { 52 | return; 53 | } 54 | 55 | double d = context.blockX() * this.xzMultiplier; 56 | double e = context.blockY() * this.yMultiplier; 57 | double f = context.blockZ() * this.xzMultiplier; 58 | double g = d / this.xzFactor; 59 | double h = e / this.yFactor; 60 | double i = f / this.xzFactor; 61 | double j = this.yMultiplier * this.smearScaleMultiplier; 62 | double k = j / this.yFactor; 63 | double l = 0.0; 64 | double m = 0.0; 65 | double n = 0.0; 66 | boolean bl = true; 67 | double o = 1.0; 68 | 69 | for (int p = 0; p < 8; p++) { 70 | ImprovedNoise improvedNoise = this.mainNoise.getOctaveNoise(p); 71 | if (improvedNoise != null) { 72 | n += improvedNoise.noise(context.blockX(), PerlinNoise.wrap(h * o), context.blockZ(), k * o, h * o) / o; 73 | } 74 | 75 | o /= 2.0; 76 | } 77 | 78 | double q = (n / 10.0 + 1.0) / 2.0; 79 | boolean bl2 = q >= 1.0; 80 | boolean bl3 = q <= 0.0; 81 | o = 1.0; 82 | 83 | for (int r = 0; r < 16; r++) { 84 | double s = d * o; //PerlinNoise.wrap(d * o); 85 | double t = PerlinNoise.wrap(e * o); 86 | double u = PerlinNoise.wrap(f * o); 87 | double v = j * o; 88 | if (!bl2) { 89 | ImprovedNoise improvedNoise2 = this.minLimitNoise.getOctaveNoise(r); 90 | if (improvedNoise2 != null) { 91 | l += improvedNoise2.noise(context.blockX(), t, context.blockZ(), v, e * o) / o; 92 | } 93 | } 94 | 95 | if (!bl3) { 96 | ImprovedNoise improvedNoise2 = this.maxLimitNoise.getOctaveNoise(r); 97 | if (improvedNoise2 != null) { 98 | m += improvedNoise2.noise(context.blockX(), t, context.blockZ(), v, e * o) / o; 99 | } 100 | } 101 | 102 | o /= 2.0; 103 | } 104 | 105 | cir.setReturnValue(Mth.clampedLerp(l / 512.0, m / 512.0, q) / 128.0); 106 | } 107 | } 108 | --------------------------------------------------------------------------------