├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── me │ └── alexdevs │ └── solstice │ ├── Paths.java │ ├── Solstice.java │ ├── api │ ├── Raycast.java │ ├── ServerLocation.java │ ├── color │ │ ├── Gradient.java │ │ └── RGBColor.java │ ├── command │ │ ├── Flags.java │ │ ├── LocalGameProfile.java │ │ ├── TimeSpan.java │ │ └── flags │ │ │ ├── ArgumentFlag.java │ │ │ ├── DoubleFlag.java │ │ │ ├── Flag.java │ │ │ ├── FloatFlag.java │ │ │ └── StringFlag.java │ ├── config │ │ ├── ConfigDataManager.java │ │ ├── IConfigDataManager.java │ │ └── serializers │ │ │ └── DateSerializer.java │ ├── events │ │ ├── CommandEvents.java │ │ ├── ModuleCommandEvents.java │ │ ├── PlayerActivityEvents.java │ │ ├── PlayerConnectionEvents.java │ │ ├── PlayerTeleportCallback.java │ │ ├── RestartEvents.java │ │ ├── SolsticeEvents.java │ │ ├── TimeBarEvents.java │ │ └── WorldSaveCallback.java │ ├── module │ │ ├── Debug.java │ │ ├── ModCommand.java │ │ ├── ModuleBase.java │ │ ├── ModuleEntrypoint.java │ │ └── Utils.java │ ├── text │ │ ├── Components.java │ │ ├── Format.java │ │ ├── RawPlaceholder.java │ │ ├── TextCapabilities.java │ │ ├── parser │ │ │ ├── CodeParser.java │ │ │ ├── LinkParser.java │ │ │ ├── MarkdownComponentParser.java │ │ │ └── MarkdownParser.java │ │ └── tag │ │ │ └── PhaseGradientTag.java │ └── utils │ │ ├── MathUtils.java │ │ ├── PlayerUtils.java │ │ └── RegistryUtils.java │ ├── core │ ├── Modules.java │ ├── Scheduler.java │ ├── ToggleableConfig.java │ ├── UserCache.java │ ├── WarmUpManager.java │ └── coreModule │ │ ├── CoreModule.java │ │ ├── commands │ │ ├── PingCommand.java │ │ ├── ServerStatCommand.java │ │ └── SolsticeCommand.java │ │ └── data │ │ ├── CoreConfig.java │ │ ├── CoreLocale.java │ │ └── CorePlayerData.java │ ├── data │ ├── PlayerData.java │ ├── PlayerDataManager.java │ └── ServerData.java │ ├── integrations │ ├── ConnectorIntegration.java │ ├── LuckPermsIntegration.java │ └── TrinketsIntegration.java │ ├── locale │ ├── Locale.java │ └── LocaleManager.java │ ├── mixin │ ├── CommandNodeAccessor.java │ ├── SolsticeMixinConfigPlugin.java │ ├── events │ │ ├── CommandEventsMixin.java │ │ └── WorldSaveEventMixin.java │ └── modules │ │ ├── admin │ │ └── ConnectionBypassMixin.java │ │ ├── afk │ │ └── FixPlayerSleepPercentageMixin.java │ │ ├── back │ │ └── PreTeleportMixin.java │ │ ├── ban │ │ └── CustomBanMessageMixin.java │ │ ├── core │ │ └── RealPingMixin.java │ │ ├── customname │ │ └── CustomDisplayNameMixin.java │ │ ├── miscellaneous │ │ └── BypassSleepingInBedCheckMixin.java │ │ ├── sign │ │ └── FormatSignMixin.java │ │ ├── spawn │ │ ├── OverrideNewPlayerSpawnPointMixin.java │ │ └── OverrideSpawnPointMixin.java │ │ ├── styling │ │ ├── CustomAdvancementMixin.java │ │ ├── CustomChatMessageMixin.java │ │ ├── CustomConnectionMessagesMixin.java │ │ ├── CustomDeathMessageMixin.java │ │ ├── CustomSentMessageMixin.java │ │ ├── InjectCustomChatMessageMixin.java │ │ └── PlayerDisconnectMixin.java │ │ └── tablist │ │ ├── CustomPlayerListNameMixin.java │ │ ├── PlayerOrderMixin.java │ │ └── UpdatePlayerListMixin.java │ └── modules │ ├── ModuleProvider.java │ ├── admin │ └── AdminModule.java │ ├── afk │ ├── AfkModule.java │ ├── commands │ │ ├── ActiveTimeCommand.java │ │ └── AfkCommand.java │ └── data │ │ ├── AfkConfig.java │ │ ├── AfkLocale.java │ │ ├── AfkPlayerData.java │ │ ├── AfkServerData.java │ │ ├── LeaderboardEntry.java │ │ └── PlayerActivityState.java │ ├── announcement │ ├── AnnouncementModule.java │ └── data │ │ └── AnnouncementConfig.java │ ├── back │ ├── BackModule.java │ ├── commands │ │ └── BackCommand.java │ └── data │ │ ├── BackConfig.java │ │ ├── BackLocale.java │ │ └── BackPlayerData.java │ ├── ban │ ├── BanModule.java │ ├── commands │ │ ├── BanCommand.java │ │ ├── TempBanCommand.java │ │ └── UnbanCommand.java │ ├── data │ │ └── BanLocale.java │ └── formatters │ │ └── BanMessageFormatter.java │ ├── broadcast │ ├── BroadcastModule.java │ ├── commands │ │ ├── BroadcastCommand.java │ │ └── PlainBroadcastCommand.java │ └── data │ │ └── BroadcastConfig.java │ ├── commandSpy │ ├── CommandSpyModule.java │ └── data │ │ ├── CommandSpyConfig.java │ │ └── CommandSpyLocale.java │ ├── cooldown │ ├── CooldownModule.java │ ├── commands │ │ └── CooldownCommand.java │ └── data │ │ ├── CooldownConfig.java │ │ ├── CooldownLocale.java │ │ └── CooldownSetting.java │ ├── customName │ ├── CustomNameModule.java │ ├── commands │ │ └── NicknameCommand.java │ └── data │ │ ├── CustomNameConfig.java │ │ ├── CustomNameLocale.java │ │ └── CustomNamePlayerData.java │ ├── enderchest │ ├── EnderChestModule.java │ ├── commands │ │ └── EnderChestCommand.java │ └── data │ │ └── EnderChestLocale.java │ ├── experiments │ ├── ExperimentsModule.java │ └── commands │ │ ├── FlagsCommand.java │ │ ├── SafeTeleportCommand.java │ │ └── TimeSpanCommand.java │ ├── extinguish │ ├── ExtinguishModule.java │ └── commands │ │ └── ExtinguishCommand.java │ ├── feed │ ├── FeedModule.java │ ├── commands │ │ └── FeedCommand.java │ └── data │ │ └── FeedLocale.java │ ├── fly │ ├── FlyModule.java │ ├── commands │ │ └── FlyCommand.java │ └── data │ │ ├── FlyLocale.java │ │ └── FlyPlayerData.java │ ├── god │ ├── GodModule.java │ ├── commands │ │ └── GodCommand.java │ └── data │ │ ├── GodLocale.java │ │ └── GodPlayerData.java │ ├── hat │ ├── HatModule.java │ ├── commands │ │ └── HatCommand.java │ └── data │ │ ├── HatConfig.java │ │ └── HatLocale.java │ ├── heal │ ├── HealModule.java │ ├── commands │ │ └── HealCommand.java │ └── data │ │ └── HealLocale.java │ ├── helpOp │ ├── HelpOpModule.java │ ├── commands │ │ └── HelpOpCommand.java │ └── data │ │ └── HelpOpLocale.java │ ├── home │ ├── HomeModule.java │ ├── commands │ │ ├── DeleteHomeCommand.java │ │ ├── HomeCommand.java │ │ ├── HomeOtherCommand.java │ │ ├── HomesCommand.java │ │ └── SetHomeCommand.java │ └── data │ │ ├── HomeConfig.java │ │ ├── HomeLocale.java │ │ └── HomePlayerData.java │ ├── ignite │ ├── IgniteModule.java │ └── commands │ │ └── IgniteCommand.java │ ├── ignore │ ├── IgnoreModule.java │ ├── commands │ │ ├── IgnoreCommand.java │ │ └── IgnoreListCommand.java │ └── data │ │ ├── IgnoreLocale.java │ │ └── IgnorePlayerData.java │ ├── info │ ├── InfoModule.java │ ├── commands │ │ ├── InfoCommand.java │ │ ├── MotdCommand.java │ │ └── RulesCommand.java │ └── data │ │ ├── InfoConfig.java │ │ └── InfoLocale.java │ ├── inventorySee │ ├── ImmutableSlot.java │ ├── InventorySeeModule.java │ ├── commands │ │ └── InventorySeeCommand.java │ └── data │ │ └── InventorySeeLocale.java │ ├── item │ ├── ItemModule.java │ ├── commands │ │ ├── ItemLoreCommand.java │ │ ├── ItemNameCommand.java │ │ ├── MoreCommand.java │ │ └── RepairCommand.java │ └── data │ │ └── ItemLocale.java │ ├── jail │ ├── JailModule.java │ ├── commands │ │ ├── CheckJailCommand.java │ │ ├── JailCommand.java │ │ ├── JailsCommand.java │ │ └── UnjailCommand.java │ └── data │ │ ├── JailConfig.java │ │ ├── JailLocale.java │ │ ├── JailPlayerData.java │ │ └── JailServerData.java │ ├── kick │ ├── KickModule.java │ └── commands │ │ └── KickCommand.java │ ├── kit │ ├── KitInventory.java │ ├── KitModule.java │ ├── Utils.java │ ├── commands │ │ ├── KitCommand.java │ │ └── KitsCommand.java │ └── data │ │ ├── Kit.java │ │ ├── KitConfig.java │ │ ├── KitLocale.java │ │ ├── KitPlayerData.java │ │ └── KitServerData.java │ ├── mail │ ├── MailModule.java │ ├── commands │ │ └── MailCommand.java │ └── data │ │ ├── MailLocale.java │ │ ├── MailPlayerData.java │ │ └── PlayerMail.java │ ├── miscellaneous │ ├── DummyExplosion.java │ ├── MiscellaneousModule.java │ ├── commands │ │ ├── EffectsCommand.java │ │ ├── KittyCannonCommand.java │ │ ├── NudgeCommand.java │ │ ├── RocketCommand.java │ │ ├── SleepCommand.java │ │ ├── SpeedCommand.java │ │ └── TopCommand.java │ └── data │ │ └── MiscellaneousLocale.java │ ├── mute │ ├── MuteModule.java │ ├── commands │ │ ├── MuteCommand.java │ │ └── UnmuteCommand.java │ └── data │ │ ├── MuteLocale.java │ │ └── MutePlayerData.java │ ├── near │ ├── NearModule.java │ ├── commands │ │ └── NearCommand.java │ └── data │ │ ├── NearConfig.java │ │ └── NearLocale.java │ ├── note │ ├── NoteModule.java │ ├── commands │ │ └── NotesCommand.java │ └── data │ │ ├── Note.java │ │ ├── NoteConfig.java │ │ ├── NoteLocale.java │ │ └── NotePlayerData.java │ ├── notifications │ ├── NotificationsModule.java │ ├── commands │ │ └── NotificationsCommand.java │ └── data │ │ ├── NotificationsConfig.java │ │ ├── NotificationsLocale.java │ │ ├── NotificationsPlayerData.java │ │ └── PlayerNotificationSettings.java │ ├── placeholders │ └── PlaceholdersModule.java │ ├── powertool │ ├── Action.java │ ├── PowerToolModule.java │ ├── commands │ │ └── PowerToolCommand.java │ └── data │ │ ├── PowerToolLocale.java │ │ └── PowerToolPlayerData.java │ ├── restart │ ├── RestartModule.java │ ├── commands │ │ └── RestartCommand.java │ └── data │ │ ├── RestartConfig.java │ │ └── RestartLocale.java │ ├── rtp │ ├── RTPModule.java │ ├── commands │ │ └── RTPCommand.java │ ├── core │ │ └── Locator.java │ └── data │ │ ├── RTPConfig.java │ │ └── RTPLocale.java │ ├── seen │ ├── SeenModule.java │ ├── commands │ │ └── SeenCommand.java │ └── data │ │ └── SeenLocale.java │ ├── sign │ └── SignModule.java │ ├── skull │ ├── SkullModule.java │ └── commands │ │ └── SkullCommand.java │ ├── smite │ ├── SmiteModule.java │ └── commands │ │ └── SmiteCommand.java │ ├── spawn │ ├── SpawnModule.java │ ├── commands │ │ ├── FirstSpawnCommand.java │ │ ├── SetFirstSpawnCommand.java │ │ ├── SetSpawnCommand.java │ │ └── SpawnCommand.java │ └── data │ │ ├── SpawnConfig.java │ │ ├── SpawnLocale.java │ │ └── SpawnServerData.java │ ├── staffChat │ ├── StaffChatModule.java │ ├── commands │ │ └── StaffChatCommand.java │ └── data │ │ └── StaffChatLocale.java │ ├── styling │ ├── CustomSentMessage.java │ ├── StylingModule.java │ ├── data │ │ └── StylingConfig.java │ └── formatters │ │ ├── AdvancementFormatter.java │ │ ├── ChatFormatter.java │ │ ├── ConnectionActivityFormatter.java │ │ ├── DeathFormatter.java │ │ └── EmoteFormatter.java │ ├── sudo │ ├── SudoModule.java │ └── commands │ │ ├── DoAsCommand.java │ │ └── SudoCommand.java │ ├── suicide │ ├── SuicideModule.java │ └── commands │ │ └── SuicideCommand.java │ ├── tablist │ ├── TabListModule.java │ └── data │ │ └── TabListConfig.java │ ├── teleportHere │ ├── TeleportHereModule.java │ └── commands │ │ └── TeleportHereCommand.java │ ├── teleportOffline │ ├── TeleportOfflineModule.java │ └── commands │ │ └── TeleportOfflineCommand.java │ ├── teleportPosition │ ├── TeleportPositionModule.java │ └── commands │ │ └── TeleportPositionCommand.java │ ├── teleportRequest │ ├── TeleportRequestModule.java │ ├── commands │ │ ├── TeleportAcceptCommand.java │ │ ├── TeleportAskCommand.java │ │ ├── TeleportAskHereCommand.java │ │ └── TeleportDenyCommand.java │ └── data │ │ ├── Request.java │ │ ├── TeleportConfig.java │ │ └── TeleportLocale.java │ ├── tell │ ├── TellModule.java │ ├── commands │ │ ├── ReplyCommand.java │ │ └── TellCommand.java │ └── data │ │ └── TellLocale.java │ ├── timeBar │ ├── TimeBar.java │ ├── TimeBarModule.java │ └── commands │ │ └── TimeBarCommand.java │ ├── trash │ ├── TrashModule.java │ ├── commands │ │ └── TrashCommand.java │ └── data │ │ └── TrashLocale.java │ ├── utilities │ ├── UtilitiesModule.java │ ├── commands │ │ ├── AnvilCommand.java │ │ ├── CartographyCommand.java │ │ ├── GrindstoneCommand.java │ │ ├── LoomCommand.java │ │ ├── SmithingCommand.java │ │ ├── StonecutterCommand.java │ │ └── WorkbenchCommand.java │ └── virtualScreenHandlers │ │ ├── VirtualAnvilScreenHandler.java │ │ ├── VirtualCartographyTableScreenHandler.java │ │ ├── VirtualCraftingScreenHandler.java │ │ ├── VirtualGrindstoneScreenHandler.java │ │ ├── VirtualLoomScreenHandler.java │ │ ├── VirtualSmithingScreenHandler.java │ │ └── VirtualStonecutterScreenHandler.java │ └── warp │ ├── WarpModule.java │ ├── commands │ ├── DeleteWarpCommand.java │ ├── SetWarpCommand.java │ ├── WarpCommand.java │ └── WarpsCommand.java │ └── data │ ├── WarpLocale.java │ └── WarpServerData.java └── resources ├── META-INF └── neoforge.mods.toml ├── assets └── solstice │ └── info │ ├── formatting.txt │ ├── motd.txt │ └── rules.txt ├── fabric.mod.json ├── solstice.accesswidener └── solstice.mixins.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Versions** 27 | - Minecraft: (e.g. 1.20.1) 28 | - Solstice: (e.g. 0.4.0+1.20.1) 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.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 16 | ] 17 | runs-on: ubuntu-22.04 18 | steps: 19 | - name: checkout repository 20 | uses: actions/checkout@v4 21 | - name: validate gradle wrapper 22 | uses: gradle/wrapper-validation-action@v2 23 | - name: setup jdk ${{ matrix.java }} 24 | uses: actions/setup-java@v4 25 | with: 26 | java-version: ${{ matrix.java }} 27 | distribution: 'microsoft' 28 | - name: make gradle wrapper executable 29 | run: chmod +x ./gradlew 30 | - uses: actions/cache@v4 31 | with: 32 | path: | 33 | ~/.gradle/caches 34 | ~/.gradle/wrapper 35 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 36 | restore-keys: | 37 | ${{ runner.os }}-gradle- 38 | - name: build 39 | run: ./gradlew remapJar 40 | - name: capture build artifacts 41 | if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: Artifacts 45 | path: build/libs/ 46 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish on GitHub, CurseForge & Modrinth 2 | 3 | on: [ workflow_dispatch ] 4 | 5 | env: 6 | MINECRAFT_VERSION: 1.21.1 7 | JAVA_VERSION: 21 8 | VERSION: 1.9.2+1.21.1 9 | RELEASE_NAME: Solstice 1.9.2 for Minecraft 1.21.1 10 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 11 | CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} 12 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 13 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 14 | 15 | permissions: 16 | contents: write 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Check Environment Variables 23 | run: env 24 | 25 | - name: Checkout Repository 26 | uses: actions/checkout@v3 27 | with: 28 | submodules: true 29 | 30 | - name: Setup Java 31 | uses: actions/setup-java@v2 32 | with: 33 | distribution: "temurin" 34 | java-version: 21 35 | 36 | - name: Make Gradle Wrapper Executable 37 | if: ${{ runner.os != 'Windows' }} 38 | run: chmod +x ./gradlew 39 | 40 | - name: Build 41 | run: ./gradlew clean build 42 | 43 | - name: Publish to Maven 44 | run: ./gradlew publish 45 | 46 | - name: Publish 47 | uses: Kir-Antipov/mc-publish@v3.3 48 | with: 49 | curseforge-id: 1149875 50 | curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} 51 | 52 | modrinth-id: uIvrDZas 53 | modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 54 | 55 | github-tag: "v${{env.VERSION}}" 56 | github-token: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | name: "${{env.RELEASE_NAME}}" 59 | version: "${{env.VERSION}}" 60 | version-type: release 61 | changelog-file: CHANGELOG.md 62 | 63 | loaders: fabric 64 | game-versions: "${{env.MINECRAFT_VERSION}}" 65 | java: "${{env.JAVA_VERSION}}" -------------------------------------------------------------------------------- /.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 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - Bugfix safe teleport not working correctly. 2 | - Bugfix RTP attempt throwing StackOverflowException. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2025 Alessandro "AlexDevs" Proto 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | org.gradle.parallel=true 4 | 5 | # Fabric Properties 6 | # check these on https://fabricmc.net/develop 7 | minecraft_version=1.21.1 8 | loader_version=0.16.14 9 | 10 | parchment_mappings=2024.11.17 11 | 12 | # Mod Properties 13 | mod_version=1.9.2 14 | maven_group=me.alexdevs 15 | archives_base_name=solstice 16 | 17 | # Dependencies 18 | fabric_version=0.116.4+1.21.1 19 | 20 | configurate_version=4.1.2 21 | permissions_api_version=0.2-SNAPSHOT 22 | placeholderapi_version=2.4.2+1.21 23 | sgui_version=1.6.1+1.21.1 24 | commoneconomy_version=1.1.1 25 | 26 | trinkets_version=3.10.0 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ale32bit/Solstice/e07e35d3bd4de6d01f10af0a5df3edaad24c86b3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/Paths.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | 5 | import java.nio.file.Path; 6 | 7 | public class Paths { 8 | public static final Path configDirectory = FabricLoader.getInstance().getConfigDir().resolve(Solstice.MOD_ID); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/Raycast.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.server.level.ServerPlayer; 5 | import net.minecraft.world.level.ClipContext; 6 | import net.minecraft.world.phys.BlockHitResult; 7 | import net.minecraft.world.phys.Vec3; 8 | 9 | public class Raycast { 10 | public static BlockHitResult cast(ServerPlayer player, double maxDistance) { 11 | var world = player.serverLevel(); 12 | var eyePos = player.getEyePosition(); 13 | var rotVec = player.getLookAngle(); 14 | 15 | var raycastEnd = eyePos.add(rotVec.scale(maxDistance)); 16 | 17 | var rcContext = new ClipContext( 18 | eyePos, 19 | raycastEnd, 20 | ClipContext.Block.OUTLINE, 21 | ClipContext.Fluid.NONE, 22 | player 23 | ); 24 | 25 | return world.clip(rcContext); 26 | } 27 | 28 | public static BlockPos getBlockPos(ServerPlayer player, double maxDistance) { 29 | var result = cast(player, maxDistance); 30 | if (result.getType() == BlockHitResult.Type.BLOCK) { 31 | return result.getBlockPos(); 32 | } 33 | return null; 34 | } 35 | 36 | public static Vec3 getEntityPos(ServerPlayer player, double maxDistance) { 37 | var result = cast(player, maxDistance); 38 | if (result.getType() == BlockHitResult.Type.ENTITY) { 39 | return result.getLocation(); 40 | } 41 | return null; 42 | } 43 | 44 | public static Vec3 getPos(ServerPlayer player, double maxDistance) { 45 | var result = cast(player, maxDistance); 46 | if (result.getType() != BlockHitResult.Type.MISS) { 47 | return result.getLocation(); 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/color/Gradient.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.color; 2 | 3 | import net.minecraft.network.chat.TextColor; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class Gradient { 7 | public static TextColor lerp(final float t, final @NotNull RGBColor a, final @NotNull RGBColor b) { 8 | final float clampedT = Math.min(1.0f, Math.max(0.0f, t)); 9 | final int ar = a.red(); 10 | final int br = b.red(); 11 | final int ag = a.green(); 12 | final int bg = b.green(); 13 | final int ab = a.blue(); 14 | final int bb = b.blue(); 15 | final var color = new RGBColor( 16 | Math.round(ar + clampedT * (br - ar)), 17 | Math.round(ag + clampedT * (bg - ag)), 18 | Math.round(ab + clampedT * (bb - ab)) 19 | ); 20 | 21 | return TextColor.fromRgb(color.getInt()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/color/RGBColor.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.color; 2 | 3 | import net.minecraft.network.chat.TextColor; 4 | 5 | public class RGBColor { 6 | public final int color; 7 | 8 | public RGBColor(TextColor color) { 9 | this.color = color.getValue(); 10 | } 11 | 12 | public RGBColor(int color) { 13 | this.color = color; 14 | } 15 | 16 | public RGBColor(int red, int green, int blue) { 17 | this.color = (red << 16) | (green << 8) | blue; 18 | } 19 | 20 | public RGBColor(byte red, byte green, byte blue) { 21 | this.color = (red << 16) | (green << 8) | blue; 22 | } 23 | 24 | public int red() { 25 | return (color >> 16) & 0xFF; 26 | } 27 | 28 | public int green() { 29 | return (color >> 8) & 0xFF; 30 | } 31 | 32 | public int blue() { 33 | return color & 0xFF; 34 | } 35 | 36 | public int getInt() { 37 | return color; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/command/flags/ArgumentFlag.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.command.flags; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | 5 | import java.util.List; 6 | 7 | public abstract class ArgumentFlag extends Flag { 8 | public ArgumentFlag(String name, List shortNames) { 9 | super(name, shortNames); 10 | } 11 | 12 | public abstract T getValue(); 13 | 14 | @Override 15 | public abstract void accept(String input) throws CommandSyntaxException; 16 | 17 | @Override 18 | public boolean acceptsValue() { 19 | return true; 20 | } 21 | 22 | @Override 23 | public abstract ArgumentFlag clone(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/command/flags/DoubleFlag.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.command.flags; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.DoubleArgumentType; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | 7 | import java.util.List; 8 | 9 | public class DoubleFlag extends ArgumentFlag { 10 | protected double value; 11 | protected final DoubleArgumentType type; 12 | 13 | public DoubleFlag(String name, List shortFlags) { 14 | super(name, shortFlags); 15 | type = DoubleArgumentType.doubleArg(); 16 | } 17 | 18 | @Override 19 | public Double getValue() { 20 | return value; 21 | } 22 | 23 | @Override 24 | public void accept(String input) throws CommandSyntaxException { 25 | this.isUsed = true; 26 | value = type.parse(new StringReader(input)); 27 | } 28 | 29 | @Override 30 | public DoubleFlag clone() { 31 | return new DoubleFlag(name, shortFlags); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/command/flags/Flag.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.command.flags; 2 | 3 | import com.mojang.brigadier.LiteralMessage; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class Flag implements Comparable { 13 | public static final SimpleCommandExceptionType UNEXPECTED_VALUE = new SimpleCommandExceptionType(new LiteralMessage("Unexpected value")); 14 | 15 | protected final String name; 16 | protected final List shortFlags = new ArrayList<>(); 17 | protected boolean isUsed = false; 18 | 19 | public Flag(String name, List shortNames) { 20 | this.name = name; 21 | this.shortFlags.addAll(shortNames); 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public List getShortFlags() { 29 | return Collections.unmodifiableList(shortFlags); 30 | } 31 | 32 | public boolean acceptsValue() { 33 | return false; 34 | } 35 | 36 | public boolean isUsed() { 37 | return isUsed; 38 | } 39 | 40 | @Override 41 | public Flag clone() { 42 | return new Flag(name, shortFlags); 43 | } 44 | 45 | 46 | public void accept(String value) throws CommandSyntaxException { 47 | throw UNEXPECTED_VALUE.create(); 48 | } 49 | 50 | public void accept() { 51 | isUsed = true; 52 | } 53 | 54 | public static Flag of(String name) { 55 | return new Flag(name, Collections.emptyList()); 56 | } 57 | 58 | @Override 59 | public int compareTo(@NotNull Flag o) { 60 | return this.name.compareTo(o.name); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/command/flags/FloatFlag.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.command.flags; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.FloatArgumentType; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | 7 | import java.util.List; 8 | 9 | public class FloatFlag extends ArgumentFlag { 10 | protected Float value; 11 | protected final FloatArgumentType type; 12 | 13 | public FloatFlag(String name, List shortFlags) { 14 | super(name, shortFlags); 15 | type = FloatArgumentType.floatArg(); 16 | } 17 | 18 | @Override 19 | public Float getValue() { 20 | return value; 21 | } 22 | 23 | @Override 24 | public void accept(String input) throws CommandSyntaxException { 25 | this.isUsed = true; 26 | value = type.parse(new StringReader(input)); 27 | } 28 | 29 | @Override 30 | public FloatFlag clone() { 31 | return new FloatFlag(name, shortFlags); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/command/flags/StringFlag.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.command.flags; 2 | 3 | import com.mojang.brigadier.StringReader; 4 | import com.mojang.brigadier.arguments.StringArgumentType; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | 7 | import java.util.List; 8 | 9 | public class StringFlag extends ArgumentFlag { 10 | protected String value; 11 | protected final StringArgumentType type; 12 | public StringFlag(String name, List shortFlags) { 13 | super(name, shortFlags); 14 | type = StringArgumentType.string(); 15 | } 16 | 17 | @Override 18 | public String getValue() { 19 | return value; 20 | } 21 | 22 | @Override 23 | public void accept(String input) throws CommandSyntaxException { 24 | this.isUsed = true; 25 | value = type.parse(new StringReader(input)); 26 | } 27 | 28 | public static String get(String name, List flags) { 29 | for(var flag : flags) { 30 | if(flag.getName().equals(name) && flag instanceof StringFlag stringFlag) { 31 | return stringFlag.getValue(); 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | @Override 38 | public StringFlag clone() { 39 | return new StringFlag(name, shortFlags); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/config/IConfigDataManager.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.config; 2 | 3 | import net.minecraft.resources.ResourceLocation; 4 | import org.spongepowered.configurate.ConfigurateException; 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 6 | 7 | import java.nio.file.Path; 8 | import java.util.function.Supplier; 9 | 10 | public interface IConfigDataManager { 11 | /** 12 | * Register a config section. 13 | * @param id ID of the section with namespace. This uses the ID of the mod as the namespace and the section ID as the path. 14 | * @param classOfConfig Class of the config itself. Remember to include the annotation @{@link ConfigSerializable} 15 | * @param creator Supplier of the config that contains the default values when creating the initial data. 16 | * @param Class of the config. 17 | */ 18 | void registerData(ResourceLocation id, Class classOfConfig, Supplier creator); 19 | 20 | /** 21 | * Get the object of the configuration data. 22 | * @param classOfConfig Class of the config. 23 | * @return Configuration object 24 | * @param Class of the config. 25 | */ 26 | T getData(Class classOfConfig); 27 | 28 | /** 29 | * Save the config file to disk. 30 | */ 31 | void save(); 32 | 33 | /** 34 | * Load the config file from disk, overwriting the loaded config. 35 | */ 36 | void load() throws ConfigurateException; 37 | 38 | /** 39 | * Get the path of the configuration file. 40 | * @return Path of the configuration file. 41 | */ 42 | Path getPath(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/config/serializers/DateSerializer.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.config.serializers; 2 | 3 | import org.spongepowered.configurate.serialize.ScalarSerializer; 4 | 5 | import java.lang.reflect.Type; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.function.Predicate; 10 | 11 | public final class DateSerializer extends ScalarSerializer { 12 | public static final DateSerializer TYPE = new DateSerializer(); 13 | 14 | /** 15 | * ISO 8601 Date format 16 | */ 17 | public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX"; 18 | public static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT); 19 | 20 | DateSerializer() { 21 | super(Date.class); 22 | } 23 | 24 | @Override 25 | public Date deserialize(final Type type, final Object obj) { 26 | try { 27 | return DATE_FORMATTER.parse(obj.toString()); 28 | } catch (ParseException e) { 29 | throw new RuntimeException(e); 30 | } 31 | } 32 | 33 | @Override 34 | public Object serialize(final Date item, final Predicate> typeSupported) { 35 | return DATE_FORMATTER.format(item); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/CommandEvents.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.commands.CommandSourceStack; 6 | 7 | public class CommandEvents { 8 | /** 9 | * This event is used on all command attempts. 10 | */ 11 | public static final Event ALLOW_COMMAND = EventFactory.createArrayBacked(AllowCommand.class, callbacks -> 12 | (source, command) -> { 13 | for (var callback : callbacks) { 14 | if(!callback.allowCommand(source, command)) 15 | return false; 16 | } 17 | return true; 18 | }); 19 | 20 | /** 21 | * This event is used on all command attempts. 22 | */ 23 | public static final Event COMMAND = EventFactory.createArrayBacked(Command.class, callbacks -> 24 | (player, command) -> { 25 | for (var callback : callbacks) { 26 | callback.onCommand(player, command); 27 | } 28 | }); 29 | 30 | @FunctionalInterface 31 | public interface AllowCommand { 32 | boolean allowCommand(CommandSourceStack source, String command); 33 | } 34 | 35 | @FunctionalInterface 36 | public interface Command { 37 | void onCommand(CommandSourceStack source, String command); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/ModuleCommandEvents.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.api.module.ModuleBase; 6 | import net.fabricmc.fabric.api.event.Event; 7 | import net.fabricmc.fabric.api.event.EventFactory; 8 | import net.minecraft.commands.CommandSourceStack; 9 | 10 | public class ModuleCommandEvents { 11 | /** 12 | * This event is used on Solstice commands. 13 | */ 14 | public static final Event ALLOW_COMMAND = EventFactory.createArrayBacked(AllowCommand.class, callbacks -> 15 | (node, context, command) -> { 16 | for (var callback : callbacks) { 17 | if (!callback.allowCommand(node, context, command)) 18 | return false; 19 | } 20 | return true; 21 | }); 22 | 23 | /** 24 | * This event is used on Solstice commands. 25 | */ 26 | public static final Event COMMAND = EventFactory.createArrayBacked(Command.class, callbacks -> 27 | (node, context, command) -> { 28 | for (var callback : callbacks) { 29 | callback.onCommand(node, context, command); 30 | } 31 | }); 32 | 33 | @FunctionalInterface 34 | public interface AllowCommand { 35 | boolean allowCommand(String node, CommandContext context, ModCommand command); 36 | } 37 | 38 | @FunctionalInterface 39 | public interface Command { 40 | void onCommand(String node, CommandContext context, ModCommand command); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/PlayerActivityEvents.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import me.alexdevs.solstice.modules.afk.AfkModule; 4 | import net.fabricmc.fabric.api.event.Event; 5 | import net.fabricmc.fabric.api.event.EventFactory; 6 | import net.minecraft.server.level.ServerPlayer; 7 | 8 | public final class PlayerActivityEvents { 9 | 10 | public static final Event AFK = EventFactory.createArrayBacked(Afk.class, callbacks -> (handler) -> { 11 | for (Afk callback : callbacks) { 12 | callback.onAfk(handler); 13 | } 14 | }); 15 | 16 | public static final Event AFK_RETURN = EventFactory.createArrayBacked(AfkReturn.class, callbacks -> (handler, reason) -> { 17 | for (AfkReturn callback : callbacks) { 18 | callback.onAfkReturn(handler, reason); 19 | } 20 | }); 21 | 22 | @FunctionalInterface 23 | public interface Afk { 24 | void onAfk(ServerPlayer player); 25 | } 26 | 27 | @FunctionalInterface 28 | public interface AfkReturn { 29 | void onAfkReturn(ServerPlayer player, AfkModule.AfkTriggerReason reason); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/PlayerConnectionEvents.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.fabricmc.fabric.api.event.Event; 5 | import net.fabricmc.fabric.api.event.EventFactory; 6 | 7 | public class PlayerConnectionEvents { 8 | public static final Event WHITELIST_BYPASS = EventFactory.createArrayBacked(WhitelistBypass.class, callbacks -> 9 | (profile) -> { 10 | for (var callback : callbacks) { 11 | if (callback.bypassWhitelist(profile)) 12 | return true; 13 | } 14 | return false; 15 | } 16 | ); 17 | 18 | public static final Event FULL_SERVER_BYPASS = EventFactory.createArrayBacked(FullServerBypass.class, callbacks -> 19 | (profile) -> { 20 | for (var callback : callbacks) { 21 | if (callback.bypassFullServer(profile)) 22 | return true; 23 | } 24 | return false; 25 | } 26 | ); 27 | 28 | @FunctionalInterface 29 | public interface WhitelistBypass { 30 | boolean bypassWhitelist(GameProfile profile); 31 | } 32 | 33 | @FunctionalInterface 34 | public interface FullServerBypass { 35 | boolean bypassFullServer(GameProfile profile); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/PlayerTeleportCallback.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import net.fabricmc.fabric.api.event.Event; 5 | import net.fabricmc.fabric.api.event.EventFactory; 6 | import net.minecraft.server.level.ServerPlayer; 7 | 8 | public interface PlayerTeleportCallback { 9 | Event EVENT = EventFactory.createArrayBacked(PlayerTeleportCallback.class, 10 | (listeners) -> (player, origin, destination) -> { 11 | for (PlayerTeleportCallback listener : listeners) { 12 | listener.teleport(player, origin, destination); 13 | } 14 | }); 15 | 16 | void teleport(ServerPlayer player, ServerLocation origin, ServerLocation destination); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/RestartEvents.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import me.alexdevs.solstice.modules.timeBar.TimeBar; 4 | import net.fabricmc.fabric.api.event.Event; 5 | import net.fabricmc.fabric.api.event.EventFactory; 6 | 7 | public class RestartEvents { 8 | public static final Event SCHEDULED = EventFactory.createArrayBacked(Schedule.class, callbacks -> 9 | (timeBar, type) -> { 10 | for (Schedule callback : callbacks) { 11 | callback.onSchedule(timeBar, type); 12 | } 13 | }); 14 | 15 | public static final Event CANCELED = EventFactory.createArrayBacked(Cancel.class, callbacks -> 16 | (timeBar) -> { 17 | for (Cancel callback : callbacks) { 18 | callback.onCancel(timeBar); 19 | } 20 | }); 21 | 22 | @FunctionalInterface 23 | public interface Schedule { 24 | void onSchedule(TimeBar timeBar, RestartType type); 25 | } 26 | 27 | @FunctionalInterface 28 | public interface Cancel { 29 | void onCancel(TimeBar timeBar); 30 | } 31 | 32 | public enum RestartType { 33 | AUTOMATIC, 34 | MANUAL 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/events/WorldSaveCallback.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.events; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.server.MinecraftServer; 6 | 7 | public interface WorldSaveCallback { 8 | Event EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class, (callbacks) -> 9 | (server, suppressLogs, flush, force) -> { 10 | for (WorldSaveCallback callback : callbacks) { 11 | callback.onSave(server, suppressLogs, flush, force); 12 | } 13 | 14 | }); 15 | 16 | void onSave(MinecraftServer server, boolean suppressLogs, boolean flush, boolean force); 17 | } -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/module/Debug.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.module; 2 | 3 | import net.minecraft.resources.ResourceLocation; 4 | 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | public class Debug { 10 | public static final HashSet commandDebugList = new HashSet<>(); 11 | 12 | public record CommandDebug(ResourceLocation module, String command, List commands, String permission) { 13 | @Override 14 | public boolean equals(Object o) { 15 | if (this == o) return true; 16 | if (!(o instanceof CommandDebug that)) return false; 17 | return Objects.equals(module, that.module) && Objects.equals(command, that.command) && Objects.equals(permission, that.permission); 18 | } 19 | 20 | @Override 21 | public int hashCode() { 22 | return Objects.hash(module, command, permission); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/module/ModuleEntrypoint.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.module; 2 | 3 | import java.util.HashSet; 4 | 5 | /** 6 | * Modules provider for Solstice. 7 | * 8 | *

