├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── spyglass_astronomy │ │ │ ├── icon.png │ │ │ ├── textures │ │ │ ├── star_spyglass_scope.png │ │ │ └── constellation_spyglass_scope.png │ │ │ └── lang │ │ │ ├── zh_cn.json │ │ │ └── en_us.json │ ├── spyglass_astronomy.mixins.json │ └── fabric.mod.json │ └── java │ └── com │ └── nettakrim │ └── spyglass_astronomy │ ├── mixin │ ├── ClientWorldAccessor.java │ ├── BiomeAccessAccessor.java │ ├── SkyRenderingMixin.java │ ├── MinecraftClientMixin.java │ ├── WorldRendererMixin.java │ ├── InGameHudMixin.java │ ├── GameRendererMixin.java │ ├── MouseMixin.java │ └── ChatHudMixin.java │ ├── IntTetrisBagRandom.java │ ├── AstralObject.java │ ├── commands │ ├── AdminCommand.java │ ├── admin_subcommands │ │ ├── BypassCommand.java │ │ ├── RenameCommand.java │ │ ├── ChangesCommand.java │ │ ├── YearLengthCommand.java │ │ ├── SeedCommand.java │ │ ├── StarCountCommand.java │ │ └── ConstellationsCommand.java │ ├── SelectCommand.java │ ├── HideCommand.java │ ├── SpyglassAstronomyCommands.java │ ├── NameCommand.java │ ├── ShareCommand.java │ └── InfoCommand.java │ ├── Orbit.java │ ├── Knowledge.java │ ├── Star.java │ ├── StarLine.java │ ├── Constellation.java │ ├── OrbitingBody.java │ ├── SpaceDataManager.java │ └── SpaceRenderingManager.java ├── settings.gradle ├── .gitignore ├── gradle.properties ├── .github └── workflows │ └── build.yml ├── python └── Kepler.py ├── gradlew.bat ├── README.md ├── LICENSE.md └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nettakrim/Spyglass-Astronomy/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/spyglass_astronomy/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nettakrim/Spyglass-Astronomy/HEAD/src/main/resources/assets/spyglass_astronomy/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/spyglass_astronomy/textures/star_spyglass_scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nettakrim/Spyglass-Astronomy/HEAD/src/main/resources/assets/spyglass_astronomy/textures/star_spyglass_scope.png -------------------------------------------------------------------------------- /src/main/resources/assets/spyglass_astronomy/textures/constellation_spyglass_scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nettakrim/Spyglass-Astronomy/HEAD/src/main/resources/assets/spyglass_astronomy/textures/constellation_spyglass_scope.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .factorypath 25 | .classpath 26 | .project 27 | 28 | # macos 29 | 30 | *.DS_Store 31 | 32 | # fabric 33 | 34 | run/ 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx4G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/develop 6 | minecraft_version=1.21.9 7 | yarn_mappings=1.21.9+build.1 8 | loader_version=0.17.2 9 | 10 | # Mod Properties 11 | mod_version = 1.0.19-mc1.21.9 12 | maven_group = com.nettakrim.spyglass_astronomy 13 | archives_base_name = spyglass_astronomy 14 | 15 | # Dependencies 16 | fabric_version=0.134.0+1.21.9 17 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/ClientWorldAccessor.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | import net.minecraft.client.network.ClientPlayNetworkHandler; 3 | import net.minecraft.client.world.ClientWorld; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(ClientWorld.class) 8 | public interface ClientWorldAccessor { 9 | @Accessor 10 | ClientPlayNetworkHandler getNetworkHandler(); 11 | } -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/BiomeAccessAccessor.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import net.minecraft.world.biome.source.BiomeAccess; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | //https://github.com/Johni0702/bobby/blob/master/src/main/java/de/johni0702/minecraft/bobby/mixin/BiomeAccessAccessor.java 8 | @Mixin(BiomeAccess.class) 9 | public interface BiomeAccessAccessor { 10 | @Accessor 11 | long getSeed(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/spyglass_astronomy.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.nettakrim.spyglass_astronomy.mixin", 5 | "compatibilityLevel": "JAVA_21", 6 | "mixins": [ 7 | ], 8 | "client": [ 9 | "BiomeAccessAccessor", 10 | "ChatHudMixin", 11 | "ClientWorldAccessor", 12 | "GameRendererMixin", 13 | "InGameHudMixin", 14 | "MinecraftClientMixin", 15 | "MouseMixin", 16 | "SkyRenderingMixin", 17 | "WorldRendererMixin" 18 | ], 19 | "injectors": { 20 | "defaultRequire": 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/IntTetrisBagRandom.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import java.util.ArrayList; 4 | 5 | import net.minecraft.util.math.random.Random; 6 | 7 | public class IntTetrisBagRandom { 8 | public ArrayList list; 9 | public final int max; 10 | private final Random random; 11 | 12 | public IntTetrisBagRandom(Random random, int max) { 13 | this.random = random; 14 | this.max = max; 15 | reset(); 16 | } 17 | 18 | public void reset() { 19 | list = new ArrayList<>(); 20 | for (int x = 0; x < max+1; x++) { 21 | list.add(x); 22 | } 23 | } 24 | 25 | public int get() { 26 | if (list.size() == 0) reset(); 27 | int pos = random.nextBetween(0, list.size()-1); 28 | return list.remove(pos); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/SkyRenderingMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 4 | import com.nettakrim.spyglass_astronomy.SpaceRenderingManager; 5 | 6 | import net.minecraft.client.render.*; 7 | import net.minecraft.client.util.math.MatrixStack; 8 | 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | 12 | @Mixin(SkyRendering.class) 13 | public class SkyRenderingMixin { 14 | @WrapWithCondition( 15 | method = "renderCelestialBodies", 16 | at = @At(value = "INVOKE", target="Lnet/minecraft/client/render/SkyRendering;renderStars(FLnet/minecraft/client/util/math/MatrixStack;)V") 17 | ) 18 | private boolean stopStarRender(SkyRendering instance, float brightness, MatrixStack matrices) { 19 | return SpaceRenderingManager.oldStarsVisible; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "spyglass_astronomy", 4 | "version": "${version}", 5 | 6 | "name": "Spyglass Astronomy", 7 | "description": "Explore a procedurally generated solar system through the lens of a spyglass!", 8 | "authors": [ 9 | "Nettakrim" 10 | ], 11 | "contact": { 12 | "sources": "https://github.com/Nettakrim/Spyglass-Astronomy" 13 | }, 14 | 15 | "license": "LGPL-3.0", 16 | "icon": "assets/spyglass_astronomy/icon.png", 17 | 18 | "environment": "client", 19 | "entrypoints": { 20 | "client": [ 21 | "com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient" 22 | ] 23 | }, 24 | "mixins": [ 25 | "spyglass_astronomy.mixins.json" 26 | ], 27 | 28 | "depends": { 29 | "fabricloader": ">=0.14.21", 30 | "fabric": "*", 31 | "minecraft": ">=1.21.9", 32 | "java": ">=21" 33 | }, 34 | "suggests": { 35 | "another-mod": "*" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/AstralObject.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | public class AstralObject { 4 | public final Star star; 5 | public final OrbitingBody orbitingBody; 6 | public final boolean isStar; 7 | 8 | public AstralObject(Star star) { 9 | this.star = star; 10 | this.orbitingBody = null; 11 | this.isStar = true; 12 | } 13 | 14 | public AstralObject(OrbitingBody orbitingBody) { 15 | this.star = null; 16 | this.orbitingBody = orbitingBody; 17 | this.isStar = false; 18 | } 19 | 20 | public void select() { 21 | if (isStar) star.select(); 22 | else orbitingBody.select(); 23 | } 24 | 25 | public static boolean isNull(AstralObject astralObject) { 26 | if (astralObject == null) return true; 27 | if (astralObject.isStar) { 28 | return astralObject.star == null; 29 | } else { 30 | return astralObject.orbitingBody == null; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 4 | 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.world.ClientWorld; 7 | 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(MinecraftClient.class) 14 | public class MinecraftClientMixin { 15 | @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;Z)V", at = @At("RETURN")) 16 | private void saveSpace(CallbackInfo ci) { 17 | SpyglassAstronomyClient.saveSpace(); 18 | SpyglassAstronomyClient.ready = false; 19 | } 20 | 21 | @Inject(method = "joinWorld", at = @At("TAIL")) 22 | private void loadSpace(ClientWorld world, CallbackInfo ci) { 23 | SpyglassAstronomyClient.loadSpace(world, true); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/AdminCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.tree.LiteralCommandNode; 4 | 5 | import com.nettakrim.spyglass_astronomy.commands.admin_subcommands.*; 6 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | 9 | public class AdminCommand { 10 | public static LiteralCommandNode getCommandNode() { 11 | LiteralCommandNode adminNode = ClientCommandManager 12 | .literal("sga:admin") 13 | .build(); 14 | 15 | adminNode.addChild(BypassCommand.getCommandNode()); 16 | adminNode.addChild(ChangesCommand.getCommandNode()); 17 | adminNode.addChild(ConstellationsCommand.getCommandNode()); 18 | adminNode.addChild(RenameCommand.getCommandNode()); 19 | adminNode.addChild(SeedCommand.getCommandNode()); 20 | adminNode.addChild(StarCountCommand.getCommandNode()); 21 | adminNode.addChild(YearLengthCommand.getCommandNode()); 22 | 23 | return adminNode; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/BypassCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 6 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | 9 | public class BypassCommand { 10 | public static LiteralCommandNode getCommandNode() { 11 | LiteralCommandNode bypassNode = ClientCommandManager 12 | .literal("bypassknowledge") 13 | .executes(BypassCommand::bypassKnowledge) 14 | .build(); 15 | 16 | return bypassNode; 17 | } 18 | 19 | public static int bypassKnowledge(CommandContext context) { 20 | if (SpyglassAstronomyClient.knowledge.bypassKnowledge()) { 21 | SpyglassAstronomyClient.say("commands.admin.bypass.on"); 22 | } else { 23 | SpyglassAstronomyClient.say("commands.admin.bypass.off"); 24 | } 25 | return 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/WorldRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.mojang.blaze3d.buffers.GpuBufferSlice; 5 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 6 | import net.minecraft.client.render.*; 7 | import net.minecraft.client.render.state.SkyRenderState; 8 | import net.minecraft.client.util.math.MatrixStack; 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(WorldRenderer.class) 16 | public class WorldRendererMixin { 17 | @Shadow 18 | private int ticks; 19 | 20 | @Inject(at = @At("HEAD"), method = "tick") 21 | private void updateStars(CallbackInfo ci) { 22 | SpyglassAstronomyClient.spaceRenderingManager.updateSpace(ticks); 23 | } 24 | 25 | @Inject(method = "method_62215", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/SkyRendering;renderCelestialBodies(Lnet/minecraft/client/util/math/MatrixStack;FIFF)V")) 26 | public void renderSky(GpuBufferSlice gpuBufferSlice, SkyRenderState skyRenderState, CallbackInfo ci, @Local MatrixStack matrices) { 27 | SpyglassAstronomyClient.spaceRenderingManager.render(matrices, skyRenderState); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 15 | 21, # Current Java LTS & minimum supported by Minecraft 16 | ] 17 | # and run on both Linux and Windows 18 | os: [ubuntu-20.04, windows-2022] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: checkout repository 22 | uses: actions/checkout@v2 23 | - name: validate gradle wrapper 24 | uses: gradle/wrapper-validation-action@v1 25 | - name: setup jdk ${{ matrix.java }} 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: ${{ matrix.java }} 29 | - name: make gradle wrapper executable 30 | if: ${{ runner.os != 'Windows' }} 31 | run: chmod +x ./gradlew 32 | - name: build 33 | run: ./gradlew build 34 | - name: capture build artifacts 35 | if: ${{ runner.os == 'Linux' && matrix.java == '21' }} # Only upload artifacts built from latest java on one OS 36 | uses: actions/upload-artifact@v2 37 | with: 38 | name: Artifacts 39 | path: build/libs/ 40 | -------------------------------------------------------------------------------- /python/Kepler.py: -------------------------------------------------------------------------------- 1 | #https://www.johndcook.com/blog/2022/11/02/keplers-equation-python/ 2 | from numpy import sqrt, cbrt, pi, sin, cos, arcsin, arctan 3 | 4 | # This will solve the special form of the cubic we need. 5 | def solve_cubic(a, c, d): 6 | assert(a > 0 and c > 0) 7 | p = c/a 8 | q = d/a 9 | k = sqrt( q**2/4 + p**3/27 ) 10 | return cbrt(-q/2 - k) + cbrt(-q/2 + k) 11 | 12 | # Machin's starting point for Newton's method 13 | # See johndcook.com/blog/2022/11/01/kepler-newton/ 14 | def machin(e, M): 15 | n = sqrt(5 + sqrt(16 + 9/e)) 16 | a = n*(e*(n**2 - 1)+1)/6 17 | c = n*(1-e) 18 | d = -M 19 | s = solve_cubic(a, c, d) 20 | return n*arcsin(s) 21 | 22 | def solve_kepler(e, M): 23 | "Find E such that M = E - e sin E." 24 | if (e == 0): return M 25 | assert(0 <= e < 1) 26 | assert(0 <= M <= pi) 27 | f = lambda E: E - e*sin(E) - M 28 | E = machin(e, M) 29 | tolerance = 1e-10 30 | # Newton's method 31 | while (abs(f(E)) > tolerance): 32 | E -= f(E)/(1 - e*cos(E)) 33 | return E 34 | 35 | def get_angle(e, E): 36 | beta = e/(1+sqrt(1-e*e)) 37 | return E+2*arctan((beta*sin(E))/(1-beta*cos(E))) 38 | 39 | s="" 40 | for y in range(0,32): 41 | e = y/32 42 | if e == 1: 43 | e = 0.99 44 | s+="\nnew float[] {" 45 | for x in range(0,32): 46 | M = (x/31)*pi 47 | f = get_angle(e,solve_kepler(e, M)) 48 | s+=str(f)+"f, " 49 | s=s[:-2] 50 | s+="}," 51 | s=s[:-1] 52 | print(s) -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/InGameHudMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 5 | 6 | import net.minecraft.client.gl.RenderPipelines; 7 | import net.minecraft.client.gui.DrawContext; 8 | import net.minecraft.client.gui.hud.InGameHud; 9 | import net.minecraft.util.Identifier; 10 | 11 | import org.spongepowered.asm.mixin.Mixin; 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 | 17 | @Mixin(InGameHud.class) 18 | public class InGameHudMixin { 19 | @Unique 20 | private static final Identifier CONSTELLATION_SPYGLASS_SCOPE = Identifier.of(SpyglassAstronomyClient.MODID,"textures/constellation_spyglass_scope.png"); 21 | @Unique 22 | private static final Identifier STAR_SPYGLASS_SCOPE = Identifier.of(SpyglassAstronomyClient.MODID,"textures/star_spyglass_scope.png"); 23 | 24 | @Inject(method = "renderSpyglassOverlay",at = @At(value = "INVOKE",target = "Lnet/minecraft/client/gui/DrawContext;fill(Lcom/mojang/blaze3d/pipeline/RenderPipeline;IIIII)V", ordinal = 0)) 25 | public void renderSpyglassMode(DrawContext context, float scale, CallbackInfo ci, @Local(ordinal = 2) int k, @Local(ordinal = 3) int l, @Local(ordinal = 0) int i, @Local(ordinal = 1) int j){ 26 | if (SpyglassAstronomyClient.editMode != 0) { 27 | context.drawTexture(RenderPipelines.GUI_TEXTURED, SpyglassAstronomyClient.editMode == 1 ? CONSTELLATION_SPYGLASS_SCOPE : STAR_SPYGLASS_SCOPE, k, l, 0.0F, 0.0F, i, j, i, j); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import net.minecraft.client.network.AbstractClientPlayerEntity; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | 8 | import net.minecraft.client.render.GameRenderer; 9 | 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | 12 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | //https://github.com/Nova-Committee/AbsolutelyNotAZoomMod/blob/fabric/universal/src/main/java/committee/nova/anazm/mixin/GameRendererMixin.java 16 | 17 | @Mixin(GameRenderer.class) 18 | public abstract class GameRendererMixin { 19 | @Shadow private float fovMultiplier; 20 | @Shadow private float lastFovMultiplier; 21 | @Inject( 22 | method = "updateFovMultiplier", 23 | at = @At("HEAD"), 24 | cancellable = true 25 | ) 26 | private void updateFovMultiplier(CallbackInfo ci) { 27 | if (SpyglassAstronomyClient.zoom == 0) return; 28 | 29 | if (!(SpyglassAstronomyClient.client.player.isUsingSpyglass() && SpyglassAstronomyClient.client.options.getPerspective().isFirstPerson())) SpyglassAstronomyClient.zoom = 0; 30 | 31 | float f = 1.0f; 32 | if (SpyglassAstronomyClient.client.getCameraEntity() instanceof AbstractClientPlayerEntity abstractClientPlayerEntity) { 33 | f = abstractClientPlayerEntity.getFovMultiplier(true, 0); 34 | } 35 | //1.25892541179 would be more accurate, but it doesnt really matter 36 | f *= (float)Math.pow(1.25d, SpyglassAstronomyClient.zoom); 37 | this.lastFovMultiplier = this.fovMultiplier; 38 | this.fovMultiplier += (f - this.fovMultiplier) * 0.5f; 39 | if (this.fovMultiplier > 1.5f) { 40 | this.fovMultiplier = 1.5f; 41 | } 42 | if (this.fovMultiplier < 0.01f) { 43 | this.fovMultiplier = 0.01f; 44 | } 45 | 46 | ci.cancel(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/RenameCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.arguments.IntegerArgumentType; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.nettakrim.spyglass_astronomy.commands.NameCommand; 6 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 7 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 8 | import net.minecraft.command.argument.MessageArgumentType; 9 | 10 | public class RenameCommand { 11 | public static LiteralCommandNode getCommandNode() { 12 | LiteralCommandNode renameNode = ClientCommandManager 13 | .literal("rename") 14 | .build(); 15 | 16 | LiteralCommandNode constellationNameNode = ClientCommandManager 17 | .literal("constellation") 18 | .then( 19 | ClientCommandManager.argument("index", IntegerArgumentType.integer(0)) 20 | .then( 21 | ClientCommandManager.argument("name", MessageArgumentType.message()) 22 | .executes(NameCommand::nameConstellation) 23 | ) 24 | ) 25 | .build(); 26 | 27 | LiteralCommandNode starNameNode = ClientCommandManager 28 | .literal("star") 29 | .then( 30 | ClientCommandManager.argument("index", IntegerArgumentType.integer(0)) 31 | .then( 32 | ClientCommandManager.argument("name", MessageArgumentType.message()) 33 | .executes(NameCommand::nameStar) 34 | ) 35 | ) 36 | .build(); 37 | 38 | LiteralCommandNode orbitingBodyNameNode = ClientCommandManager 39 | .literal("planet") 40 | .then( 41 | ClientCommandManager.argument("index", IntegerArgumentType.integer(0)) 42 | .then( 43 | ClientCommandManager.argument("name", MessageArgumentType.message()) 44 | .executes(NameCommand::nameOrbitingBody) 45 | ) 46 | ) 47 | .build(); 48 | 49 | renameNode.addChild(constellationNameNode); 50 | renameNode.addChild(starNameNode); 51 | renameNode.addChild(orbitingBodyNameNode); 52 | return renameNode; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/Orbit.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import org.joml.Vector3f; 4 | 5 | import net.minecraft.util.math.MathHelper; 6 | import net.minecraft.util.math.RotationAxis; 7 | 8 | public class Orbit { 9 | public final float period; 10 | public final float eccentricity; 11 | public final float semiMajorAxis; 12 | public final float distance; 13 | public final float rotation; 14 | public final float ascension; 15 | public final float inclination; 16 | public final float timeOffset; 17 | 18 | public float lastLocalTime; 19 | 20 | //orbit speed scale, k = 1 means that a period of 1 lasts 1 minecraft day 21 | private final static double k = 1; 22 | 23 | public Orbit(double period, double eccentricity, float rotation, float ascension, float inclination, float timeOffset) { 24 | double semiMajorAxis = Math.cbrt((period*period)/k); 25 | double distance = semiMajorAxis*(1-eccentricity*eccentricity); 26 | 27 | this.period = (float) period; 28 | this.eccentricity = (float) eccentricity; 29 | this.semiMajorAxis = (float) semiMajorAxis; 30 | this.distance = (float) distance; 31 | 32 | this.rotation = rotation; 33 | this.ascension = ascension; 34 | this.inclination = inclination; 35 | this.timeOffset = timeOffset; 36 | } 37 | 38 | public float getLocalAngleAtLocalTime(float t) { 39 | return KeplerLookup.getAt(eccentricity, (t%1)*2); 40 | } 41 | 42 | public Vector3f getLocalPositionAtLocalTime(float t, boolean updateLastPos) { 43 | if (t < 0) t++; 44 | if (updateLastPos) this.lastLocalTime = t; 45 | float f = getLocalAngleAtLocalTime(t); 46 | float cosAngle = MathHelper.cos(f); 47 | float scale = distance/(1+eccentricity*cosAngle); 48 | return new Vector3f(cosAngle * scale, MathHelper.sin(f) * scale, 0); 49 | } 50 | 51 | public Vector3f getRotatedPositionAtGlobalTime(Long day, float dayFraction, boolean updateLastPos) { 52 | Vector3f pos = getLocalPositionAtLocalTime((((day%period)/period)+(dayFraction/period)+timeOffset)%1, updateLastPos); 53 | rotateLocalPosition(pos); 54 | return pos; 55 | } 56 | 57 | public void rotateLocalPosition(Vector3f vector) { 58 | vector.rotate(RotationAxis.POSITIVE_X.rotationDegrees(inclination)); 59 | vector.rotate(RotationAxis.POSITIVE_Y.rotationDegrees(ascension)); 60 | vector.rotate(RotationAxis.POSITIVE_Z.rotationDegrees(rotation)); 61 | } 62 | 63 | public Vector3f getLastRotatedPosition() { 64 | Vector3f pos = getLocalPositionAtLocalTime(lastLocalTime, false); 65 | rotateLocalPosition(pos); 66 | return pos; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/ChangesCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 6 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 7 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | 10 | public class ChangesCommand { 11 | public static LiteralCommandNode getCommandNode() { 12 | LiteralCommandNode changesNode = ClientCommandManager 13 | .literal("changes") 14 | .build(); 15 | 16 | LiteralCommandNode discardNode = ClientCommandManager 17 | .literal("discard") 18 | .executes(ChangesCommand::discardUnsavedChanges) 19 | .build(); 20 | 21 | LiteralCommandNode saveNode = ClientCommandManager 22 | .literal("save") 23 | .executes(ChangesCommand::saveChanges) 24 | .build(); 25 | 26 | LiteralCommandNode queryNode = ClientCommandManager 27 | .literal("query") 28 | .executes(ChangesCommand::queryChanges) 29 | .build(); 30 | 31 | changesNode.addChild(discardNode); 32 | changesNode.addChild(saveNode); 33 | changesNode.addChild(queryNode); 34 | return changesNode; 35 | } 36 | 37 | private static int saveChanges(CommandContext context) { 38 | int changes = SpaceDataManager.getChanges(); 39 | if (changes != 0) { 40 | SpyglassAstronomyClient.say("commands.admin.changes.save", Integer.toString(changes)); 41 | } else { 42 | SpyglassAstronomyClient.say("commands.admin.changes.save.none"); 43 | } 44 | SpyglassAstronomyClient.saveSpace(); 45 | return 1; 46 | } 47 | 48 | private static int discardUnsavedChanges(CommandContext context) { 49 | int changes = SpaceDataManager.getChanges(); 50 | if (changes != 0) { 51 | SpyglassAstronomyClient.say("commands.admin.changes.discard", Integer.toString(changes)); 52 | } else { 53 | SpyglassAstronomyClient.say("commands.admin.changes.discard.none"); 54 | } 55 | SpyglassAstronomyClient.discardUnsavedChanges(); 56 | return 1; 57 | } 58 | 59 | private static int queryChanges(CommandContext context) { 60 | SpyglassAstronomyClient.say("commands.admin.changes.query", Integer.toString(SpaceDataManager.getChanges())); 61 | return 1; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/YearLengthCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.arguments.FloatArgumentType; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.tree.LiteralCommandNode; 6 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 7 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 8 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 9 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 10 | 11 | public class YearLengthCommand { 12 | public static LiteralCommandNode getCommandNode() { 13 | LiteralCommandNode yearLengthNode = ClientCommandManager 14 | .literal("yearlength") 15 | .build(); 16 | 17 | LiteralCommandNode queryNode = ClientCommandManager 18 | .literal("query") 19 | .executes(YearLengthCommand::queryYearLength) 20 | .build(); 21 | 22 | LiteralCommandNode resetNode = ClientCommandManager 23 | .literal("reset") 24 | .executes(YearLengthCommand::resetYearLength) 25 | .build(); 26 | 27 | LiteralCommandNode setNode = ClientCommandManager 28 | .literal("set") 29 | .then( 30 | ClientCommandManager.argument("days", FloatArgumentType.floatArg(1f/8f)) 31 | .executes(YearLengthCommand::setYearLength) 32 | ) 33 | .build(); 34 | 35 | yearLengthNode.addChild(queryNode); 36 | yearLengthNode.addChild(resetNode); 37 | yearLengthNode.addChild(setNode); 38 | return yearLengthNode; 39 | } 40 | 41 | private static int setYearLength(CommandContext context) { 42 | return setYearLength(FloatArgumentType.getFloat(context, "days")); 43 | } 44 | 45 | private static int resetYearLength(CommandContext context) { 46 | return setYearLength(8f); 47 | } 48 | 49 | public static int setYearLength(float yearLength) { 50 | SpyglassAstronomyClient.say("commands.admin.yearlength.set", Float.toString(yearLength), Float.toString(SpyglassAstronomyClient.spaceDataManager.getYearLength())); 51 | SpyglassAstronomyClient.spaceDataManager.setYearLength(yearLength); 52 | SpyglassAstronomyClient.generatePlanets(null, true); 53 | SpaceDataManager.makeChange(); 54 | return 1; 55 | } 56 | 57 | private static int queryYearLength(CommandContext context) { 58 | SpyglassAstronomyClient.say("commands.admin.yearlength.query", Float.toString(SpyglassAstronomyClient.spaceDataManager.getYearLength())); 59 | return 1; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/MouseMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 4 | import com.llamalad7.mixinextras.sugar.Local; 5 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 6 | 7 | import net.minecraft.client.Mouse; 8 | import net.minecraft.client.network.ClientPlayerEntity; 9 | import net.minecraft.entity.player.PlayerInventory; 10 | import net.minecraft.util.math.MathHelper; 11 | 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Unique; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 18 | 19 | @Mixin(Mouse.class) 20 | public class MouseMixin { 21 | @Unique 22 | private double sensitivityScale; 23 | 24 | @Inject(at = @At("TAIL"), method = "updateMouse") 25 | public void updateMouse(CallbackInfo ci) { 26 | if (SpyglassAstronomyClient.isDrawingConstellation) { 27 | SpyglassAstronomyClient.updateDrawingConstellation(); 28 | } 29 | } 30 | @WrapWithCondition( 31 | method = "onMouseScroll", 32 | at = @At( 33 | value = "INVOKE", 34 | target = "Lnet/minecraft/entity/player/PlayerInventory;setSelectedSlot(I)V" 35 | ) 36 | ) 37 | private boolean onMouseScroll(PlayerInventory instance, int slot, @Local int i){ 38 | ClientPlayerEntity player = SpyglassAstronomyClient.client.player; 39 | if(player != null && player.isUsingSpyglass()){ 40 | SpyglassAstronomyClient.zoom = MathHelper.clamp(SpyglassAstronomyClient.zoom - (float)i, -10, 10); 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | @ModifyVariable( 47 | method = "updateMouse", 48 | at = @At( 49 | value = "INVOKE", 50 | target = "Lnet/minecraft/client/tutorial/TutorialManager;onUpdateMouse(DD)V" 51 | ), 52 | ordinal = 1 53 | ) 54 | private double changeXSensitivity(double d) { 55 | ClientPlayerEntity player = SpyglassAstronomyClient.client.player; 56 | double angleScale; 57 | if (player != null && player.isUsingSpyglass() && SpyglassAstronomyClient.client.options.getPerspective().isFirstPerson()) { 58 | sensitivityScale = (float)Math.pow(1.25d, SpyglassAstronomyClient.zoom); 59 | float cosAngle = (MathHelper.cos(player.getPitch()/180*MathHelper.PI)); 60 | if (cosAngle < 0) cosAngle *= -1; 61 | cosAngle = Math.max(cosAngle, (Math.max(SpyglassAstronomyClient.zoom,0)+1)/11); 62 | angleScale = 1/cosAngle; 63 | } else { 64 | sensitivityScale = 1; 65 | angleScale = 1; 66 | } 67 | return d * sensitivityScale * angleScale; 68 | } 69 | 70 | @ModifyVariable( 71 | method = "updateMouse", 72 | at = @At( 73 | value = "INVOKE", 74 | target = "Lnet/minecraft/client/tutorial/TutorialManager;onUpdateMouse(DD)V" 75 | ), 76 | ordinal = 2 77 | ) 78 | private double changeYSensitivity(double d) { 79 | return d * sensitivityScale; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/mixin/ChatHudMixin.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.mixin; 2 | 3 | 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 10 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 11 | import com.nettakrim.spyglass_astronomy.commands.SpyglassAstronomyCommands; 12 | 13 | import net.minecraft.network.message.MessageSignatureData; 14 | import net.minecraft.client.gui.hud.MessageIndicator; 15 | import net.minecraft.client.gui.hud.ChatHud; 16 | import net.minecraft.text.Text; 17 | 18 | @Mixin(ChatHud.class) 19 | public class ChatHudMixin { 20 | @Inject(at = @At("TAIL"), method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V") 21 | public void onChat(Text messageText, MessageSignatureData messageSignatureData, MessageIndicator messageIndicator, CallbackInfo ci) { 22 | String message = messageText.getString(); 23 | int sgaIndex = message.indexOf("sga:"); 24 | if (sgaIndex == -1) return; 25 | 26 | String data = message.substring(sgaIndex+4); 27 | int firstIndex = data.indexOf("|"); 28 | if (firstIndex == -1) return; 29 | int secondIndex = data.indexOf("|", firstIndex+1); 30 | data = data.substring(0, secondIndex == -1 ? firstIndex : secondIndex); 31 | if (data.charAt(1) != '_') return; 32 | 33 | 34 | switch (data.charAt(0)) { 35 | case 'c' -> { 36 | //constellation shared with sga:c_Name|AAAA| 37 | if (secondIndex == -1) return; 38 | String constellationName = data.substring(2, firstIndex); 39 | String constellationData = data.substring(firstIndex + 1, secondIndex); 40 | Text constellationText = SpyglassAstronomyCommands.getClickHere( 41 | "commands.share.receive.constellation", 42 | "/sga:admin constellations add " + constellationData + " " + constellationName, 43 | true, 44 | constellationName 45 | ); 46 | SpyglassAstronomyClient.sayText(constellationText); 47 | } 48 | case 's' -> { 49 | //star shared with sga:s_Name|index| 50 | if (secondIndex == -1) return; 51 | String starName = data.substring(2, firstIndex); 52 | int starIndex; 53 | try { 54 | starIndex = Integer.parseInt(data.substring(firstIndex + 1, secondIndex)); 55 | } catch (Exception e) { 56 | break; 57 | } 58 | Text starText = SpyglassAstronomyCommands.getClickHere( 59 | "commands.share.receive.star", 60 | "/sga:admin rename star " + starIndex + " " + starName, 61 | true, 62 | starName 63 | ); 64 | SpyglassAstronomyClient.sayText(starText); 65 | } 66 | case 'p' -> { 67 | //planets shared with sga:p_Name|index| 68 | if (secondIndex == -1) return; 69 | String orbitingBodyName = data.substring(2, firstIndex); 70 | int orbitingBodyIndex; 71 | try { 72 | orbitingBodyIndex = Integer.parseInt(data.substring(firstIndex + 1, secondIndex)); 73 | } catch (Exception e) { 74 | break; 75 | } 76 | if (orbitingBodyIndex >= SpyglassAstronomyClient.orbitingBodies.size()) break; 77 | OrbitingBody orbitingBody = SpyglassAstronomyClient.orbitingBodies.get(orbitingBodyIndex); 78 | Text orbitingBodyText = SpyglassAstronomyCommands.getClickHere( 79 | "commands.share.receive." + (orbitingBody.isPlanet ? "planet" : "comet"), 80 | "/sga:admin rename planet " + orbitingBodyIndex + " " + orbitingBodyName, 81 | true, 82 | orbitingBodyName 83 | ); 84 | SpyglassAstronomyClient.sayText(orbitingBodyText); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/SelectCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.nettakrim.spyglass_astronomy.Constellation; 6 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 7 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 8 | import com.nettakrim.spyglass_astronomy.Star; 9 | 10 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | import net.minecraft.command.argument.MessageArgumentType; 13 | 14 | public class SelectCommand { 15 | public static LiteralCommandNode getCommandNode() { 16 | LiteralCommandNode selectNode = ClientCommandManager 17 | .literal("sga:select") 18 | .build(); 19 | 20 | LiteralCommandNode constellationSelectNode = ClientCommandManager 21 | .literal("constellation") 22 | .then( 23 | ClientCommandManager.argument("name", MessageArgumentType.message()) 24 | .suggests(SpyglassAstronomyCommands.constellations) 25 | .executes(SelectCommand::selectConstellation) 26 | ) 27 | .build(); 28 | 29 | LiteralCommandNode starSelectNode = ClientCommandManager 30 | .literal("star") 31 | .then( 32 | ClientCommandManager.argument("name", MessageArgumentType.message()) 33 | .suggests(SpyglassAstronomyCommands.stars) 34 | .executes(SelectCommand::selectStar) 35 | ) 36 | .build(); 37 | 38 | LiteralCommandNode orbitingBodySelectNode = ClientCommandManager 39 | .literal("planet") 40 | .then( 41 | ClientCommandManager.argument("name", MessageArgumentType.message()) 42 | .suggests(SpyglassAstronomyCommands.orbitingBodies) 43 | .executes(SelectCommand::selectOrbitingBody) 44 | ) 45 | .build(); 46 | 47 | selectNode.addChild(constellationSelectNode); 48 | selectNode.addChild(starSelectNode); 49 | selectNode.addChild(orbitingBodySelectNode); 50 | return selectNode; 51 | } 52 | 53 | private static int selectConstellation(CommandContext context) { 54 | Constellation constellation = SpyglassAstronomyCommands.getConstellation(context); 55 | if (constellation == null) { 56 | return -1; 57 | } 58 | if (SpyglassAstronomyClient.isntHoldingSpyglass()) { 59 | SpyglassAstronomyClient.say("commands.select.constellation.fail"); 60 | return -1; 61 | } 62 | constellation.select(); 63 | SpyglassAstronomyClient.say("commands.select.constellation", constellation.name); 64 | return 1; 65 | } 66 | 67 | private static int selectStar(CommandContext context) { 68 | Star star = SpyglassAstronomyCommands.getStar(context); 69 | if (star == null) { 70 | return -1; 71 | } 72 | if (SpyglassAstronomyClient.isntHoldingSpyglass()) { 73 | SpyglassAstronomyClient.say("commands.select.star.fail"); 74 | return -1; 75 | } 76 | star.select(); 77 | String starName = (star.isUnnamed() ? "Unnamed" : star.name); 78 | SpyglassAstronomyClient.say("commands.select.star", starName); 79 | return 1; 80 | } 81 | 82 | private static int selectOrbitingBody(CommandContext context) { 83 | OrbitingBody orbitingBody = SpyglassAstronomyCommands.getOrbitingBody(context); 84 | if (orbitingBody == null) { 85 | return -1; 86 | } 87 | if (SpyglassAstronomyClient.isntHoldingSpyglass()) { 88 | SpyglassAstronomyClient.say("commands.select."+(orbitingBody.isPlanet ? "planet" : "comet")+".fail"); 89 | return -1; 90 | } 91 | orbitingBody.select(); 92 | String orbitingBodyName = (orbitingBody.isUnnamed() ? "Unnamed" : orbitingBody.name); 93 | SpyglassAstronomyClient.say("commands.select."+(orbitingBody.isPlanet ? "planet" : "comet"), orbitingBodyName); 94 | return 1; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/HideCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.tree.LiteralCommandNode; 7 | import com.nettakrim.spyglass_astronomy.SpaceRenderingManager; 8 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 9 | 10 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | 13 | public class HideCommand implements Command { 14 | public static LiteralCommandNode getCommandNode() { 15 | LiteralCommandNode hideNode = ClientCommandManager 16 | .literal("sga:hide") 17 | .executes(new HideCommand()) 18 | .build(); 19 | 20 | LiteralCommandNode constellationsHideNode = ClientCommandManager 21 | .literal("constellations") 22 | .executes(HideCommand::hideConstellations) 23 | .build(); 24 | 25 | LiteralCommandNode starsHideNode = ClientCommandManager 26 | .literal("stars") 27 | .executes(HideCommand::hideStars) 28 | .build(); 29 | 30 | LiteralCommandNode orbitingBodiesHideNode = ClientCommandManager 31 | .literal("planets") 32 | .executes(HideCommand::hideOrbitingBodies) 33 | .build(); 34 | 35 | LiteralCommandNode oldStarsHideNode = ClientCommandManager 36 | .literal("vanillastars") 37 | .executes(HideCommand::hideOldStars) 38 | .build(); 39 | 40 | LiteralCommandNode dayTimeHideNode = ClientCommandManager 41 | .literal("daytime") 42 | .executes(HideCommand::hideDaytime) 43 | .build(); 44 | 45 | hideNode.addChild(constellationsHideNode); 46 | hideNode.addChild(starsHideNode); 47 | hideNode.addChild(orbitingBodiesHideNode); 48 | hideNode.addChild(oldStarsHideNode); 49 | hideNode.addChild(dayTimeHideNode); 50 | 51 | return hideNode; 52 | } 53 | 54 | @Override 55 | public int run(CommandContext context) throws CommandSyntaxException { 56 | boolean active = !(SpaceRenderingManager.constellationsVisible || SpaceRenderingManager.starsVisible || SpaceRenderingManager.orbitingBodiesVisible || SpaceRenderingManager.oldStarsVisible); 57 | SpaceRenderingManager.constellationsVisible = active; 58 | SpaceRenderingManager.starsVisible = active; 59 | SpaceRenderingManager.orbitingBodiesVisible = active; 60 | SpaceRenderingManager.oldStarsVisible = false; 61 | sayHideUpdate("all", active); 62 | return 1; 63 | } 64 | 65 | private static int hideConstellations(CommandContext context) { 66 | SpaceRenderingManager.constellationsVisible = !SpaceRenderingManager.constellationsVisible; 67 | sayHideUpdate("constellations", SpaceRenderingManager.constellationsVisible); 68 | return 1; 69 | } 70 | 71 | private static int hideStars(CommandContext context) { 72 | SpaceRenderingManager.starsVisible = !SpaceRenderingManager.starsVisible; 73 | sayHideUpdate("stars", SpaceRenderingManager.starsVisible); 74 | return 1; 75 | } 76 | 77 | private static int hideOrbitingBodies(CommandContext context) { 78 | SpaceRenderingManager.orbitingBodiesVisible = !SpaceRenderingManager.orbitingBodiesVisible; 79 | sayHideUpdate("planets", SpaceRenderingManager.orbitingBodiesVisible); 80 | return 1; 81 | } 82 | 83 | private static int hideOldStars(CommandContext context) { 84 | SpaceRenderingManager.oldStarsVisible = !SpaceRenderingManager.oldStarsVisible; 85 | sayHideUpdate("vanillastars", SpaceRenderingManager.oldStarsVisible); 86 | return 1; 87 | } 88 | 89 | private static int hideDaytime(CommandContext context) { 90 | SpaceRenderingManager.starsAlwaysVisible = !SpaceRenderingManager.starsAlwaysVisible; 91 | sayHideUpdate("daytime", SpaceRenderingManager.starsAlwaysVisible); 92 | return 1; 93 | } 94 | 95 | 96 | private static void sayHideUpdate(String base, boolean active) { 97 | SpyglassAstronomyClient.say("commands.hide."+base+(active ? ".show" :".hide")); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/Knowledge.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import java.util.ArrayList; 4 | 5 | import net.minecraft.text.MutableText; 6 | import net.minecraft.text.Text; 7 | 8 | public class Knowledge { 9 | public enum Level { 10 | NOVICE, 11 | ADEPT, 12 | EXPERT, 13 | MASTER 14 | } 15 | 16 | private Level starKnowledge; 17 | private Level orbitKnowledge; 18 | private boolean bypass; 19 | private int planets; 20 | private int comets; 21 | 22 | public void updateStarKnowledge(ArrayList constellations, ArrayList stars) { 23 | int namedStars = 0; 24 | for (Star star : stars) { 25 | if (!star.isUnnamed()) { 26 | namedStars++; 27 | } 28 | } 29 | if (constellations.size() >= 20 && namedStars >= 8) { 30 | starKnowledge = Level.MASTER; 31 | return; 32 | } 33 | if (constellations.size() >= 10 && namedStars >= 3) { 34 | starKnowledge = Level.EXPERT; 35 | return; 36 | } 37 | if (constellations.size() >= 5) { 38 | starKnowledge = Level.ADEPT; 39 | return; 40 | } 41 | starKnowledge = Level.NOVICE; 42 | } 43 | 44 | public MutableText getInstructionsToStarKnowledgeStage(int stage) { 45 | return switch (stage) { 46 | case 1 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.starknowledge.toadept", "5"); 47 | case 2 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.starknowledge.toexpert", "10", "3"); 48 | case 3 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.starknowledge.tomaster", "20", "8"); 49 | default -> Text.empty(); 50 | }; 51 | } 52 | 53 | public void updateOrbitKnowledge(ArrayList orbitingBodies, int planets, int comets) { 54 | int namedPlanets = 0; 55 | int namedComets = 0; 56 | this.planets = planets; 57 | this.comets = comets; 58 | for (OrbitingBody orbitingBody : orbitingBodies) { 59 | if (!orbitingBody.isUnnamed()) { 60 | if (orbitingBody.isPlanet) namedPlanets++; 61 | else namedComets++; 62 | } 63 | } 64 | if (namedPlanets >= planets-1 && namedComets >= comets-1) { 65 | orbitKnowledge = Level.MASTER; 66 | return; 67 | } 68 | if (namedPlanets >= planets/3*2 && namedComets >= 2) { 69 | orbitKnowledge = Level.EXPERT; 70 | return; 71 | } 72 | if (namedPlanets >= planets/3 || namedComets >= 1) { 73 | orbitKnowledge = Level.ADEPT; 74 | return; 75 | } 76 | orbitKnowledge = Level.NOVICE; 77 | } 78 | 79 | public MutableText getInstructionsToOrbitKnowledgeStage(int stage) { 80 | return switch (stage) { 81 | case 1 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.orbitknowledge.toadept", Integer.toString(planets / 3), "1"); 82 | case 2 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.orbitknowledge.toexpert", Integer.toString(planets / 3 * 2), "2"); 83 | case 3 -> Text.translatable(SpyglassAstronomyClient.MODID + ".commands.info.orbitknowledge.tomaster", Integer.toString(planets - 1), Integer.toString(comets - 1)); 84 | default -> Text.empty(); 85 | }; 86 | } 87 | 88 | private void updateFlags(Level level, int[] flags, int index) { 89 | int current = switch (level) { 90 | case NOVICE -> 0; 91 | case ADEPT -> 1; 92 | case EXPERT -> 2; 93 | case MASTER -> 3; 94 | }; 95 | if (flags[index] == -1) flags[index] = current; 96 | else flags[index] = Math.min(flags[index], current); 97 | } 98 | 99 | public Text getKnowledgeInstructions(int[] flags) { 100 | return getInstructionsToStarKnowledgeStage(flags[0]).append(getInstructionsToOrbitKnowledgeStage(flags[1])); 101 | } 102 | 103 | public boolean starKnowledgeAtleast(Level level, int[] flags) { 104 | boolean isAtleast = bypass || knowledgeAtleast(starKnowledge, level); 105 | if (!isAtleast) { 106 | updateFlags(level, flags, 0); 107 | } 108 | return isAtleast; 109 | } 110 | 111 | public boolean orbitKnowledgeAtleast(Level level, int[] flags) { 112 | boolean isAtleast = bypass || knowledgeAtleast(orbitKnowledge, level); 113 | if (!isAtleast) { 114 | updateFlags(level, flags, 1); 115 | } 116 | return isAtleast; 117 | } 118 | 119 | private boolean knowledgeAtleast(Level a, Level b) { 120 | //b <= a 121 | if (b == a) return true; 122 | if (a == Level.MASTER) return true; 123 | if (b == Level.NOVICE) return true; 124 | return a == Level.EXPERT && b == Level.ADEPT; 125 | } 126 | 127 | public boolean bypassKnowledge() { 128 | bypass = !bypass; 129 | return bypass; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/Star.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import org.joml.Vector3f; 4 | 5 | import net.minecraft.client.render.BufferBuilder; 6 | import net.minecraft.util.math.MathHelper; 7 | 8 | //https://github.com/ZtereoHYPE/nicer-skies/blob/main/src/main/java/codes/ztereohype/nicerskies/sky/star/Star.java 9 | 10 | public class Star { 11 | public final int index; 12 | 13 | private final float xCoord; 14 | private final float yCoord; 15 | private final float zCoord; 16 | 17 | private final float longitudeSin; 18 | private final float longitudeCos; 19 | 20 | private final float latitudeSin; 21 | private final float latitudeCos; 22 | 23 | private final int r; 24 | private final int g; 25 | private final int b; 26 | private final float alpha; 27 | 28 | private float angle; 29 | private float size; 30 | 31 | private final float rotationSpeed; 32 | private final float twinkleSpeed; 33 | private int currentAlpha; 34 | 35 | private int connectedStars = 0; 36 | 37 | public static Star selected; 38 | private boolean isSelected; 39 | 40 | public String name; 41 | 42 | public Star(int index, float posX, float posY, float posZ, float size, float rotationSpeed, int[] color, float alpha, float twinkleSpeed) { 43 | this.index = index; 44 | 45 | this.xCoord = posX; 46 | this.yCoord = posY; 47 | this.zCoord = posZ; 48 | 49 | this.r = color[0]; 50 | this.g = color[1]; 51 | this.b = color[2]; 52 | this.alpha = alpha; 53 | 54 | double polarAngle = Math.atan2(posX, posZ); 55 | this.longitudeSin = (float) Math.sin(polarAngle); 56 | this.longitudeCos = (float) Math.cos(polarAngle); 57 | 58 | double proj = Math.atan2(Math.sqrt(posX * posX + posZ * posZ), posY); 59 | this.latitudeSin = (float) Math.sin(proj); 60 | this.latitudeCos = (float) Math.cos(proj); 61 | 62 | this.size = size; 63 | this.angle = rotationSpeed * MathHelper.PI; 64 | this.rotationSpeed = rotationSpeed * 0.005f; 65 | this.twinkleSpeed = twinkleSpeed; 66 | } 67 | 68 | public void update(int ticks) { 69 | angle = (angle+rotationSpeed)%90; 70 | float twinkle = 1 - 2.5f * Math.max(MathHelper.sin(ticks*twinkleSpeed) - 0.75f,0); 71 | currentAlpha = (int) (getCurrentNonTwinkledAlpha() * twinkle * 255); 72 | } 73 | 74 | public float getCurrentNonTwinkledAlpha() { 75 | float heightScale = SpaceRenderingManager.getHeightScale(); 76 | float brightness = heightScale*Math.max(alpha/2 + heightScale/2, 2*alpha-1) + (1-heightScale) * alpha * alpha * alpha; 77 | if (connectedStars == 0) { 78 | return brightness; 79 | } 80 | return (brightness + (0.5f * alpha + 0.5f))/2; 81 | } 82 | 83 | public void setVertices(BufferBuilder bufferBuilder) { 84 | float angleSin = MathHelper.sin(angle); 85 | float angleCos = MathHelper.cos(angle); 86 | int colorMult = isSelected ? 1 : 0; 87 | for (int corner = 0; corner < 4; ++corner) { 88 | float x = ((corner & 2) - 1) * size; 89 | float y = ((corner + 1 & 2) - 1) * size; 90 | float rotatedA = x * angleCos - y * angleSin; 91 | float rotatedB = y * angleCos + x * angleSin; 92 | float rotatedALat = rotatedA * latitudeSin; 93 | float rotatedBLat = -(rotatedA * latitudeCos); 94 | float vertexPosX = rotatedBLat * longitudeSin - rotatedB * longitudeCos; 95 | float vertexPosZ = rotatedB * longitudeSin + rotatedBLat * longitudeCos; 96 | bufferBuilder.vertex(xCoord*100 + vertexPosX, yCoord*100 + rotatedALat, zCoord*100 + vertexPosZ).color(r >> colorMult, g << colorMult, b >> colorMult, currentAlpha); 97 | } 98 | } 99 | 100 | public Vector3f getRenderedPosition() { 101 | return new Vector3f(xCoord*100, yCoord*100, zCoord*100); 102 | } 103 | 104 | public int[] getColor() { 105 | return new int[]{r,g,b,(int) ((0.5f * alpha + 0.5f) * 255)}; 106 | } 107 | 108 | public float[] getPosition() { 109 | return new float[]{xCoord, yCoord, zCoord}; 110 | } 111 | 112 | public Vector3f getPositionAsVector3f() { 113 | return new Vector3f(xCoord, yCoord, zCoord); 114 | } 115 | 116 | public void connect() { 117 | connectedStars++; 118 | } 119 | 120 | public void disconnect() { 121 | if (connectedStars > 0) connectedStars -= 2; 122 | } 123 | 124 | public void clearAllConnections() { 125 | connectedStars = 0; 126 | } 127 | 128 | public float getAlpha() { 129 | return alpha; 130 | } 131 | 132 | public void select() { 133 | Constellation.deselect(); 134 | OrbitingBody.deselect(); 135 | if (selected != null) selected.isSelected = false; 136 | isSelected = true; 137 | selected = this; 138 | } 139 | 140 | public static void deselect() { 141 | if (selected != null) selected.isSelected = false; 142 | selected = null; 143 | } 144 | 145 | public boolean isUnnamed() { 146 | return name == null; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/SpyglassAstronomyCommands.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.suggestion.SuggestionProvider; 7 | import com.mojang.brigadier.tree.RootCommandNode; 8 | import com.nettakrim.spyglass_astronomy.Constellation; 9 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 10 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 11 | import com.nettakrim.spyglass_astronomy.Star; 12 | 13 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; 14 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 15 | import net.minecraft.command.argument.MessageArgumentType.MessageFormat; 16 | import net.minecraft.text.ClickEvent; 17 | import net.minecraft.text.Style; 18 | import net.minecraft.text.Text; 19 | 20 | public class SpyglassAstronomyCommands { 21 | public static final SuggestionProvider constellations = (context, builder) -> { 22 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 23 | builder.suggest(constellation.name); 24 | } 25 | return CompletableFuture.completedFuture(builder.build()); 26 | }; 27 | 28 | public static final SuggestionProvider stars = (context, builder) -> { 29 | for (Star star : SpyglassAstronomyClient.stars) { 30 | if (!star.isUnnamed()) builder.suggest(star.name); 31 | } 32 | return CompletableFuture.completedFuture(builder.build()); 33 | }; 34 | 35 | public static final SuggestionProvider orbitingBodies = (context, builder) -> { 36 | for (OrbitingBody orbitingBody : SpyglassAstronomyClient.orbitingBodies) { 37 | if (!orbitingBody.isUnnamed()) builder.suggest(orbitingBody.name); 38 | } 39 | return CompletableFuture.completedFuture(builder.build()); 40 | }; 41 | 42 | public static void initialize() { 43 | ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { 44 | RootCommandNode root = dispatcher.getRoot(); 45 | 46 | root.addChild(AdminCommand.getCommandNode()); 47 | root.addChild(HideCommand.getCommandNode()); 48 | root.addChild(InfoCommand.getCommandNode()); 49 | root.addChild(NameCommand.getCommandNode()); 50 | root.addChild(SelectCommand.getCommandNode()); 51 | root.addChild(ShareCommand.getCommandNode()); 52 | }); 53 | } 54 | 55 | public static Constellation getConstellation(CommandContext context) { 56 | String name = getMessageText(context); 57 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 58 | if (constellation.name.equals(name)) { 59 | return constellation; 60 | } 61 | } 62 | SpyglassAstronomyClient.say("commands.find.constellation.fail", name); 63 | return null; 64 | } 65 | 66 | public static Star getStar(CommandContext context) { 67 | String name = getMessageText(context); 68 | for (Star star : SpyglassAstronomyClient.stars) { 69 | if (star.name != null && star.name.equals(name)) { 70 | return star; 71 | } 72 | } 73 | SpyglassAstronomyClient.say("commands.find.star.fail", name); 74 | return null; 75 | } 76 | 77 | public static OrbitingBody getOrbitingBody(CommandContext context) { 78 | String name = getMessageText(context); 79 | for (OrbitingBody orbitingBody : SpyglassAstronomyClient.orbitingBodies) { 80 | if (orbitingBody.name != null && orbitingBody.name.equals(name)) { 81 | return orbitingBody; 82 | } 83 | } 84 | SpyglassAstronomyClient.say("commands.find.planet.fail", name); 85 | return null; 86 | } 87 | 88 | public static String getMessageText(CommandContext context) { 89 | return getMessageText(context, "name"); 90 | } 91 | 92 | public static String getMessageText(CommandContext context, String name) { 93 | //a lot of digging through #SayCommand to make a MessageArgumentType that works clientside 94 | MessageFormat messageFormat = context.getArgument(name, MessageFormat.class); 95 | return messageFormat.contents(); 96 | } 97 | 98 | public static Text getClickHere(String actionKey, String command, boolean run, Object... formatting) { 99 | return Text.translatable(SpyglassAstronomyClient.MODID+".commands.share.click") 100 | .setStyle(Style.EMPTY.withClickEvent(run ? new ClickEvent.RunCommand(command) : new ClickEvent.SuggestCommand(command)).withColor(SpyglassAstronomyClient.buttonTextColor)) 101 | .append(Text.translatable(SpyglassAstronomyClient.MODID+"."+actionKey, formatting).setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor))); 102 | } 103 | } -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/NameCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.arguments.IntegerArgumentType; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.brigadier.tree.LiteralCommandNode; 8 | import com.nettakrim.spyglass_astronomy.Constellation; 9 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 10 | import com.nettakrim.spyglass_astronomy.Star; 11 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 12 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 13 | 14 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 15 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 16 | import net.minecraft.command.argument.MessageArgumentType; 17 | 18 | public class NameCommand implements Command { 19 | public static LiteralCommandNode getCommandNode() { 20 | LiteralCommandNode nameNode = ClientCommandManager 21 | .literal("sga:name") 22 | .then( 23 | ClientCommandManager.argument("name", MessageArgumentType.message()) 24 | .executes(new NameCommand()) 25 | ) 26 | .build(); 27 | 28 | return nameNode; 29 | } 30 | 31 | @Override 32 | public int run(CommandContext context) throws CommandSyntaxException { 33 | String name = SpyglassAstronomyCommands.getMessageText(context); 34 | name = name.replace("|", ""); 35 | name = name.replaceAll("^ +| +$|( )+", "$1"); //remove double spaces 36 | if (Constellation.selected != null) { 37 | name(Constellation.selected, name); 38 | return 1; 39 | } 40 | if (Star.selected != null) { 41 | name(Star.selected, name); 42 | return 1; 43 | } 44 | if (OrbitingBody.selected != null) { 45 | name(OrbitingBody.selected, name); 46 | return 1; 47 | } 48 | SpyglassAstronomyClient.say("commands.name.nothingselected"); 49 | return -1; 50 | } 51 | 52 | public static int nameConstellation(CommandContext context) { 53 | int index = IntegerArgumentType.getInteger(context, "index"); 54 | int size = SpyglassAstronomyClient.constellations.size(); 55 | if (index >= size) { 56 | if (size == 0) { 57 | SpyglassAstronomyClient.say("commands.name.constellation.fail.none"); 58 | } else { 59 | SpyglassAstronomyClient.say("commands.name.constellation.fail", size); 60 | } 61 | return -1; 62 | } 63 | String name = SpyglassAstronomyCommands.getMessageText(context); 64 | name(SpyglassAstronomyClient.constellations.get(index), name); 65 | return 1; 66 | } 67 | 68 | public static int nameStar(CommandContext context) { 69 | int index = IntegerArgumentType.getInteger(context, "index"); 70 | if (index >= SpyglassAstronomyClient.stars.size()) { 71 | SpyglassAstronomyClient.say("commands.name.star.fail"); 72 | return -1; 73 | } 74 | String name = SpyglassAstronomyCommands.getMessageText(context); 75 | name(SpyglassAstronomyClient.stars.get(index), name); 76 | return 1; 77 | } 78 | 79 | public static int nameOrbitingBody(CommandContext context) { 80 | int index = IntegerArgumentType.getInteger(context, "index"); 81 | if (index >= SpyglassAstronomyClient.orbitingBodies.size()) { 82 | SpyglassAstronomyClient.say("commands.name.planet.fail"); 83 | return -1; 84 | } 85 | String name = SpyglassAstronomyCommands.getMessageText(context); 86 | name(SpyglassAstronomyClient.orbitingBodies.get(index), name); 87 | return 1; 88 | } 89 | 90 | private static void name(Constellation constellation, String name) { 91 | if (constellation.isUnnamed()) { 92 | SpyglassAstronomyClient.say("commands.name.constellation", name); 93 | } else { 94 | SpyglassAstronomyClient.say("commands.name.constellation.rename", constellation.name, name); 95 | } 96 | constellation.name = name; 97 | constellation.select(); 98 | SpaceDataManager.makeChange(); 99 | } 100 | 101 | private static void name(Star star, String name) { 102 | if (star.isUnnamed()) { 103 | SpyglassAstronomyClient.say("commands.name.star", name); 104 | } else { 105 | SpyglassAstronomyClient.say("commands.name.star.rename", star.name, name); 106 | } 107 | star.name = name; 108 | star.select(); 109 | SpaceDataManager.makeChange(); 110 | } 111 | 112 | private static void name(OrbitingBody orbitingBody, String name) { 113 | if (orbitingBody.isUnnamed()) { 114 | SpyglassAstronomyClient.say("commands.name."+(orbitingBody.isPlanet ? "planet" : "comet"), name); 115 | } else { 116 | SpyglassAstronomyClient.say("commands.name."+(orbitingBody.isPlanet ? "planet" : "comet")+".rename", orbitingBody.name, name); 117 | } 118 | orbitingBody.name = name; 119 | orbitingBody.select(); 120 | SpaceDataManager.makeChange(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/SeedCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.arguments.LongArgumentType; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.tree.LiteralCommandNode; 6 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 7 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 8 | import com.nettakrim.spyglass_astronomy.mixin.BiomeAccessAccessor; 9 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 10 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 11 | 12 | public class SeedCommand { 13 | public static LiteralCommandNode getCommandNode() { 14 | LiteralCommandNode seedNode = ClientCommandManager 15 | .literal("seed") 16 | .build(); 17 | 18 | 19 | LiteralCommandNode starNode = ClientCommandManager 20 | .literal("star") 21 | .build(); 22 | 23 | LiteralCommandNode queryStarSeedNode = ClientCommandManager 24 | .literal("query") 25 | .executes(SeedCommand::queryStarSeed) 26 | .build(); 27 | 28 | LiteralCommandNode resetStarSeedNode = ClientCommandManager 29 | .literal("reset") 30 | .executes(SeedCommand::resetStarSeed) 31 | .build(); 32 | 33 | LiteralCommandNode setStarSeedNode = ClientCommandManager 34 | .literal("set") 35 | .then( 36 | ClientCommandManager.argument("seed", LongArgumentType.longArg()) 37 | .executes(SeedCommand::setStarSeed) 38 | ) 39 | .build(); 40 | 41 | 42 | LiteralCommandNode planetNode = ClientCommandManager 43 | .literal("planet") 44 | .build(); 45 | 46 | LiteralCommandNode queryPlanetSeedNode = ClientCommandManager 47 | .literal("query") 48 | .executes(SeedCommand::queryPlanetSeed) 49 | .build(); 50 | 51 | LiteralCommandNode resetPlanetSeedNode = ClientCommandManager 52 | .literal("reset") 53 | .executes(SeedCommand::resetPlanetSeed) 54 | .build(); 55 | 56 | LiteralCommandNode setPlanetSeedNode = ClientCommandManager 57 | .literal("set") 58 | .then( 59 | ClientCommandManager.argument("seed", LongArgumentType.longArg()) 60 | .executes(SeedCommand::setPlanetSeed) 61 | ) 62 | .build(); 63 | 64 | starNode.addChild(queryStarSeedNode); 65 | starNode.addChild(resetStarSeedNode); 66 | starNode.addChild(setStarSeedNode); 67 | seedNode.addChild(starNode); 68 | 69 | planetNode.addChild(queryPlanetSeedNode); 70 | planetNode.addChild(resetPlanetSeedNode); 71 | planetNode.addChild(setPlanetSeedNode); 72 | seedNode.addChild(planetNode); 73 | return seedNode; 74 | } 75 | 76 | private static int setStarSeed(CommandContext context) { 77 | return setStarSeed(LongArgumentType.getLong(context, "seed")); 78 | } 79 | 80 | private static int resetStarSeed(CommandContext context) { 81 | return setStarSeed(((BiomeAccessAccessor)SpyglassAstronomyClient.world.getBiomeAccess()).getSeed()); 82 | } 83 | 84 | private static int queryStarSeed(CommandContext context) { 85 | SpyglassAstronomyClient.say("commands.admin.seed.star.query", Long.toString(SpyglassAstronomyClient.spaceDataManager.getStarSeed())); 86 | return 1; 87 | } 88 | 89 | private static int setStarSeed(long seed) { 90 | SpyglassAstronomyClient.say("commands.admin.seed.star.set", Long.toString(seed), Long.toString(SpyglassAstronomyClient.spaceDataManager.getStarSeed())); 91 | SpyglassAstronomyClient.spaceDataManager.setStarSeed(seed); 92 | SpyglassAstronomyClient.generateStars(null, true, true); 93 | StarCountCommand.invalidatedConstellations.clear(); 94 | SpaceDataManager.makeChange(); 95 | return 1; 96 | } 97 | 98 | 99 | 100 | private static int setPlanetSeed(CommandContext context) { 101 | return setPlanetSeed(LongArgumentType.getLong(context, "seed")); 102 | } 103 | 104 | private static int resetPlanetSeed(CommandContext context) { 105 | return setPlanetSeed(((BiomeAccessAccessor)SpyglassAstronomyClient.world.getBiomeAccess()).getSeed()); 106 | } 107 | 108 | 109 | private static int queryPlanetSeed(CommandContext context) { 110 | SpyglassAstronomyClient.say("commands.admin.seed.planet.query", Long.toString(SpyglassAstronomyClient.spaceDataManager.getPlanetSeed())); 111 | return 1; 112 | } 113 | 114 | private static int setPlanetSeed(long seed) { 115 | SpyglassAstronomyClient.say("commands.admin.seed.planet.set", Long.toString(seed), Long.toString(SpyglassAstronomyClient.spaceDataManager.getPlanetSeed())); 116 | SpyglassAstronomyClient.spaceDataManager.setPlanetSeed(seed); 117 | SpyglassAstronomyClient.generatePlanets(null, true); 118 | SpaceDataManager.makeChange(); 119 | return 1; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/ShareCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import com.mojang.brigadier.tree.LiteralCommandNode; 7 | import com.nettakrim.spyglass_astronomy.Constellation; 8 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 9 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 10 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 11 | import com.nettakrim.spyglass_astronomy.Star; 12 | 13 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 14 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 15 | import net.minecraft.command.argument.MessageArgumentType; 16 | import net.minecraft.text.Text; 17 | 18 | public class ShareCommand implements Command { 19 | public static LiteralCommandNode getCommandNode() { 20 | LiteralCommandNode shareNode = ClientCommandManager 21 | .literal("sga:share") 22 | .executes(new ShareCommand()) 23 | .build(); 24 | 25 | LiteralCommandNode constellationShareNode = ClientCommandManager 26 | .literal("constellation") 27 | .then( 28 | ClientCommandManager.argument("name", MessageArgumentType.message()) 29 | .suggests(SpyglassAstronomyCommands.constellations) 30 | .executes(ShareCommand::shareConstellation) 31 | ) 32 | .build(); 33 | 34 | LiteralCommandNode starShareNode = ClientCommandManager 35 | .literal("star") 36 | .then( 37 | ClientCommandManager.argument("name", MessageArgumentType.message()) 38 | .suggests(SpyglassAstronomyCommands.stars) 39 | .executes(ShareCommand::shareStar) 40 | ) 41 | .build(); 42 | 43 | LiteralCommandNode orbitingBodyShareNode = ClientCommandManager 44 | .literal("planet") 45 | .then( 46 | ClientCommandManager.argument("name", MessageArgumentType.message()) 47 | .suggests(SpyglassAstronomyCommands.orbitingBodies) 48 | .executes(ShareCommand::shareOrbitingBody) 49 | ) 50 | .build(); 51 | 52 | shareNode.addChild(constellationShareNode); 53 | shareNode.addChild(starShareNode); 54 | shareNode.addChild(orbitingBodyShareNode); 55 | return shareNode; 56 | } 57 | 58 | @Override 59 | public int run(CommandContext context) throws CommandSyntaxException { 60 | if (Constellation.selected != null) { 61 | share(Constellation.selected); 62 | return 1; 63 | } 64 | if (Star.selected != null) { 65 | share(Star.selected); 66 | return 1; 67 | } 68 | if (OrbitingBody.selected != null) { 69 | share(OrbitingBody.selected); 70 | return 1; 71 | } 72 | SpyglassAstronomyClient.say("commands.share.nothingselected"); 73 | return -1; 74 | } 75 | 76 | private static int shareConstellation(CommandContext context) { 77 | Constellation constellation = SpyglassAstronomyCommands.getConstellation(context); 78 | if (constellation == null) { 79 | return -1; 80 | } 81 | share(constellation); 82 | return 1; 83 | } 84 | 85 | private static int shareStar(CommandContext context) { 86 | Star star = SpyglassAstronomyCommands.getStar(context); 87 | if (star == null) { 88 | return -1; 89 | } 90 | share(star); 91 | return 1; 92 | } 93 | 94 | private static int shareOrbitingBody(CommandContext context) { 95 | OrbitingBody orbitingBody = SpyglassAstronomyCommands.getOrbitingBody(context); 96 | if (orbitingBody == null) { 97 | return -1; 98 | } 99 | share(orbitingBody); 100 | return 1; 101 | } 102 | 103 | private static void share(Constellation constellation) { 104 | Text text = SpyglassAstronomyCommands.getClickHere( 105 | "commands.share.constellation", 106 | "sga:c_"+(SpaceDataManager.encodeConstellation(null, constellation).replace(" | ", "|"))+"|", 107 | false, 108 | constellation.name 109 | ); 110 | SpyglassAstronomyClient.sayText(text); 111 | } 112 | 113 | private static void share(Star star) { 114 | String starName = (star.isUnnamed() ? "Unnamed" : star.name); 115 | Text text = SpyglassAstronomyCommands.getClickHere( 116 | "commands.share.star", 117 | "sga:s_"+starName+"|"+ star.index +"|", 118 | false, 119 | starName 120 | ); 121 | SpyglassAstronomyClient.sayText(text); 122 | } 123 | 124 | private static void share(OrbitingBody orbitingBody) { 125 | String orbitingBodyName = (orbitingBody.isUnnamed() ? "Unnamed" : orbitingBody.name); 126 | Text text = SpyglassAstronomyCommands.getClickHere( 127 | "commands.share."+(orbitingBody.isPlanet ?"planet" : "comet"), 128 | "sga:p_"+orbitingBodyName+"|"+ SpyglassAstronomyClient.orbitingBodies.indexOf(orbitingBody) +"|", 129 | false, 130 | orbitingBodyName 131 | ); 132 | SpyglassAstronomyClient.sayText(text); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/StarCountCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.arguments.IntegerArgumentType; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.tree.LiteralCommandNode; 6 | import com.nettakrim.spyglass_astronomy.Constellation; 7 | import com.nettakrim.spyglass_astronomy.SpaceDataManager; 8 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 9 | import com.nettakrim.spyglass_astronomy.StarLine; 10 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 11 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class StarCountCommand { 16 | public static LiteralCommandNode getCommandNode() { 17 | LiteralCommandNode starCountNode = ClientCommandManager 18 | .literal("starcount") 19 | .build(); 20 | 21 | LiteralCommandNode queryNode = ClientCommandManager 22 | .literal("query") 23 | .executes(StarCountCommand::queryStarCount) 24 | .build(); 25 | 26 | LiteralCommandNode resetNode = ClientCommandManager 27 | .literal("reset") 28 | .executes(StarCountCommand::resetStarCount) 29 | .build(); 30 | 31 | LiteralCommandNode setNode = ClientCommandManager 32 | .literal("set") 33 | .then( 34 | ClientCommandManager.argument("amount", IntegerArgumentType.integer(0,4095)) 35 | .executes(StarCountCommand::setStarCount) 36 | ) 37 | .build(); 38 | 39 | starCountNode.addChild(queryNode); 40 | starCountNode.addChild(resetNode); 41 | starCountNode.addChild(setNode); 42 | return starCountNode; 43 | } 44 | 45 | public static final ArrayList invalidatedConstellations = new ArrayList<>(); 46 | 47 | private static int setStarCount(CommandContext context) { 48 | return setStarCount(IntegerArgumentType.getInteger(context, "amount")); 49 | } 50 | 51 | private static int resetStarCount(CommandContext context) { 52 | return setStarCount(1024); 53 | } 54 | 55 | private static int setStarCount(int amount) { 56 | boolean reducedStars = amount < SpyglassAstronomyClient.getStarCount(); 57 | SpyglassAstronomyClient.say("commands.admin.starcount.set", Integer.toString(amount), Integer.toString(SpyglassAstronomyClient.getStarCount())); 58 | SpyglassAstronomyClient.setStarCount(amount); 59 | SpyglassAstronomyClient.generateStars(null, true, false); 60 | 61 | if (reducedStars) { 62 | ArrayList validConstellations = new ArrayList<>(SpyglassAstronomyClient.constellations.size()); 63 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 64 | boolean valid = true; 65 | for (StarLine line : constellation.getLines()) { 66 | int star = line.getOtherStar(-1); 67 | int max = Math.max(line.getOtherStar(star), star); 68 | if (max >= amount) { 69 | invalidatedConstellations.add(constellation); 70 | valid = false; 71 | break; 72 | } 73 | } 74 | if (valid) { 75 | validConstellations.add(constellation); 76 | } 77 | } 78 | int difference = SpyglassAstronomyClient.constellations.size()-validConstellations.size(); 79 | if (difference != 0) { 80 | SpyglassAstronomyClient.say("commands.admin.starcount.set.invalidate", difference); 81 | } 82 | SpyglassAstronomyClient.constellations.clear(); 83 | for (Constellation constellation : validConstellations) { 84 | ConstellationsCommand.addConstellation(constellation, false, false); 85 | } 86 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 87 | } else { 88 | ArrayList validConstellations = new ArrayList<>(invalidatedConstellations.size()); 89 | for (Constellation constellation : invalidatedConstellations) { 90 | boolean valid = true; 91 | for (StarLine line : constellation.getLines()) { 92 | int star = line.getOtherStar(-1); 93 | int max = Math.max(line.getOtherStar(star), star); 94 | if (max >= amount) { 95 | valid = false; 96 | break; 97 | } 98 | } 99 | if (valid) { 100 | validConstellations.add(constellation); 101 | } 102 | } 103 | if (validConstellations.size() != 0) { 104 | int validated = 0; 105 | for (Constellation constellation : validConstellations) { 106 | if (ConstellationsCommand.addConstellation(constellation, false, false) == 1) { 107 | invalidatedConstellations.remove(constellation); 108 | validated++; 109 | } 110 | } 111 | if (validated > 0) { 112 | SpyglassAstronomyClient.say("commands.admin.starcount.set.validate", validated); 113 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 114 | } 115 | } 116 | } 117 | 118 | SpaceDataManager.makeChange(); 119 | return 1; 120 | } 121 | 122 | private static int queryStarCount(CommandContext context) { 123 | SpyglassAstronomyClient.say("commands.admin.starcount.query", Integer.toString(SpyglassAstronomyClient.getStarCount())); 124 | return 1; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/StarLine.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import org.joml.Vector3f; 4 | 5 | import net.minecraft.client.render.BufferBuilder; 6 | import net.minecraft.util.math.MathHelper; 7 | 8 | public class StarLine { 9 | public static float distance = 1.1f; 10 | public static float width = 0.15f; 11 | public static float visibilityMultiplier = 0.35f; 12 | 13 | private int starAIndex; 14 | private int starBIndex; 15 | 16 | private Vector3f starAPosition; 17 | private Vector3f starBPosition; 18 | 19 | private int[] starAColor; 20 | private int[] starBColor; 21 | 22 | private Vector3f vertexA1; 23 | private Vector3f vertexA2; 24 | private Vector3f vertexB1; 25 | private Vector3f vertexB2; 26 | 27 | public StarLine(int startIndex, int endIndex, boolean starsReady) { 28 | this.starAIndex = startIndex; 29 | this.starBIndex = endIndex; 30 | 31 | if (starsReady) { 32 | initialise(); 33 | } 34 | } 35 | 36 | public void initialise() { 37 | Star starA = SpyglassAstronomyClient.stars.get(this.starAIndex); 38 | this.starAPosition = starA.getRenderedPosition(); 39 | this.starAColor = starA.getColor(); 40 | starA.connect(); 41 | 42 | Star starB = SpyglassAstronomyClient.stars.get(this.starBIndex); 43 | this.starBPosition = starB.getRenderedPosition(); 44 | this.starBColor = starB.getColor(); 45 | starB.connect(); 46 | 47 | calculateVertices(); 48 | } 49 | 50 | public StarLine(Star startStar) { 51 | this.starAIndex = startStar.index; 52 | this.starBIndex = -1; 53 | 54 | this.starAPosition = startStar.getRenderedPosition(); 55 | this.starAColor = startStar.getColor(); 56 | 57 | this.starBPosition = startStar.getRenderedPosition(); 58 | this.starBColor = new int[]{255,255,255,255}; 59 | } 60 | 61 | public void updateDrawing(Vector3f position) { 62 | this.starBPosition = position; 63 | calculateVertices(); 64 | } 65 | 66 | public boolean finishDrawing(Star endStar) { 67 | if (this.starAIndex == endStar.index) return false; 68 | if (getSquaredLength() > 20000) return false; 69 | this.starBIndex = endStar.index; 70 | this.starBPosition = endStar.getRenderedPosition(); 71 | this.starBColor = endStar.getColor(); 72 | 73 | endStar.connect(); 74 | SpyglassAstronomyClient.stars.get(starAIndex).connect(); 75 | calculateVertices(); 76 | return true; 77 | } 78 | 79 | public float getSquaredLength() { 80 | Vector3f length = new Vector3f(starAPosition.x, starAPosition.y, starAPosition.z); 81 | length.sub(starBPosition); 82 | return SpyglassAstronomyClient.getSquaredDistance(length.x, length.y, length.z); 83 | } 84 | 85 | public void calculateVertices() { 86 | Vector3f direction = new Vector3f(starBPosition.x,starBPosition.y,starBPosition.z); 87 | direction.sub(starAPosition); 88 | float dirX = direction.x; 89 | float dirY = direction.y; 90 | float dirZ = direction.z; 91 | float sqrDistance = dirX * dirX + dirY * dirY + dirZ * dirZ; 92 | direction.normalize(); 93 | direction.mul(distance * (Math.min(MathHelper.sqrt(sqrDistance), 4f)/4)); 94 | 95 | Vector3f perpendicular = new Vector3f(direction); 96 | perpendicular.cross(starAPosition); 97 | perpendicular.normalize(); 98 | perpendicular.mul(width); 99 | 100 | float posAX = starAPosition.x + direction.x; 101 | float posAY = starAPosition.y + direction.y; 102 | float posAZ = starAPosition.z + direction.z; 103 | 104 | vertexA1 = new Vector3f(posAX + perpendicular.x, posAY + perpendicular.y, posAZ + perpendicular.z); 105 | vertexA2 = new Vector3f(posAX - perpendicular.x, posAY - perpendicular.y, posAZ - perpendicular.z); 106 | 107 | perpendicular = new Vector3f(-direction.x,-direction.y,-direction.z); 108 | perpendicular.cross(starAPosition); 109 | perpendicular.normalize(); 110 | perpendicular.mul(width); 111 | 112 | float posBX = starBPosition.x - direction.x; 113 | float posBY = starBPosition.y - direction.y; 114 | float posBZ = starBPosition.z - direction.z; 115 | 116 | vertexB1 = new Vector3f(posBX + perpendicular.x, posBY + perpendicular.y, posBZ + perpendicular.z); 117 | vertexB2 = new Vector3f(posBX - perpendicular.x, posBY - perpendicular.y, posBZ - perpendicular.z); 118 | } 119 | 120 | public void setVertices(BufferBuilder bufferBuilder, boolean isSelected) { 121 | if (vertexA1 == null) calculateVertices(); 122 | float drawingMultipler = 1; 123 | if (starBIndex == -1) { 124 | drawingMultipler = MathHelper.clamp((20000f-getSquaredLength())/5000f, 0f, 1f); 125 | } 126 | 127 | int ar = starAColor[0]; 128 | int br = starBColor[0]; 129 | 130 | int bg = starAColor[1]; 131 | int ag = starBColor[1]; 132 | 133 | int bb = starAColor[2]; 134 | int ab = starBColor[2]; 135 | 136 | int aa = (int)(starAColor[3] * visibilityMultiplier * drawingMultipler); 137 | int ba = (int)(starBColor[3] * visibilityMultiplier * drawingMultipler); 138 | 139 | if (isSelected) { 140 | ar = (int)(ag*0.8f); 141 | br = (int)(bg*0.8f); 142 | ag = (int)(ag*0.5f); 143 | bg = (int)(bg*0.5f); 144 | ab = (int)Math.min(ab*1.5f, 255f); 145 | bb = (int)Math.min(bb*1.5f, 255f); 146 | 147 | aa = aa*2; 148 | ba = ba*2; 149 | } 150 | 151 | bufferBuilder.vertex( 152 | vertexA1.x, 153 | vertexA1.y, 154 | vertexA1.z) 155 | .color(ar, ag, ab, aa); 156 | 157 | bufferBuilder.vertex( 158 | vertexA2.x, 159 | vertexA2.y, 160 | vertexA2.z) 161 | .color(ar, ag, ab, aa); 162 | 163 | bufferBuilder.vertex( 164 | vertexB1.x, 165 | vertexB1.y, 166 | vertexB1.z) 167 | .color(br, bg, bb, ba); 168 | 169 | bufferBuilder.vertex( 170 | vertexB2.x, 171 | vertexB2.y, 172 | vertexB2.z) 173 | .color(br, bg, bb, ba); 174 | } 175 | 176 | public void clear() { 177 | SpyglassAstronomyClient.stars.get(starAIndex).disconnect(); 178 | SpyglassAstronomyClient.stars.get(starBIndex).disconnect(); 179 | } 180 | 181 | public Star[] getStars() { 182 | return new Star[]{SpyglassAstronomyClient.stars.get(starAIndex), SpyglassAstronomyClient.stars.get(starBIndex)}; 183 | } 184 | 185 | public boolean isSame(int a, int b) { 186 | return (a == this.starAIndex && b == this.starBIndex) || (a == this.starBIndex && b == this.starAIndex); 187 | } 188 | 189 | public boolean isSame(StarLine other) { 190 | return isSame(other.starAIndex, other.starBIndex); 191 | } 192 | 193 | public boolean intersects(int a, int b) { 194 | return a == this.starAIndex || b == this.starBIndex || a == this.starBIndex || b == this.starAIndex; 195 | } 196 | 197 | public boolean intersects(StarLine other) { 198 | return intersects(other.starAIndex, other.starBIndex); 199 | } 200 | 201 | public boolean hasStar(int star) { 202 | return this.starAIndex == star || this.starBIndex == star; 203 | } 204 | 205 | public int getOtherStar(int star) { 206 | return this.starAIndex == star ? this.starBIndex : this.starAIndex; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/Constellation.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.joml.Vector3f; 6 | 7 | import net.minecraft.client.render.BufferBuilder; 8 | import net.minecraft.util.math.MathHelper; 9 | 10 | public class Constellation { 11 | private final ArrayList lines = new ArrayList<>(); 12 | 13 | public String name = "Unnamed"; 14 | 15 | private Vector3f averagePositionBuffer; 16 | private boolean averagePositionValid; 17 | 18 | public static Constellation selected; 19 | private boolean isSelected; 20 | 21 | public Constellation() { 22 | 23 | } 24 | 25 | public Constellation(StarLine starLine) { 26 | lines.add(starLine); 27 | } 28 | 29 | public void setVertices(BufferBuilder bufferBuilder, boolean forceSelected) { 30 | for (StarLine line : lines) { 31 | line.setVertices(bufferBuilder, isSelected || forceSelected); 32 | } 33 | } 34 | 35 | public Constellation addLineCanRemove(StarLine starLine) { 36 | averagePositionValid = false; 37 | int end = lines.size(); 38 | for (int i = 0; i < end; i++) { 39 | StarLine line = lines.get(i); 40 | if (line.isSame(starLine)) { 41 | line.clear(); 42 | lines.remove(i); 43 | return trySplit(starLine); 44 | } 45 | } 46 | lines.add(starLine); 47 | return null; 48 | } 49 | 50 | public void addLine(StarLine starLine) { 51 | for (StarLine line : lines) { 52 | if (line.isSame(starLine)) return; 53 | } 54 | lines.add(starLine); 55 | averagePositionValid = false; 56 | } 57 | 58 | public Constellation trySplit(StarLine reference) { 59 | Star[] refStars = reference.getStars(); 60 | int start = refStars[0].index; 61 | int end = refStars[1].index; 62 | 63 | ArrayList found = new ArrayList<>(); 64 | boolean continueSearch = true; 65 | 66 | found.add(start); 67 | 68 | //flood outwards from one end, stop if other end found (as in a loop exists), otherwise log all found 69 | while (continueSearch) { 70 | continueSearch = false; 71 | for (StarLine line : lines) { 72 | for (int index = 0; index < found.size(); index++) { 73 | int star = found.get(index); 74 | if (line.hasStar(star)) { 75 | if (line.hasStar(end)) { 76 | return null; 77 | } 78 | int other = line.getOtherStar(star); 79 | if (!found.contains(other)) { 80 | found.add(other); 81 | continueSearch = true; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | //get all stars in the original constellation 89 | ArrayList all = new ArrayList<>(); 90 | for (StarLine line : lines) { 91 | Star[] stars = line.getStars(); 92 | int a = stars[0].index; 93 | int b = stars[1].index; 94 | if (!all.contains(a)) { 95 | all.add(a); 96 | } 97 | if (!all.contains(b)) { 98 | all.add(b); 99 | } 100 | } 101 | 102 | //if the amount of stars found is not the entire constellation (i dont remember what purpose this serves >_>) 103 | if (found.size() != all.size()) { 104 | //add all lines that were not found to the new constellation 105 | Constellation newConstellation = new Constellation(); 106 | for (StarLine line : lines) { 107 | boolean isFound = false; 108 | for (int star : found) { 109 | if (line.hasStar(star)) { 110 | isFound = true; 111 | break; 112 | } 113 | } 114 | if (!isFound) { 115 | newConstellation.addLine(line); 116 | } 117 | } 118 | 119 | //i wouldve thought this is equivalent to the if statement this is within, but apparently not 120 | if (newConstellation.lines.size() == lines.size()) { 121 | return null; 122 | } 123 | 124 | //remove the lines of the old constellation from the new one 125 | for (StarLine newLine : newConstellation.lines) { 126 | int index = 0; 127 | while (index < lines.size()) { 128 | if (lines.get(index).isSame(newLine)) { 129 | lines.remove(index); 130 | } else { 131 | index++; 132 | } 133 | } 134 | } 135 | newConstellation.name = name; 136 | return newConstellation; 137 | } 138 | 139 | return null; 140 | } 141 | 142 | public boolean lineIntersects(StarLine starLine) { 143 | for (StarLine line : lines) { 144 | if (line.intersects(starLine)) return true; 145 | } 146 | return false; 147 | } 148 | 149 | public boolean hasNoMatchingLine(StarLine starLine) { 150 | for (StarLine line : lines) { 151 | if (line.isSame(starLine)) return false; 152 | } 153 | return true; 154 | } 155 | 156 | public boolean hasStar(Star star) { 157 | int index = star.index; 158 | for (StarLine line : lines) { 159 | if (line.hasStar(index)) return true; 160 | } 161 | return false; 162 | } 163 | 164 | public ArrayList getLines() { 165 | return lines; 166 | } 167 | 168 | public void initaliseStarLines() { 169 | for (StarLine line : lines) { 170 | line.initialise(); 171 | } 172 | } 173 | 174 | public Vector3f getAveragePosition() { 175 | if (averagePositionValid) return averagePositionBuffer; 176 | 177 | averagePositionBuffer = new Vector3f(); 178 | ArrayList stars = new ArrayList<>(); 179 | for (StarLine line : lines) { 180 | Star[] lineStars = line.getStars(); 181 | if (!stars.contains(lineStars[0])) stars.add(lineStars[0]); 182 | if (!stars.contains(lineStars[1])) stars.add(lineStars[1]); 183 | } 184 | for (Star star : stars) { 185 | averagePositionBuffer.add(star.getPositionAsVector3f()); 186 | } 187 | float x = averagePositionBuffer.x; 188 | float y = averagePositionBuffer.y; 189 | float z = averagePositionBuffer.z; 190 | float isqrt = MathHelper.inverseSqrt(x * x + y * y + z * z); 191 | averagePositionBuffer.mul(isqrt); 192 | averagePositionValid = true; 193 | 194 | return new Vector3f(averagePositionBuffer); 195 | } 196 | 197 | public void select() { 198 | Star.deselect(); 199 | OrbitingBody.deselect(); 200 | if (selected != null) selected.isSelected = false; 201 | isSelected = true; 202 | selected = this; 203 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 204 | } 205 | 206 | public static void deselect() { 207 | if (selected != null) selected.isSelected = false; 208 | selected = null; 209 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 210 | } 211 | 212 | public boolean isUnnamed() { 213 | return name.equals("Unnamed"); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spyglass Astronomy 2 | Explore a procedurally generated solar system through the lens of a spyglass! 3 | 4 | The mod is entirely client-side and designed for helping with world-building and immersion, it is encouraged you think of lore about all the constellations you draw and planets you name! 5 | 6 | Planets and stars will be the same for all players on a particular world, and are generated randomly (and largely scientifically accurately) per world, and you can easily share constellations between players using a client-side command 7 | 8 | ![Screenshot of some planets and constellations from atop a mountain](https://cdn.modrinth.com/data/EdBSdqge/images/3dc17c154655fdc2dc7fc3989a38e16e9052d269.png) 9 | # How to use 10 | 11 | To interact with the cosmos there are three spyglass modes: vanilla, constellation (amethyst), and star (emerald), these are cycled with the pick block button (middle click) while zoomed in. 12 | 13 | Constellation mode allows you to draw constellations by holding the attack button (left click) and drawing between stars, as well as being able to select constellations 14 | 15 | Star mode allows you to select stars and planets, note that comets are referred to as *planets* when using commands 16 | 17 | Once an object is selected you can name it with `/sga:name `, or get useful information with `/sga:info`, you can also get the information of a specific object without selecting it by following the suggestions after typing `/sga:info ` 18 | 19 | If you're playing on a multiplayer server, you can use `/sga:share` or `/sga:share ` to share the currently selected or specified object respectively, this gives you a code you can send in chat or /message to someone directly, which allows them to easily add a constellation or name of a planet or star on their side, provided they have the mod too, of course. 20 | 21 | All the client side commands can be seen by looking at the suggestions after typing `/sga:`, or by referring to the list below 22 | 23 | # Commands 24 | 25 | ### `/sga:info` 26 | 27 | most information is not given until you fulfil a certain criteria, this can be bypassed with `/sga:admin bypassknowledge` 28 | 29 | `/sga:info` 30 | 31 | redirects to the appropriate command for the currently selected constellation, star or planet 32 | 33 | `/sga:info constellation ` 34 | 35 | gets information about the constellation with name `` 36 | - Name 37 | - Current position in the sky (the two numbers shown on F3) 38 | - What time of year it is most visible 39 | 40 | `/sga:info star ` 41 | 42 | gets information about the star with name `` 43 | - Name 44 | - Current position in the sky (the two numbers shown on F3) 45 | - What time of year it is most visible 46 | - Distance from the solar system (in LY) 47 | 48 | `/sga:info planet ` 49 | 50 | gets information about the planet with name `` 51 | - Name 52 | - Type 53 | - Orbital Period 54 | - Orbital Resonance (amount of days between each closest approach of thisworld and the specified planet, assuming circular orbits) 55 | - Current position in orbit (% of the way through the planet's year) 56 | - Distance from thisworld (in AU) 57 | - Current position in the sky (the two numbers shown on F3) 58 | - Eccentricity 59 | - Inclination 60 | 61 | `/sga:info thisworld` 62 | 63 | gets information about the minecraft world's orbit 64 | - Current In-game Time 65 | - Current Moon-phase 66 | - Orbital Period 67 | - Current position in orbit (% of the way through the planet's year) 68 | - Eccentricity 69 | - Inclination 70 | 71 | 72 | `/sga:info solarsystem` 73 | 74 | gets information about the solar system 75 | - List of planets (including thisworld) in order of distance from sun 76 | - List of comets in arbitrary order 77 | - Days since worlds creation 78 | 79 | ### `/sga:select` 80 | 81 | `/sga:select constellation|star|planet ` 82 | 83 | selects the specified object with name `` 84 | 85 | ### `/sga:name` 86 | 87 | `/sga:name ` 88 | 89 | names the currently selected object ``, quotations are not needed (its like the `/say` command) 90 | 91 | ### `/sga:share` 92 | 93 | `/sga:share` 94 | 95 | redirects to the appropriate command for the currently selected constellation, star or planet 96 | 97 | `/sga:share constellation|star|planet ` 98 | 99 | displays a [Click Here] button, clicking the button will open chat with some suggested text representing the object, if anyone receives this text as part of a message (e.g, though just sending it in chat directly, or with a /msg), they get a [Click Here] button which when clicked gives them the shared object 100 | 101 | ### `/sga:admin` 102 | 103 | there is generally little reason to use `sga:admin` commands other than while setting up a world, but some may still be useful for messing around 104 | 105 | `/sga:admin bypassknowledge` 106 | 107 | bypass knowledge checks for `/sga:info` until a re-log or until it is run again 108 | 109 | `/sga:admin changes discard|save|query` 110 | 111 | discard reverts to the last saved data 112 | 113 | save saves data, although it is saved automatically whenever you leave the world or change dimension 114 | 115 | query says how many changes since last save/discard, what counts as a change can be quite sensitive, so it may be higher than you expect 116 | 117 | `/sga:admin constellations add` 118 | 119 | used by the share command to add constellations, there is little reason to use this yourself 120 | 121 | `/sga:admin constellations generate` 122 | 123 | generates constellations randomly based on the star seed 124 | 125 | `/sga:admin constellations remove ` 126 | 127 | removes the constellation with name ``, if no name is given it will remove the currently selected constellation 128 | 129 | `/sga:admin constellations removeall` 130 | 131 | removes all constellations 132 | 133 | `/sga:admin rename constellation|star|planet ` 134 | 135 | used by the share command to rename the ``th object of the specified type to ``, then select it 136 | there is little reason to use this yourself 137 | 138 | `/sga:admin seed planet|star query|reset|(set )` 139 | 140 | gets, resets, or sets the seed that planets or stars use to generate, by default this is the biome seed of the world 141 | 142 | `/sga:admin starcount query|reset|(set )` 143 | 144 | gets, resets, or sets the amount of stars in the sky to ``, default 1024, max 4095 145 | 146 | `/sga:admin yearlength query|reset|(set )` 147 | 148 | gets, resets, or sets the length of the year in days, decimals are allowed, but it has to be 0.125 or more, by default it is 8 149 | 150 | ### `/sga:hide` 151 | 152 | `/sga:hide` 153 | 154 | toggles visibilities of stars, constellations, and planets 155 | 156 | `/sga:hide stars|constellations|planets` 157 | 158 | toggles visibility of the respective type of object 159 | 160 | `/sga:hide vanillastars` 161 | 162 | toggles visibility of the boring, vanilla stars, I don't know why you would want to do this :P 163 | 164 | # Helpful code I used here and there 165 | 166 | [Nicer Skies](https://github.com/ZtereoHYPE/nicer-skies/blob/main/src/main/java/codes/ztereohype/nicerskies/sky/star/Star.java) 167 | 168 | [Bobby](https://github.com/Johni0702/bobby/blob/master/src/main/java/de/johni0702/minecraft/bobby/mixin/BiomeAccessAccessor.java), [Bobby](https://github.com/Johni0702/bobby/blob/d2024a2d63c63d0bccf2eafcab17dd7bf9d26710/src/main/java/de/johni0702/minecraft/bobby/FakeChunkManager.java#L342), and [Bobby Yet Again](https://github.com/Johni0702/bobby/blob/d2024a2d63c63d0bccf2eafcab17dd7bf9d26710/src/main/java/de/johni0702/minecraft/bobby/FakeChunkManager.java#L86) 169 | 170 | [Absolutely Not A Zoom Mod](https://github.com/Nova-Committee/AbsolutelyNotAZoomMod/blob/fabric/universal/src/main/java/committee/nova/anazm/mixin/GameRendererMixin.java) 171 | 172 | [Time Display](https://github.com/Iru21/TimeDisplay/blob/master/src/main/kotlin/me/iru/timedisplay/TimeUtils.kt) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /src/main/resources/assets/spyglass_astronomy/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "spyglass_astronomy.commands.admin.bypass.on" : "已跳过探索限制,再次输入以重置", 3 | "spyglass_astronomy.commands.admin.bypass.off" : "重新启用探索限制", 4 | 5 | "spyglass_astronomy.commands.admin.changes.discard" : "未保存的更改 %s 将会丢失", 6 | "spyglass_astronomy.commands.admin.changes.discard.none" : "无未保存的更改", 7 | "spyglass_astronomy.commands.admin.changes.save" : "未保存的更改 %s 已保存", 8 | "spyglass_astronomy.commands.admin.changes.save.none" : "无未保存的更改", 9 | "spyglass_astronomy.commands.admin.changes.query" : "自上次更改后已有 %s 次更改", 10 | 11 | "spyglass_astronomy.commands.admin.constellations.remove" : "移除星座 \"%s\"", 12 | "spyglass_astronomy.commands.admin.constellations.remove.nothingselected": "选择一个星座或者使用/sga:admin constellations remove 删除一个星座", 13 | "spyglass_astronomy.commands.admin.constellations.remove.all" : "删除所有 %s 星座", 14 | "spyglass_astronomy.commands.admin.constellations.add" : "添加新的星座 \"%s\"", 15 | "spyglass_astronomy.commands.admin.constellations.add.fail.data": "无法添加新星座 \"%s\": 无效数据", 16 | "spyglass_astronomy.commands.admin.constellations.add.fail.stars": "无法添加新星座 \"%s\": 至少需要 %s 颗星辰,目前有 %s 颗", 17 | "spyglass_astronomy.commands.admin.constellations.add.fail.collision" : "无法添加新星座 \"%s\": 与 \"%s\" 冲突", 18 | "spyglass_astronomy.commands.admin.constellations.add.edit": "改变了现有的星座 \"%s\"", 19 | "spyglass_astronomy.commands.admin.constellations.generate": "创建了新星座 %s ", 20 | 21 | "spyglass_astronomy.commands.admin.seed.planet.set" : "设置行星种子为 %s (改动前 %s)", 22 | "spyglass_astronomy.commands.admin.seed.planet.query": "当前行星种子为 %s", 23 | "spyglass_astronomy.commands.admin.seed.star.set" : "设置星辰种子为 %s (改动前 %s)", 24 | "spyglass_astronomy.commands.admin.seed.star.query": "当前星辰种子为 %s", 25 | 26 | "spyglass_astronomy.commands.admin.starcount.set" : "设置星辰数量为 %s (改动前 %s)", 27 | "spyglass_astronomy.commands.admin.starcount.set.invalidate" : "%s 由于缺少星辰,星座不再有效 - 重新添加星辰可以尽量恢复星座", 28 | "spyglass_astronomy.commands.admin.starcount.set.validate" : "%s 星座已经再次有效并且被恢复", 29 | "spyglass_astronomy.commands.admin.starcount.query": "当前有 %s 颗星辰", 30 | 31 | "spyglass_astronomy.commands.admin.yearlength.set" : "设置一年长度为 %s (改动前 %s)", 32 | "spyglass_astronomy.commands.admin.yearlength.query": "当前一年长度为 %s 天", 33 | 34 | 35 | 36 | "spyglass_astronomy.commands.hide.all.hide" : "已全部隐藏,再次运行/sga:hide 以显示", 37 | "spyglass_astronomy.commands.hide.all.show" : "已全部显示,再次运行/sga:hide 以隐藏", 38 | "spyglass_astronomy.commands.hide.constellations.hide" : "星座已全部隐藏,再次运行/sga:hide constellations 以显示", 39 | "spyglass_astronomy.commands.hide.constellations.show" : "星座已全部显示,再次运行/sga:hide constellations 以隐藏", 40 | "spyglass_astronomy.commands.hide.stars.hide" : "星辰已全部隐藏,再次运行/sga:hide stars 以显示", 41 | "spyglass_astronomy.commands.hide.stars.show" : "星辰已全部显示,再次运行/sga:hide stars 以隐藏", 42 | "spyglass_astronomy.commands.hide.planets.hide" : "行星和彗星已全部隐藏,再次运行/sga:hide planets 以显示", 43 | "spyglass_astronomy.commands.hide.planets.show" : "行星和彗星已全部显示,再次运行/sga:hide planets 以隐藏", 44 | "spyglass_astronomy.commands.hide.vanillastars.hide" : "原版星辰已全部隐藏,再次运行/sga:hide vanillastars 以显示", 45 | "spyglass_astronomy.commands.hide.vanillastars.show" : "原版星辰已全部显示,再次运行/sga:hide vanillastars 以隐藏", 46 | "spyglass_astronomy.commands.hide.daytime.hide" : "#All night-only, run /sga:hide daytime again to show all during day", 47 | "spyglass_astronomy.commands.hide.daytime.show" : "#All shown during day, run /sga:hide daytime again for night-only", 48 | 49 | "spyglass_astronomy.commands.name.constellation" : "新星座被命名为 \"%s\"", 50 | "spyglass_astronomy.commands.name.constellation.rename" : "星座 \"%s\" 被重命名为 \"%s\"", 51 | "spyglass_astronomy.commands.name.constellation.fail": "没有足够的星座,目标数字必须小于 %s", 52 | "spyglass_astronomy.commands.name.constellation.fail.none": "没有可用星座,尝试先创造一些星座", 53 | "spyglass_astronomy.commands.name.star" : "新星辰被命名为 \"%s\"", 54 | "spyglass_astronomy.commands.name.star.rename" : "星辰 \"%s\" 被重命名为 \"%s\"", 55 | "spyglass_astronomy.commands.name.star.fail": "没有足够的星辰,请检查星辰数量是否正确 (/sga:admin 星辰数量查询)", 56 | "spyglass_astronomy.commands.name.planet" : "新行星被命名为 \"%s\"", 57 | "spyglass_astronomy.commands.name.planet.rename" : "行星 \"%s\" 被重命名为 \"%s\"", 58 | "spyglass_astronomy.commands.name.comet" : "新彗星被命名为 \"%s\"", 59 | "spyglass_astronomy.commands.name.comet.rename" : "彗星 \"%s\" 被重命名为 \"%s\"", 60 | "spyglass_astronomy.commands.name.planet.fail": "没有足够的行星或彗星,请检查行星种子是否正确 (/sga:admin 行星种子查询)", 61 | "spyglass_astronomy.commands.name.nothingselected" : "必须选择一个对象才能为其命名", 62 | 63 | "spyglass_astronomy.commands.select.constellation" : "选择星座 \"%s\"", 64 | "spyglass_astronomy.commands.select.constellation.fail" : "必须手持望远镜才能选择星座", 65 | "spyglass_astronomy.commands.select.star" : "选择星辰 \"%s\"", 66 | "spyglass_astronomy.commands.select.star.fail" : "必须手持望远镜才能选择星辰", 67 | "spyglass_astronomy.commands.select.planet" : "选择行星 \"%s\"", 68 | "spyglass_astronomy.commands.select.planet.fail" : "必须手持望远镜才能选择行星", 69 | "spyglass_astronomy.commands.select.comet" : "选择彗星 \"%s\"", 70 | "spyglass_astronomy.commands.select.comet.fail" : "必须手持望远镜才能选择彗星", 71 | 72 | "spyglass_astronomy.commands.share.click" : "[点击此处] ", 73 | "spyglass_astronomy.commands.share.constellation" : "分享星座 \"%s\"", 74 | "spyglass_astronomy.commands.share.star" : "分享星辰 \"%s\"", 75 | "spyglass_astronomy.commands.share.planet" : "分享行星 \"%s\"", 76 | "spyglass_astronomy.commands.share.comet" : "分享彗星 \"%s\"", 77 | "spyglass_astronomy.commands.share.receive.constellation" : " 添加新的星座 \"%s\"", 78 | "spyglass_astronomy.commands.share.receive.star" : " 添加新的星辰 \"%s\"", 79 | "spyglass_astronomy.commands.share.receive.planet" : " 添加新的行星 \"%s\"", 80 | "spyglass_astronomy.commands.share.receive.comet" : " 添加新的彗星 \"%s\"", 81 | "spyglass_astronomy.commands.share.nothingselected" : "选择一个对象或使用/sga:share 分享", 82 | 83 | "spyglass_astronomy.commands.find.constellation.fail" : "找不到名称为 \"%s\" 的星座", 84 | "spyglass_astronomy.commands.find.star.fail" : "找不到名称为 \"%s\" 的星辰", 85 | "spyglass_astronomy.commands.find.planet.fail" : "找不到名称为 \"%s\" 的行星或彗星", 86 | 87 | 88 | 89 | "spyglass_astronomy.commands.info.constellation.name" : "\n名称: %s", 90 | 91 | "spyglass_astronomy.commands.info.star.name" : "\n名称: %s", 92 | "spyglass_astronomy.commands.info.star.distance" : "\n距离: %s LY(光年)", 93 | 94 | "spyglass_astronomy.commands.info.planet.name" : "\n名称: %s", 95 | "spyglass_astronomy.commands.info.planet.type.terrestial" : "\n种类: 类地行星", 96 | "spyglass_astronomy.commands.info.planet.type.habitable" : "\n种类: 宜居行星", 97 | "spyglass_astronomy.commands.info.planet.type.oceanplanet" : "\n种类: 海洋行星", 98 | "spyglass_astronomy.commands.info.planet.type.iceplanet" : "\n种类: 冰质行星", 99 | "spyglass_astronomy.commands.info.planet.type.gasgiant" : "\n种类: 气态巨行星", 100 | "spyglass_astronomy.commands.info.planet.type.icegiant" : "\n种类: 冰态巨行星", 101 | "spyglass_astronomy.commands.info.planet.type.comet" : "\n种类: 彗星", 102 | 103 | "spyglass_astronomy.commands.info.thisworld.time" : "\n时间: %s", 104 | "spyglass_astronomy.commands.info.thisworld.moonphase" : "\n月相: ", 105 | 106 | "spyglass_astronomy.commands.info.solarsystem.planets" : "\n行星: ", 107 | "spyglass_astronomy.commands.info.solarsystem.comets" : "\n彗星: ", 108 | "spyglass_astronomy.commands.info.solarsystem.thisworld" : "\n此世界", 109 | "spyglass_astronomy.commands.info.solarsystem.named" : "\n%s", 110 | "spyglass_astronomy.commands.info.solarsystem.unknown" : "\n???", 111 | "spyglass_astronomy.commands.info.solarsystem.time" : "\n时间: %s.%s 天", 112 | 113 | "spyglass_astronomy.commands.info.visibility.angle" : "\n当前角度: %s %s", 114 | "spyglass_astronomy.commands.info.visibility.time.moonphase" : "\n可见期: ", 115 | "spyglass_astronomy.commands.info.visibility.time.date" : "\n可见期:一年中的 %s 天 (%s 天内)", 116 | "spyglass_astronomy.commands.info.visibility.time.always" : "\n可见期: 总是", 117 | 118 | "spyglass_astronomy.commands.info.orbit.period" : "\n周期: %s 天", 119 | "spyglass_astronomy.commands.info.orbit.resonance" : "\n轨道共振: %s 天", 120 | "spyglass_astronomy.commands.info.orbit.position" : "\n当前轨道位置: %s%%", 121 | "spyglass_astronomy.commands.info.orbit.distance" : "\n距离: %s AU(天文单位)", 122 | "spyglass_astronomy.commands.info.orbit.angle" : "\n当前角度: %s %s", 123 | "spyglass_astronomy.commands.info.orbit.eccentricity" : "\n轨道偏心率: %s", 124 | "spyglass_astronomy.commands.info.orbit.ascension": "\n升交点经度: %s", 125 | "spyglass_astronomy.commands.info.orbit.inclination" : "\n倾角: %s", 126 | 127 | "spyglass_astronomy.commands.info.starknowledge.toadept" : "\n绘制 %s 个星座以了解更多", 128 | "spyglass_astronomy.commands.info.starknowledge.toexpert" : "\n绘制 %s 个星座并且命名 %s 星辰以了解更多", 129 | "spyglass_astronomy.commands.info.starknowledge.tomaster" : "\n绘制 %s 个星座并且命名 %s 星辰以了解更多", 130 | "spyglass_astronomy.commands.info.orbitknowledge.toadept" : "\n命名 %s 颗行星和 %s 颗彗星以了解更多", 131 | "spyglass_astronomy.commands.info.orbitknowledge.toexpert" : "\n命名 %s 颗行星和 %s 颗彗星以了解更多", 132 | "spyglass_astronomy.commands.info.orbitknowledge.tomaster" : "\n命名 %s 颗行星和 %s 颗彗星以了解更多", 133 | 134 | "spyglass_astronomy.commands.info.moonphase.0" : "▉ 满月", 135 | "spyglass_astronomy.commands.info.moonphase.1" : "▜ 渐亏凸月", 136 | "spyglass_astronomy.commands.info.moonphase.2" : "▐ 下弦月", 137 | "spyglass_astronomy.commands.info.moonphase.3" : " ] 残月", 138 | "spyglass_astronomy.commands.info.moonphase.4" : "[] 新月", 139 | "spyglass_astronomy.commands.info.moonphase.5" : "[ 蛾眉月", 140 | "spyglass_astronomy.commands.info.moonphase.6" : "▌ 上弦月", 141 | "spyglass_astronomy.commands.info.moonphase.7" : "▛ 渐盈凸月", 142 | 143 | "spyglass_astronomy.commands.info.nothingselected" : "选择一个对象并使用/sga:info 以得到相关信息", 144 | 145 | 146 | 147 | "spyglass_astronomy.constellation.merge" : "新的连线合并了 \"%s\" 和 \"%s\" 两个星座", 148 | "spyglass_astronomy.constellation.split" : "删除线把星座 \"%s\" 拆分成两个", 149 | "spyglass_astronomy.constellation.remove" : "移除星座 \"%s\"", 150 | 151 | "spyglass_astronomy.prompt.name.constellation" : "使用/sga:name 命名这个星座!", 152 | "spyglass_astronomy.prompt.name.star" : "使用/sga:name 命名这个星辰!", 153 | "spyglass_astronomy.prompt.name.planet" : "使用/sga:name 命名这个行星!", 154 | "spyglass_astronomy.prompt.name.comet" : "使用/sga:name 命名这个彗星!", 155 | "spyglass_astronomy.prompt.unnamed.constellation": "未命名星座", 156 | "spyglass_astronomy.prompt.unnamed.constellationandstar": "未命名星座 | %s", 157 | "spyglass_astronomy.prompt.unnamed.star": "未命名星辰", 158 | "spyglass_astronomy.prompt.unnamed.planet": "未命名行星", 159 | "spyglass_astronomy.prompt.unnamed.comet": "未命名彗星", 160 | "spyglass_astronomy.prompt.constellation" : "%s", 161 | "spyglass_astronomy.prompt.constellationandstar" : "%s | %s", 162 | "spyglass_astronomy.prompt.star" : "%s", 163 | "spyglass_astronomy.prompt.planet" : "%s", 164 | "spyglass_astronomy.prompt.comet": "%s", 165 | 166 | "spyglass_astronomy.say" : "[天文学望远镜] ", 167 | "spyglass_astronomy.longsay" : "- [天文学望远镜] -" 168 | } -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/OrbitingBody.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.joml.Quaternionf; 6 | import org.joml.Vector3f; 7 | 8 | import net.minecraft.client.render.BufferBuilder; 9 | import net.minecraft.util.math.MathHelper; 10 | import net.minecraft.util.math.RotationAxis; 11 | 12 | public class OrbitingBody { 13 | public final Orbit orbit; 14 | private ArrayList moons; 15 | 16 | private final float size; 17 | private final float albedo; 18 | private final float rotationSpeed; 19 | public final boolean isPlanet; 20 | private final int decoration; 21 | private final int[] mainColor; 22 | private final int[] secondaryColor; 23 | public final OrbitingBodyType type; 24 | 25 | private float angle; 26 | private int currentAlpha; 27 | 28 | private Vector3f axis1; 29 | private Vector3f axis2; 30 | private Vector3f quad1vertex1; 31 | private Vector3f quad1vertex2; 32 | private Vector3f quad1vertex3; 33 | private Vector3f quad1vertex4; 34 | private Vector3f quad2vertex1; 35 | private Vector3f quad2vertex2; 36 | private Vector3f quad2vertex3; 37 | private Vector3f quad2vertex4; 38 | 39 | private Vector3f position; 40 | 41 | public String name; 42 | 43 | public static OrbitingBody selected; 44 | private boolean isSelected; 45 | 46 | public OrbitingBody(Orbit orbit, float size, float albedo, float rotationSpeed, boolean isPlanet, int decoration, int[] mainColor, int[] secondaryColor, OrbitingBodyType type) { 47 | this.orbit = orbit; 48 | this.size = size; 49 | this.albedo = albedo; 50 | this.rotationSpeed = rotationSpeed * 0.1f; 51 | this.isPlanet = isPlanet; 52 | this.decoration = decoration; 53 | this.mainColor = mainColor; 54 | this.secondaryColor = secondaryColor; 55 | this.type = type; 56 | } 57 | 58 | public void addMoon(OrbitingBody moon) { 59 | moons.add(moon); 60 | } 61 | 62 | public void update(int ticks, Vector3f referencePosition, Vector3f normalisedReferencePosition, Long day, float dayFraction) { 63 | angle = (angle+rotationSpeed)%360; 64 | 65 | position = orbit.getRotatedPositionAtGlobalTime(day, dayFraction, true); 66 | 67 | Vector3f similarityVector = new Vector3f(position); 68 | similarityVector.normalize(); 69 | float similarity = similarityVector.dot(normalisedReferencePosition); 70 | 71 | position.sub(referencePosition); 72 | float sqrDistance = SpyglassAstronomyClient.getSquaredDistance(position.x, position.y, position.z); 73 | float inverseSqrt = MathHelper.inverseSqrt(sqrDistance); 74 | position.mul(inverseSqrt); 75 | 76 | float distance = (1/inverseSqrt)/SpyglassAstronomyClient.earthOrbit.semiMajorAxis; 77 | 78 | float visibilityScale = Math.min(MathHelper.sqrt(distance),8); 79 | 80 | //this isn't needed to run every frame 81 | if ((ticks&7) == 0 || axis1 == null) { 82 | //it may seem a bit weird allowing dayFraction to be outside 0-1, but it doesn't matter 83 | axis1 = orbit.getRotatedPositionAtGlobalTime(day, dayFraction-(orbit.period/32), false); 84 | axis1.sub(referencePosition); 85 | axis1.normalize(); 86 | axis1.sub(position); 87 | axis1.normalize(); 88 | 89 | axis2 = new Vector3f(axis1); 90 | axis2.rotate(RotationAxis.of(position).rotationDegrees(90)); 91 | 92 | float sizeScale = MathHelper.clamp( 93 | (size/visibilityScale)*3, 94 | 0.25f,1.5f); 95 | 96 | axis1.mul(sizeScale); 97 | axis2.mul(sizeScale); 98 | } 99 | 100 | float heightScale = SpaceRenderingManager.getHeightScale(); 101 | float heightFactor = 1; 102 | 103 | float alphaRaw = Math.max((( 104 | Math.min( 105 | albedo*10*heightScale-visibilityScale+heightFactor, 106 | 1 107 | )+heightScale 108 | )/2 109 | ), 110 | 0 111 | ); 112 | if (similarity < -0.5) { 113 | float offsetSimilarity = 2*(similarity+0.5f); 114 | alphaRaw *= 1-offsetSimilarity*offsetSimilarity; 115 | } 116 | currentAlpha = (int)(alphaRaw*255); 117 | 118 | Quaternionf rotation = RotationAxis.of(position).rotationDegrees(angle); 119 | Vector3f rotatedAxis1 = new Vector3f(axis1); 120 | Vector3f rotatedAxis2 = new Vector3f(axis2); 121 | rotatedAxis1.rotate(rotation); 122 | rotatedAxis2.rotate(rotation); 123 | 124 | float x = position.x*100; 125 | float y = position.y*100; 126 | float z = position.z*100; 127 | quad1vertex1 = new Vector3f(x, y, z); 128 | quad1vertex2 = new Vector3f(x, y, z); 129 | quad1vertex3 = new Vector3f(x, y, z); 130 | quad1vertex4 = new Vector3f(x, y, z); 131 | quad1vertex1.sub(rotatedAxis2); 132 | quad1vertex2.sub(rotatedAxis1); 133 | quad1vertex3.add(rotatedAxis2); 134 | quad1vertex4.add(rotatedAxis1); 135 | 136 | if (isPlanet) { 137 | switch (decoration) { 138 | case 0 -> { 139 | //triangle half 140 | quad2vertex1 = new Vector3f(x, y, z); 141 | quad2vertex2 = new Vector3f(quad1vertex2); 142 | quad2vertex3 = new Vector3f(quad1vertex3); 143 | quad2vertex4 = new Vector3f(quad1vertex4); 144 | } 145 | case 1 -> { 146 | //ring 147 | float ringOut = 1.3f; 148 | float ringIn = 0.9f; 149 | Quaternionf slowOppositeRotation = RotationAxis.of(position).rotationDegrees(-angle / 2); 150 | Vector3f in1 = new Vector3f(axis1); 151 | in1.rotate(slowOppositeRotation); 152 | Vector3f out1 = new Vector3f(in1); 153 | Vector3f in2 = new Vector3f(axis2); 154 | in2.rotate(slowOppositeRotation); 155 | Vector3f out2 = new Vector3f(in2); 156 | in1.mul(ringIn); 157 | out1.mul(ringOut); 158 | in2.mul(ringIn); 159 | out2.mul(ringOut); 160 | quad2vertex1 = new Vector3f(x, y, z); 161 | quad2vertex2 = new Vector3f(x, y, z); 162 | quad2vertex3 = new Vector3f(x, y, z); 163 | quad2vertex4 = new Vector3f(x, y, z); 164 | quad2vertex1.sub(out2); 165 | quad2vertex1.sub(in1); 166 | quad2vertex2.sub(out1); 167 | quad2vertex2.sub(in2); 168 | quad2vertex3.add(out2); 169 | quad2vertex3.add(in1); 170 | quad2vertex4.add(out1); 171 | quad2vertex4.add(in2); 172 | } 173 | case 2, 3 -> { 174 | //quarter, quarter with point 175 | if (decoration == 2) quad2vertex1 = new Vector3f(x, y, z); 176 | else quad2vertex1 = new Vector3f(quad1vertex1); 177 | quad2vertex2 = new Vector3f(x, y, z); 178 | quad2vertex3 = new Vector3f(quad1vertex3); 179 | quad2vertex4 = new Vector3f(x, y, z); 180 | Vector3f offset1 = new Vector3f(rotatedAxis2); 181 | Vector3f offset2 = new Vector3f(rotatedAxis2); 182 | offset1.add(rotatedAxis1); 183 | offset2.sub(rotatedAxis1); 184 | offset1.mul(0.5f); 185 | offset2.mul(0.5f); 186 | quad2vertex2.add(offset2); 187 | quad2vertex4.add(offset1); 188 | } 189 | } 190 | } else { 191 | quad2vertex1 = new Vector3f(x, y, z); 192 | quad2vertex2 = new Vector3f(x, y, z); 193 | quad2vertex3 = new Vector3f(x, y, z); 194 | quad2vertex4 = new Vector3f(x, y, z); 195 | Vector3f trailEnd = new Vector3f(axis1); 196 | trailEnd.mul(6); 197 | quad2vertex1.add(trailEnd); 198 | quad2vertex2.add(trailEnd); 199 | quad2vertex3.add(axis1); 200 | quad2vertex4.add(axis1); 201 | 202 | Vector3f trailWidth = new Vector3f(axis2); 203 | trailWidth.mul(0.75f); 204 | 205 | if (decoration != 0) { 206 | quad2vertex1.add(trailWidth); 207 | quad2vertex2.sub(trailWidth); 208 | } 209 | if (decoration != 2) { 210 | quad2vertex3.sub(trailWidth); 211 | quad2vertex4.add(trailWidth); 212 | } 213 | } 214 | } 215 | 216 | public void setVertices(BufferBuilder bufferBuilder) { 217 | int colorMul = isSelected ? 1 : 0; 218 | int r1 = mainColor[0] >> colorMul; 219 | int g1 = mainColor[1]; 220 | int b1 = mainColor[2] >> colorMul; 221 | int r2 = secondaryColor[0] >> colorMul; 222 | int g2 = secondaryColor[1]; 223 | int b2 = secondaryColor[2] >> colorMul; 224 | int decorationAlpha = currentAlpha/3; 225 | 226 | bufferBuilder.vertex( 227 | quad1vertex1.x, 228 | quad1vertex1.y, 229 | quad1vertex1.z 230 | ).color(r1, g1, b1, currentAlpha); 231 | 232 | bufferBuilder.vertex( 233 | quad1vertex2.x, 234 | quad1vertex2.y, 235 | quad1vertex2.z 236 | ).color(r1, g1, b1, currentAlpha); 237 | 238 | bufferBuilder.vertex( 239 | quad1vertex3.x, 240 | quad1vertex3.y, 241 | quad1vertex3.z 242 | ).color(r1, g1, b1, currentAlpha); 243 | 244 | bufferBuilder.vertex( 245 | quad1vertex4.x, 246 | quad1vertex4.y, 247 | quad1vertex4.z 248 | ).color(r1, g1, b1, currentAlpha); 249 | 250 | bufferBuilder.vertex( 251 | quad2vertex1.x, 252 | quad2vertex1.y, 253 | quad2vertex1.z 254 | ).color(r2, g2, b2, isPlanet ? decorationAlpha : 0); 255 | 256 | bufferBuilder.vertex( 257 | quad2vertex2.x, 258 | quad2vertex2.y, 259 | quad2vertex2.z 260 | ).color(r2, g2, b2, isPlanet ? decorationAlpha : 0); 261 | 262 | bufferBuilder.vertex( 263 | quad2vertex3.x, 264 | quad2vertex3.y, 265 | quad2vertex3.z 266 | ).color(r2, g2, b2, decorationAlpha); 267 | 268 | bufferBuilder.vertex( 269 | quad2vertex4.x, 270 | quad2vertex4.y, 271 | quad2vertex4.z 272 | ).color(r2, g2, b2, decorationAlpha); 273 | } 274 | 275 | public Vector3f getPosition() { 276 | return position; 277 | } 278 | 279 | public float getCurrentNonTwinkledAlpha() { 280 | return ((float)currentAlpha)/255; 281 | } 282 | 283 | public void select() { 284 | Constellation.deselect(); 285 | Star.deselect(); 286 | if (selected != null) selected.isSelected = false; 287 | isSelected = true; 288 | selected = this; 289 | } 290 | 291 | public static void deselect() { 292 | if (selected != null) selected.isSelected = false; 293 | selected = null; 294 | } 295 | 296 | public boolean isUnnamed() { 297 | return name == null; 298 | } 299 | 300 | public enum OrbitingBodyType { 301 | //ordered roughly by required closeness to sun 302 | TERRESTIAL, 303 | HABITABLE, 304 | OCEANPLANET, 305 | ICEPLANET, 306 | GASGIANT, 307 | ICEGIANT, 308 | COMET 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/main/resources/assets/spyglass_astronomy/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "spyglass_astronomy.commands.admin.bypass.on" : "Knowledge Checks bypassed, run again to reset", 3 | "spyglass_astronomy.commands.admin.bypass.off" : "Knowledge Checks re-enabled", 4 | 5 | "spyglass_astronomy.commands.admin.changes.discard" : "Discarded %s unsaved Changes", 6 | "spyglass_astronomy.commands.admin.changes.discard.none" : "No unsaved Changes to Discard", 7 | "spyglass_astronomy.commands.admin.changes.save" : "Saved %s unsaved Changes", 8 | "spyglass_astronomy.commands.admin.changes.save.none" : "No unsaved Changes to Save", 9 | "spyglass_astronomy.commands.admin.changes.query" : "There have been %s Changes since last save", 10 | 11 | "spyglass_astronomy.commands.admin.constellations.remove" : "Removed Constellation \"%s\"", 12 | "spyglass_astronomy.commands.admin.constellations.remove.nothingselected" : "Select a Constellation or use /sga:admin constellations remove to Remove a Constellation", 13 | "spyglass_astronomy.commands.admin.constellations.remove.all" : "Removed all %s Constellations", 14 | "spyglass_astronomy.commands.admin.constellations.add" : "Added new Constellation \"%s\"", 15 | "spyglass_astronomy.commands.admin.constellations.add.fail.data" : "Cannot add new Constellation \"%s\": invalid data", 16 | "spyglass_astronomy.commands.admin.constellations.add.fail.stars" : "Cannot add new Constellation \"%s\": at least %s Stars are required, there are currently %s", 17 | "spyglass_astronomy.commands.admin.constellations.add.fail.collision" : "Cannot add new Constellation \"%s\": collision with \"%s\"", 18 | "spyglass_astronomy.commands.admin.constellations.add.edit" : "Changed existing Constellation \"%s\"", 19 | "spyglass_astronomy.commands.admin.constellations.generate" : "Created %s new Constellations", 20 | 21 | "spyglass_astronomy.commands.admin.seed.planet.set" : "Set Planet Seed to %s (was %s)", 22 | "spyglass_astronomy.commands.admin.seed.planet.query" : "The Planet Seed is %s", 23 | "spyglass_astronomy.commands.admin.seed.star.set" : "Set Star Seed to %s (was %s)", 24 | "spyglass_astronomy.commands.admin.seed.star.query" : "The Star Seed is %s", 25 | 26 | "spyglass_astronomy.commands.admin.starcount.set" : "Set Star Count to %s (was %s)", 27 | "spyglass_astronomy.commands.admin.starcount.set.invalidate" : "%s Constellations are no longer valid due to missing Stars - Returning the Stars will Recover as many Constellations as possible", 28 | "spyglass_astronomy.commands.admin.starcount.set.validate" : "%s Constellations are valid again and have been Recovered", 29 | "spyglass_astronomy.commands.admin.starcount.query" : "There are %s stars", 30 | 31 | "spyglass_astronomy.commands.admin.yearlength.set" : "Set Year Length to %s days (was %s days)", 32 | "spyglass_astronomy.commands.admin.yearlength.query" : "The Year Length is %s days", 33 | 34 | 35 | 36 | "spyglass_astronomy.commands.hide.all.hide" : "All hidden, run /sga:hide again to show", 37 | "spyglass_astronomy.commands.hide.all.show" : "All shown, run /sga:hide again to hide", 38 | "spyglass_astronomy.commands.hide.constellations.hide" : "Constellations hidden, run /sga:hide constellations again to show", 39 | "spyglass_astronomy.commands.hide.constellations.show" : "Constellations shown, run /sga:hide constellations again to hide", 40 | "spyglass_astronomy.commands.hide.stars.hide" : "Stars hidden, run /sga:hide stars again to show", 41 | "spyglass_astronomy.commands.hide.stars.show" : "Stars shown, run /sga:hide stars again to hide", 42 | "spyglass_astronomy.commands.hide.planets.hide" : "Planets and Comets hidden, run /sga:hide planets again to show", 43 | "spyglass_astronomy.commands.hide.planets.show" : "Planets and Comets shown, run /sga:hide planets again to hide", 44 | "spyglass_astronomy.commands.hide.vanillastars.hide" : "Vanilla Stars hidden, run /sga:hide vanillastars again to show", 45 | "spyglass_astronomy.commands.hide.vanillastars.show" : "Vanilla Stars shown, run /sga:hide vanillastars again to hide", 46 | "spyglass_astronomy.commands.hide.daytime.hide" : "All night-only, run /sga:hide daytime again to show all during day", 47 | "spyglass_astronomy.commands.hide.daytime.show" : "All shown during day, run /sga:hide daytime again for night-only", 48 | 49 | "spyglass_astronomy.commands.name.constellation" : "Named new Constellation \"%s\"", 50 | "spyglass_astronomy.commands.name.constellation.rename" : "Renamed Constellation \"%s\" to \"%s\"", 51 | "spyglass_astronomy.commands.name.constellation.fail" : "Not enough Constellations exist, try a number less than %s", 52 | "spyglass_astronomy.commands.name.constellation.fail.none" : "No Constellations exist, try creating some first", 53 | "spyglass_astronomy.commands.name.star" : "Named Star \"%s\"", 54 | "spyglass_astronomy.commands.name.star.rename" : "Renamed Star \"%s\" to \"%s\"", 55 | "spyglass_astronomy.commands.name.star.fail" : "Not enough Stars exist, are you using the correct starcount? (/sga:admin starcount query)", 56 | "spyglass_astronomy.commands.name.planet" : "Named Planet \"%s\"", 57 | "spyglass_astronomy.commands.name.planet.rename" : "Renamed Planet \"%s\" to \"%s\"", 58 | "spyglass_astronomy.commands.name.comet" : "Named Comet \"%s\"", 59 | "spyglass_astronomy.commands.name.comet.rename" : "Renamed Comet \"%s\" to \"%s\"", 60 | "spyglass_astronomy.commands.name.planet.fail" : "Not enough Planets or Comets exist, are you using the correct planet seed? (/sga:admin seed planet query)", 61 | "spyglass_astronomy.commands.name.nothingselected" : "Select an object to Name it", 62 | 63 | "spyglass_astronomy.commands.select.constellation" : "Selected Constellation \"%s\"", 64 | "spyglass_astronomy.commands.select.constellation.fail" : "Spyglass must be held to be able to select Constellations", 65 | "spyglass_astronomy.commands.select.star" : "Selected Star \"%s\"", 66 | "spyglass_astronomy.commands.select.star.fail" : "Spyglass must be held to be able to select Stars", 67 | "spyglass_astronomy.commands.select.planet" : "Selected Planet \"%s\"", 68 | "spyglass_astronomy.commands.select.planet.fail" : "Spyglass must be held to be able to select Planets", 69 | "spyglass_astronomy.commands.select.comet" : "Selected Comet \"%s\"", 70 | "spyglass_astronomy.commands.select.comet.fail" : "Spyglass must be held to be able to select Comets", 71 | 72 | "spyglass_astronomy.commands.share.click" : "[Click Here] ", 73 | "spyglass_astronomy.commands.share.constellation" : "to Share Constellation \"%s\"", 74 | "spyglass_astronomy.commands.share.star" : "to Share Star \"%s\"", 75 | "spyglass_astronomy.commands.share.planet" : "to Share Planet \"%s\"", 76 | "spyglass_astronomy.commands.share.comet" : "to Share Comet \"%s\"", 77 | "spyglass_astronomy.commands.share.receive.constellation" : "to add Constellation \"%s\"", 78 | "spyglass_astronomy.commands.share.receive.star" : "to add Star \"%s\"", 79 | "spyglass_astronomy.commands.share.receive.planet" : "to add Planet \"%s\"", 80 | "spyglass_astronomy.commands.share.receive.comet" : "to add Comet \"%s\"", 81 | "spyglass_astronomy.commands.share.nothingselected" : "Select an object or use /sga:share to Share", 82 | 83 | "spyglass_astronomy.commands.find.constellation.fail" : "No Constellation with name \"%s\" could be found", 84 | "spyglass_astronomy.commands.find.star.fail" : "No Star with name \"%s\" could be found", 85 | "spyglass_astronomy.commands.find.planet.fail" : "No Planet or Comet with name \"%s\" could be found", 86 | 87 | 88 | 89 | "spyglass_astronomy.commands.info.constellation.name" : "\nName: %s", 90 | 91 | "spyglass_astronomy.commands.info.star.name" : "\nName: %s", 92 | "spyglass_astronomy.commands.info.star.distance" : "\nDistance: %s LY", 93 | 94 | "spyglass_astronomy.commands.info.planet.name" : "\nName: %s", 95 | "spyglass_astronomy.commands.info.planet.type.terrestial" : "\nType: Terrestial", 96 | "spyglass_astronomy.commands.info.planet.type.habitable" : "\nType: Habitable", 97 | "spyglass_astronomy.commands.info.planet.type.oceanplanet" : "\nType: Ocean Planet", 98 | "spyglass_astronomy.commands.info.planet.type.iceplanet" : "\nType: Ice Planet", 99 | "spyglass_astronomy.commands.info.planet.type.gasgiant" : "\nType: Gas Giant", 100 | "spyglass_astronomy.commands.info.planet.type.icegiant" : "\nType: Ice Giant", 101 | "spyglass_astronomy.commands.info.planet.type.comet" : "\nType: Comet", 102 | 103 | "spyglass_astronomy.commands.info.thisworld.time" : "\nTime: %s", 104 | "spyglass_astronomy.commands.info.thisworld.moonphase" : "\nMoon Phase: ", 105 | 106 | "spyglass_astronomy.commands.info.solarsystem.planets" : "\nPlanets: ", 107 | "spyglass_astronomy.commands.info.solarsystem.comets" : "\nComets: ", 108 | "spyglass_astronomy.commands.info.solarsystem.thisworld" : "\nThis World", 109 | "spyglass_astronomy.commands.info.solarsystem.named" : "\n%s", 110 | "spyglass_astronomy.commands.info.solarsystem.unknown" : "\n???", 111 | "spyglass_astronomy.commands.info.solarsystem.time" : "\nTime: %s.%s Days", 112 | 113 | "spyglass_astronomy.commands.info.visibility.angle" : "\nCurrent Angle: %s %s", 114 | "spyglass_astronomy.commands.info.visibility.time.moonphase" : "\nMost Visible During: ", 115 | "spyglass_astronomy.commands.info.visibility.time.date" : "\nMost Visible During: %s Days into a year (In %s Days)", 116 | "spyglass_astronomy.commands.info.visibility.time.always" : "\nMost Visible During: Always", 117 | 118 | "spyglass_astronomy.commands.info.orbit.period" : "\nPeriod: %s Days", 119 | "spyglass_astronomy.commands.info.orbit.resonance" : "\nResonance: %s Days", 120 | "spyglass_astronomy.commands.info.orbit.position" : "\nCurrent position in orbit: %s%%", 121 | "spyglass_astronomy.commands.info.orbit.distance" : "\nDistance: %s AU", 122 | "spyglass_astronomy.commands.info.orbit.angle" : "\nCurrent Angle: %s %s", 123 | "spyglass_astronomy.commands.info.orbit.eccentricity" : "\nEccentricity: %s", 124 | "spyglass_astronomy.commands.info.orbit.ascension" : "\nAscension: %s", 125 | "spyglass_astronomy.commands.info.orbit.inclination" : "\nInclination: %s", 126 | 127 | "spyglass_astronomy.commands.info.starknowledge.toadept" : "\nDraw %s Constellations to learn more", 128 | "spyglass_astronomy.commands.info.starknowledge.toexpert" : "\nDraw %s Constellations and Name %s Stars to learn more", 129 | "spyglass_astronomy.commands.info.starknowledge.tomaster" : "\nDraw %s Constellations and Name %s Stars to learn more", 130 | "spyglass_astronomy.commands.info.orbitknowledge.toadept" : "\nName %s Planets or %s Comet to learn more", 131 | "spyglass_astronomy.commands.info.orbitknowledge.toexpert" : "\nName %s Planets and %s Comets to learn more", 132 | "spyglass_astronomy.commands.info.orbitknowledge.tomaster" : "\nName %s Planets and %s Comets to learn more", 133 | 134 | "spyglass_astronomy.commands.info.moonphase.0" : "▉ Full Moon", 135 | "spyglass_astronomy.commands.info.moonphase.1" : "▜ Waning Gibbous", 136 | "spyglass_astronomy.commands.info.moonphase.2" : "▐ Last Quarter", 137 | "spyglass_astronomy.commands.info.moonphase.3" : " ] Waning Crescent", 138 | "spyglass_astronomy.commands.info.moonphase.4" : "[] New Moon", 139 | "spyglass_astronomy.commands.info.moonphase.5" : "[ Waxing Crescent", 140 | "spyglass_astronomy.commands.info.moonphase.6" : "▌ First Quarter", 141 | "spyglass_astronomy.commands.info.moonphase.7" : "▛ Waxing Gibbous", 142 | 143 | "spyglass_astronomy.commands.info.nothingselected" : "Select an object or use /sga:info to get Information on Something", 144 | 145 | 146 | 147 | "spyglass_astronomy.constellation.merge" : "New Line merged two Constellations \"%s\" and \"%s\"", 148 | "spyglass_astronomy.constellation.split" : "Removing Line split Constellation \"%s\" into two", 149 | "spyglass_astronomy.constellation.remove" : "Removed Constellation \"%s\"", 150 | 151 | "spyglass_astronomy.prompt.name.constellation" : "Use /sga:name to name this Constellation!", 152 | "spyglass_astronomy.prompt.name.star" : "Use /sga:name to name this Star!", 153 | "spyglass_astronomy.prompt.name.planet" : "Use /sga:name to name this Planet!", 154 | "spyglass_astronomy.prompt.name.comet" : "Use /sga:name to name this Comet!", 155 | "spyglass_astronomy.prompt.unnamed.constellation" : "Unnamed Constellation", 156 | "spyglass_astronomy.prompt.unnamed.constellationandstar" : "Unnamed Constellation | %s", 157 | "spyglass_astronomy.prompt.unnamed.star" : "Unnamed Star", 158 | "spyglass_astronomy.prompt.unnamed.planet" : "Unnamed Planet", 159 | "spyglass_astronomy.prompt.unnamed.comet" : "Unnamed Comet", 160 | "spyglass_astronomy.prompt.constellation" : "%s", 161 | "spyglass_astronomy.prompt.constellationandstar" : "%s | %s", 162 | "spyglass_astronomy.prompt.star" : "%s", 163 | "spyglass_astronomy.prompt.planet" : "%s", 164 | "spyglass_astronomy.prompt.comet" : "%s", 165 | 166 | "spyglass_astronomy.say" : "[Spyglass Astronomy] ", 167 | "spyglass_astronomy.longsay" : "- [Spyglass Astronomy] -" 168 | } -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/admin_subcommands/ConstellationsCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands.admin_subcommands; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.tree.LiteralCommandNode; 5 | import com.nettakrim.spyglass_astronomy.*; 6 | import com.nettakrim.spyglass_astronomy.commands.SpyglassAstronomyCommands; 7 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 8 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 9 | import net.minecraft.command.argument.MessageArgumentType; 10 | import net.minecraft.util.math.MathHelper; 11 | import net.minecraft.util.math.random.Random; 12 | import org.joml.Vector3f; 13 | 14 | public class ConstellationsCommand { 15 | public static LiteralCommandNode getCommandNode() { 16 | LiteralCommandNode constellationsNode = ClientCommandManager 17 | .literal("constellations") 18 | .build(); 19 | 20 | LiteralCommandNode addNode = ClientCommandManager 21 | .literal("add") 22 | .then( 23 | ClientCommandManager.argument("data", MessageArgumentType.message()) 24 | .executes(ConstellationsCommand::addConstellation) 25 | ) 26 | .build(); 27 | 28 | LiteralCommandNode removeNode = ClientCommandManager 29 | .literal("remove") 30 | .executes(ConstellationsCommand::removeSelectedConstellation) 31 | .then( 32 | ClientCommandManager.argument("name", MessageArgumentType.message()) 33 | .suggests(SpyglassAstronomyCommands.constellations) 34 | .executes(ConstellationsCommand::removeConstellation) 35 | ) 36 | .build(); 37 | 38 | LiteralCommandNode removeAllNode = ClientCommandManager 39 | .literal("removeall") 40 | .executes(ConstellationsCommand::removeAllConstellations) 41 | .build(); 42 | 43 | LiteralCommandNode generateNode = ClientCommandManager 44 | .literal("generate") 45 | .executes(ConstellationsCommand::generateConstellations) 46 | .build(); 47 | 48 | constellationsNode.addChild(addNode); 49 | constellationsNode.addChild(removeNode); 50 | constellationsNode.addChild(removeAllNode); 51 | constellationsNode.addChild(generateNode); 52 | return constellationsNode; 53 | } 54 | 55 | private static int addConstellation(CommandContext context) { 56 | String dataRaw = SpyglassAstronomyCommands.getMessageText(context,"data"); 57 | int index = dataRaw.indexOf(' '); 58 | if (index == -1) { 59 | SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.data", dataRaw); 60 | return -1; 61 | } 62 | Constellation constellation = SpaceDataManager.decodeConstellation(null, dataRaw.substring(index+1), dataRaw.substring(0, index)); 63 | if (constellation.getLines().size() == 0) { 64 | SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.data", constellation.name); 65 | return -1; 66 | } 67 | 68 | int max = 0; 69 | for (StarLine line : constellation.getLines()) { 70 | int star = line.getOtherStar(-1); 71 | max = Math.max(Math.max(line.getOtherStar(star), star), max); 72 | } 73 | if (max >= SpyglassAstronomyClient.getStarCount()) { 74 | if (max < 4096) { 75 | SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.stars", constellation.name, max, SpyglassAstronomyClient.getStarCount()); 76 | } else { 77 | SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.data", constellation.name); 78 | } 79 | return -1; 80 | } 81 | 82 | return addConstellation(constellation, true, true); 83 | } 84 | 85 | public static int addConstellation(Constellation constellation, boolean select, boolean sayFeedback) { 86 | Constellation potentialMatch = null; 87 | for (Constellation targetConstellation : SpyglassAstronomyClient.constellations) { 88 | boolean intersects = false; 89 | boolean isDifferent = false; 90 | for (StarLine line : constellation.getLines()) { 91 | if (targetConstellation.lineIntersects(line)) { 92 | intersects = true; 93 | } else { 94 | isDifferent = true; 95 | } 96 | if (intersects && targetConstellation.hasNoMatchingLine(line)) { 97 | isDifferent = true; 98 | break; 99 | } 100 | } 101 | 102 | if (intersects) { 103 | if (!isDifferent) { 104 | for (StarLine line : targetConstellation.getLines()) { 105 | if (constellation.hasNoMatchingLine(line)) { 106 | isDifferent = true; 107 | break; 108 | } 109 | } 110 | } 111 | 112 | if (isDifferent) { 113 | if (potentialMatch == null && !constellation.isUnnamed() && constellation.name.equals(targetConstellation.name)) { 114 | potentialMatch = targetConstellation; 115 | } else { 116 | if (sayFeedback) SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.collision", constellation.name, targetConstellation.name); 117 | if (select) targetConstellation.select(); 118 | return -1; 119 | } 120 | } else { 121 | if (constellation.isUnnamed() || constellation.name.equals(targetConstellation.name)) { 122 | if (sayFeedback) SpyglassAstronomyClient.say("commands.admin.constellations.add.fail.collision", constellation.name, targetConstellation.name); 123 | if (select) targetConstellation.select(); 124 | return -1; 125 | } else { 126 | if (sayFeedback) SpyglassAstronomyClient.say("commands.name.constellation.rename", targetConstellation.name, constellation.name); 127 | targetConstellation.name = constellation.name; 128 | if (select) targetConstellation.select(); 129 | SpaceDataManager.makeChange(); 130 | return 1; 131 | } 132 | } 133 | } 134 | } 135 | 136 | if (potentialMatch == null) { 137 | if (sayFeedback) SpyglassAstronomyClient.say("commands.admin.constellations.add", constellation.name); 138 | } else { 139 | if (sayFeedback) SpyglassAstronomyClient.say("commands.admin.constellations.add.edit", potentialMatch.name); 140 | clearConnections(potentialMatch); 141 | SpyglassAstronomyClient.constellations.remove(potentialMatch); 142 | } 143 | 144 | if (select) constellation.select(); 145 | constellation.initaliseStarLines(); 146 | SpyglassAstronomyClient.constellations.add(constellation); 147 | 148 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 149 | SpaceDataManager.makeChange(); 150 | return 1; 151 | } 152 | 153 | private static int removeConstellation(CommandContext context) { 154 | Constellation constellation = SpyglassAstronomyCommands.getConstellation(context); 155 | return removeConstellation(constellation); 156 | } 157 | 158 | private static int removeSelectedConstellation(CommandContext context) { 159 | Constellation constellation = Constellation.selected; 160 | Constellation.deselect(); 161 | return removeConstellation(constellation); 162 | } 163 | 164 | private static int removeConstellation(Constellation constellation) { 165 | if (constellation == null) { 166 | SpyglassAstronomyClient.say("commands.admin.constellations.remove.nothingselected"); 167 | return -1; 168 | } 169 | SpyglassAstronomyClient.say("commands.admin.constellations.remove", constellation.name); 170 | clearConnections(constellation); 171 | SpyglassAstronomyClient.constellations.remove(constellation); 172 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 173 | SpaceDataManager.makeChange(); 174 | return 1; 175 | } 176 | 177 | private static int removeAllConstellations(CommandContext context) { 178 | SpyglassAstronomyClient.say("commands.admin.constellations.remove.all", SpyglassAstronomyClient.constellations.size()); 179 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 180 | clearConnections(constellation); 181 | } 182 | SpyglassAstronomyClient.constellations.clear(); 183 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 184 | SpaceDataManager.makeChange(); 185 | return 1; 186 | } 187 | 188 | private static int generateConstellations(CommandContext context) { 189 | Random random = Random.create(SpyglassAstronomyClient.spaceDataManager.getStarSeed()); 190 | 191 | int constellations = random.nextBetween(15,20); 192 | int spawned = 0; 193 | //https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere 194 | float phi = MathHelper.PI * (MathHelper.sqrt(5f) - 1f); 195 | for (int i = 0; i < constellations; i++) { 196 | float x = 1 - (i / (float)(constellations - 1)) * 2; 197 | float radius = MathHelper.sqrt(1 - x * x); 198 | 199 | float theta = phi * i; 200 | 201 | float y = MathHelper.cos(theta) * radius; 202 | float z = MathHelper.sin(theta) * radius; 203 | 204 | Vector3f position = new Vector3f(x, y, z); 205 | position.add((random.nextFloat()*0.3f)-0.15f, (random.nextFloat()*0.3f)-0.15f, (random.nextFloat()*0.3f)-0.15f); 206 | position.normalize(); 207 | 208 | if (createRandomConstellation(random, position)) { 209 | spawned++; 210 | } 211 | } 212 | 213 | SpyglassAstronomyClient.say("commands.admin.constellations.generate", Integer.toString(spawned)); 214 | SpyglassAstronomyClient.spaceRenderingManager.scheduleConstellationsUpdate(); 215 | SpaceDataManager.makeChange(); 216 | return 1; 217 | } 218 | 219 | private static boolean createRandomConstellation(Random random, Vector3f location) { 220 | Star lastStar = null; 221 | Vector3f lastPosition = location; 222 | int lines = 0; 223 | int maxLines = random.nextBetween(4,8); 224 | int maxConnections = random.nextBetween(3,5); 225 | float maxAngle = maxLines <= 5 ? 0.98f : (maxConnections <= 3 ? 0.975f : 0.97f); 226 | Constellation constellation = new Constellation(); 227 | while (lines < maxLines) { 228 | for (Star star : SpyglassAstronomyClient.stars) { 229 | if (star.getAlpha() < random.nextFloat()*2) continue; 230 | Vector3f starPos = star.getPositionAsVector3f(); 231 | 232 | if (starPos.distanceSquared(lastPosition) > 0.1f*(star.getAlpha()+0.5f) || starPos.distanceSquared(location) > 0.2f) continue; 233 | if (lastStar != null) { 234 | StarLine starLine = new StarLine(star.index, lastStar.index, false); 235 | Vector3f direction = new Vector3f(starPos).sub(lastPosition); 236 | 237 | boolean failedCheck = false; 238 | for (StarLine line : constellation.getLines()) { 239 | if (line.intersects(star.index, lastStar.index)) { 240 | Star[] stars = line.getStars(); 241 | if (MathHelper.abs(direction.angleCos(stars[0].getPositionAsVector3f().sub(stars[1].getPositionAsVector3f()))) > maxAngle) { 242 | failedCheck = true; 243 | break; 244 | } 245 | } 246 | } 247 | if (failedCheck) continue; 248 | constellation.addLine(starLine); 249 | lines++; 250 | if (lines >= maxLines) break; 251 | } 252 | int connections = 0; 253 | for (StarLine line : constellation.getLines()) { 254 | if (line.hasStar(star.index)) connections++; 255 | } 256 | if (connections >= maxConnections || random.nextFloat() * star.getAlpha() > 0.25f) { 257 | lastStar = star; 258 | lastPosition = starPos; 259 | } 260 | } 261 | } 262 | 263 | return addConstellation(constellation, false, false) == 1; 264 | } 265 | 266 | private static void clearConnections(Constellation constellation) { 267 | for (StarLine line : constellation.getLines()) { 268 | for (Star star : line.getStars()) { 269 | star.clearAllConnections(); 270 | } 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/SpaceDataManager.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.nio.ByteBuffer; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.ArrayList; 10 | import java.util.Base64; 11 | import java.util.Optional; 12 | import java.util.Scanner; 13 | import java.util.Base64.Decoder; 14 | import java.util.Base64.Encoder; 15 | 16 | import com.nettakrim.spyglass_astronomy.mixin.BiomeAccessAccessor; 17 | 18 | import com.nettakrim.spyglass_astronomy.mixin.ClientWorldAccessor; 19 | import net.minecraft.client.MinecraftClient; 20 | import net.minecraft.client.network.ClientPlayNetworkHandler; 21 | import net.minecraft.client.network.ServerInfo; 22 | import net.minecraft.client.world.ClientWorld; 23 | import net.minecraft.server.integrated.IntegratedServer; 24 | import net.minecraft.util.WorldSavePath; 25 | 26 | public class SpaceDataManager { 27 | public static final int SAVE_FORMAT = 1; 28 | 29 | private long starSeed; 30 | private long planetSeed; 31 | private float yearLength; 32 | 33 | private final File data; 34 | private final Path storagePath; 35 | private final String fileName; 36 | private final boolean isWorldFolder; 37 | 38 | public ArrayList starDatas; 39 | public ArrayList orbitingBodyDatas; 40 | 41 | private int changesMade; 42 | 43 | public SpaceDataManager(ClientWorld world) { 44 | //https://github.com/Johni0702/bobby/blob/d2024a2d63c63d0bccf2eafcab17dd7bf9d26710/src/main/java/de/johni0702/minecraft/bobby/FakeChunkManager.java#L86 45 | long seedHash = ((BiomeAccessAccessor) world.getBiomeAccess()).getSeed(); 46 | boolean useDefault = true; 47 | Optional localPath = getLocalStorage(); 48 | this.isWorldFolder = localPath.isPresent(); 49 | storagePath = localPath.orElse(getGlobalStorage(world)); 50 | 51 | fileName = storagePath +"/"+seedHash + ".txt"; 52 | data = new File(fileName); 53 | 54 | tryMigrateOldData(); 55 | if (data.exists()) { 56 | useDefault = !loadData(); 57 | } 58 | 59 | if (useDefault) { 60 | starSeed = seedHash; 61 | planetSeed = seedHash; 62 | yearLength = 8f; 63 | } 64 | } 65 | 66 | static public Path getGlobalStorage(ClientWorld world){ 67 | return SpyglassAstronomyClient.client.runDirectory.toPath() 68 | .resolve(".spyglass_astronomy") 69 | .resolve(getCurrentWorldOrServerName(world).replaceAll("[\\\\/:*?\"<>|]", "_")) 70 | ; 71 | } 72 | 73 | static public Optional getLocalStorage(){ 74 | IntegratedServer localServer = MinecraftClient.getInstance().getServer(); 75 | if (localServer == null) 76 | return Optional.empty(); 77 | 78 | return Optional.of( 79 | localServer.getSavePath(WorldSavePath.ROOT) 80 | .resolve("data") 81 | .resolve("spyglass_astronomy") 82 | ); 83 | } 84 | 85 | public void tryMigrateOldData() { 86 | if (data.exists()) return; 87 | 88 | Path oldFile = getGlobalStorage(null).resolve(data.getName()); 89 | 90 | if (Files.exists(oldFile)) { 91 | try { 92 | Files.createDirectories(storagePath); 93 | Files.copy(oldFile, data.toPath()); 94 | SpyglassAstronomyClient.LOGGER.info(isWorldFolder ? "Migrated old astronomy data into the world's folder." : "Migrated old astronomy data to the correct server folder."); 95 | } 96 | catch (IOException e){ 97 | SpyglassAstronomyClient.LOGGER.error("Failed to migrate old data: {}", e); 98 | } 99 | } 100 | } 101 | 102 | public boolean loadData() { 103 | try { 104 | data.createNewFile(); 105 | Scanner scanner = new Scanner(data); 106 | int stage = 0; 107 | Decoder decoder = Base64.getDecoder(); 108 | int starIndex = 0; 109 | starDatas = new ArrayList<>(); 110 | orbitingBodyDatas = new ArrayList<>(); 111 | changesMade = 0; 112 | boolean useDefault = false; 113 | while (scanner.hasNextLine()) { 114 | String s = scanner.nextLine(); 115 | if (s.equals("---")) { 116 | stage++; 117 | continue; 118 | } 119 | try { 120 | switch (stage) { 121 | case 0 -> { 122 | int format = Integer.parseInt(s.replace("Spyglass Astronomy - Format: ", "")); 123 | if (format == 0) useDefault = true; 124 | } 125 | case 1 -> { 126 | String[] seeds = s.split(" "); 127 | if (seeds.length == 1) { 128 | starSeed = Long.parseLong(s); 129 | planetSeed = starSeed; 130 | } else { 131 | starSeed = Long.parseLong(seeds[0]); 132 | planetSeed = Long.parseLong(seeds[1]); 133 | } 134 | } 135 | case 2 -> { 136 | String[] constellationParts = s.split(" \\| "); 137 | SpyglassAstronomyClient.constellations.add(decodeConstellation(decoder, constellationParts[0], constellationParts[1])); 138 | } 139 | case 3 -> { 140 | int starSplit = s.indexOf(' '); 141 | starIndex += Integer.parseInt(s.substring(0, starSplit)); 142 | String starName = s.substring(starSplit + 1); 143 | starDatas.add(new StarData(starIndex, starName)); 144 | } 145 | case 4 -> { 146 | int orbitingBodySplit = s.indexOf(' '); 147 | int orbitingBodyIndex = Integer.parseInt(s.substring(0, orbitingBodySplit)); 148 | String orbitingBodyName = s.substring(orbitingBodySplit + 1); 149 | orbitingBodyDatas.add(new OrbitingBodyData(orbitingBodyIndex, orbitingBodyName)); 150 | } 151 | case 5 -> { 152 | String[] parts = s.split(" "); 153 | SpyglassAstronomyClient.setStarCount(Integer.parseInt(parts[0])); 154 | if (parts.length > 1) setYearLength(Float.parseFloat(parts[1])); 155 | else yearLength = 8; 156 | } 157 | } 158 | } catch (Exception e) { 159 | SpyglassAstronomyClient.LOGGER.info("Failed to load line in stage "+stage); 160 | } 161 | } 162 | scanner.close(); 163 | return !useDefault; 164 | } catch (IOException e) { 165 | SpyglassAstronomyClient.LOGGER.info("Failed to load data"); 166 | } 167 | return false; 168 | } 169 | 170 | public void saveData() { 171 | if (changesMade == 0) return; 172 | try { 173 | if (!data.exists()) { 174 | Files.createDirectories(storagePath); 175 | } 176 | FileWriter writer = new FileWriter(data); 177 | StringBuilder s = new StringBuilder("Spyglass Astronomy - Format: "+SAVE_FORMAT); 178 | s.append("\n---\n"); 179 | s.append(starSeed); 180 | if (planetSeed != starSeed) { 181 | s.append(' '); 182 | s.append(planetSeed); 183 | } 184 | s.append("\n---"); 185 | Encoder encoder = Base64.getEncoder(); 186 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 187 | s.append('\n'); 188 | s.append(encodeConstellation(encoder, constellation)); 189 | } 190 | s.append("\n---"); 191 | int lastIndex = 0; 192 | for (Star star : SpyglassAstronomyClient.stars) { 193 | if (!star.isUnnamed()) { 194 | s.append('\n'); 195 | s.append(star.index - lastIndex).append(" ").append(star.name); 196 | lastIndex = star.index; 197 | } 198 | } 199 | s.append("\n---"); 200 | int index = 0; 201 | for (OrbitingBody orbitingBody : SpyglassAstronomyClient.orbitingBodies) { 202 | if (!orbitingBody.isUnnamed()) { 203 | s.append('\n'); 204 | s.append(index).append(" ").append(orbitingBody.name); 205 | } 206 | index++; 207 | } 208 | s.append("\n---\n"); 209 | s.append(SpyglassAstronomyClient.getStarCount()); 210 | s.append(" "); 211 | s.append(yearLength); 212 | s.append("\n---"); 213 | 214 | writer.write(s.toString()); 215 | writer.close(); 216 | changesMade = 0; 217 | } catch (IOException e) { 218 | SpyglassAstronomyClient.LOGGER.info("Failed to save data"); 219 | } 220 | } 221 | 222 | public static String encodeConstellation(Encoder encoder, Constellation constellation) { 223 | if (encoder == null) { 224 | encoder = Base64.getEncoder(); 225 | } 226 | StringBuilder encodedConstellation = new StringBuilder(constellation.name + " | "); 227 | for (StarLine line : constellation.getLines()) { 228 | encodedConstellation.append(encodeStarLine(encoder, line.getStars())); 229 | } 230 | return encodedConstellation.toString(); 231 | } 232 | 233 | private static String encodeStarLine(Encoder encoder, Star[] stars) { 234 | int starA = stars[0].index; 235 | int starB = stars[1].index; 236 | int combined = starA + (starB << 12); 237 | ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES); 238 | bb.putInt(combined); 239 | byte[] array = bb.array(); 240 | return encoder.encodeToString(array).substring(1,6); 241 | } 242 | 243 | public static Constellation decodeConstellation(Decoder decoder, String name, String lines) { 244 | if (decoder == null) { 245 | decoder = Base64.getDecoder(); 246 | } 247 | Constellation constellation = new Constellation(); 248 | constellation.name = name; 249 | int continueIf = lines.length()-5; 250 | for (int x = 0; x <= continueIf; x+=5) { 251 | constellation.addLine(decodeStarLine(decoder, lines.substring(x, x+5))); 252 | } 253 | return constellation; 254 | } 255 | 256 | private static StarLine decodeStarLine(Decoder decoder, String s) { 257 | byte[] array = decoder.decode("A"+s+"=="); 258 | ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES); 259 | bb.put(array); 260 | bb.rewind(); 261 | int combined = bb.getInt(); 262 | int starB = combined >> 12; 263 | int starA = combined - (starB << 12); 264 | return new StarLine(starA, starB, false); 265 | } 266 | 267 | public long getStarSeed() { 268 | return starSeed; 269 | } 270 | 271 | public long getPlanetSeed() { 272 | return planetSeed; 273 | } 274 | 275 | public void setStarSeed(long starSeed) { 276 | this.starSeed = starSeed; 277 | } 278 | 279 | public void setPlanetSeed(long planetSeed) { 280 | this.planetSeed = planetSeed; 281 | } 282 | 283 | public float getYearLength() { 284 | return yearLength; 285 | } 286 | 287 | public void setYearLength(float yearLength) { 288 | this.yearLength = yearLength; 289 | } 290 | 291 | private static String getCurrentWorldOrServerName(ClientWorld world) { 292 | // https://github.com/Johni0702/bobby/blob/master/src/main/java/de/johni0702/minecraft/bobby/FakeChunkManager.java#L357 293 | IntegratedServer integratedServer = SpyglassAstronomyClient.client.getServer(); 294 | if (integratedServer != null) { 295 | return integratedServer.getSaveProperties().getLevelName(); 296 | } 297 | 298 | if (world != null) { 299 | ClientPlayNetworkHandler clientPlayNetworkHandler = ((ClientWorldAccessor)world).getNetworkHandler(); 300 | if (clientPlayNetworkHandler != null) { 301 | ServerInfo serverInfo = clientPlayNetworkHandler.getServerInfo(); 302 | if (serverInfo != null) { 303 | if (serverInfo.isRealm()) { 304 | return "realm"; 305 | } 306 | return serverInfo.address.replace(':', '_'); 307 | } 308 | } 309 | } 310 | 311 | return "unknown"; 312 | } 313 | 314 | public void loadStarDatas() { 315 | if (starDatas == null) return; 316 | for (StarData starData : starDatas) { 317 | SpyglassAstronomyClient.stars.get(starData.index).name = starData.name; 318 | } 319 | starDatas = null; 320 | } 321 | 322 | public void loadOrbitingBodyDatas() { 323 | if (orbitingBodyDatas == null) return; 324 | for (OrbitingBodyData orbitingBodyData : orbitingBodyDatas) { 325 | SpyglassAstronomyClient.orbitingBodies.get(orbitingBodyData.index).name = orbitingBodyData.name; 326 | } 327 | orbitingBodyDatas = null; 328 | } 329 | 330 | public static void makeChange() { 331 | SpyglassAstronomyClient.spaceDataManager.changesMade++; 332 | SpyglassAstronomyClient.updateKnowledge(); 333 | } 334 | 335 | public static int getChanges() { 336 | return SpyglassAstronomyClient.spaceDataManager.changesMade; 337 | } 338 | 339 | public record StarData (int index, String name) {} 340 | 341 | public record OrbitingBodyData (int index, String name) {} 342 | } 343 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/SpaceRenderingManager.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy; 2 | 3 | import com.mojang.blaze3d.buffers.GpuBuffer; 4 | import com.mojang.blaze3d.buffers.GpuBufferSlice; 5 | import com.mojang.blaze3d.pipeline.BlendFunction; 6 | import com.mojang.blaze3d.pipeline.RenderPipeline; 7 | import com.mojang.blaze3d.systems.RenderPass; 8 | import com.mojang.blaze3d.textures.GpuTextureView; 9 | import com.mojang.blaze3d.vertex.VertexFormat; 10 | import net.minecraft.client.MinecraftClient; 11 | import net.minecraft.client.gl.RenderPipelines; 12 | import net.minecraft.client.network.ClientPlayerEntity; 13 | import net.minecraft.client.render.*; 14 | 15 | import net.minecraft.client.render.state.SkyRenderState; 16 | import net.minecraft.client.util.math.MatrixStack; 17 | import net.minecraft.util.math.MathHelper; 18 | import net.minecraft.util.math.RotationAxis; 19 | 20 | import org.joml.Matrix4f; 21 | import org.joml.Matrix4fStack; 22 | import org.joml.Vector3f; 23 | 24 | import com.mojang.blaze3d.systems.RenderSystem; 25 | import org.joml.Vector4f; 26 | 27 | import java.io.File; 28 | import java.io.FileWriter; 29 | import java.io.IOException; 30 | import java.lang.reflect.Method; 31 | import java.nio.file.Files; 32 | import java.nio.file.Path; 33 | import java.util.OptionalDouble; 34 | import java.util.OptionalInt; 35 | import java.util.Scanner; 36 | 37 | public class SpaceRenderingManager { 38 | private final RenderSystem.ShapeIndexBuffer indexBuffer; 39 | 40 | private GpuBuffer starsBuffer; 41 | private int starsCount = 0; 42 | 43 | private GpuBuffer constellationsBuffer; 44 | private int constellationsCount = 0; 45 | 46 | private GpuBuffer drawingBuffer; 47 | private int drawingCount = 0; 48 | 49 | private GpuBuffer planetsBuffer; 50 | private int planetsCount = 0; 51 | 52 | private static float heightScale = 1; 53 | 54 | public static boolean constellationsVisible; 55 | public static boolean starsVisible; 56 | public static boolean orbitingBodiesVisible; 57 | public static boolean oldStarsVisible; 58 | public static boolean starsAlwaysVisible; 59 | 60 | private float starVisibility; 61 | 62 | private boolean constellationsNeedsUpdate = true; 63 | 64 | private File data = null; 65 | private final Path storagePath; 66 | private final String fileName; 67 | 68 | private static final RenderPipeline pipeline = RenderPipeline.builder(RenderPipelines.POSITION_COLOR_SNIPPET).withLocation("pipeline/sga_stars") 69 | .withVertexShader("core/position_color").withFragmentShader("core/position_color") 70 | .withVertexFormat(VertexFormats.POSITION_COLOR, VertexFormat.DrawMode.QUADS) 71 | .withBlend(BlendFunction.OVERLAY).withDepthWrite(false).build(); 72 | 73 | public SpaceRenderingManager() { 74 | storagePath = SpyglassAstronomyClient.client.runDirectory.toPath().resolve(".spyglass_astronomy"); 75 | 76 | fileName = storagePath +"/rendering.txt"; 77 | 78 | constellationsVisible = true; 79 | starsVisible = true; 80 | orbitingBodiesVisible = true; 81 | oldStarsVisible = false; 82 | starsAlwaysVisible = false; 83 | 84 | if (Files.exists(storagePath)) { 85 | data = new File(fileName); 86 | if (data.exists()) { 87 | loadData(); 88 | } 89 | } 90 | 91 | indexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS); 92 | } 93 | 94 | private void loadData() { 95 | try { 96 | if (data.exists()) { 97 | Scanner scanner = new Scanner(data); 98 | String s = scanner.nextLine(); 99 | scanner.close(); 100 | constellationsVisible = charTrue(s, 0); 101 | starsVisible = charTrue(s, 1); 102 | orbitingBodiesVisible = charTrue(s, 2); 103 | oldStarsVisible = charTrue(s, 3); 104 | starsAlwaysVisible = charTrue(s, 4); 105 | } 106 | } catch (IOException e) { 107 | SpyglassAstronomyClient.LOGGER.info("Failed to load data"); 108 | } 109 | } 110 | 111 | private boolean charTrue(String s, int index) { 112 | return index < s.length() && s.charAt(index) == '1'; 113 | } 114 | 115 | public void saveData() { 116 | try { 117 | if (data == null) { 118 | data = new File(fileName); 119 | storagePath.toFile().mkdirs(); 120 | data.createNewFile(); 121 | } 122 | FileWriter writer = new FileWriter(data); 123 | String s = (constellationsVisible ? "1" : "0") + (starsVisible ? "1" : "0") + (orbitingBodiesVisible ? "1" : "0") + (oldStarsVisible ? "1" : "0") + (starsAlwaysVisible ? "1" : "0"); 124 | writer.write(s); 125 | writer.close(); 126 | 127 | } catch (IOException e) { 128 | SpyglassAstronomyClient.LOGGER.info("Failed to save data"); 129 | } 130 | } 131 | 132 | public void updateSpace(int ticks) { 133 | updateHeightScale(); 134 | if (Constellation.selected != null) { 135 | ClientPlayerEntity player = SpyglassAstronomyClient.client.player; 136 | if (player == null || SpyglassAstronomyClient.isntHoldingSpyglass()) { 137 | Constellation.deselect(); 138 | constellationsNeedsUpdate = true; 139 | } 140 | } 141 | if (constellationsNeedsUpdate) { 142 | updateConstellations(); 143 | constellationsNeedsUpdate = false; 144 | } 145 | 146 | if (Star.selected != null) { 147 | ClientPlayerEntity player = SpyglassAstronomyClient.client.player; 148 | if (player == null || SpyglassAstronomyClient.isntHoldingSpyglass()) { 149 | Star.deselect(); 150 | } 151 | } 152 | 153 | if (OrbitingBody.selected != null) { 154 | ClientPlayerEntity player = SpyglassAstronomyClient.client.player; 155 | if (player == null || SpyglassAstronomyClient.isntHoldingSpyglass()) { 156 | OrbitingBody.deselect(); 157 | } 158 | } 159 | 160 | updateStars(ticks); 161 | 162 | updateOrbits(ticks); 163 | } 164 | 165 | public void scheduleConstellationsUpdate() { 166 | constellationsNeedsUpdate = true; 167 | } 168 | 169 | public void cancelDrawing() { 170 | drawingCount = 0; 171 | } 172 | 173 | private void updateConstellations() { 174 | drawingCount = SpyglassAstronomyClient.isDrawingConstellation ? 1 : 0; 175 | 176 | if (SpyglassAstronomyClient.constellations.isEmpty()) { 177 | constellationsCount = 0; 178 | return; 179 | } 180 | 181 | BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); 182 | 183 | for (Constellation constellation : SpyglassAstronomyClient.constellations) { 184 | constellation.setVertices(bufferBuilder, false); 185 | } 186 | 187 | BuiltBuffer builtBuffer = bufferBuilder.end(); 188 | if (constellationsBuffer != null) { 189 | constellationsBuffer.close(); 190 | } 191 | constellationsBuffer = RenderSystem.getDevice().createBuffer(() -> "SGA Constellations Buffer", 40, builtBuffer.getBuffer()); 192 | constellationsCount = builtBuffer.getDrawParameters().indexCount(); 193 | builtBuffer.close(); 194 | } 195 | 196 | private void updateStars(int ticks) { 197 | if (SpyglassAstronomyClient.stars.isEmpty()) { 198 | starsCount = 0; 199 | return; 200 | } 201 | 202 | BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); 203 | 204 | for (Star star : SpyglassAstronomyClient.stars) { 205 | star.update(ticks); 206 | star.setVertices(bufferBuilder); 207 | } 208 | 209 | if (starsBuffer != null) { 210 | starsBuffer.close(); 211 | } 212 | BuiltBuffer builtBuffer = bufferBuilder.end(); 213 | starsBuffer = RenderSystem.getDevice().createBuffer(() -> "SGA Stars Buffer", 40, builtBuffer.getBuffer()); 214 | starsCount = builtBuffer.getDrawParameters().indexCount(); 215 | builtBuffer.close(); 216 | } 217 | 218 | private void updateOrbits(int ticks) { 219 | if (SpyglassAstronomyClient.orbitingBodies.isEmpty()) { 220 | orbitingBodiesVisible = false; 221 | return; 222 | } 223 | 224 | BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); 225 | 226 | Long day = SpyglassAstronomyClient.getDay(); 227 | float dayFraction = SpyglassAstronomyClient.getDayFraction(); 228 | 229 | Vector3f referencePosition = SpyglassAstronomyClient.earthOrbit.getRotatedPositionAtGlobalTime(day, dayFraction, true); 230 | Vector3f normalisedReferencePosition = new Vector3f(referencePosition); 231 | normalisedReferencePosition.normalize(); 232 | 233 | for (OrbitingBody orbitingBody : SpyglassAstronomyClient.orbitingBodies) { 234 | orbitingBody.update(ticks, referencePosition, normalisedReferencePosition, day, dayFraction); 235 | orbitingBody.setVertices(bufferBuilder); 236 | } 237 | 238 | BuiltBuffer builtBuffer = bufferBuilder.end(); 239 | if (planetsBuffer != null) { 240 | planetsBuffer.close(); 241 | } 242 | planetsBuffer = RenderSystem.getDevice().createBuffer(() -> "SGA Planets Buffer", 40, builtBuffer.getBuffer()); 243 | planetsCount = builtBuffer.getDrawParameters().indexCount(); 244 | builtBuffer.close(); 245 | } 246 | 247 | private void updateDrawingConstellation() { 248 | BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); 249 | 250 | SpyglassAstronomyClient.drawingConstellation.setVertices(bufferBuilder, true); 251 | 252 | BuiltBuffer builtBuffer = bufferBuilder.end(); 253 | if (drawingBuffer != null) { 254 | drawingBuffer.close(); 255 | } 256 | drawingBuffer = RenderSystem.getDevice().createBuffer(() -> "SGA Drawing Buffer", 40, builtBuffer.getBuffer()); 257 | drawingCount = builtBuffer.getDrawParameters().indexCount(); 258 | builtBuffer.close(); 259 | } 260 | 261 | public void render(MatrixStack matrices, SkyRenderState skyRenderState) { 262 | starVisibility = starsAlwaysVisible ? 1 : skyRenderState.starBrightness; 263 | if (starVisibility > 0) { 264 | float colorScale = starVisibility+Math.min(heightScale, 0.5f); 265 | 266 | if (constellationsVisible && SpyglassAstronomyClient.isDrawingConstellation || drawingCount > 0) { 267 | updateDrawingConstellation(); 268 | } 269 | 270 | GpuTextureView mainColor = MinecraftClient.getInstance().getFramebuffer().getColorAttachmentView(); 271 | GpuTextureView mainDepth = MinecraftClient.getInstance().getFramebuffer().getDepthAttachmentView(); 272 | Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack(); 273 | 274 | if (starsVisible || constellationsVisible) { 275 | matrices.push(); 276 | matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-90.0f)); 277 | matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(SpyglassAstronomyClient.getStarAngle())); 278 | matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(45f)); 279 | 280 | matrix4fStack.pushMatrix(); 281 | matrix4fStack.mul(matrices.peek().getPositionMatrix()); 282 | 283 | GpuBufferSlice gpuBufferSlice = RenderSystem.getDynamicUniforms().write(matrix4fStack, new Vector4f(colorScale, colorScale, colorScale, starVisibility), new Vector3f(), new Matrix4f(), 0.0F); 284 | RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Stars", mainColor, OptionalInt.empty(), mainDepth, OptionalDouble.empty()); 285 | renderPass.setUniform("DynamicTransforms", gpuBufferSlice); 286 | 287 | try { 288 | renderPass.setPipeline(pipeline); 289 | 290 | if (starsVisible) { 291 | draw(renderPass, starsBuffer, starsCount); 292 | } 293 | 294 | if (constellationsVisible) { 295 | draw(renderPass, constellationsBuffer, constellationsCount); 296 | 297 | if (SpyglassAstronomyClient.isDrawingConstellation) { 298 | draw(renderPass, drawingBuffer, drawingCount); 299 | } 300 | } 301 | } catch (Throwable ignored) {} 302 | 303 | renderPass.close(); 304 | matrices.pop(); 305 | matrix4fStack.popMatrix(); 306 | } 307 | 308 | if (orbitingBodiesVisible && planetsCount > 0) { 309 | matrices.push(); 310 | matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(SpyglassAstronomyClient.getPositionInOrbit(360f) * (1 - 1 / SpyglassAstronomyClient.earthOrbit.period) + 180)); 311 | 312 | matrix4fStack.pushMatrix(); 313 | matrix4fStack.mul(matrices.peek().getPositionMatrix()); 314 | 315 | GpuBufferSlice gpuBufferSlice = RenderSystem.getDynamicUniforms().write(matrix4fStack, new Vector4f(colorScale, colorScale, colorScale, starVisibility), new Vector3f(), new Matrix4f(), 0.0F); 316 | RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Planets", mainColor, OptionalInt.empty(), mainDepth, OptionalDouble.empty()); 317 | renderPass.setUniform("DynamicTransforms", gpuBufferSlice); 318 | 319 | try { 320 | renderPass.setPipeline(pipeline); 321 | draw(renderPass, planetsBuffer, planetsCount); 322 | } catch (Throwable ignored) {} 323 | 324 | renderPass.close(); 325 | matrices.pop(); 326 | matrix4fStack.popMatrix(); 327 | } 328 | } 329 | } 330 | 331 | public static void assignIrisPipeline() { 332 | try { 333 | String irisApiPackage = "net.irisshaders.iris.api.v0."; 334 | 335 | Class irisApiClass = Class.forName(irisApiPackage + "IrisApi"); 336 | Object INSTANCE = irisApiClass.getMethod("getInstance").invoke(null); 337 | 338 | Class irisProgramClass = Class.forName(irisApiPackage + "IrisProgram"); 339 | Enum SKY_BASIC = Enum.valueOf(irisProgramClass.asSubclass(Enum.class), "SKY_BASIC"); 340 | 341 | Method assignPipeline = irisApiClass.getMethod("assignPipeline", RenderPipeline.class, irisProgramClass); 342 | assignPipeline.invoke(INSTANCE, pipeline, SKY_BASIC); 343 | } catch (Exception ignored) { 344 | SpyglassAstronomyClient.LOGGER.error("Failed to assign pipeline. Shader compatibility may be broken"); 345 | } 346 | } 347 | 348 | public static void updateHeightScale() { 349 | heightScale = MathHelper.clamp((SpyglassAstronomyClient.getHeight()-32f)/256f, 0f, 1f); 350 | } 351 | 352 | public static float getHeightScale() { 353 | return heightScale; 354 | } 355 | 356 | public boolean starsCurrentlyVisible() { 357 | return starVisibility > 0; 358 | } 359 | 360 | private void draw(RenderPass renderPass, GpuBuffer gpuBuffer, int count) { 361 | if (count == 0) return; 362 | 363 | renderPass.setVertexBuffer(0, gpuBuffer); 364 | renderPass.setIndexBuffer(indexBuffer.getIndexBuffer(count), indexBuffer.getIndexType()); 365 | renderPass.drawIndexed(0,0, count, 1); 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/main/java/com/nettakrim/spyglass_astronomy/commands/InfoCommand.java: -------------------------------------------------------------------------------- 1 | package com.nettakrim.spyglass_astronomy.commands; 2 | 3 | import com.mojang.brigadier.tree.LiteralCommandNode; 4 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; 5 | import net.minecraft.command.argument.MessageArgumentType; 6 | import net.minecraft.text.Style; 7 | import org.joml.Vector3f; 8 | 9 | import com.mojang.brigadier.Command; 10 | import com.mojang.brigadier.context.CommandContext; 11 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 12 | import com.nettakrim.spyglass_astronomy.Constellation; 13 | import com.nettakrim.spyglass_astronomy.Orbit; 14 | import com.nettakrim.spyglass_astronomy.OrbitingBody; 15 | import com.nettakrim.spyglass_astronomy.SpyglassAstronomyClient; 16 | import com.nettakrim.spyglass_astronomy.Star; 17 | import com.nettakrim.spyglass_astronomy.Knowledge.Level; 18 | 19 | import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; 20 | import net.minecraft.text.MutableText; 21 | import net.minecraft.text.Text; 22 | import net.minecraft.util.math.MathHelper; 23 | import net.minecraft.util.math.RotationAxis; 24 | 25 | public class InfoCommand implements Command { 26 | 27 | public static LiteralCommandNode getCommandNode() { 28 | LiteralCommandNode infoNode = ClientCommandManager 29 | .literal("sga:info") 30 | .executes(new InfoCommand()) 31 | .build(); 32 | 33 | LiteralCommandNode constellationInfoNode = ClientCommandManager 34 | .literal("constellation") 35 | .then( 36 | ClientCommandManager.argument("name", MessageArgumentType.message()) 37 | .suggests(SpyglassAstronomyCommands.constellations) 38 | .executes(InfoCommand::getConstellationInfo) 39 | ) 40 | .build(); 41 | 42 | LiteralCommandNode starInfoNode = ClientCommandManager 43 | .literal("star") 44 | .then( 45 | ClientCommandManager.argument("name", MessageArgumentType.message()) 46 | .suggests(SpyglassAstronomyCommands.stars) 47 | .executes(InfoCommand::getStarInfo) 48 | ) 49 | .build(); 50 | 51 | LiteralCommandNode orbitingBodyInfoNode = ClientCommandManager 52 | .literal("planet") 53 | .then( 54 | ClientCommandManager.argument("name", MessageArgumentType.message()) 55 | .suggests(SpyglassAstronomyCommands.orbitingBodies) 56 | .executes(InfoCommand::getOrbitingBodyInfo) 57 | ) 58 | .build(); 59 | 60 | LiteralCommandNode earthInfoNode = ClientCommandManager 61 | .literal("thisworld") 62 | .executes(InfoCommand::getEarthInfo) 63 | .build(); 64 | 65 | LiteralCommandNode solarSystemInfoNode = ClientCommandManager 66 | .literal("solarsystem") 67 | .executes(InfoCommand::getSolarSystemInfo) 68 | .build(); 69 | 70 | infoNode.addChild(constellationInfoNode); 71 | infoNode.addChild(starInfoNode); 72 | infoNode.addChild(orbitingBodyInfoNode); 73 | infoNode.addChild(earthInfoNode); 74 | infoNode.addChild(solarSystemInfoNode); 75 | return infoNode; 76 | } 77 | 78 | @Override 79 | public int run(CommandContext context) throws CommandSyntaxException { 80 | if (Constellation.selected != null) { 81 | displayInfo(Constellation.selected); 82 | return 1; 83 | } 84 | if (Star.selected != null) { 85 | displayInfo(Star.selected); 86 | return 1; 87 | } 88 | if (OrbitingBody.selected != null) { 89 | displayInfo(OrbitingBody.selected); 90 | return 1; 91 | } 92 | SpyglassAstronomyClient.say("commands.info.nothingselected"); 93 | return -1; 94 | } 95 | 96 | private static int getConstellationInfo(CommandContext context) { 97 | Constellation constellation = SpyglassAstronomyCommands.getConstellation(context); 98 | if (constellation == null) { 99 | return -1; 100 | } 101 | displayInfo(constellation); 102 | return 1; 103 | } 104 | 105 | private static int getStarInfo(CommandContext context) { 106 | Star star = SpyglassAstronomyCommands.getStar(context); 107 | if (star == null) { 108 | return -1; 109 | } 110 | displayInfo(star); 111 | return 1; 112 | } 113 | 114 | private static int getOrbitingBodyInfo(CommandContext context) { 115 | OrbitingBody orbitingBody = SpyglassAstronomyCommands.getOrbitingBody(context); 116 | if (orbitingBody == null) { 117 | return -1; 118 | } 119 | displayInfo(orbitingBody); 120 | return 1; 121 | } 122 | 123 | private static int getEarthInfo(CommandContext context) { 124 | displayEarthInfo(); 125 | return 1; 126 | } 127 | 128 | private static int getSolarSystemInfo(CommandContext context) { 129 | displaySolarSystemInfo(); 130 | return 1; 131 | } 132 | 133 | private static void displayInfo(Constellation constellation) { 134 | int[] flags = new int[] {-1, -1}; 135 | MutableText text = Text.empty(); 136 | text.append(translate("constellation.name", constellation.name)); 137 | 138 | Vector3f position = constellation.getAveragePosition(); 139 | staticVisibilityInfo(text, position, flags); 140 | 141 | text.append(SpyglassAstronomyClient.knowledge.getKnowledgeInstructions(flags)); 142 | text.setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor)); 143 | SpyglassAstronomyClient.longSay(text); 144 | } 145 | 146 | private static void displayInfo(Star star) { 147 | int[] flags = new int[] {-1, -1}; 148 | MutableText text = Text.empty(); 149 | text.append(translate("star.name", star.isUnnamed() ? "Unnamed" : star.name)); 150 | 151 | Vector3f position = star.getPositionAsVector3f(); 152 | staticVisibilityInfo(text, position, flags); 153 | 154 | if (SpyglassAstronomyClient.knowledge.starKnowledgeAtleast(Level.MASTER, flags)) { 155 | //most visible stars are within 1000 light years, the stars index is used to add a bit of randomness to the distance 156 | float alpha = star.getAlpha(); 157 | int distance = (int)((1-alpha)*1000); 158 | distance += star.index%100; 159 | if (distance < 1) distance = 1; 160 | text.append(translate("star.distance", Integer.toString(distance))); 161 | } 162 | 163 | text.append(SpyglassAstronomyClient.knowledge.getKnowledgeInstructions(flags)); 164 | text.setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor)); 165 | SpyglassAstronomyClient.longSay(text); 166 | } 167 | 168 | private static void displayInfo(OrbitingBody orbitingBody) { 169 | int[] flags = new int[] {-1, -1}; 170 | MutableText text = Text.empty(); 171 | text.append(translate("planet.name", orbitingBody.isUnnamed() ? "Unnamed" : orbitingBody.name)); 172 | text.append(translate("planet.type."+orbitingBody.type.toString().toLowerCase())); 173 | orbitInfo(text, orbitingBody.orbit, flags); 174 | 175 | text.append(SpyglassAstronomyClient.knowledge.getKnowledgeInstructions(flags)); 176 | text.setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor)); 177 | SpyglassAstronomyClient.longSay(text); 178 | } 179 | 180 | private static void displayEarthInfo() { 181 | int[] flags = new int[] {-1, -1}; 182 | MutableText text = Text.empty(); 183 | text.append(translate("thisworld.time", getMinecraftTime())); 184 | text.append(translate("thisworld.moonphase")).append(translate("moonphase."+Integer.toString(SpyglassAstronomyClient.world.getMoonPhase(), SINGLE_SUCCESS))); 185 | orbitInfo(text, SpyglassAstronomyClient.earthOrbit, flags); 186 | 187 | text.append(SpyglassAstronomyClient.knowledge.getKnowledgeInstructions(flags)); 188 | text.setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor)); 189 | SpyglassAstronomyClient.longSay(text); 190 | } 191 | 192 | private static void displaySolarSystemInfo() { 193 | int[] flags = new int[] {-1, -1}; 194 | MutableText text = Text.empty(); 195 | text.append(translate("solarsystem.planets")); 196 | int stage = 0; 197 | for (OrbitingBody orbitingBody : SpyglassAstronomyClient.orbitingBodies) { 198 | if (stage == 0 && orbitingBody.orbit.period > SpyglassAstronomyClient.earthOrbit.period) { 199 | text.append(translate("solarsystem.thisworld")); 200 | stage = 1; 201 | } 202 | if (stage == 1 && !orbitingBody.isPlanet) { 203 | if (!SpyglassAstronomyClient.knowledge.orbitKnowledgeAtleast(Level.ADEPT, flags)) break; 204 | text.append(translate("solarsystem.comets")); 205 | stage = 2; 206 | } 207 | if (orbitingBody.isUnnamed()) { 208 | text.append(translate("solarsystem.unknown")); 209 | } else { 210 | text.append(translate("solarsystem.named", orbitingBody.name)); 211 | } 212 | } 213 | 214 | if (SpyglassAstronomyClient.knowledge.orbitKnowledgeAtleast(Level.EXPERT, flags)) { 215 | text.append(translate("solarsystem.time", Long.toString(SpyglassAstronomyClient.getDay()), Float.toString(MathHelper.floor(SpyglassAstronomyClient.getDayFraction()*100)/100f).replace("0.",""))); 216 | } 217 | 218 | text.append(SpyglassAstronomyClient.knowledge.getKnowledgeInstructions(flags)); 219 | text.setStyle(Style.EMPTY.withColor(SpyglassAstronomyClient.textColor)); 220 | SpyglassAstronomyClient.longSay(text); 221 | } 222 | 223 | private static void staticVisibilityInfo(MutableText text, Vector3f position, int[] flags) { 224 | Vector3f pos = new Vector3f(position); 225 | pos.rotate(RotationAxis.POSITIVE_Y.rotationDegrees(45f)); 226 | pos.rotate(RotationAxis.POSITIVE_X.rotationDegrees(SpyglassAstronomyClient.getStarAngle())); 227 | pos.rotate(RotationAxis.POSITIVE_Y.rotationDegrees(-90.0f)); 228 | 229 | float yaw = (float)(Math.atan2(pos.x, pos.z)*-180d/Math.PI); 230 | float angle = (float)(Math.atan2(Math.sqrt(pos.x * pos.x + pos.z * pos.z), pos.y)*180d/Math.PI)-90; 231 | 232 | text.append(translate("visibility.angle", prettyFloat(yaw), prettyFloat(angle))); 233 | 234 | if (SpyglassAstronomyClient.knowledge.starKnowledgeAtleast(Level.ADEPT, flags)) { 235 | pos = new Vector3f(position); 236 | pos.rotate(RotationAxis.POSITIVE_Y.rotationDegrees(45f)); 237 | pos.rotate(RotationAxis.POSITIVE_X.rotationDegrees(SpyglassAstronomyClient.starAngleMultiplier*(0.75f/SpyglassAstronomyClient.earthOrbit.period))); 238 | pos.rotate(RotationAxis.POSITIVE_Y.rotationDegrees(-90.0f)); 239 | if (MathHelper.abs(pos.z) < 0.9f) { 240 | //some of the values may be slightly innacurate (eg off by one) with fractional periods 241 | float referenceYaw = (float)(Math.atan2(pos.x, pos.z)*-180d/Math.PI); 242 | angle = (float)(Math.atan2(Math.sqrt(pos.x * pos.x + pos.z * pos.z), pos.y)*180d/Math.PI)-90; 243 | if (referenceYaw < 0) angle = 180 - angle; 244 | if (angle < 0) angle += 360; 245 | float period = SpyglassAstronomyClient.earthOrbit.period; 246 | angle = (period - MathHelper.floor((angle/360)*period+0.5f)) % period; 247 | int nearestDay = (int)angle; 248 | if (period == 8) { 249 | text.append(translate("visibility.time.moonphase")).append(translate("moonphase."+ nearestDay)); 250 | } else { 251 | int inDays = nearestDay - (int)Math.round(SpyglassAstronomyClient.getDay()%(double)period); 252 | if (inDays < 0) inDays += (int)period; 253 | text.append(translate("visibility.time.date", nearestDay, inDays)); 254 | } 255 | } else { 256 | text.append(translate("visibility.time.always")); 257 | } 258 | } 259 | } 260 | 261 | private static void orbitInfo(MutableText text, Orbit orbit, int[] flags) { 262 | if (SpyglassAstronomyClient.knowledge.starKnowledgeAtleast(Level.NOVICE, flags)) { 263 | text.append(translate("orbit.period", prettyFloat(orbit.period))); 264 | } 265 | 266 | boolean isEarth = orbit == SpyglassAstronomyClient.earthOrbit; 267 | 268 | if (!isEarth) { 269 | if (SpyglassAstronomyClient.knowledge.starKnowledgeAtleast(Level.ADEPT, flags)) { 270 | float max = Math.max(orbit.period, SpyglassAstronomyClient.earthOrbit.period); 271 | float min = Math.min(orbit.period, SpyglassAstronomyClient.earthOrbit.period); 272 | // max/min is often 1 less iteration for getDenominator 273 | double[] fraction = getFraction(max/min); 274 | 275 | float resonance = ((float)fraction[1]*max)/(float)(fraction[0] - fraction[1]); 276 | text.append(translate("orbit.resonance", prettyFloat(resonance))); 277 | } 278 | } 279 | 280 | if ((isEarth && SpyglassAstronomyClient.knowledge.starKnowledgeAtleast(Level.ADEPT, flags)) || SpyglassAstronomyClient.knowledge.orbitKnowledgeAtleast(Level.EXPERT, flags)) { 281 | text.append(translate("orbit.position",Integer.toString((int)(orbit.lastLocalTime*100)))); 282 | } 283 | if (!isEarth) { 284 | if (SpyglassAstronomyClient.knowledge.orbitKnowledgeAtleast(Level.EXPERT, flags)) { 285 | Vector3f pos = orbit.getLastRotatedPosition(); 286 | 287 | Vector3f earthPos = SpyglassAstronomyClient.earthOrbit.getLastRotatedPosition(); 288 | pos.sub(earthPos); 289 | float sqrDistance = SpyglassAstronomyClient.getSquaredDistance(pos.x, pos.y, pos.z); 290 | 291 | text.append(translate("orbit.distance", prettyFloat(MathHelper.sqrt(sqrDistance)/SpyglassAstronomyClient.earthOrbit.semiMajorAxis))); 292 | 293 | pos.normalize(); 294 | pos.rotate(RotationAxis.POSITIVE_Z.rotationDegrees((SpyglassAstronomyClient.getPositionInOrbit(360f)*(1-1/SpyglassAstronomyClient.earthOrbit.period)+180))); 295 | 296 | float yaw = (float)(Math.atan2(pos.x, pos.z)*-180d/Math.PI); 297 | float angle = (float)(Math.atan2(Math.sqrt(pos.x * pos.x + pos.z * pos.z), pos.y)*180d/Math.PI)-90; 298 | 299 | text.append(translate("orbit.angle", prettyFloat(yaw), prettyFloat(angle))); 300 | } 301 | } 302 | 303 | if (SpyglassAstronomyClient.knowledge.orbitKnowledgeAtleast(Level.MASTER, flags)) { 304 | text.append(translate("orbit.eccentricity", prettyFloat(orbit.eccentricity))); 305 | text.append(translate("orbit.ascension", prettyFloat(orbit.ascension))); 306 | text.append(translate("orbit.inclination", prettyFloat(orbit.inclination))); 307 | } 308 | } 309 | 310 | static private double[] getFraction(double x){ 311 | double tolerance = 1.0E-6; 312 | double h1=1; double h2=0; 313 | double k1=0; double k2=1; 314 | double b = x; 315 | do { 316 | double a = Math.floor(b); 317 | double aux = h1; h1 = a*h1+h2; h2 = aux; 318 | aux = k1; k1 = a*k1+k2; k2 = aux; 319 | b = 1/(b-a); 320 | } while (Math.abs(x-h1/k1) > x*tolerance); 321 | 322 | return new double[] {h1,k1}; 323 | } 324 | 325 | private static String prettyFloat(float f) { 326 | if (f == MathHelper.floor(f)) { 327 | return Integer.toString((int)f); 328 | } else { 329 | f = Math.round(f*100); 330 | return Float.toString(f/100); 331 | } 332 | } 333 | 334 | //https://github.com/Iru21/TimeDisplay/blob/master/src/main/kotlin/me/iru/timedisplay/TimeUtils.kt 335 | private static String getMinecraftTime() { 336 | long timeDay = SpyglassAstronomyClient.world.getTimeOfDay(); 337 | int dayTicks = (int)(timeDay % 24000); 338 | int hour = (dayTicks / 1000 + 6) % 24; 339 | int min = ((int)(dayTicks / 16.666666f)) % 60; 340 | int sec = ((int)(dayTicks / 0.277777f)) % 60; 341 | return formatTime(hour, min, sec); 342 | } 343 | 344 | private static String formatTime(int hour, int min, int sec) { 345 | String time = Integer.toString(sec); 346 | if (time.length() == 1) time = "0"+time; 347 | time = min + ":" + time; 348 | if (time.length() == 4) time ="0"+time; 349 | time = hour + ":" + time; 350 | if (time.length() == 7) time ="0"+time; 351 | return time; 352 | } 353 | 354 | private static Text translate(String key, Object... formatting) { 355 | return Text.translatable(SpyglassAstronomyClient.MODID+".commands.info."+key, formatting); 356 | } 357 | } 358 | --------------------------------------------------------------------------------