In {@code fabric.mod.json}, the entrypoint is defined with {@code solstice} key.

9 | * 10 | * Provide a set of {@code ModuleBase} modules to register. 11 | * 12 | * @see ModuleBase 13 | */ 14 | 15 | @FunctionalInterface 16 | public interface ModuleEntrypoint { 17 | HashSet register(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/module/Utils.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.module; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | 5 | public class Utils { 6 | public static void removeCommands(CommandDispatcher dispatcher, String... commandNames) { 7 | for (String commandName : commandNames) { 8 | var command = dispatcher.getRoot().getChild(commandName); 9 | if (command != null) { 10 | dispatcher.getRoot().getChildren().remove(command); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/text/RawPlaceholder.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.text; 2 | 3 | import java.util.Map; 4 | import java.util.regex.Pattern; 5 | 6 | public class RawPlaceholder { 7 | public static final Pattern PATTERN = Format.PLACEHOLDER_PATTERN; 8 | 9 | public static String parse(String input, Map placeholders) { 10 | var matcher = PATTERN.matcher(input); 11 | while (matcher.find()) { 12 | var chunk = matcher.group(); 13 | var key = matcher.group("id"); 14 | input = input.replace(chunk, placeholders.getOrDefault(key, "")); 15 | } 16 | return input; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/text/TextCapabilities.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.text; 2 | 3 | public enum TextCapabilities { 4 | MARKDOWN, 5 | ADVANCED, 6 | LEGACY, 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/text/parser/MarkdownParser.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.text.parser; 2 | 3 | import eu.pb4.placeholders.api.parsers.MarkdownLiteParserV1; 4 | import eu.pb4.placeholders.api.parsers.MarkdownLiteParserV1.MarkdownFormat; 5 | import eu.pb4.placeholders.api.parsers.NodeParser; 6 | 7 | public class MarkdownParser { 8 | public static final MarkdownFormat[] ALL = new MarkdownFormat[]{ 9 | //MarkdownFormat.QUOTE, 10 | MarkdownFormat.BOLD, 11 | MarkdownFormat.ITALIC, 12 | MarkdownFormat.UNDERLINE, 13 | MarkdownFormat.STRIKETHROUGH, 14 | MarkdownFormat.SPOILER, 15 | MarkdownFormat.URL 16 | }; 17 | 18 | public static final NodeParser defaultParser = createParser(ALL); 19 | 20 | public static NodeParser createParser(MarkdownFormat[] capabilities) { 21 | var mdParser = new MarkdownLiteParserV1( 22 | MarkdownComponentParser::spoilerFormatting, 23 | MarkdownComponentParser::quoteFormatting, 24 | MarkdownComponentParser::urlFormatting, 25 | capabilities 26 | ); 27 | 28 | return NodeParser.merge(new CodeParser(), mdParser, new LinkParser()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/utils/MathUtils.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.utils; 2 | 3 | import java.util.Optional; 4 | 5 | public class MathUtils { 6 | public static Optional parseDouble(String value) { 7 | try { 8 | return Optional.of(Double.parseDouble(value)); 9 | } catch (Exception e) { 10 | return Optional.empty(); 11 | } 12 | } 13 | 14 | public static double clamp(double value, double min, double max) { 15 | return Math.max(min, Math.min(max, value)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/utils/PlayerUtils.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.utils; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import me.alexdevs.solstice.Solstice; 5 | import net.minecraft.server.level.ClientInformation; 6 | import net.minecraft.server.level.ServerPlayer; 7 | import java.util.UUID; 8 | 9 | public class PlayerUtils { 10 | public static boolean isOnline(UUID uuid) { 11 | return Solstice.server.getPlayerList().getPlayer(uuid) != null; 12 | } 13 | 14 | public static ServerPlayer loadOfflinePlayer(GameProfile profile) { 15 | if (isOnline(profile.getId())) { 16 | return null; 17 | } 18 | 19 | var playerManager = Solstice.server.getPlayerList(); 20 | var player = playerManager.getPlayerForLogin(profile, ClientInformation.createDefault()); 21 | playerManager.load(player); 22 | return player; 23 | } 24 | 25 | public static void saveOfflinePlayer(ServerPlayer player) { 26 | if (isOnline(player.getUUID())) { 27 | Solstice.LOGGER.warn("Tried to save offline player data for a player that is online."); 28 | return; 29 | } 30 | var saveHandler = Solstice.server.playerDataStorage; 31 | saveHandler.save(player); 32 | Solstice.server.getPlayerList().remove(player); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/api/utils/RegistryUtils.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.api.utils; 2 | 3 | import net.minecraft.core.HolderLookup; 4 | import net.minecraft.world.level.biome.Biome; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class RegistryUtils { 10 | public static List getBiomes(HolderLookup.RegistryLookup registry, boolean includeTags) { 11 | var biomes = new ArrayList<>(registry.listElementIds().map(q -> q.location().toString()).toList()); 12 | if (includeTags) { 13 | biomes.addAll(registry.listTagIds().map(q -> '#' + q.location().toString()).toList()); 14 | } 15 | 16 | return biomes; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/core/Scheduler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.core; 2 | 3 | import java.util.concurrent.ConcurrentLinkedQueue; 4 | import java.util.concurrent.ScheduledFuture; 5 | import java.util.concurrent.ScheduledThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class Scheduler extends ScheduledThreadPoolExecutor { 9 | private final ConcurrentLinkedQueue queue; 10 | 11 | public Scheduler(int corePoolSize, ConcurrentLinkedQueue queue) { 12 | super(corePoolSize); 13 | this.queue = queue; 14 | } 15 | 16 | public ScheduledFuture scheduleSync(Runnable command, long delay, TimeUnit unit) { 17 | return this.schedule(() -> queue.add(command), delay, unit); 18 | } 19 | 20 | public ScheduledFuture scheduleWithFixedDelaySync(Runnable command, long initialDelay, long delay, TimeUnit unit) { 21 | return this.scheduleWithFixedDelay(() -> queue.add(command), initialDelay, delay, unit); 22 | } 23 | 24 | public ScheduledFuture scheduleAtFixedRateSync(Runnable command, long initialDelay, long period, TimeUnit unit) { 25 | return this.scheduleAtFixedRate(() -> queue.add(command), initialDelay, period, unit); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/core/WarmUpManager.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.core; 2 | 3 | public class WarmUpManager { 4 | public WarmUpManager() { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/core/coreModule/data/CoreConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.core.coreModule.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class CoreConfig { 8 | @Comment("Generic date format to use.\nMetric format: dd/MM/yyyy\nUSA format: MM/dd/yyyy") 9 | public String dateFormat = "dd/MM/yyyy"; 10 | 11 | @Comment("Generic time format to use.\n24h format: HH:mm\n12h format: hh:mm a") 12 | public String timeFormat = "HH:mm"; 13 | 14 | @Comment("Generic date + time format to use.") 15 | public String dateTimeFormat = "dd/MM/yyyy HH:mm"; 16 | 17 | @Comment("Format to use when displaying links in chat.") 18 | public String link = "${label}"; 19 | 20 | @Comment("Format to use when hovering over the link in chat.") 21 | public String linkHover = "${url}"; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/core/coreModule/data/CorePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.core.coreModule.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.Date; 7 | 8 | public class CorePlayerData { 9 | public String username; 10 | public @Nullable Date firstJoinedDate; 11 | public @Nullable Date lastSeenDate; 12 | public @Nullable String ipAddress; 13 | public @Nullable ServerLocation logoffPosition = null; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/integrations/ConnectorIntegration.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.integrations; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | 6 | public class ConnectorIntegration { 7 | public static final String CONNECTOR_ID = "connector"; 8 | private static boolean isForge = false; 9 | 10 | public static void register() { 11 | var optContainer = FabricLoader.getInstance().getModContainer(CONNECTOR_ID); 12 | if (optContainer.isPresent()) { 13 | isForge = true; 14 | Solstice.LOGGER.warn("Sinytra connector detected. Support may be limited!"); 15 | } 16 | } 17 | 18 | public static boolean isForge() { 19 | return isForge; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/integrations/TrinketsIntegration.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.integrations; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | 5 | public class TrinketsIntegration { 6 | public static boolean isAvailable() { 7 | return FabricLoader.getInstance().isModLoaded("trinkets"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/locale/Locale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.locale; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.api.text.Format; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | import java.util.Map; 9 | import java.util.function.Supplier; 10 | 11 | public class Locale { 12 | public final ResourceLocation id; 13 | 14 | private final Supplier> localeSupplier; 15 | 16 | public Locale(ResourceLocation id, Supplier> localeSupplier) { 17 | this.id = id; 18 | this.localeSupplier = localeSupplier; 19 | } 20 | 21 | public String raw(String path) { 22 | String fullPath; 23 | if (path.startsWith("~")) { 24 | fullPath = "shared." + path.substring(1); 25 | } else if (path.startsWith("/")) { 26 | fullPath = path.substring(1); 27 | } else { 28 | var id = this.id.toString().replace(':', '.'); 29 | fullPath = "module." + id + "." + path; 30 | } 31 | 32 | return localeSupplier.get().get(fullPath); 33 | } 34 | 35 | public Component get(String path) { 36 | var src = this.raw(path); 37 | return Format.parse(src); 38 | } 39 | 40 | public Component get(String path, PlaceholderContext context) { 41 | var src = this.raw(path); 42 | return Format.parse(src, context); 43 | } 44 | 45 | public Component get(String path, Map placeholders) { 46 | var src = this.raw(path); 47 | return Format.parse(src, placeholders); 48 | } 49 | 50 | public Component get(String path, PlaceholderContext context, Map placeholders) { 51 | var src = this.raw(path); 52 | return Format.parse(src, context, placeholders); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/CommandNodeAccessor.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.tree.CommandNode; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Mutable; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | @Mixin(value = CommandNode.class, remap = false) 10 | public interface CommandNodeAccessor{ 11 | @Accessor("command") 12 | @Mutable 13 | void setCommand(Command command); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/SolsticeMixinConfigPlugin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin; 2 | 3 | import me.alexdevs.solstice.core.ToggleableConfig; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 6 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 7 | 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class SolsticeMixinConfigPlugin implements IMixinConfigPlugin { 12 | private final ToggleableConfig config = ToggleableConfig.get(); 13 | public static final String packageBase = "me.alexdevs.solstice.mixin.modules."; 14 | 15 | @Override 16 | public void onLoad(String mixinPackage) { 17 | } 18 | 19 | @Override 20 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 21 | if (mixinClassName.startsWith(packageBase)) { 22 | var moduleMixin = mixinClassName.replace(packageBase, ""); 23 | var parts = moduleMixin.split("\\."); 24 | var module = parts[0].toLowerCase(); 25 | return config.isEnabled(module); 26 | } 27 | return true; 28 | } 29 | 30 | @Override 31 | public String getRefMapperConfig() { 32 | return null; 33 | } 34 | 35 | @Override 36 | public void acceptTargets(Set myTargets, Set otherTargets) { 37 | 38 | } 39 | 40 | @Override 41 | public List getMixins() { 42 | return null; 43 | } 44 | 45 | @Override 46 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 47 | 48 | } 49 | 50 | @Override 51 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/events/WorldSaveEventMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.events; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.events.WorldSaveCallback; 5 | import net.minecraft.server.MinecraftServer; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | @Mixin(MinecraftServer.class) 12 | public class WorldSaveEventMixin { 13 | @Inject(method = "saveEverything", at = @At("TAIL")) 14 | public void save(boolean suppressLogs, boolean flush, boolean force, CallbackInfoReturnable cir) { 15 | try { 16 | WorldSaveCallback.EVENT.invoker().onSave((MinecraftServer) (Object) this, suppressLogs, flush, force); 17 | } catch (Exception e) { 18 | Solstice.LOGGER.error("Exception emitting world save event", e); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/admin/ConnectionBypassMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.admin; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.events.PlayerConnectionEvents; 6 | import net.minecraft.server.dedicated.DedicatedPlayerList; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | @Mixin(DedicatedPlayerList.class) 13 | public abstract class ConnectionBypassMixin { 14 | @Inject(method = "isWhiteListed", at = @At("HEAD"), cancellable = true) 15 | public void solstice$bypassWhitelist(GameProfile profile, CallbackInfoReturnable cir) { 16 | try { 17 | if (PlayerConnectionEvents.WHITELIST_BYPASS.invoker().bypassWhitelist(profile)) 18 | cir.setReturnValue(true); 19 | } catch (Exception e) { 20 | Solstice.LOGGER.error("Error checking whitelist bypass for profile {}", profile.getId(), e); 21 | } 22 | } 23 | 24 | @Inject(method = "canBypassPlayerLimit", at = @At("HEAD"), cancellable = true) 25 | public void solstice$bypassPlayerLimit(GameProfile profile, CallbackInfoReturnable cir) { 26 | try { 27 | if (PlayerConnectionEvents.FULL_SERVER_BYPASS.invoker().bypassFullServer(profile)) 28 | cir.setReturnValue(true); 29 | } catch (Exception e) { 30 | Solstice.LOGGER.error("Error checking full server bypass for profile {}", profile.getId(), e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/afk/FixPlayerSleepPercentageMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.afk; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.ModuleProvider; 5 | import me.alexdevs.solstice.modules.afk.AfkModule; 6 | import net.minecraft.server.level.ServerPlayer; 7 | import net.minecraft.server.players.SleepStatus; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | @Mixin(SleepStatus.class) 13 | public abstract class FixPlayerSleepPercentageMixin { 14 | @Redirect(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isSpectator()Z")) 15 | public boolean solstice$fixTotalPlayers(ServerPlayer player) { 16 | return player.isSpectator() || ModuleProvider.AFK.isPlayerAfk(player); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/back/PreTeleportMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.back; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.ServerLocation; 5 | import me.alexdevs.solstice.modules.ModuleProvider; 6 | import me.alexdevs.solstice.modules.back.BackModule; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.server.level.ServerPlayer; 9 | import net.minecraft.world.entity.RelativeMovement; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | import java.util.Set; 16 | 17 | @Mixin(ServerPlayer.class) 18 | public abstract class PreTeleportMixin { 19 | @Inject(method = "teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FF)Z", at = @At("HEAD")) 20 | public void solstice$getPreTeleportLocation(ServerLevel world, double destX, double destY, double destZ, Set flags, float yaw, float pitch, CallbackInfoReturnable cir) { 21 | var player = (ServerPlayer) (Object) this; 22 | ModuleProvider.BACK.setPlayerLastLocation(player.getUUID(), new ServerLocation(player)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/ban/CustomBanMessageMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.ban; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.mojang.authlib.GameProfile; 5 | import me.alexdevs.solstice.Solstice; 6 | import me.alexdevs.solstice.modules.ban.formatters.BanMessageFormatter; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.network.chat.MutableComponent; 9 | import net.minecraft.server.players.PlayerList; 10 | import net.minecraft.server.players.UserBanListEntry; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | import java.net.SocketAddress; 17 | 18 | @Mixin(PlayerList.class) 19 | public abstract class CustomBanMessageMixin { 20 | @Inject(method = "canPlayerLogin", at = @At(value = "RETURN", ordinal = 0), cancellable = true) 21 | public void solstice$formatBanMessage(SocketAddress address, GameProfile profile, CallbackInfoReturnable cir, @Local UserBanListEntry bannedPlayerEntry, @Local MutableComponent mutableText) { 22 | try { 23 | var reasonText = BanMessageFormatter.format(profile, bannedPlayerEntry); 24 | cir.setReturnValue(reasonText); 25 | } catch (Exception ex) { 26 | Solstice.LOGGER.error("Something went wrong while formatting the ban message", ex); 27 | 28 | // Ensure the original text message is returned to avoid exploits and bypass the ban 29 | cir.setReturnValue(mutableText); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/core/RealPingMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.core; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.server.network.ServerCommonPacketListenerImpl; 5 | import org.objectweb.asm.Opcodes; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(ServerCommonPacketListenerImpl.class) 12 | public abstract class RealPingMixin { 13 | @Shadow 14 | private int latency; 15 | 16 | @Redirect(method = "handleKeepAlive", at = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ServerCommonPacketListenerImpl;latency:I", opcode = Opcodes.PUTFIELD)) 17 | public void solstice$realPing(ServerCommonPacketListenerImpl instance, int value, @Local int i) { 18 | latency = i; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/customname/CustomDisplayNameMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.customname; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.ModuleProvider; 5 | import me.alexdevs.solstice.modules.customName.CustomNameModule; 6 | import net.minecraft.network.chat.MutableComponent; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraft.world.entity.player.Player; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(Player.class) 16 | public abstract class CustomDisplayNameMixin { 17 | @Shadow 18 | private MutableComponent decorateDisplayNameComponent(MutableComponent component) { 19 | return null; 20 | } 21 | 22 | @Inject(method = "getDisplayName", at = @At("HEAD"), cancellable = true) 23 | public void solstice$getDisplayName(CallbackInfoReturnable cir) { 24 | var name = ModuleProvider.CUSTOMNAME.getNameForPlayer((ServerPlayer) (Object) this); 25 | cir.setReturnValue(decorateDisplayNameComponent(name)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/miscellaneous/BypassSleepingInBedCheckMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.miscellaneous; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.ModuleProvider; 5 | import me.alexdevs.solstice.modules.miscellaneous.MiscellaneousModule; 6 | import net.minecraft.world.entity.LivingEntity; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | @Mixin(LivingEntity.class) 13 | public abstract class BypassSleepingInBedCheckMixin { 14 | @Inject(method = "checkBedExists", at = @At("HEAD"), cancellable = true) 15 | private void isSleepingInBed(CallbackInfoReturnable cir) { 16 | if (ModuleProvider.MISCELLANEOUS.isCommandSleep((LivingEntity) (Object) this)) { 17 | cir.setReturnValue(true); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/sign/FormatSignMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.sign; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.ModuleProvider; 5 | import me.alexdevs.solstice.modules.sign.SignModule; 6 | import net.minecraft.server.network.FilteredText; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.level.block.entity.SignBlockEntity; 9 | import net.minecraft.world.level.block.entity.SignText; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | import java.util.List; 16 | 17 | @Mixin(SignBlockEntity.class) 18 | public abstract class FormatSignMixin { 19 | 20 | @Inject(method = "setMessages", at = @At("HEAD"), cancellable = true) 21 | private void solstice$formatSignText(Player player, List messages, SignText text, CallbackInfoReturnable cir) { 22 | if (ModuleProvider.SIGN.canFormatSign(player)) { 23 | try { 24 | text = SignModule.formatSign(messages, text); 25 | cir.setReturnValue(text); 26 | } catch (Exception e) { 27 | Solstice.LOGGER.error("Something went wrong while formatting a sign!", e); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/spawn/OverrideNewPlayerSpawnPointMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.spawn; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import me.alexdevs.solstice.modules.ModuleProvider; 5 | import net.minecraft.nbt.CompoundTag; 6 | import net.minecraft.resources.ResourceKey; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.players.PlayerList; 10 | import net.minecraft.world.level.Level; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | import java.util.Optional; 16 | 17 | @Mixin(PlayerList.class) 18 | public abstract class OverrideNewPlayerSpawnPointMixin { 19 | // Lnet/minecraft/server/MinecraftServer;getLevel(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/server/level/ServerLevel; 20 | @Redirect( 21 | method = "placeNewPlayer", 22 | at = @At( 23 | value = "INVOKE", 24 | target = "Lnet/minecraft/server/MinecraftServer;getLevel(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/server/level/ServerLevel;" 25 | ) 26 | ) 27 | public ServerLevel solstice$overrideWorld(MinecraftServer server, ResourceKey dimension, @Local Optional optional) { 28 | if (optional.isEmpty()) { 29 | var spawn = ModuleProvider.SPAWN; 30 | var firstSpawn = spawn.getFirstSpawn(); 31 | if (firstSpawn != null) { 32 | return firstSpawn.getWorld(server); 33 | } 34 | return spawn.getGlobalSpawnWorld(); 35 | } 36 | return server.getLevel(dimension); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/styling/CustomAdvancementMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.styling; 2 | 3 | import me.alexdevs.solstice.modules.styling.formatters.AdvancementFormatter; 4 | import net.minecraft.advancements.AdvancementHolder; 5 | import net.minecraft.advancements.AdvancementType; 6 | import net.minecraft.network.chat.MutableComponent; 7 | import net.minecraft.server.level.ServerPlayer; 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.CallbackInfoReturnable; 12 | 13 | @Mixin(AdvancementType.class) 14 | public abstract class CustomAdvancementMixin { 15 | 16 | @Inject(method = "createAnnouncement", at = @At("HEAD"), cancellable = true) 17 | public void solstice$getCustomAnnouncement(AdvancementHolder advancement, ServerPlayer player, CallbackInfoReturnable cir) { 18 | cir.setReturnValue(AdvancementFormatter.getText(player, advancement, (AdvancementType) (Object) this).copy()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/styling/CustomChatMessageMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.styling; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import me.alexdevs.solstice.modules.styling.CustomSentMessage; 5 | import net.minecraft.network.chat.OutgoingChatMessage; 6 | import net.minecraft.network.chat.PlayerChatMessage; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraft.server.players.PlayerList; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Redirect; 12 | 13 | @Mixin(PlayerList.class) 14 | public abstract class CustomChatMessageMixin { 15 | @Redirect( 16 | method = "broadcastChatMessage(Lnet/minecraft/network/chat/PlayerChatMessage;Ljava/util/function/Predicate;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/network/chat/ChatType$Bound;)V", 17 | at = @At( 18 | value = "INVOKE", 19 | target = "Lnet/minecraft/network/chat/OutgoingChatMessage;create(Lnet/minecraft/network/chat/PlayerChatMessage;)Lnet/minecraft/network/chat/OutgoingChatMessage;") 20 | ) 21 | private OutgoingChatMessage solstice$broadcast(PlayerChatMessage message, @Local(argsOnly = true) ServerPlayer sender) { 22 | return CustomSentMessage.of(message, sender); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/styling/CustomDeathMessageMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.styling; 2 | 3 | import me.alexdevs.solstice.modules.styling.formatters.DeathFormatter; 4 | import net.minecraft.network.chat.Component; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.world.damagesource.CombatTracker; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(ServerPlayer.class) 12 | public abstract class CustomDeathMessageMixin { 13 | @Redirect(method = "die", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatTracker;getDeathMessage()Lnet/minecraft/network/chat/Component;")) 14 | private Component solstice$getDeathMessage(CombatTracker instance) { 15 | var player = (ServerPlayer) (Object) this; 16 | return DeathFormatter.onDeath(player, instance); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/styling/CustomSentMessageMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.styling; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.styling.CustomSentMessage; 5 | import net.minecraft.network.chat.OutgoingChatMessage; 6 | import net.minecraft.network.chat.PlayerChatMessage; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | @Mixin(OutgoingChatMessage.class) 13 | public interface CustomSentMessageMixin { 14 | @Inject(method = "create", at = @At("HEAD"), cancellable = true) 15 | private static void solstice$of(PlayerChatMessage message, CallbackInfoReturnable cir) { 16 | if (message.isSystem()) { 17 | cir.setReturnValue(new CustomSentMessage.Profileless(message.decoratedContent())); 18 | } else { 19 | var sender = Solstice.server.getPlayerList().getPlayer(message.sender()); 20 | cir.setReturnValue(new CustomSentMessage.Chat(message, sender)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/styling/PlayerDisconnectMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.styling; 2 | 3 | import me.alexdevs.solstice.modules.styling.StylingModule; 4 | import me.alexdevs.solstice.modules.styling.formatters.ConnectionActivityFormatter; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.server.level.ServerPlayer; 7 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 8 | import net.minecraft.server.players.PlayerList; 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.Redirect; 13 | 14 | @Mixin(ServerGamePacketListenerImpl.class) 15 | public abstract class PlayerDisconnectMixin { 16 | @Shadow 17 | public ServerPlayer player; 18 | 19 | @Redirect(method = "removePlayerFromWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V")) 20 | private void solstice$sendLeaveMessage(PlayerList playerList, Component message, boolean bypassHiddenChat) { 21 | var formattedMessage = ConnectionActivityFormatter.onLeave(this.player); 22 | StylingModule.broadcastActivity(playerList, formattedMessage, bypassHiddenChat); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/tablist/CustomPlayerListNameMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.tablist; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.text.Format; 6 | import me.alexdevs.solstice.modules.tablist.data.TabListConfig; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.server.level.ServerPlayer; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(ServerPlayer.class) 15 | public abstract class CustomPlayerListNameMixin { 16 | @Inject(method = "getTabListDisplayName", at = @At("HEAD"), cancellable = true) 17 | private void solstice$customizePlayerListName(CallbackInfoReturnable callback) { 18 | if (Solstice.configManager.getData(TabListConfig.class).enable) { 19 | var player = (ServerPlayer) (Object) this; 20 | var playerContext = PlaceholderContext.of(player); 21 | var text = Format.parse(Solstice.configManager.getData(TabListConfig.class).playerTabName, playerContext); 22 | callback.setReturnValue(text); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/mixin/modules/tablist/UpdatePlayerListMixin.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.mixin.modules.tablist; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.modules.tablist.data.TabListConfig; 5 | import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; 6 | import net.minecraft.server.level.ServerPlayer; 7 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | import java.util.EnumSet; 15 | import java.util.List; 16 | 17 | @Mixin(ServerGamePacketListenerImpl.class) 18 | public abstract class UpdatePlayerListMixin { 19 | @Shadow 20 | public ServerPlayer player; 21 | 22 | @Inject(method = "tick", at = @At("TAIL")) 23 | private void solstice$updatePlayerList(CallbackInfo ci) { 24 | if (Solstice.configManager.getData(TabListConfig.class).enable) { 25 | var packet = new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED), List.of(this.player)); 26 | this.player.getServer().getPlayerList().broadcastAll(packet); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/admin/AdminModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.admin; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.events.PlayerConnectionEvents; 5 | import me.alexdevs.solstice.api.module.ModuleBase; 6 | import me.lucko.fabric.api.permissions.v0.Permissions; 7 | import net.minecraft.resources.ResourceLocation; 8 | 9 | public class AdminModule extends ModuleBase { 10 | public AdminModule(ResourceLocation id) { 11 | super(id); 12 | } 13 | 14 | @Override 15 | public void init() { 16 | PlayerConnectionEvents.WHITELIST_BYPASS.register(profile -> { 17 | try { 18 | return Permissions.check(profile, getWhitelistBypassPermission(), false).get(); 19 | } catch (Exception e) { 20 | Solstice.LOGGER.error("Error checking whitelist bypass for profile {}", profile.getId(), e); 21 | } 22 | return false; 23 | }); 24 | 25 | PlayerConnectionEvents.FULL_SERVER_BYPASS.register(profile -> { 26 | try { 27 | return Permissions.check(profile, getFullServerBypassPermission(), false).get(); 28 | } catch (Exception e) { 29 | Solstice.LOGGER.error("Error checking full server bypass for profile {}", profile.getId(), e); 30 | } 31 | return false; 32 | }); 33 | } 34 | 35 | public String getWhitelistBypassPermission() { 36 | return getPermissionNode("bypass.whitelist"); 37 | } 38 | 39 | public String getFullServerBypassPermission() { 40 | return getPermissionNode("bypass.fullserver"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/commands/AfkCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.afk.AfkModule; 6 | import net.minecraft.commands.CommandSourceStack; 7 | 8 | import java.util.List; 9 | 10 | import static net.minecraft.commands.Commands.literal; 11 | 12 | public class AfkCommand extends ModCommand { 13 | public AfkCommand(AfkModule module) { 14 | super(module); 15 | } 16 | 17 | @Override 18 | public List getNames() { 19 | return List.of("afk"); 20 | } 21 | 22 | @Override 23 | public LiteralArgumentBuilder command(String name) { 24 | return literal(name) 25 | .requires(require(true)) 26 | .executes(context -> { 27 | var player = context.getSource().getPlayerOrException(); 28 | 29 | module.setPlayerAfk(player, !module.isPlayerAfk(player)); 30 | 31 | return 1; 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/data/AfkLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.data; 2 | 3 | import java.util.Map; 4 | 5 | public class AfkLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("goneAfk", "%player:displayname% is now AFK"), 8 | Map.entry("returnAfk", "%player:displayname% is no longer AFK"), 9 | Map.entry("yourActiveTime", "Your active time is ${activeTime}."), 10 | Map.entry("playerActiveTime", "${player}'s active time is ${activeTime}."), 11 | Map.entry("neverPlayed", "This player never played."), 12 | Map.entry("notFound", "Player not found."), 13 | Map.entry("leaderboardHeader", "Active Time Leaderboard:"), 14 | Map.entry("leaderboardEntry", " ${index}. ${player}: ${time}"), 15 | Map.entry("activeTimeSet", "Set ${player}'s active time to ${time}!"), 16 | Map.entry("leaderboardRecalculated", "Recalculated active time leaderboard!") 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/data/AfkPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.data; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class AfkPlayerData { 6 | @Expose 7 | public int activeTime = 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/data/AfkServerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class AfkServerData { 7 | public boolean forceCalculateLeaderboard = true; 8 | public List leaderboard = new ArrayList<>(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/data/LeaderboardEntry.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.data; 2 | 3 | import java.util.UUID; 4 | 5 | public class LeaderboardEntry { 6 | protected String name; 7 | protected final UUID uuid; 8 | protected int activeTime; 9 | 10 | public LeaderboardEntry(String name, UUID uuid, int activeTime) { 11 | this.name = name; 12 | this.uuid = uuid; 13 | this.activeTime = activeTime; 14 | } 15 | 16 | public String name() { 17 | return name; 18 | } 19 | 20 | public void name(String name) { 21 | this.name = name; 22 | } 23 | 24 | public UUID uuid() { 25 | return uuid; 26 | } 27 | 28 | public int activeTime() { 29 | return activeTime; 30 | } 31 | 32 | public void activeTime(int activeTime) { 33 | this.activeTime = activeTime; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/afk/data/PlayerActivityState.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.afk.data; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.ServerLocation; 5 | import me.alexdevs.solstice.modules.ModuleProvider; 6 | import me.alexdevs.solstice.modules.afk.AfkModule; 7 | import me.lucko.fabric.api.permissions.v0.Permissions; 8 | import net.minecraft.server.level.ServerPlayer; 9 | 10 | public class PlayerActivityState { 11 | public ServerLocation location; 12 | public int lastUpdate; 13 | public boolean isAfk; 14 | public boolean afkEnabled; 15 | public boolean activeTimeEnabled; 16 | 17 | public PlayerActivityState(ServerPlayer player, int lastUpdate) { 18 | var module = ModuleProvider.AFK; 19 | this.location = new ServerLocation(player); 20 | this.lastUpdate = lastUpdate; 21 | this.isAfk = false; 22 | this.afkEnabled = Permissions.check(player, module.getPermissionNode(), true); 23 | this.activeTimeEnabled = Permissions.check(player, module.getPermissionNode("activetime"), true); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/announcement/data/AnnouncementConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.announcement.data; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 5 | import org.spongepowered.configurate.objectmapping.meta.Comment; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @ConfigSerializable 11 | public class AnnouncementConfig { 12 | @Comment("Enable automatic announcements functionality.") 13 | public boolean enable = true; 14 | @Comment("Pick the next announcement randomly, else linearly.") 15 | public boolean pickRandomly = false; 16 | // every 5 mins 17 | @Comment("Send announcement every X seconds. Defaults to 300 seconds.") 18 | public int delay = 300; 19 | @Comment("Announcement list. Announcements can have a permission as condition. If result is true, the permission has to be granted, else the permission has to be denied (or unset).") 20 | public ArrayList announcements = new ArrayList<>(List.of( 21 | new Announcement("Tip! Solstice is open-source! Contribute on GitHub!"), 22 | new Announcement("Fun fact! This announcement is only visible to players that do not have the 'solstice.example' permission granted!", "solstice.example", false) 23 | )); 24 | 25 | @ConfigSerializable 26 | public record Announcement(String text, @Nullable String permission, @Nullable Boolean result) { 27 | public Announcement(String text) { 28 | this(text, null, null); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/back/data/BackConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.back.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 5 | import org.spongepowered.configurate.objectmapping.meta.Comment; 6 | 7 | @ConfigSerializable 8 | public class BackConfig { 9 | @Comment("Keep the location of players' back location saved between restarts and player reconnections.") 10 | public boolean persistLocation = true; 11 | 12 | @Comment("Clear the /back location of an online player after the configured seconds. Set to -1 to disable. Default: -1") 13 | public int clearTimeout = -1; 14 | 15 | @Comment("Clear the /back location of an offline player after the configured seconds. Set to -1 to disable. Default: 300 (5 minutes)") 16 | public int offlineClearTimeout = -1; 17 | 18 | // It's actually a cuboid and the "range" is the length 19 | @Comment("Range for the safe position checks.") 20 | public int safeCheckRange = ServerLocation.SAFE_RANGE; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/back/data/BackLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.back.data; 2 | 3 | import java.util.Map; 4 | 5 | public class BackLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("teleporting", "Teleporting to previous location..."), 8 | Map.entry("noPosition", "There is no location to return back to."), 9 | Map.entry("teleportFailed", "Could not safely teleport to previous location.\n ${forceBackButton}"), 10 | Map.entry("forceLabel", "Force back"), 11 | Map.entry("forceHover", "Force teleport to previous location. This may be dangerous!") 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/back/data/BackPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.back.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.Date; 7 | 8 | public class BackPlayerData { 9 | public @Nullable Date lastTeleportDate; 10 | public @Nullable ServerLocation lastTeleportLocation; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ban/BanModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ban; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.ban.commands.BanCommand; 5 | import me.alexdevs.solstice.modules.ban.commands.TempBanCommand; 6 | import me.alexdevs.solstice.modules.ban.commands.UnbanCommand; 7 | import me.alexdevs.solstice.modules.ban.data.BanLocale; 8 | import net.minecraft.resources.ResourceLocation; 9 | 10 | public class BanModule extends ModuleBase.Toggleable { 11 | public BanModule(ResourceLocation id) { 12 | super(id); 13 | } 14 | 15 | @Override 16 | public void init() { 17 | registerLocale(BanLocale.MODULE); 18 | 19 | commands.add(new BanCommand(this)); 20 | commands.add(new TempBanCommand(this)); 21 | commands.add(new UnbanCommand(this)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ban/data/BanLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ban.data; 2 | 3 | import java.util.Map; 4 | 5 | public class BanLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("banMessageFormat", "You are banned from this server:\n\n${reason}"), 8 | Map.entry("tempBanMessageFormat", "You are temporarily banned from this server:\n\n${reason}\n\nExpires: ${expiry_date}") 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ban/formatters/BanMessageFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ban.formatters; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import eu.pb4.placeholders.api.PlaceholderContext; 5 | import me.alexdevs.solstice.Solstice; 6 | import me.alexdevs.solstice.api.text.Format; 7 | import me.alexdevs.solstice.core.coreModule.CoreModule; 8 | import me.alexdevs.solstice.modules.ModuleProvider; 9 | import me.alexdevs.solstice.modules.ban.BanModule; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.server.players.UserBanListEntry; 12 | 13 | import java.text.SimpleDateFormat; 14 | import java.util.Map; 15 | 16 | public class BanMessageFormatter { 17 | public static Component format(GameProfile profile, UserBanListEntry entry) { 18 | var locale = ModuleProvider.BAN.locale(); 19 | var coreConfig = CoreModule.getConfig(); 20 | var df = new SimpleDateFormat(coreConfig.dateTimeFormat); 21 | 22 | var context = PlaceholderContext.of(profile, Solstice.server); 23 | var expiryDate = Component.nullToEmpty(entry.getExpires() != null ? df.format(entry.getExpires()) : "never"); 24 | Map placeholders = Map.of( 25 | "reason", Format.parse(entry.getReason(), context), 26 | "creation_date", Component.nullToEmpty(df.format(entry.getCreated())), 27 | "expiry_date", expiryDate, 28 | "source", Component.nullToEmpty(entry.getSource()) 29 | ); 30 | 31 | var format = entry.getExpires() != null ? locale.raw("tempBanMessageFormat") : locale.raw("banMessageFormat"); 32 | 33 | return Format.parse(format, context, placeholders); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/broadcast/BroadcastModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.broadcast; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.broadcast.commands.BroadcastCommand; 6 | import me.alexdevs.solstice.modules.broadcast.commands.PlainBroadcastCommand; 7 | import me.alexdevs.solstice.modules.broadcast.data.BroadcastConfig; 8 | import net.minecraft.resources.ResourceLocation; 9 | public class BroadcastModule extends ModuleBase.Toggleable { 10 | 11 | 12 | public BroadcastModule(ResourceLocation id) { 13 | super(id); 14 | } 15 | 16 | @Override 17 | public void init() { 18 | registerConfig(BroadcastConfig.class, BroadcastConfig::new); 19 | 20 | commands.add(new BroadcastCommand(this)); 21 | commands.add(new PlainBroadcastCommand(this)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/broadcast/commands/PlainBroadcastCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.broadcast.commands; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import eu.pb4.placeholders.api.PlaceholderContext; 6 | import me.alexdevs.solstice.Solstice; 7 | import me.alexdevs.solstice.api.module.ModCommand; 8 | import me.alexdevs.solstice.api.text.Format; 9 | import me.alexdevs.solstice.modules.broadcast.BroadcastModule; 10 | import net.minecraft.commands.CommandSourceStack; 11 | 12 | import java.util.List; 13 | 14 | import static net.minecraft.commands.Commands.argument; 15 | import static net.minecraft.commands.Commands.literal; 16 | 17 | public class PlainBroadcastCommand extends ModCommand { 18 | 19 | public PlainBroadcastCommand(BroadcastModule module) { 20 | super(module); 21 | } 22 | 23 | @Override 24 | public List getNames() { 25 | return List.of("plainbroadcast", "pbc"); 26 | } 27 | 28 | @Override 29 | public LiteralArgumentBuilder command(String name) { 30 | return literal(name) 31 | .requires(require("plain", 2)) 32 | .then(argument("message", StringArgumentType.greedyString()) 33 | .executes(context -> { 34 | var message = StringArgumentType.getString(context, "message"); 35 | var serverContext = PlaceholderContext.of(context.getSource().getServer()); 36 | 37 | Solstice.getInstance().broadcast(Format.parse(message, serverContext)); 38 | 39 | return 1; 40 | })); 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/broadcast/data/BroadcastConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.broadcast.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class BroadcastConfig { 8 | @Comment("Format to use when broadcasting a message.") 9 | public String format = "[Broadcast] ${message}"; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/commandSpy/data/CommandSpyConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.commandSpy.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @ConfigSerializable 10 | public class CommandSpyConfig { 11 | @Comment("Commands to ignore.") 12 | public ArrayList ignoredCommands = new ArrayList<>(List.of( 13 | "tell", "w", "msg", "dm", "r", "staffchat", "sc", "helpop", "sos" 14 | )); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/commandSpy/data/CommandSpyLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.commandSpy.data; 2 | 3 | import java.util.Map; 4 | 5 | public class CommandSpyLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("spyFormat", "\uD83D\uDC41 ${player}: /${command}") 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/cooldown/data/CooldownConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.cooldown.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | @ConfigSerializable 10 | public class CooldownConfig { 11 | @Comment("Key-value pairs of commands to check for cooldown. You can use Regular Expressions as command patterns.\nCommand = seconds to cool down.") 12 | public Map nodes = Map.ofEntries( 13 | Map.entry("rtp", CooldownSetting.of(List.of("rtp", "rtp.*"), 600)) 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/cooldown/data/CooldownLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.cooldown.data; 2 | 3 | import java.util.Map; 4 | 5 | public class CooldownLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("cooldown", "You are on cooldown for ${timespan}."), 8 | Map.entry("checkResult", "${player} has a cooldown of ${timespan} for ${key}."), 9 | Map.entry("noCooldown", "${player} has no cooldown for ${key}."), 10 | Map.entry("exempt", "${player} is exempt for ${key}."), 11 | Map.entry("cleared", "Cleared ${key} cooldown for ${player}.") 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/cooldown/data/CooldownSetting.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.cooldown.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | 5 | import java.util.List; 6 | 7 | @ConfigSerializable 8 | public class CooldownSetting { 9 | public static CooldownSetting of(List nodes, int cooldown) { 10 | var setting = new CooldownSetting(); 11 | setting.nodes = nodes; 12 | setting.cooldown = cooldown; 13 | 14 | return setting; 15 | } 16 | 17 | public List nodes = List.of(); 18 | public int cooldown = 0; 19 | 20 | private String key; 21 | public void setKey(String key) { 22 | this.key = key; 23 | } 24 | 25 | public String getKey() { 26 | return key; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/customName/data/CustomNameConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.customName.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @ConfigSerializable 10 | public class CustomNameConfig { 11 | @Comment("Customize player display names based on their LuckPerms group. Priority is determined by the list order: first comes before last.") 12 | public ArrayList nameFormats = new ArrayList<>(List.of( 13 | new NameFormat("admin", "${prefix}${name}${suffix}"), 14 | new NameFormat("default", "${prefix}${name}${suffix}") 15 | )); 16 | 17 | @ConfigSerializable 18 | public record NameFormat(String group, String format) { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/customName/data/CustomNameLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.customName.data; 2 | 3 | import java.util.Map; 4 | 5 | public class CustomNameLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("setSelf", "Nickname set to ${nickname}!"), 8 | Map.entry("clearedSelf", "Nickname cleared!"), 9 | Map.entry("setOther", "${player}'s nickname set to ${nickname}!"), 10 | Map.entry("clearedOther", "${player}'s nickname cleared!"), 11 | Map.entry("errorEmpty", "Username cannot be empty!") 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/customName/data/CustomNamePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.customName.data; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | public class CustomNamePlayerData { 6 | public @Nullable String nickname; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/enderchest/EnderChestModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.enderchest; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.enderchest.commands.EnderChestCommand; 6 | import me.alexdevs.solstice.modules.enderchest.data.EnderChestLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | public class EnderChestModule extends ModuleBase.Toggleable { 9 | 10 | 11 | public EnderChestModule(ResourceLocation id) { 12 | super(id); 13 | } 14 | 15 | @Override 16 | public void init() { 17 | registerLocale(EnderChestLocale.LOCALE); 18 | 19 | commands.add(new EnderChestCommand(this)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/enderchest/data/EnderChestLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.enderchest.data; 2 | 3 | import java.util.Map; 4 | 5 | public class EnderChestLocale { 6 | public static final Map LOCALE = Map.ofEntries( 7 | Map.entry("title", "${player}'s Ender Chest"), 8 | Map.entry("exempt", "You cannot open this Ender Chest because the player is exempt."), 9 | Map.entry("opened", "Opened ${player}'s Ender Chest.") 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/experiments/ExperimentsModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.experiments; 2 | 3 | import me.alexdevs.solstice.api.module.ModCommand; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.experiments.commands.FlagsCommand; 6 | import me.alexdevs.solstice.modules.experiments.commands.SafeTeleportCommand; 7 | import me.alexdevs.solstice.modules.experiments.commands.TimeSpanCommand; 8 | import net.minecraft.resources.ResourceLocation; 9 | 10 | import java.util.Collection; 11 | import java.util.List; 12 | public class ExperimentsModule extends ModuleBase { 13 | public static final boolean ENABLED = false; 14 | 15 | 16 | public ExperimentsModule(ResourceLocation id) { 17 | super(id); 18 | } 19 | 20 | @Override 21 | public void init() { 22 | commands.add(new TimeSpanCommand(this)); 23 | commands.add(new FlagsCommand(this)); 24 | commands.add(new SafeTeleportCommand(this)); 25 | } 26 | 27 | @Override 28 | public Collection> getCommands() { 29 | if (!ENABLED) return List.of(); 30 | 31 | return commands; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/experiments/commands/TimeSpanCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.experiments.commands; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import me.alexdevs.solstice.api.command.TimeSpan; 8 | import me.alexdevs.solstice.api.module.ModCommand; 9 | import me.alexdevs.solstice.modules.experiments.ExperimentsModule; 10 | import net.minecraft.commands.CommandSourceStack; 11 | import net.minecraft.network.chat.Component; 12 | import java.util.List; 13 | 14 | import static net.minecraft.commands.Commands.argument; 15 | import static net.minecraft.commands.Commands.literal; 16 | 17 | public class TimeSpanCommand extends ModCommand { 18 | 19 | public TimeSpanCommand(ExperimentsModule module) { 20 | super(module); 21 | } 22 | 23 | @Override 24 | public List getNames() { 25 | return List.of("timespan"); 26 | } 27 | 28 | @Override 29 | public LiteralArgumentBuilder command(String name) { 30 | return literal(name) 31 | .requires(require(true)) 32 | .then(argument("timespan", StringArgumentType.string()) 33 | .suggests(TimeSpan::suggest) 34 | .executes(this::execute)); 35 | } 36 | 37 | private int execute(CommandContext context) throws CommandSyntaxException { 38 | var timespan = TimeSpan.getTimeSpan(context, "timespan"); 39 | context.getSource().sendSuccess(() -> Component.nullToEmpty(String.format("Got %s (%d)", TimeSpan.toShortString(timespan), timespan)), false); 40 | return 1; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/extinguish/ExtinguishModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.extinguish; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.extinguish.commands.ExtinguishCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | public class ExtinguishModule extends ModuleBase.Toggleable { 7 | 8 | 9 | public ExtinguishModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new ExtinguishCommand(this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/feed/FeedModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.feed; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.feed.commands.FeedCommand; 6 | import me.alexdevs.solstice.modules.feed.data.FeedLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | public class FeedModule extends ModuleBase.Toggleable { 9 | 10 | 11 | public FeedModule(ResourceLocation id) { 12 | super(id); 13 | } 14 | 15 | @Override 16 | public void init() { 17 | registerLocale(FeedLocale.MODULE); 18 | 19 | commands.add(new FeedCommand(this)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/feed/data/FeedLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.feed.data; 2 | 3 | import java.util.Map; 4 | 5 | public class FeedLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("fed", "Fed ${entity}!"), 8 | Map.entry("fedMultiple", "Fed ${count} players!") 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/fly/FlyModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.fly; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.fly.commands.FlyCommand; 6 | import me.alexdevs.solstice.modules.fly.data.FlyLocale; 7 | import me.alexdevs.solstice.modules.fly.data.FlyPlayerData; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; 9 | import net.minecraft.resources.ResourceLocation; 10 | public class FlyModule extends ModuleBase.Toggleable { 11 | 12 | 13 | public FlyModule(ResourceLocation id) { 14 | super(id); 15 | } 16 | 17 | @Override 18 | public void init() { 19 | registerLocale(FlyLocale.MODULE); 20 | registerPlayerData(FlyPlayerData.class, FlyPlayerData::new); 21 | 22 | commands.add(new FlyCommand(this)); 23 | 24 | ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { 25 | var player = handler.getPlayer(); 26 | 27 | var data = Solstice.playerData.get(player).getData(FlyPlayerData.class); 28 | if(data.flightEnabled) { 29 | var abilities = player.getAbilities(); 30 | abilities.mayfly = true; 31 | player.onUpdateAbilities(); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/fly/data/FlyLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.fly.data; 2 | 3 | import java.util.Map; 4 | 5 | public class FlyLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("enabled", "Flight enabled"), 8 | Map.entry("disabled", "Flight disabled"), 9 | Map.entry("enabledForOther", "Flight enabled for ${player}"), 10 | Map.entry("disabledForOther", "Flight disabled for ${player}") 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/fly/data/FlyPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.fly.data; 2 | 3 | public class FlyPlayerData { 4 | public boolean flightEnabled = false; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/god/GodModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.god; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.god.commands.GodCommand; 6 | import me.alexdevs.solstice.modules.god.data.GodLocale; 7 | import me.alexdevs.solstice.modules.god.data.GodPlayerData; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; 9 | import net.minecraft.resources.ResourceLocation; 10 | public class GodModule extends ModuleBase.Toggleable { 11 | 12 | 13 | public GodModule(ResourceLocation id) { 14 | super(id); 15 | } 16 | 17 | @Override 18 | public void init() { 19 | registerLocale(GodLocale.MODULE); 20 | registerPlayerData(GodPlayerData.class, GodPlayerData::new); 21 | 22 | commands.add(new GodCommand(this)); 23 | 24 | ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { 25 | var player = handler.getPlayer(); 26 | 27 | var data = Solstice.playerData.get(player).getData(GodPlayerData.class); 28 | if(data.invulnerabilityEnabled) { 29 | var abilities = player.getAbilities(); 30 | abilities.invulnerable = true; 31 | player.onUpdateAbilities(); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/god/data/GodLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.god.data; 2 | 3 | import java.util.Map; 4 | 5 | public class GodLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("enabled", "Invincibility enabled"), 8 | Map.entry("disabled", "Invincibility disabled"), 9 | Map.entry("enabledForOther", "Invincibility enabled for ${player}"), 10 | Map.entry("disabledForOther", "Invincibility disabled for ${player}") 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/god/data/GodPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.god.data; 2 | 3 | public class GodPlayerData { 4 | public boolean invulnerabilityEnabled = false; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/hat/data/HatConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.hat.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.List; 7 | 8 | @ConfigSerializable 9 | public class HatConfig { 10 | 11 | @Comment("Make the filter setting act as a whitelist instead of a blacklist.") 12 | public boolean whitelistFilter = false; 13 | 14 | @Comment("Items & tags to allow/deny. See the 'whitelist-filter' setting to change the behaviour of this list.\nUse '#' as prefix to filter as tag.") 15 | public List filter = List.of( 16 | "#c:shulker_boxes" 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/hat/data/HatLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.hat.data; 2 | 3 | import java.util.Map; 4 | 5 | public class HatLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("emptyStack", "You are not holding any item!"), 8 | Map.entry("success", "Check out your new hat!"), 9 | Map.entry("notAllowed", "You cannot wear this item!") 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/heal/HealModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.heal; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.heal.commands.HealCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | import me.alexdevs.solstice.modules.heal.data.HealLocale; 7 | 8 | public class HealModule extends ModuleBase.Toggleable { 9 | public HealModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | registerLocale(HealLocale.MODULE); 16 | 17 | commands.add(new HealCommand(this)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/heal/data/HealLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.heal.data; 2 | 3 | import java.util.Map; 4 | 5 | public class HealLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("healed", "Healed ${entity}!"), 8 | Map.entry("healedMultiple", "Healed ${count} entities!"), 9 | Map.entry("noLiving", "There are no living entities in the selector!") 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/helpOp/HelpOpModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.helpOp; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.helpOp.commands.HelpOpCommand; 6 | import me.alexdevs.solstice.modules.helpOp.data.HelpOpLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | 9 | public class HelpOpModule extends ModuleBase.Toggleable { 10 | public HelpOpModule(ResourceLocation id) { 11 | super(id); 12 | } 13 | 14 | @Override 15 | public void init() { 16 | registerLocale(HelpOpLocale.MODULE); 17 | 18 | commands.add(new HelpOpCommand(this)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/helpOp/data/HelpOpLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.helpOp.data; 2 | 3 | import java.util.Map; 4 | 5 | public class HelpOpLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("helpRequestMessage", "❗ [%player:displayname%] ${message}"), 8 | Map.entry("helpRequestFeedback", "Help request sent: ${message}") 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/home/HomeModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.home; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.home.commands.*; 6 | import me.alexdevs.solstice.modules.home.data.HomeConfig; 7 | import me.alexdevs.solstice.modules.home.data.HomeLocale; 8 | import me.alexdevs.solstice.modules.home.data.HomePlayerData; 9 | import net.minecraft.resources.ResourceLocation; 10 | 11 | import java.util.UUID; 12 | public class HomeModule extends ModuleBase.Toggleable { 13 | 14 | 15 | public HomeModule(ResourceLocation id) { 16 | super(id); 17 | } 18 | 19 | @Override 20 | public void init() { 21 | registerConfig(HomeConfig.class, HomeConfig::new); 22 | registerPlayerData(HomePlayerData.class, HomePlayerData::new); 23 | registerLocale(HomeLocale.MODULE); 24 | 25 | commands.add(new HomeCommand(this)); 26 | commands.add(new SetHomeCommand(this)); 27 | commands.add(new HomesCommand(this)); 28 | commands.add(new DeleteHomeCommand(this)); 29 | commands.add(new HomeOtherCommand(this)); 30 | } 31 | 32 | public HomePlayerData getData(UUID player) { 33 | return Solstice.playerData.get(player).getData(HomePlayerData.class); 34 | } 35 | 36 | public HomeConfig getConfig() { 37 | return Solstice.configManager.getData(HomeConfig.class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/home/data/HomeConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.home.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.Map; 7 | 8 | @ConfigSerializable 9 | public class HomeConfig { 10 | 11 | @Comment("The amount of homes a player can set based on their permission group. = .\nUse the permission 'solstice.home.unlimited' to bypass this limit.") 12 | public Map homes = Map.of( 13 | "default", 5, 14 | "vip", 10 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/home/data/HomePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.home.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 5 | 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | @ConfigSerializable 9 | public class HomePlayerData { 10 | public ConcurrentHashMap homes = new ConcurrentHashMap<>(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ignite/IgniteModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ignite; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.ignite.commands.IgniteCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | public class IgniteModule extends ModuleBase.Toggleable { 7 | 8 | 9 | public IgniteModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new IgniteCommand(this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ignore/IgnoreModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ignore; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.ModuleProvider; 6 | import me.alexdevs.solstice.modules.ignore.commands.IgnoreCommand; 7 | import me.alexdevs.solstice.modules.ignore.commands.IgnoreListCommand; 8 | import me.alexdevs.solstice.modules.ignore.data.IgnoreLocale; 9 | import me.alexdevs.solstice.modules.ignore.data.IgnorePlayerData; 10 | import me.lucko.fabric.api.permissions.v0.Permissions; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.server.level.ServerPlayer; 13 | 14 | import java.util.UUID; 15 | public class IgnoreModule extends ModuleBase.Toggleable { 16 | 17 | 18 | public IgnoreModule(ResourceLocation id) { 19 | super(id); 20 | } 21 | 22 | @Override 23 | public void init() { 24 | registerLocale(IgnoreLocale.MODULE); 25 | registerPlayerData(IgnorePlayerData.class, IgnorePlayerData::new); 26 | 27 | commands.add(new IgnoreCommand(this)); 28 | commands.add(new IgnoreListCommand(this)); 29 | } 30 | 31 | public IgnorePlayerData getPlayerData(UUID playerUuid) { 32 | return Solstice.playerData.get(playerUuid).getData(IgnorePlayerData.class); 33 | } 34 | 35 | public boolean isIgnoring(ServerPlayer player, ServerPlayer target) { 36 | if(!isEnabled()) 37 | return false; 38 | 39 | return getPlayerData(player.getUUID()).ignoredPlayers.contains(target.getUUID()) && !Permissions.check(target, this.getPermissionNode("exempt"), 2); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ignore/data/IgnoreLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ignore.data; 2 | 3 | import java.util.Map; 4 | 5 | public class IgnoreLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("playerNotFound", "Could not find this player"), 8 | Map.entry("targetIsSelf", "You cannot ignore yourself."), 9 | Map.entry("blockedPlayer", "${targetName} is now ignored."), 10 | Map.entry("unblockedPlayer", "${targetName} is no longer ignored."), 11 | Map.entry("ignoreList", "Ignored players: ${playerList}"), 12 | Map.entry("ignoreListFormat", "${player}"), 13 | Map.entry("ignoreListComma", ", "), 14 | Map.entry("ignoreListEmpty", "You are not ignoring anyone at the moment.") 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/ignore/data/IgnorePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.ignore.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.UUID; 5 | 6 | public class IgnorePlayerData { 7 | public ArrayList ignoredPlayers = new ArrayList<>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/info/commands/MotdCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.info.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import eu.pb4.placeholders.api.PlaceholderContext; 5 | import me.alexdevs.solstice.api.module.ModCommand; 6 | import me.alexdevs.solstice.modules.info.InfoModule; 7 | import net.minecraft.commands.CommandSourceStack; 8 | 9 | import java.util.List; 10 | 11 | import static net.minecraft.commands.Commands.literal; 12 | 13 | public class MotdCommand extends ModCommand { 14 | public MotdCommand(InfoModule module) { 15 | super(module); 16 | } 17 | 18 | @Override 19 | public List getNames() { 20 | return List.of("motd"); 21 | } 22 | 23 | @Override 24 | public LiteralArgumentBuilder command(String name) { 25 | return literal(name) 26 | .requires(require("motd", true)) 27 | .executes(context -> { 28 | var sourceContext = PlaceholderContext.of(context.getSource()); 29 | 30 | context.getSource().sendSystemMessage(module.buildMotd(sourceContext)); 31 | 32 | return 1; 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/info/commands/RulesCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.info.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import eu.pb4.placeholders.api.PlaceholderContext; 5 | import me.alexdevs.solstice.api.module.ModCommand; 6 | import me.alexdevs.solstice.modules.info.InfoModule; 7 | import net.minecraft.commands.CommandSourceStack; 8 | 9 | import java.util.List; 10 | 11 | import static net.minecraft.commands.Commands.literal; 12 | 13 | public class RulesCommand extends ModCommand { 14 | public RulesCommand(InfoModule module) { 15 | super(module); 16 | } 17 | 18 | @Override 19 | public List getNames() { 20 | return List.of("rules"); 21 | } 22 | 23 | @Override 24 | public LiteralArgumentBuilder command(String name) { 25 | return literal(name) 26 | .requires(require("rules", true)) 27 | .executes(context -> { 28 | var sourceContext = PlaceholderContext.of(context.getSource()); 29 | var rules = module.getPage("rules", sourceContext); 30 | context.getSource().sendSuccess(() -> rules, false); 31 | return 1; 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/info/data/InfoConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.info.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class InfoConfig { 8 | @Comment("Send the 'Message Of The Day' to the player when joining the server. Content is in the 'config/solstice/info/motd.txt' file.") 9 | public boolean enableMotd = true; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/info/data/InfoLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.info.data; 2 | 3 | import java.util.Map; 4 | 5 | public class InfoLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("pageNotFound", "This page does not exist!"), 8 | Map.entry("pageError", "There was an error opening the info page."), 9 | Map.entry("pageList", "Available pages: ${pageList}"), 10 | Map.entry("pagesFormat", "${page}"), 11 | Map.entry("pagesComma", ", "), 12 | Map.entry("noPages ", "There are no pages so far.") 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/inventorySee/ImmutableSlot.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.inventorySee; 2 | 3 | import net.minecraft.world.Container; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.Slot; 6 | import net.minecraft.world.item.ItemStack; 7 | 8 | public class ImmutableSlot extends Slot { 9 | 10 | public ImmutableSlot(Container inventory, int index, int x, int y) { 11 | super(inventory, index, x, y); 12 | } 13 | 14 | @Override 15 | public boolean mayPickup(Player playerEntity) { 16 | return false; 17 | } 18 | 19 | @Override 20 | public boolean mayPlace(ItemStack stack) { 21 | return false; 22 | } 23 | 24 | @Override 25 | public boolean allowModification(Player player) { 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/inventorySee/InventorySeeModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.inventorySee; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.inventorySee.commands.InventorySeeCommand; 6 | import me.alexdevs.solstice.modules.inventorySee.data.InventorySeeLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | public class InventorySeeModule extends ModuleBase.Toggleable { 9 | 10 | 11 | public InventorySeeModule(ResourceLocation id) { 12 | super(id); 13 | } 14 | 15 | @Override 16 | public void init() { 17 | registerLocale(InventorySeeLocale.MODULE); 18 | 19 | commands.add(new InventorySeeCommand(this)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/inventorySee/data/InventorySeeLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.inventorySee.data; 2 | 3 | import java.util.Map; 4 | 5 | public class InventorySeeLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("exempt", "You cannot open this inventory because the user is exempt."), 8 | Map.entry("openedInventory", "Opened ${user}'s inventory."), 9 | Map.entry("openedTrinkets", "Opened ${user}'s trinkets inventory."), 10 | Map.entry("trinketsNotInstalled", "Trinkets not available because the mod is missing."), 11 | Map.entry("playerNotFound", "Player not found!"), 12 | Map.entry("offlineNotAllowed", "You cannot open offline player inventories.") 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/item/ItemModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.item; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.item.commands.ItemLoreCommand; 6 | import me.alexdevs.solstice.modules.item.commands.ItemNameCommand; 7 | import me.alexdevs.solstice.modules.item.commands.MoreCommand; 8 | import me.alexdevs.solstice.modules.item.commands.RepairCommand; 9 | import me.alexdevs.solstice.modules.item.data.ItemLocale; 10 | import net.minecraft.resources.ResourceLocation; 11 | public class ItemModule extends ModuleBase.Toggleable { 12 | 13 | public ItemModule(ResourceLocation id) { 14 | super(id); 15 | } 16 | 17 | @Override 18 | public void init() { 19 | registerLocale(ItemLocale.MODULE); 20 | 21 | commands.add(new ItemLoreCommand(this)); 22 | commands.add(new ItemNameCommand(this)); 23 | commands.add(new RepairCommand(this)); 24 | commands.add(new MoreCommand(this)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/item/commands/MoreCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.item.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.item.ItemModule; 6 | import net.minecraft.commands.CommandSourceStack; 7 | import net.minecraft.commands.Commands; 8 | 9 | import java.util.List; 10 | 11 | public class MoreCommand extends ModCommand { 12 | public MoreCommand(ItemModule module) { 13 | super(module); 14 | } 15 | 16 | @Override 17 | public List getNames() { 18 | return List.of("more", "stack"); 19 | } 20 | 21 | @Override 22 | public LiteralArgumentBuilder command(String name) { 23 | return Commands.literal(name) 24 | .requires(require("more", 2)) 25 | .executes(context -> { 26 | var player = context.getSource().getPlayerOrException(); 27 | var item = player.getMainHandItem(); 28 | 29 | if(item.isEmpty()) { 30 | context.getSource().sendSuccess(() -> module.locale().get("noItem"), false); 31 | return 0; 32 | } 33 | 34 | item.setCount(item.getMaxStackSize()); 35 | 36 | context.getSource().sendSuccess(() -> module.locale().get("stackRefilled"), false); 37 | 38 | return 1; 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/item/data/ItemLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.item.data; 2 | 3 | import java.util.Map; 4 | 5 | public class ItemLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("noItem", "You are not holding any item!"), 8 | Map.entry("nameSet", "Successfully set the new item name!"), 9 | Map.entry("nameCleared", "Item name cleared!"), 10 | Map.entry("loreSet", "Successfully set the new item lore!"), 11 | Map.entry("loreCleared", "Item lore cleared!"), 12 | Map.entry("repaired", "Item repaired!"), 13 | Map.entry("alreadyRepaired", "This item is already repaired!"), 14 | Map.entry("notRepairable", "This item is not repairable!"), 15 | Map.entry("stackRefilled", "Item stack refilled!") 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/jail/data/JailConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.jail.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.List; 7 | 8 | @ConfigSerializable 9 | public class JailConfig { 10 | @Comment("List of commands the jailed players can execute.") 11 | public List allowedCommands = List.of( 12 | "afk", 13 | "ignore", 14 | "msg", "tell", "w", "dm", "r", "reply", 15 | "mail", 16 | "info", "motd", "rules" 17 | ); 18 | 19 | @Comment("Mute jailed players. They will not be able to send chat messages.") 20 | public boolean mute = false; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/jail/data/JailPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.jail.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.Date; 7 | import java.util.UUID; 8 | 9 | public class JailPlayerData { 10 | public boolean jailed = false; 11 | public @Nullable String jailName = null; 12 | public @Nullable UUID jailedBy = null; 13 | public @Nullable Date jailedOn = null; 14 | public int jailTime = 0; 15 | public @Nullable String jailReason = null; 16 | public @Nullable ServerLocation previousLocation = null; 17 | public boolean teleportToPreviousLocation = false; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/jail/data/JailServerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.jail.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | public class JailServerData { 9 | public Map jails = new ConcurrentHashMap<>(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kick/KickModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kick; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.kick.commands.KickCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class KickModule extends ModuleBase.Toggleable { 8 | 9 | public KickModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new KickCommand(this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/KitInventory.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit; 2 | 3 | import net.minecraft.core.NonNullList; 4 | import net.minecraft.world.Container; 5 | import net.minecraft.world.entity.player.Player; 6 | import net.minecraft.world.item.ItemStack; 7 | 8 | public class KitInventory implements Container { 9 | public static final int SIZE = 27; 10 | 11 | private final NonNullList items = NonNullList.withSize(SIZE, ItemStack.EMPTY); 12 | 13 | @Override 14 | public int getContainerSize() { 15 | return SIZE; 16 | } 17 | 18 | @Override 19 | public boolean isEmpty() { 20 | return items.isEmpty(); 21 | } 22 | 23 | @Override 24 | public ItemStack getItem(int slot) { 25 | return items.get(slot); 26 | } 27 | 28 | @Override 29 | public ItemStack removeItem(int slot, int amount) { 30 | var stack = getItem(slot); 31 | var taken = stack.copyWithCount(amount); 32 | stack.shrink(amount); 33 | return taken; 34 | } 35 | 36 | @Override 37 | public ItemStack removeItemNoUpdate(int slot) { 38 | var stack = items.get(slot); 39 | return stack.copyAndClear(); 40 | } 41 | 42 | @Override 43 | public void setItem(int slot, ItemStack stack) { 44 | items.set(slot, stack); 45 | } 46 | 47 | @Override 48 | public void setChanged() { 49 | 50 | } 51 | 52 | @Override 53 | public boolean stillValid(Player player) { 54 | return false; 55 | } 56 | 57 | @Override 58 | public void clearContent() { 59 | items.clear(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/Utils.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | import eu.pb4.sgui.api.gui.SimpleGui; 5 | import me.alexdevs.solstice.Solstice; 6 | import net.minecraft.nbt.TagParser; 7 | import net.minecraft.world.inventory.Slot; 8 | import net.minecraft.world.item.ItemStack; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class Utils { 14 | public static String serializeItemStack(ItemStack itemStack) { 15 | var registry = Solstice.server.registryAccess(); 16 | var nbt = itemStack.save(registry); 17 | return nbt.getAsString(); 18 | } 19 | 20 | public static ItemStack deserializeItemStack(String string) throws CommandSyntaxException { 21 | var registry = Solstice.server.registryAccess(); 22 | var nbt = TagParser.parseTag(string); 23 | return ItemStack.parseOptional(registry, nbt); 24 | } 25 | 26 | public static KitInventory createInventory(List items) { 27 | var inventory = new KitInventory(); 28 | for (var i = 0; i < items.size(); i++) { 29 | inventory.setItem(i, items.get(i)); 30 | } 31 | return inventory; 32 | } 33 | 34 | public static List getItemStacks(KitInventory inventory) { 35 | var items = new ArrayList(); 36 | for (var i = 0; i < inventory.getContainerSize(); i++) { 37 | var stack = inventory.getItem(i); 38 | if(!stack.isEmpty()) { 39 | items.add(stack); 40 | } 41 | } 42 | return items; 43 | } 44 | 45 | public static void redirect(SimpleGui container, KitInventory inventory) { 46 | for(var i = 0; i < container.getSize(); i++) { 47 | container.setSlotRedirect(i, new Slot(inventory, i, 0, 0)); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/data/Kit.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit.data; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.modules.kit.Utils; 6 | import net.minecraft.world.item.ItemStack; 7 | import net.minecraft.world.item.Items; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class Kit { 14 | /** 15 | * itemStacks nbt is serialized and deserialized 16 | */ 17 | public List itemStacks = new ArrayList<>(); 18 | public boolean oneTime = false; 19 | public int cooldownSeconds = 0; 20 | public boolean firstJoin = false; 21 | public @Nullable String icon; 22 | 23 | public List getItemStacks() { 24 | var stacks = new ArrayList(); 25 | 26 | for (var stackNbt : itemStacks) { 27 | try { 28 | stacks.add(Utils.deserializeItemStack(stackNbt)); 29 | } catch (CommandSyntaxException e) { 30 | Solstice.LOGGER.error("Could not load item from kit", e); 31 | } 32 | } 33 | 34 | return stacks; 35 | } 36 | 37 | public ItemStack getIcon() { 38 | var defaultStack = Items.DIRT.getDefaultInstance(); 39 | if (icon == null) { 40 | return defaultStack; 41 | } 42 | try { 43 | return Utils.deserializeItemStack(icon); 44 | } catch (CommandSyntaxException e) { 45 | return defaultStack; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/data/KitConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class KitConfig { 8 | @Comment("Require 'solstice.kit.kits.' permission node by default to claim a kit.") 9 | public boolean requirePermission = false; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/data/KitPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit.data; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | 6 | public class KitPlayerData { 7 | public HashMap claimedKits = new HashMap<>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/kit/data/KitServerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.kit.data; 2 | 3 | import java.util.HashMap; 4 | 5 | public class KitServerData { 6 | public HashMap kits = new HashMap<>(); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/mail/data/MailLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.mail.data; 2 | 3 | import java.util.Map; 4 | 5 | public class MailLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("mailPending", "You have pending mails! Run /mail to read your mails."), 8 | Map.entry("replyButton", "Reply"), 9 | Map.entry("deleteButton", "Delete"), 10 | Map.entry("readButton", "Read"), 11 | Map.entry("hoverReply", "Click to reply to the mail"), 12 | Map.entry("hoverDelete", "Click to delete the mail"), 13 | Map.entry("hoverRead", "Click to read the mail"), 14 | Map.entry("playerNotFound", "Player ${recipient} not found!"), 15 | Map.entry("mailSent", "Mail sent!"), 16 | Map.entry("mailReceived", "You received a new mail! Run /mail to read the emails!"), 17 | Map.entry("mailDetails", "From ${sender} on ${date}\n ${message}\n\n ${replyButton} ${deleteButton}"), 18 | Map.entry("mailListHeader", "Your mails:"), 19 | Map.entry("mailListEntry", "${index}. From ${sender} on ${date} ${readButton}"), 20 | Map.entry("notFound", "Mail not found"), 21 | Map.entry("mailDeleted", "Mail deleted!"), 22 | Map.entry("emptyMailbox", "Your mailbox is empty.") 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/mail/data/MailPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.mail.data; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class MailPlayerData { 6 | public ArrayList mails = new ArrayList<>(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/mail/data/PlayerMail.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.mail.data; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 5 | 6 | import java.util.Date; 7 | import java.util.UUID; 8 | 9 | public class PlayerMail { 10 | public String message; 11 | public UUID sender; 12 | public Date date; 13 | 14 | public PlayerMail(String message, UUID sender) { 15 | this.message = message; 16 | this.sender = sender; 17 | this.date = new Date(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/miscellaneous/DummyExplosion.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.miscellaneous; 2 | 3 | import net.minecraft.core.particles.ParticleTypes; 4 | import net.minecraft.core.particles.SimpleParticleType; 5 | import net.minecraft.server.level.ServerLevel; 6 | import net.minecraft.sounds.SoundEvents; 7 | import net.minecraft.sounds.SoundSource; 8 | import net.minecraft.world.phys.Vec3; 9 | 10 | public class DummyExplosion { 11 | public static void spawn(ServerLevel world, Vec3 pos, float power) { 12 | world.playSound(null, pos.x, pos.y, pos.z, SoundEvents.GENERIC_EXPLODE, SoundSource.BLOCKS, 4.0F, (1.0F + (world.random.nextFloat() - world.random.nextFloat()) * 0.2F) * 0.7F); 13 | SimpleParticleType particle; 14 | if(power >= 2.0) { 15 | particle = ParticleTypes.EXPLOSION_EMITTER; 16 | } else { 17 | particle = ParticleTypes.EXPLOSION; 18 | } 19 | world.sendParticles(particle, pos.x, pos.y, pos.z, 1, 1, 0, 0, 1); 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/miscellaneous/commands/TopCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.miscellaneous.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.miscellaneous.MiscellaneousModule; 6 | import net.minecraft.commands.CommandSourceStack; 7 | import net.minecraft.commands.Commands; 8 | import net.minecraft.world.level.levelgen.Heightmap; 9 | 10 | import java.util.List; 11 | 12 | public class TopCommand extends ModCommand { 13 | public TopCommand(MiscellaneousModule module) { 14 | super(module); 15 | } 16 | 17 | @Override 18 | public List getNames() { 19 | return List.of("top"); 20 | } 21 | 22 | @Override 23 | public LiteralArgumentBuilder command(String name) { 24 | return Commands.literal(name) 25 | .requires(require("top.base", 2)) 26 | .executes(context -> { 27 | var player = context.getSource().getPlayerOrException(); 28 | 29 | var world = player.serverLevel(); 30 | var top = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, player.blockPosition()); 31 | var pos = top.getCenter(); 32 | 33 | player.teleportTo(pos.x(), pos.y(), pos.z()); 34 | 35 | player.setDeltaMovement(player.getDeltaMovement().multiply(1.0, 0.0, 1.0)); 36 | player.setOnGround(true); 37 | 38 | context.getSource().sendSuccess(() -> module.locale().get("top"), false); 39 | 40 | return 1; 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/miscellaneous/data/MiscellaneousLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.miscellaneous.data; 2 | 3 | import java.util.Map; 4 | 5 | public class MiscellaneousLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("noEffects", "This player has no active effects."), 8 | Map.entry("effectHeader", "Active effects:"), 9 | Map.entry("effect", "${effect}: x${amplifier} for ${duration}"), 10 | Map.entry("infinite", "infinite"), 11 | 12 | Map.entry("top", "Whoosh!"), 13 | 14 | Map.entry("walkSpeedReset", "Walk speed reset."), 15 | Map.entry("walkSpeedSet", "Walk speed set to ${speed}."), 16 | Map.entry("flySpeedReset", "Flight speed reset."), 17 | Map.entry("flySpeedSet", "Flight speed set to ${speed}.") 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/mute/data/MuteLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.mute.data; 2 | 3 | import java.util.Map; 4 | 5 | public class MuteLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("muted", "Muted ${player}!"), 8 | Map.entry("mutedMultiple", "Muted ${count} players!"), 9 | Map.entry("mutedTimespan", "Muted ${player} for ${timespan}!"), 10 | Map.entry("mutedMultipleTimespan", "Muted ${count} players for ${timespan}!"), 11 | 12 | Map.entry("unmuted", "Unmuted ${player}!"), 13 | Map.entry("unmutedMultiple", "Unmuted ${count} players!"), 14 | 15 | Map.entry("youAreMuted", "You are muted!") 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/mute/data/MutePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.mute.data; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.Date; 6 | 7 | public class MutePlayerData { 8 | public boolean muted = false; 9 | public @Nullable Date mutedUntil = null; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/near/NearModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.near; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.near.commands.NearCommand; 6 | import me.alexdevs.solstice.modules.near.data.NearConfig; 7 | import me.alexdevs.solstice.modules.near.data.NearLocale; 8 | import net.minecraft.resources.ResourceLocation; 9 | public class NearModule extends ModuleBase.Toggleable { 10 | 11 | 12 | public NearModule(ResourceLocation id) { 13 | super(id); 14 | } 15 | 16 | @Override 17 | public void init() { 18 | registerConfig(NearConfig.class, NearConfig::new); 19 | registerLocale(NearLocale.MODULE); 20 | 21 | commands.add(new NearCommand(this)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/near/data/NearConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.near.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class NearConfig { 8 | @Comment("Max range in blocks. Defaults to 48 blocks.") 9 | public int maxRange = 48; 10 | 11 | @Comment("Default range in blocks. Defaults to 32 blocks.") 12 | public int defaultRange = 32; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/near/data/NearLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.near.data; 2 | 3 | import java.util.Map; 4 | 5 | public class NearLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("noOne", "There are no players near you."), 8 | Map.entry("nearestPlayers", "Nearest players: ${playerList}"), 9 | Map.entry("format", "${player} (${distance})"), 10 | Map.entry("comma", ", ") 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/note/data/Note.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.note.data; 2 | 3 | import java.util.Date; 4 | import java.util.UUID; 5 | 6 | public class Note { 7 | public UUID createdBy; 8 | public Date creationDate = new Date(); 9 | public String note; 10 | 11 | public Note(String note, UUID createdBy) { 12 | this.note = note; 13 | this.createdBy = createdBy; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/note/data/NoteConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.note.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class NoteConfig { 8 | @Comment("Show a player's note when they login to users with the permission 'solstice.note.showonlogin'") 9 | public boolean showLogin = true; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/note/data/NoteLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.note.data; 2 | 3 | import java.util.Map; 4 | 5 | public class NoteLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("deleteButton", "Delete"), 8 | Map.entry("checkButton", "Check"), 9 | Map.entry("hoverDelete", "Click to delete the note"), 10 | Map.entry("hoverCheck", "Click to check the note"), 11 | Map.entry("userNotFound", "User ${user} not found!"), 12 | Map.entry("noteAdded", "Note added!"), 13 | Map.entry("noteDetails", "From ${operator} on ${date}\n ${message}\n\n ${deleteButton}"), 14 | Map.entry("noteListHeader", "${user}'s notes:"), 15 | Map.entry("noteListEntry", "[${date}] ${operator}: ${message} ${checkButton}"), 16 | Map.entry("notFound", "Note not found"), 17 | Map.entry("noteDeleted", "Note deleted!"), 18 | Map.entry("emptyNotes", "There are no notes for this user."), 19 | Map.entry("notesCleared", "All ${user}'s notes cleared!"), 20 | Map.entry("loginInfo", "${user} has ${notes} notes! ${checkButton}"), 21 | Map.entry("addedNotification", "${operator} added a note to ${user}! ${checkButton}") 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/note/data/NotePlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.note.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class NotePlayerData { 7 | public List notes = new ArrayList<>(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/notifications/data/NotificationsConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.notifications.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | 5 | @ConfigSerializable 6 | public class NotificationsConfig { 7 | public DefaultValues defaultValues = new DefaultValues(); 8 | 9 | @ConfigSerializable 10 | public static class DefaultValues { 11 | public String soundId = "minecraft:block.note_block.bell"; 12 | public float pitch = 1f; 13 | public float volume = 1f; 14 | public boolean afkOnly = true; 15 | public boolean onChat = true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/notifications/data/NotificationsPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.notifications.data; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | public class NotificationsPlayerData { 6 | public boolean enable = true; 7 | @Nullable 8 | public Boolean afkOnly = null; 9 | @Nullable 10 | public String soundId = null; 11 | @Nullable 12 | public Float pitch = null; 13 | @Nullable 14 | public Float volume = null; 15 | @Nullable 16 | public Boolean onChat = null; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/notifications/data/PlayerNotificationSettings.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.notifications.data; 2 | 3 | public record PlayerNotificationSettings(String soundId, float pitch, float volume, boolean afkOnly, boolean onChat) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/placeholders/PlaceholdersModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.placeholders; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderResult; 4 | import eu.pb4.placeholders.api.Placeholders; 5 | import me.alexdevs.solstice.api.module.ModuleBase; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | public class PlaceholdersModule extends ModuleBase.Toggleable { 9 | 10 | public static final String ENTITY = "entity"; 11 | 12 | public PlaceholdersModule(ResourceLocation id) { 13 | super(id); 14 | } 15 | 16 | @Override 17 | public void init() { 18 | Placeholders.register(ResourceLocation.fromNamespaceAndPath(ENTITY, "name"), (context, str) -> { 19 | if (!context.hasEntity()) { 20 | return PlaceholderResult.invalid("No entity!"); 21 | } 22 | var entity = context.entity(); 23 | return PlaceholderResult.value(entity.getName()); 24 | }); 25 | 26 | Placeholders.register(ResourceLocation.fromNamespaceAndPath(ENTITY, "displayname"), (context, str) -> { 27 | if (!context.hasEntity()) { 28 | return PlaceholderResult.invalid("No entity!"); 29 | } 30 | var entity = context.entity(); 31 | return PlaceholderResult.value(entity.getDisplayName()); 32 | }); 33 | 34 | Placeholders.register(ResourceLocation.fromNamespaceAndPath(ENTITY, "uuid"), (context, str) -> { 35 | if (!context.hasEntity()) { 36 | return PlaceholderResult.invalid("No entity!"); 37 | } 38 | var entity = context.entity(); 39 | return PlaceholderResult.value(entity.getStringUUID()); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/powertool/Action.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.powertool; 2 | 3 | import net.minecraft.util.StringRepresentable; 4 | 5 | import java.util.Arrays; 6 | 7 | public enum Action implements StringRepresentable { 8 | USE, 9 | ATTACK_BLOCK, 10 | ATTACK_ENTITY, 11 | INTERACT_BLOCK, 12 | INTERACT_ENTITY; 13 | 14 | @Override 15 | public String getSerializedName() { 16 | return this.name().toLowerCase(); 17 | } 18 | 19 | public static String[] stringValues() { 20 | return Arrays.stream(values()).map(Action::getSerializedName).toArray(String[]::new); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/powertool/data/PowerToolLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.powertool.data; 2 | 3 | import java.util.Map; 4 | 5 | public class PowerToolLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("emptyHand", "You are not holding any item."), 8 | Map.entry("actionSet", "Command bound to ${item} on ${action}."), 9 | Map.entry("actionCleared", "Command cleared from ${item} on ${action}."), 10 | Map.entry("allCleared", "All commands cleared from ${item}!"), 11 | Map.entry("noAction", "This action is not bound for this item."), 12 | Map.entry("check", "Actions for ${item}:"), 13 | Map.entry("checkEntry", " - ${action}: ${command}"), 14 | Map.entry("checkEntryNotSet", " - ${action}: Not set") 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/powertool/data/PowerToolPlayerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.powertool.data; 2 | 3 | import me.alexdevs.solstice.modules.powertool.Action; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class PowerToolPlayerData { 9 | public Map> powerTools = new HashMap<>(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/restart/data/RestartConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.restart.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @ConfigSerializable 10 | public class RestartConfig { 11 | @Comment("Enable auto restart functionality.") 12 | public boolean enable = true; 13 | 14 | @Comment("Restart the server at exactly the following times. Time is local.") 15 | public ArrayList restartAt = new ArrayList<>(List.of( 16 | "06:00", 17 | "18:00" 18 | )); 19 | 20 | @Comment("Sound to play when sending the restart notification in chat.") 21 | public String restartSound = "minecraft:block.note_block.bell"; 22 | 23 | @Comment("Pitch of the sound.") 24 | public float restartSoundPitch = 0.9f; 25 | 26 | @Comment("Style of the restart bar.") 27 | public String barStyle = "NOTCHED_10"; 28 | 29 | @Comment("Color of the restart bar.") 30 | public String barColor = "RED"; 31 | 32 | @Comment("Milestones of the restart notifications in seconds.") 33 | public ArrayList restartNotifications = new ArrayList<>(List.of( 34 | 600, 35 | 300, 36 | 120, 37 | 60, 38 | 30, 39 | 15, 40 | 10, 41 | 5, 42 | 4, 43 | 3, 44 | 2, 45 | 1 46 | )); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/restart/data/RestartLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.restart.data; 2 | 3 | import java.util.Map; 4 | 5 | public class RestartLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("barLabel", "Server restarting in ${remaining_time}"), 8 | Map.entry("kickMessage", "The server is restarting!"), 9 | Map.entry("chatMessage", "The server is restarting in ${remaining_time}") 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/rtp/RTPModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.rtp; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.rtp.commands.RTPCommand; 6 | import me.alexdevs.solstice.modules.rtp.core.Locator; 7 | import me.alexdevs.solstice.modules.rtp.data.RTPConfig; 8 | import me.alexdevs.solstice.modules.rtp.data.RTPLocale; 9 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.server.level.ServerPlayer; 13 | import net.minecraft.world.level.biome.Biome; 14 | 15 | import java.util.ArrayList; 16 | public class RTPModule extends ModuleBase.Toggleable { 17 | 18 | 19 | private final ArrayList locators = new ArrayList<>(); 20 | 21 | public RTPModule(ResourceLocation id) { 22 | super(id); 23 | } 24 | 25 | @Override 26 | public void init() { 27 | registerLocale(RTPLocale.MODULE); 28 | registerConfig(RTPConfig.class, RTPConfig::new); 29 | 30 | commands.add(new RTPCommand(this)); 31 | 32 | ServerTickEvents.END_SERVER_TICK.register(server -> locators.removeIf(Locator::tick)); 33 | } 34 | 35 | public RTPConfig getConfig() { 36 | return Solstice.configManager.getData(RTPConfig.class); 37 | } 38 | 39 | public Locator createLocator(ServerPlayer player) { 40 | var locator = new Locator(player, player.serverLevel(), getConfig()); 41 | locators.add(locator); 42 | return locator; 43 | } 44 | 45 | public Locator createLocatorWithBiome(ServerPlayer player, ResourceKey biome) { 46 | var locator = new Locator(player, player.serverLevel(), getConfig(), biome); 47 | locators.add(locator); 48 | return locator; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/rtp/data/RTPLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.rtp.data; 2 | 3 | import java.util.Map; 4 | 5 | public class RTPLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("searching", "Finding a good spot..."), 8 | Map.entry("timeout", "Could not find a valid spot in a timely manner."), 9 | Map.entry("tooManyAttempts", "Too many failed attempts at locating a valid spot."), 10 | Map.entry("unsafe", "Could not place you in a safe spot."), 11 | Map.entry("success", "Teleporting to a random location..."), 12 | Map.entry("noWorldPermission", "You do not have permission to run this command in this world."), 13 | Map.entry("noBiomePermission", "You do not have permission to use this biome.") 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/seen/SeenModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.seen; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.seen.commands.SeenCommand; 6 | import me.alexdevs.solstice.modules.seen.data.SeenLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | public class SeenModule extends ModuleBase.Toggleable { 9 | 10 | 11 | public SeenModule(ResourceLocation id) { 12 | super(id); 13 | } 14 | 15 | @Override 16 | public void init() { 17 | registerLocale(SeenLocale.MODULE); 18 | 19 | commands.add(new SeenCommand(this)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/seen/data/SeenLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.seen.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class SeenLocale { 9 | public static final Map MODULE; 10 | private static final ArrayList base = new ArrayList<>(List.of( 11 | "${username}'s information:", 12 | " UUID: ${uuid}", 13 | " First seen: ${firstSeenDate}", 14 | " Last seen: ${lastSeenDate}" 15 | )); 16 | private static final ArrayList extended = new ArrayList<>(List.of( 17 | " IP Address: ${ipAddress}", 18 | " Location: ${location}" 19 | )); 20 | 21 | static { 22 | var map = new HashMap(); 23 | map.put("playerNotFound", "Could not find this player"); 24 | map.put("base", String.join("\n", base)); 25 | map.put("extended", String.join("\n", extended)); 26 | map.put("online", "online"); 27 | map.put("neverJoined", "Never joined"); 28 | map.put("unknown", "unknown"); 29 | 30 | MODULE = Map.copyOf(map); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/sign/SignModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.sign; 2 | 3 | import eu.pb4.placeholders.api.parsers.LegacyFormattingParser; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.lucko.fabric.api.permissions.v0.Permissions; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.server.network.FilteredText; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.block.entity.SignText; 10 | import java.util.List; 11 | 12 | public class SignModule extends ModuleBase.Toggleable { 13 | 14 | public SignModule(ResourceLocation id) { 15 | super(id); 16 | } 17 | 18 | @Override 19 | public void init() { 20 | } 21 | 22 | public static SignText formatSign(List messages, SignText text) { 23 | for (var i = 0; i < messages.size(); i++) { 24 | var message = messages.get(i); 25 | var line = message.raw(); 26 | text = text.setMessage(i, LegacyFormattingParser.ALL.parseNode(line).toText()); 27 | } 28 | return text; 29 | } 30 | 31 | public boolean canFormatSign(Player player) { 32 | return Permissions.check(player, getPermissionNode("format"), 2); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/skull/SkullModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.skull; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import com.mojang.authlib.properties.PropertyMap; 5 | import me.alexdevs.solstice.api.module.ModuleBase; 6 | import me.alexdevs.solstice.modules.skull.commands.SkullCommand; 7 | import net.minecraft.core.component.DataComponents; 8 | import net.minecraft.resources.ResourceLocation; 9 | import net.minecraft.world.item.ItemStack; 10 | import net.minecraft.world.item.Items; 11 | import net.minecraft.world.item.component.ResolvableProfile; 12 | 13 | import java.util.Optional; 14 | import java.util.UUID; 15 | public class SkullModule extends ModuleBase.Toggleable { 16 | 17 | 18 | public SkullModule(ResourceLocation id) { 19 | super(id); 20 | } 21 | 22 | @Override 23 | public void init() { 24 | commands.add(new SkullCommand(this)); 25 | } 26 | 27 | public ItemStack createSkull(String name) { 28 | var skull = Items.PLAYER_HEAD.getDefaultInstance(); 29 | name = name.substring(0, Math.min(name.length(), 16)); 30 | skull.set(DataComponents.PROFILE, new ResolvableProfile(Optional.of(name), Optional.empty(), new PropertyMap())); 31 | return skull; 32 | } 33 | 34 | public ItemStack createSkull(UUID uuid) { 35 | var skull = Items.PLAYER_HEAD.getDefaultInstance(); 36 | skull.set(DataComponents.PROFILE, new ResolvableProfile(Optional.empty(), Optional.of(uuid), new PropertyMap())); 37 | return skull; 38 | } 39 | 40 | public ItemStack createSkull(GameProfile profile) { 41 | var skull = Items.PLAYER_HEAD.getDefaultInstance(); 42 | skull.set(DataComponents.PROFILE, new ResolvableProfile(profile)); 43 | return skull; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/skull/commands/SkullCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.skull.commands; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import me.alexdevs.solstice.api.module.ModCommand; 8 | import me.alexdevs.solstice.modules.skull.SkullModule; 9 | import net.minecraft.commands.CommandSourceStack; 10 | 11 | import java.util.List; 12 | 13 | import static net.minecraft.commands.Commands.argument; 14 | import static net.minecraft.commands.Commands.literal; 15 | 16 | public class SkullCommand extends ModCommand { 17 | public SkullCommand(SkullModule module) { 18 | super(module); 19 | } 20 | 21 | @Override 22 | public List getNames() { 23 | return List.of("skull"); 24 | } 25 | 26 | @Override 27 | public LiteralArgumentBuilder command(String name) { 28 | return literal(name) 29 | .requires(require(2)) 30 | .executes(context -> execute(context, context.getSource().getPlayerOrException().getGameProfile().getName())) 31 | .then(argument("name", StringArgumentType.word()) 32 | .executes(context -> execute(context, StringArgumentType.getString(context, "name")))); 33 | } 34 | 35 | private int execute(CommandContext context, String skullName) throws CommandSyntaxException { 36 | var player = context.getSource().getPlayerOrException(); 37 | var skull = module.createSkull(skullName); 38 | 39 | player.getInventory().add(skull); 40 | return 1; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/smite/SmiteModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.smite; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.smite.commands.SmiteCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | public class SmiteModule extends ModuleBase.Toggleable { 7 | 8 | 9 | public SmiteModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new SmiteCommand(this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/spawn/data/SpawnConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.spawn.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class SpawnConfig { 8 | 9 | @Comment("Require that the player has the permission of the world 'solstice.spawn.worlds.' to warp to its spawn.\nMind that 'solstice.spawn.worlds.base' is required to be able to use the `world` argument.") 10 | public boolean requireWorldPermission = true; 11 | 12 | @Comment("This setting defines whether `/spawn` and respawning work on a per world or global server basis.") 13 | public GlobalSpawn globalSpawn = new GlobalSpawn(); 14 | 15 | @ConfigSerializable 16 | public static class GlobalSpawn { 17 | @Comment("Send the player to the global spawn instead of the world spawn when using the /spawn command.") 18 | public boolean onSpawnCommand = false; 19 | 20 | @Comment("Send the player to the global spawn when respawning, respecting bed and anchor.") 21 | public boolean onRespawnSoft = false; 22 | 23 | @Comment("Send the player to the global spawn regardless of their bed or anchor when respawning.") 24 | public boolean onRespawn = false; 25 | 26 | @Comment("Send the player to the global spawn when logging in.") 27 | public boolean onLogin = false; 28 | 29 | @Comment("ID of the world to use as global spawn. Minecraft dimensions: 'minecraft:overworld', 'minecraft:the_nether', 'minecraft:the_end'") 30 | public String targetSpawnWorld = "minecraft:overworld"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/spawn/data/SpawnLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.spawn.data; 2 | 3 | import java.util.Map; 4 | 5 | public class SpawnLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("teleporting", "Teleporting to spawn..."), 8 | Map.entry("noWorldPermission", "You do not have permission to teleport to this world spawn."), 9 | Map.entry("noFirstSpawn", "There is no first spawn yet."), 10 | Map.entry("firstSpawnSet", "First spawn set!"), 11 | Map.entry("firstSpawnDeleted", "First spawn deleted!"), 12 | Map.entry("worldSpawnSet", "${world} spawn point set to ${coordinates}.") 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/spawn/data/SpawnServerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.spawn.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class SpawnServerData { 10 | @Deprecated 11 | public @Nullable ServerLocation spawn = null; 12 | 13 | public @Nullable ServerLocation firstSpawn = null; 14 | 15 | public Map spawnPoints = new HashMap<>(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/staffChat/data/StaffChatLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.staffChat.data; 2 | 3 | import java.util.Map; 4 | 5 | public class StaffChatLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("message", "\uD83D\uDD27 ${name}: ${message}"), 8 | Map.entry("enabled", "Staff chat enabled. All your messages will be sent to staff chat instead."), 9 | Map.entry("disabled", "Staff chat disabled.") 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/styling/formatters/AdvancementFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.styling.formatters; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.text.Format; 6 | import me.alexdevs.solstice.modules.ModuleProvider; 7 | import me.alexdevs.solstice.modules.styling.StylingModule; 8 | import net.minecraft.advancements.AdvancementHolder; 9 | import net.minecraft.advancements.AdvancementType; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.server.level.ServerPlayer; 12 | 13 | import java.util.Map; 14 | 15 | public class AdvancementFormatter { 16 | public static Component getText(ServerPlayer player, AdvancementHolder entry, AdvancementType frame) { 17 | var title = entry.value().display().get().getTitle(); 18 | var description = entry.value().display().get().getDescription(); 19 | 20 | var config = ModuleProvider.STYLING.getConfig(); 21 | 22 | String advancementFormat = switch (frame) { 23 | case GOAL -> config.advancementGoal; 24 | case CHALLENGE -> config.advancementChallenge; 25 | case TASK -> config.advancementTask; 26 | }; 27 | 28 | var playerContext = PlaceholderContext.of(player); 29 | 30 | Map placeholders = Map.of( 31 | "frame", frame.getDisplayName(), 32 | "title", title, 33 | "description", description 34 | ); 35 | 36 | return Format.parse(advancementFormat, playerContext, placeholders); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/styling/formatters/ChatFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.styling.formatters; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.api.text.Components; 5 | import me.alexdevs.solstice.api.text.Format; 6 | import me.alexdevs.solstice.modules.ModuleProvider; 7 | import net.minecraft.network.chat.ChatType; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.network.chat.PlayerChatMessage; 10 | import net.minecraft.server.level.ServerPlayer; 11 | 12 | import java.util.Map; 13 | 14 | public class ChatFormatter { 15 | @Deprecated(forRemoval = true) 16 | public static void sendChatMessage(ServerPlayer receiver, PlayerChatMessage message, ChatType.Bound params, ServerPlayer sender) { 17 | var text = getFormattedMessage(message, sender); 18 | 19 | receiver.sendSystemMessage(text); 20 | } 21 | 22 | public static Component getFormattedMessage(PlayerChatMessage message, ServerPlayer player) { 23 | Component messageText = Components.chat(message, player); 24 | 25 | var chatFormat = ModuleProvider.STYLING.getChatFormat(player); 26 | 27 | var playerContext = PlaceholderContext.of(player); 28 | return Format.parse( 29 | chatFormat, 30 | playerContext, 31 | Map.of( 32 | "message", messageText 33 | ) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/styling/formatters/ConnectionActivityFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.styling.formatters; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.text.Format; 6 | import me.alexdevs.solstice.modules.ModuleProvider; 7 | import me.alexdevs.solstice.modules.styling.StylingModule; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.server.level.ServerPlayer; 10 | 11 | import java.util.Map; 12 | 13 | public class ConnectionActivityFormatter { 14 | public static Component onJoin(ServerPlayer player) { 15 | var config = ModuleProvider.STYLING.getConfig(); 16 | var playerContext = PlaceholderContext.of(player); 17 | return Format.parse( 18 | config.joinFormat, 19 | playerContext 20 | ); 21 | } 22 | 23 | public static Component onJoinRenamed(ServerPlayer player, String previousName) { 24 | var config = ModuleProvider.STYLING.getConfig(); 25 | var playerContext = PlaceholderContext.of(player); 26 | return Format.parse( 27 | config.joinRenamedFormat, 28 | playerContext, 29 | Map.of("previousName", Component.nullToEmpty(previousName)) 30 | ); 31 | } 32 | 33 | public static Component onLeave(ServerPlayer player) { 34 | var config = ModuleProvider.STYLING.getConfig(); 35 | var playerContext = PlaceholderContext.of(player); 36 | return Format.parse( 37 | config.leaveFormat, 38 | playerContext 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/styling/formatters/DeathFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.styling.formatters; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.text.Format; 6 | import me.alexdevs.solstice.modules.ModuleProvider; 7 | import me.alexdevs.solstice.modules.styling.StylingModule; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.world.damagesource.CombatTracker; 11 | 12 | import java.util.Map; 13 | 14 | public class DeathFormatter { 15 | public static Component onDeath(ServerPlayer player, CombatTracker instance) { 16 | var config = ModuleProvider.STYLING.getConfig(); 17 | var deathMessage = instance.getDeathMessage(); 18 | var playerContext = PlaceholderContext.of(player); 19 | 20 | return Format.parse( 21 | config.deathFormat, 22 | playerContext, 23 | Map.of("message", deathMessage) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/styling/formatters/EmoteFormatter.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.styling.formatters; 2 | 3 | import eu.pb4.placeholders.api.PlaceholderContext; 4 | import me.alexdevs.solstice.Solstice; 5 | import me.alexdevs.solstice.api.text.Components; 6 | import me.alexdevs.solstice.api.text.Format; 7 | import me.alexdevs.solstice.modules.ModuleProvider; 8 | import me.alexdevs.solstice.modules.styling.StylingModule; 9 | import net.minecraft.network.chat.ChatType; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.network.chat.PlayerChatMessage; 12 | import net.minecraft.server.level.ServerPlayer; 13 | 14 | import java.util.Map; 15 | 16 | public class EmoteFormatter { 17 | public static void sendEmoteMessage(ServerPlayer receiver, PlayerChatMessage message, ChatType.Bound params, ServerPlayer sender) { 18 | var config = ModuleProvider.STYLING.getConfig(); 19 | var playerContext = PlaceholderContext.of(sender); 20 | 21 | Component messageText = Components.chat(message, sender); 22 | 23 | var text = Format.parse( 24 | config.emoteFormat, 25 | playerContext, 26 | Map.of( 27 | "message", messageText 28 | ) 29 | ); 30 | 31 | receiver.sendSystemMessage(text); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/sudo/SudoModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.sudo; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.sudo.commands.DoAsCommand; 5 | import me.alexdevs.solstice.modules.sudo.commands.SudoCommand; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | public class SudoModule extends ModuleBase.Toggleable { 9 | public SudoModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new SudoCommand(this)); 16 | commands.add(new DoAsCommand(this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/suicide/SuicideModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.suicide; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.suicide.commands.SuicideCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class SuicideModule extends ModuleBase.Toggleable { 8 | 9 | 10 | public SuicideModule(ResourceLocation id) { 11 | super(id); 12 | } 13 | 14 | @Override 15 | public void init() { 16 | commands.add(new SuicideCommand(this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/suicide/commands/SuicideCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.suicide.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.suicide.SuicideModule; 6 | import net.minecraft.commands.CommandSourceStack; 7 | 8 | import java.util.List; 9 | 10 | import static net.minecraft.commands.Commands.literal; 11 | 12 | public class SuicideCommand extends ModCommand { 13 | public SuicideCommand(SuicideModule module) { 14 | super(module); 15 | } 16 | 17 | @Override 18 | public List getNames() { 19 | return List.of("suicide"); 20 | } 21 | 22 | @Override 23 | public LiteralArgumentBuilder command(String name) { 24 | return literal(name) 25 | .requires(require(true)) 26 | .executes(context -> { 27 | var player = context.getSource().getPlayerOrException(); 28 | 29 | player.kill(); 30 | 31 | return 1; 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/tablist/data/TabListConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.tablist.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @ConfigSerializable 10 | public class TabListConfig { 11 | @Comment("Enable the custom tab list functionality.") 12 | public boolean enable = true; 13 | 14 | @Comment("Send tab list updates every X milliseconds. Defaults to 250 ms.") 15 | public int delay = 250; 16 | 17 | @Comment("How fast the phase is. Lower is faster. Defaults to 300") 18 | public double phasePeriod = 300; 19 | 20 | @Comment("Header lines") 21 | public ArrayList header = new ArrayList<>(List.of( 22 | " " 23 | )); 24 | 25 | @Comment("Footer lines") 26 | public ArrayList footer = new ArrayList<>(List.of( 27 | " " 28 | )); 29 | 30 | @Comment("Format to use when displaying the player name in the tab list.") 31 | public String playerTabName = "%solstice:afk%%player:displayname_visual%"; 32 | 33 | /*@Comment("Order of players by group to display in the player list.") 34 | public List groupsOrder = List.of( 35 | "owner", 36 | "admin", 37 | "moderator", 38 | "helper", 39 | "default" 40 | );*/ 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportHere/TeleportHereModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportHere; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.teleportHere.commands.TeleportHereCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class TeleportHereModule extends ModuleBase.Toggleable { 8 | 9 | 10 | public TeleportHereModule(ResourceLocation id) { 11 | super(id); 12 | } 13 | 14 | @Override 15 | public void init() { 16 | commands.add(new TeleportHereCommand(this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportOffline/TeleportOfflineModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportOffline; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.teleportOffline.commands.TeleportOfflineCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class TeleportOfflineModule extends ModuleBase.Toggleable { 8 | 9 | 10 | public TeleportOfflineModule(ResourceLocation id) { 11 | super(id); 12 | } 13 | 14 | @Override 15 | public void init() { 16 | commands.add(new TeleportOfflineCommand(this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportPosition/TeleportPositionModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportPosition; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.teleportPosition.commands.TeleportPositionCommand; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class TeleportPositionModule extends ModuleBase.Toggleable { 8 | 9 | public TeleportPositionModule(ResourceLocation id) { 10 | super(id); 11 | } 12 | 13 | @Override 14 | public void init() { 15 | commands.add(new TeleportPositionCommand(this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportRequest/data/Request.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportRequest.data; 2 | 3 | import net.minecraft.server.level.ServerPlayer; 4 | 5 | public class Request { 6 | public enum Direction { 7 | SOURCE_TO_TARGET, 8 | TARGET_TO_SOURCE, 9 | } 10 | 11 | private final ServerPlayer source; 12 | private int remainingTime; 13 | private final Direction direction; 14 | 15 | public Request(ServerPlayer source, int remainingTime, Direction direction) { 16 | this.source = source; 17 | this.remainingTime = remainingTime; 18 | this.direction = direction; 19 | } 20 | 21 | public ServerPlayer getSource() { 22 | return source; 23 | } 24 | 25 | public int getRemainingTime() { 26 | return remainingTime; 27 | } 28 | 29 | public boolean tickDown() { 30 | return remainingTime-- <= 0; 31 | } 32 | 33 | public Direction getDirection() { 34 | return direction; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportRequest/data/TeleportConfig.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportRequest.data; 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 4 | import org.spongepowered.configurate.objectmapping.meta.Comment; 5 | 6 | @ConfigSerializable 7 | public class TeleportConfig { 8 | @Comment("The teleport request times out after the following seconds. Defaults to 120 seconds.") 9 | public int teleportRequestTimeout = 120; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/teleportRequest/data/TeleportLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.teleportRequest.data; 2 | 3 | import java.util.Map; 4 | 5 | public class TeleportLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("teleporting", "Teleporting..."), 8 | Map.entry("requestSent", "Teleport request sent."), 9 | Map.entry("pendingTeleport", "${requesterPlayer} requested to teleport to you.\n ${acceptButton} ${refuseButton}"), 10 | Map.entry("pendingTeleportHere", "${requesterPlayer} requested you to teleport to them.\n ${acceptButton} ${refuseButton}"), 11 | Map.entry("noPending", "There are no pending teleport requests for you."), 12 | Map.entry("unavailable", "This request expired or is no longer available."), 13 | Map.entry("playerUnavailable", "The other player is no longer available."), 14 | 15 | Map.entry("targetAccepted", "Teleport request accepted."), 16 | Map.entry("sourceAccepted", "${player} accepted your teleport request!"), 17 | Map.entry("targetRefused", "Teleport request refused."), 18 | Map.entry("sourceRefused", "${player} refused your teleport request!") 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/tell/data/TellLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.tell.data; 2 | 3 | import java.util.Map; 4 | 5 | public class TellLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("playerNotFound", "Player ${targetPlayer} not found!"), 8 | Map.entry("you", "You"), 9 | Map.entry("message", "[${sourcePlayer} ${targetPlayer}] ${message}"), 10 | Map.entry("messageSpy", "\uD83D\uDC41 [${sourcePlayer} → ${targetPlayer}] ${message}"), 11 | Map.entry("noLastSenderReply", "You have no one to reply to.") // relatable 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/trash/TrashModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.trash; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.trash.commands.TrashCommand; 6 | import me.alexdevs.solstice.modules.trash.data.TrashLocale; 7 | import net.minecraft.resources.ResourceLocation; 8 | 9 | public class TrashModule extends ModuleBase.Toggleable { 10 | 11 | 12 | public TrashModule(ResourceLocation id) { 13 | super(id); 14 | } 15 | 16 | @Override 17 | public void init() { 18 | registerLocale(TrashLocale.MODULE); 19 | 20 | commands.add(new TrashCommand(this)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/trash/commands/TrashCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.trash.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.trash.TrashModule; 6 | import net.minecraft.commands.CommandSourceStack; 7 | import net.minecraft.world.SimpleMenuProvider; 8 | import net.minecraft.world.inventory.ChestMenu; 9 | import java.util.List; 10 | 11 | import static net.minecraft.commands.Commands.literal; 12 | 13 | public class TrashCommand extends ModCommand { 14 | public TrashCommand(TrashModule module) { 15 | super(module); 16 | } 17 | 18 | @Override 19 | public List getNames() { 20 | return List.of("trash"); 21 | } 22 | 23 | @Override 24 | public LiteralArgumentBuilder command(String name) { 25 | return literal(name) 26 | .requires(require(2)) 27 | .executes(context -> { 28 | var player = context.getSource().getPlayerOrException(); 29 | 30 | player.openMenu( 31 | new SimpleMenuProvider((syncId, inventory, playerx) -> 32 | ChestMenu.threeRows(syncId, inventory), 33 | module.locale().get("trash"))); 34 | 35 | return 1; 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/trash/data/TrashLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.trash.data; 2 | 3 | import java.util.Map; 4 | 5 | public class TrashLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("trash", "Trash Bin") 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/UtilitiesModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities; 2 | 3 | import me.alexdevs.solstice.api.module.ModuleBase; 4 | import me.alexdevs.solstice.modules.utilities.commands.*; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | public class UtilitiesModule extends ModuleBase.Toggleable { 8 | public UtilitiesModule(ResourceLocation id) { 9 | super(id); 10 | } 11 | 12 | @Override 13 | public void init() { 14 | commands.add(new AnvilCommand(this)); 15 | commands.add(new CartographyCommand(this)); 16 | commands.add(new GrindstoneCommand(this)); 17 | commands.add(new LoomCommand(this)); 18 | commands.add(new SmithingCommand(this)); 19 | commands.add(new StonecutterCommand(this)); 20 | commands.add(new WorkbenchCommand(this)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/commands/AnvilCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.utilities.UtilitiesModule; 6 | import me.alexdevs.solstice.modules.utilities.virtualScreenHandlers.VirtualAnvilScreenHandler; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.stats.Stats; 10 | import net.minecraft.world.SimpleMenuProvider; 11 | import net.minecraft.world.inventory.ContainerLevelAccess; 12 | import java.util.List; 13 | 14 | import static net.minecraft.commands.Commands.literal; 15 | 16 | public class AnvilCommand extends ModCommand { 17 | public AnvilCommand(UtilitiesModule module) { 18 | super(module); 19 | } 20 | 21 | @Override 22 | public List getNames() { 23 | return List.of("anvil"); 24 | } 25 | 26 | @Override 27 | public LiteralArgumentBuilder command(String name) { 28 | return literal(name) 29 | .requires(require("anvil", 2)) 30 | .executes(context -> { 31 | var player = context.getSource().getPlayerOrException(); 32 | var screen = new SimpleMenuProvider( 33 | (syncId, inventory, p) -> 34 | new VirtualAnvilScreenHandler(syncId, inventory, ContainerLevelAccess.create(player.level(), player.blockPosition())), 35 | Component.translatable("container.repair")); 36 | player.openMenu(screen); 37 | player.awardStat(Stats.INTERACT_WITH_ANVIL); 38 | 39 | return 1; 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/commands/LoomCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.utilities.UtilitiesModule; 6 | import me.alexdevs.solstice.modules.utilities.virtualScreenHandlers.VirtualLoomScreenHandler; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.stats.Stats; 10 | import net.minecraft.world.SimpleMenuProvider; 11 | import net.minecraft.world.inventory.ContainerLevelAccess; 12 | import java.util.List; 13 | 14 | import static net.minecraft.commands.Commands.literal; 15 | 16 | public class LoomCommand extends ModCommand { 17 | public LoomCommand(UtilitiesModule module) { 18 | super(module); 19 | } 20 | 21 | @Override 22 | public List getNames() { 23 | return List.of("loom"); 24 | } 25 | 26 | @Override 27 | public LiteralArgumentBuilder command(String name) { 28 | return literal(name) 29 | .requires(require("loom", 2)) 30 | .executes(context -> { 31 | var player = context.getSource().getPlayerOrException(); 32 | var screen = new SimpleMenuProvider( 33 | (syncId, inventory, p) -> 34 | new VirtualLoomScreenHandler(syncId, inventory, ContainerLevelAccess.create(player.level(), player.blockPosition())), 35 | Component.translatable("container.loom")); 36 | player.openMenu(screen); 37 | player.awardStat(Stats.INTERACT_WITH_LOOM); 38 | return 1; 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/commands/SmithingCommand.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import me.alexdevs.solstice.api.module.ModCommand; 5 | import me.alexdevs.solstice.modules.utilities.UtilitiesModule; 6 | import me.alexdevs.solstice.modules.utilities.virtualScreenHandlers.VirtualSmithingScreenHandler; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.stats.Stats; 10 | import net.minecraft.world.SimpleMenuProvider; 11 | import net.minecraft.world.inventory.ContainerLevelAccess; 12 | import java.util.List; 13 | 14 | import static net.minecraft.commands.Commands.literal; 15 | 16 | public class SmithingCommand extends ModCommand { 17 | public SmithingCommand(UtilitiesModule module) { 18 | super(module); 19 | } 20 | 21 | @Override 22 | public List getNames() { 23 | return List.of("smithing"); 24 | } 25 | 26 | @Override 27 | public LiteralArgumentBuilder command(String name) { 28 | return literal(name) 29 | .requires(require("smithing", 2)) 30 | .executes(context -> { 31 | var player = context.getSource().getPlayerOrException(); 32 | var screen = new SimpleMenuProvider( 33 | (syncId, inventory, p) -> 34 | new VirtualSmithingScreenHandler(syncId, inventory, ContainerLevelAccess.create(player.level(), player.blockPosition())), 35 | Component.translatable("container.upgrade")); 36 | player.openMenu(screen); 37 | player.awardStat(Stats.INTERACT_WITH_SMITHING_TABLE); 38 | 39 | return 1; 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualAnvilScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.AnvilMenu; 6 | import net.minecraft.world.inventory.ContainerLevelAccess; 7 | 8 | public class VirtualAnvilScreenHandler extends AnvilMenu { 9 | public VirtualAnvilScreenHandler(int syncId, Inventory inventory, ContainerLevelAccess context) { 10 | super(syncId, inventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualCartographyTableScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.CartographyTableMenu; 6 | import net.minecraft.world.inventory.ContainerLevelAccess; 7 | 8 | public class VirtualCartographyTableScreenHandler extends CartographyTableMenu { 9 | public VirtualCartographyTableScreenHandler(int syncId, Inventory inventory, ContainerLevelAccess context) { 10 | super(syncId, inventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualCraftingScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.ContainerLevelAccess; 6 | import net.minecraft.world.inventory.CraftingMenu; 7 | 8 | public class VirtualCraftingScreenHandler extends CraftingMenu { 9 | public VirtualCraftingScreenHandler(int syncId, Inventory playerInventory, ContainerLevelAccess context) { 10 | super(syncId, playerInventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualGrindstoneScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.ContainerLevelAccess; 6 | import net.minecraft.world.inventory.GrindstoneMenu; 7 | 8 | public class VirtualGrindstoneScreenHandler extends GrindstoneMenu { 9 | public VirtualGrindstoneScreenHandler(int syncId, Inventory playerInventory, ContainerLevelAccess context) { 10 | super(syncId, playerInventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualLoomScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.ContainerLevelAccess; 6 | import net.minecraft.world.inventory.LoomMenu; 7 | 8 | public class VirtualLoomScreenHandler extends LoomMenu { 9 | public VirtualLoomScreenHandler(int syncId, Inventory playerInventory, ContainerLevelAccess context) { 10 | super(syncId, playerInventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualSmithingScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.ContainerLevelAccess; 6 | import net.minecraft.world.inventory.SmithingMenu; 7 | 8 | public class VirtualSmithingScreenHandler extends SmithingMenu { 9 | public VirtualSmithingScreenHandler(int syncId, Inventory playerInventory, ContainerLevelAccess context) { 10 | super(syncId, playerInventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/utilities/virtualScreenHandlers/VirtualStonecutterScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.utilities.virtualScreenHandlers; 2 | 3 | import net.minecraft.world.entity.player.Inventory; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.inventory.ContainerLevelAccess; 6 | import net.minecraft.world.inventory.StonecutterMenu; 7 | 8 | public class VirtualStonecutterScreenHandler extends StonecutterMenu { 9 | public VirtualStonecutterScreenHandler(int syncId, Inventory playerInventory, ContainerLevelAccess context) { 10 | super(syncId, playerInventory, context); 11 | } 12 | 13 | @Override 14 | public boolean stillValid(Player player) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/warp/WarpModule.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.warp; 2 | 3 | import me.alexdevs.solstice.Solstice; 4 | import me.alexdevs.solstice.api.module.ModuleBase; 5 | import me.alexdevs.solstice.modules.warp.commands.DeleteWarpCommand; 6 | import me.alexdevs.solstice.modules.warp.commands.SetWarpCommand; 7 | import me.alexdevs.solstice.modules.warp.commands.WarpCommand; 8 | import me.alexdevs.solstice.modules.warp.commands.WarpsCommand; 9 | import me.alexdevs.solstice.modules.warp.data.WarpLocale; 10 | import me.alexdevs.solstice.modules.warp.data.WarpServerData; 11 | import me.lucko.fabric.api.permissions.v0.Permissions; 12 | import net.minecraft.resources.ResourceLocation; 13 | import net.minecraft.server.level.ServerPlayer; 14 | 15 | public class WarpModule extends ModuleBase.Toggleable { 16 | 17 | 18 | public WarpModule(ResourceLocation id) { 19 | super(id); 20 | } 21 | 22 | @Override 23 | public void init() { 24 | registerLocale(WarpLocale.MODULE); 25 | registerServerData(WarpServerData.class, WarpServerData::new); 26 | 27 | commands.add(new WarpCommand(this)); 28 | commands.add(new WarpsCommand(this)); 29 | commands.add(new SetWarpCommand(this)); 30 | commands.add(new DeleteWarpCommand(this)); 31 | } 32 | 33 | public boolean canUseWarp(ServerPlayer player, String warpName) { 34 | return Permissions.check(player, getPermissionNode("warps." + warpName), true); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/warp/data/WarpLocale.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.warp.data; 2 | 3 | import java.util.Map; 4 | 5 | public class WarpLocale { 6 | public static final Map MODULE = Map.ofEntries( 7 | Map.entry("teleporting", "Warping to ${warp}..."), 8 | Map.entry("warpNotFound", "The warp ${warp} does not exist!"), 9 | Map.entry("warpList", "Server warps: ${warpList}"), 10 | Map.entry("warpsFormat", "${warp}"), 11 | Map.entry("warpsComma", ", "), 12 | Map.entry("noWarps", "There are no warps so far."), 13 | Map.entry("noPermission", "You do not have permission to warp to ${warp}."), 14 | Map.entry("deleted", "Warp ${warp} deleted!"), 15 | Map.entry("created", "New warp ${warp} created!") 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/alexdevs/solstice/modules/warp/data/WarpServerData.java: -------------------------------------------------------------------------------- 1 | package me.alexdevs.solstice.modules.warp.data; 2 | 3 | import me.alexdevs.solstice.api.ServerLocation; 4 | 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | public class WarpServerData { 8 | public ConcurrentHashMap warps = new ConcurrentHashMap<>(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[1,)" 3 | license="MIT" 4 | 5 | [properties] 6 | "connector:placeholder"=true 7 | 8 | [[mods]] 9 | modId="solstice" 10 | version="0.0.1" 11 | displayName="Solstice Essentials" 12 | authors="AlexDevs" 13 | description='''Server commands, custom chat formats and more''' 14 | 15 | [[dependencies.solstice]] 16 | modId="forge" 17 | mandatory=true 18 | versionRange="[47,)" 19 | ordering="NONE" 20 | side="SERVER" 21 | 22 | [[dependencies.solstice]] 23 | modId="minecraft" 24 | mandatory=true 25 | versionRange="[1.21.1]" 26 | ordering="NONE" 27 | side="SERVER" 28 | 29 | [[dependencies.solstice]] 30 | modId="connector" 31 | mandatory=true 32 | versionRange="*" 33 | ordering="NONE" 34 | side="BOTH" -------------------------------------------------------------------------------- /src/main/resources/assets/solstice/info/formatting.txt: -------------------------------------------------------------------------------- 1 | This page is an example of the formatting and placeholders used in pages and command texts. 2 | 3 | Formatting: 4 | 5 | Colors: 6 | yellow, dark_blue, dark_purple, gold, red, aqua, gray, light_purple, white, dark_gray, green, dark_green, blue, dark_aqua, dark_green, black. 7 | 8 | Decorations: 9 | strikethrough, st, underline, underlined, u, italic, i, obfuscated, obf(obfuscated, obf), bold, b 10 | 11 | Fonts: 12 | default, uniform(uniform), alt(alt) 13 | 14 | Gradients: 15 | smooth white to black, hard white to black, rainbow 16 | 17 | For the complete documentation on text formatting check out this link. 18 | 19 | Placeholders: 20 | 21 | Player name: %player:name% 22 | Player display name: %player:displayname% 23 | 24 | For the complete documentation on placeholders check out this link. -------------------------------------------------------------------------------- /src/main/resources/assets/solstice/info/motd.txt: -------------------------------------------------------------------------------- 1 | Welcome to the server, %player:displayname%! 2 | 3 | The world time is %world:time%. 4 | There are %server:online%/%server:max_players% online players. 5 | 6 | Make sure to read the /rules! -------------------------------------------------------------------------------- /src/main/resources/assets/solstice/info/rules.txt: -------------------------------------------------------------------------------- 1 | 1. Respect players. 2 | 2. Respect staff members. 3 | 3. Enjoy your stay! -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "solstice", 4 | "version": "${version}", 5 | "name": "Solstice Essentials", 6 | "description": "Server commands, custom chat formats and more", 7 | "authors": [ 8 | { 9 | "name": "AlexDevs", 10 | "contact": { 11 | "email": "solstice@alexdevs.me", 12 | "homepage": "https://alexdevs.me" 13 | } 14 | } 15 | ], 16 | "contact": { 17 | "sources": "https://github.com/Ale32bit/Solstice.git", 18 | "issues": "https://github.com/Ale32bit/Solstice/issues", 19 | "homepage": "https://solstice.alexdevs.me", 20 | "email": "solstice@alexdevs.me" 21 | }, 22 | "license": "MIT", 23 | "environment": "*", 24 | "entrypoints": { 25 | "main": [ 26 | "me.alexdevs.solstice.Solstice" 27 | ], 28 | "solstice": [ 29 | "me.alexdevs.solstice.modules.ModuleProvider" 30 | ] 31 | }, 32 | "mixins": [ 33 | "solstice.mixins.json" 34 | ], 35 | "depends": { 36 | "fabricloader": ">=0.16.0", 37 | "minecraft": "~1.21.1", 38 | "java": ">=21", 39 | "fabric-api": "*" 40 | }, 41 | "recommends": { 42 | "luckperms": "*" 43 | }, 44 | "accessWidener": "solstice.accesswidener" 45 | } -------------------------------------------------------------------------------- /src/main/resources/solstice.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | # RTP 3 | accessible method net/minecraft/server/level/ServerChunkCache getVisibleChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; 4 | 5 | # Inventory see 6 | accessible field net/minecraft/server/MinecraftServer playerDataStorage Lnet/minecraft/world/level/storage/PlayerDataStorage; -------------------------------------------------------------------------------- /src/main/resources/solstice.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.alexdevs.solstice.mixin", 5 | "compatibilityLevel": "JAVA_21", 6 | "plugin": "me.alexdevs.solstice.mixin.SolsticeMixinConfigPlugin", 7 | "client": [], 8 | "server": [], 9 | "injectors": { 10 | "defaultRequire": 1 11 | }, 12 | "mixins": [ 13 | "CommandNodeAccessor", 14 | "events.CommandEventsMixin", 15 | "events.WorldSaveEventMixin", 16 | "modules.admin.ConnectionBypassMixin", 17 | "modules.afk.FixPlayerSleepPercentageMixin", 18 | "modules.back.PreTeleportMixin", 19 | "modules.ban.CustomBanMessageMixin", 20 | "modules.core.RealPingMixin", 21 | "modules.customname.CustomDisplayNameMixin", 22 | "modules.miscellaneous.BypassSleepingInBedCheckMixin", 23 | "modules.sign.FormatSignMixin", 24 | "modules.spawn.OverrideNewPlayerSpawnPointMixin", 25 | "modules.spawn.OverrideSpawnPointMixin", 26 | "modules.styling.CustomAdvancementMixin", 27 | "modules.styling.CustomChatMessageMixin", 28 | "modules.styling.CustomConnectionMessagesMixin", 29 | "modules.styling.CustomDeathMessageMixin", 30 | "modules.styling.CustomSentMessageMixin", 31 | "modules.styling.InjectCustomChatMessageMixin", 32 | "modules.styling.PlayerDisconnectMixin", 33 | "modules.tablist.CustomPlayerListNameMixin", 34 | "modules.tablist.PlayerOrderMixin", 35 | "modules.tablist.UpdatePlayerListMixin" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------