From d1e16a47819509ed645bb93e1a173e0a97025cef Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 8 Jan 2025 19:25:29 +0100 Subject: build: Move mod to subproject --- .github/workflows/build.yml | 6 +- build-src/src/main/kotlin/helpers.kt | 10 + build.gradle.kts | 186 +------------------ gradle.properties | 5 +- ledger-rules.pro | 4 - log4j2.xml | 5 - mod/build.gradle.kts | 170 +++++++++++++++++ mod/gradle.properties | 3 + mod/ledger-rules.pro | 4 + mod/log4j2.xml | 5 + .../nea/ledger/init/AutoDiscoveryMixinPlugin.java | 193 +++++++++++++++++++ .../moe/nea/ledger/mixin/AccessorGuiEditSign.java | 12 ++ .../moe/nea/ledger/mixin/MouseClickEventPatch.java | 18 ++ .../mixin/OnInitializationCompletePatch.java | 18 ++ .../mixin/devenv/RegisterModResourcesPatch.java | 66 +++++++ .../main/kotlin/moe/nea/ledger/ConfigCommand.kt | 31 ++++ .../main/kotlin/moe/nea/ledger/DebouncedValue.kt | 38 ++++ .../main/kotlin/moe/nea/ledger/DebugDataCommand.kt | 34 ++++ mod/src/main/kotlin/moe/nea/ledger/DevUtil.kt | 7 + .../main/kotlin/moe/nea/ledger/ExpiringValue.kt | 30 +++ mod/src/main/kotlin/moe/nea/ledger/ItemChange.kt | 84 +++++++++ mod/src/main/kotlin/moe/nea/ledger/ItemId.kt | 35 ++++ .../main/kotlin/moe/nea/ledger/ItemIdProvider.kt | 188 +++++++++++++++++++ mod/src/main/kotlin/moe/nea/ledger/ItemUtil.kt | 90 +++++++++ mod/src/main/kotlin/moe/nea/ledger/Ledger.kt | 205 +++++++++++++++++++++ mod/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt | 29 +++ mod/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt | 136 ++++++++++++++ .../main/kotlin/moe/nea/ledger/LogChatCommand.kt | 27 +++ mod/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt | 22 +++ mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt | 117 ++++++++++++ mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt | 197 ++++++++++++++++++++ .../main/kotlin/moe/nea/ledger/ScoreboardUtil.kt | 29 +++ .../kotlin/moe/nea/ledger/TelemetryProvider.kt | 66 +++++++ .../main/kotlin/moe/nea/ledger/TransactionType.kt | 35 ++++ .../main/kotlin/moe/nea/ledger/TriggerCommand.kt | 34 ++++ .../kotlin/moe/nea/ledger/config/DebugOptions.kt | 13 ++ .../kotlin/moe/nea/ledger/config/LedgerConfig.kt | 35 ++++ .../kotlin/moe/nea/ledger/config/MainOptions.kt | 27 +++ .../nea/ledger/config/SynchronizationOptions.kt | 11 ++ .../main/kotlin/moe/nea/ledger/config/UpdateUi.kt | 17 ++ .../kotlin/moe/nea/ledger/config/UpdateUiMarker.kt | 6 + .../kotlin/moe/nea/ledger/database/DBLogEntry.kt | 24 +++ .../kotlin/moe/nea/ledger/database/DBUpgrade.kt | 68 +++++++ .../kotlin/moe/nea/ledger/database/Database.kt | 57 ++++++ .../kotlin/moe/nea/ledger/database/Upgrades.kt | 20 ++ .../main/kotlin/moe/nea/ledger/database/schema.dot | 23 +++ .../moe/nea/ledger/events/BeforeGuiAction.kt | 11 ++ .../kotlin/moe/nea/ledger/events/ChatReceived.kt | 15 ++ .../moe/nea/ledger/events/ExtraSupplyIdEvent.kt | 12 ++ .../kotlin/moe/nea/ledger/events/GuiClickEvent.kt | 9 + .../nea/ledger/events/InitializationComplete.kt | 6 + .../nea/ledger/events/RegistrationFinishedEvent.kt | 7 + .../moe/nea/ledger/events/SupplyDebugInfo.kt | 10 + .../kotlin/moe/nea/ledger/events/TriggerEvent.kt | 7 + .../kotlin/moe/nea/ledger/events/WorldLoadEvent.kt | 5 + .../moe/nea/ledger/events/WorldSwitchEvent.kt | 6 + .../ledger/modules/AccessorySwapperDetection.kt | 34 ++++ .../moe/nea/ledger/modules/AllowanceDetection.kt | 37 ++++ .../nea/ledger/modules/AuctionHouseDetection.kt | 143 ++++++++++++++ .../kotlin/moe/nea/ledger/modules/BankDetection.kt | 49 +++++ .../moe/nea/ledger/modules/BazaarDetection.kt | 58 ++++++ .../moe/nea/ledger/modules/BazaarOrderDetection.kt | 95 ++++++++++ .../kotlin/moe/nea/ledger/modules/BitsDetection.kt | 62 +++++++ .../moe/nea/ledger/modules/BitsShopDetection.kt | 66 +++++++ .../moe/nea/ledger/modules/ChestDetection.kt | 48 +++++ .../ledger/modules/DragonEyePlacementDetection.kt | 47 +++++ .../nea/ledger/modules/DragonSacrificeDetection.kt | 72 ++++++++ .../nea/ledger/modules/DungeonChestDetection.kt | 95 ++++++++++ .../moe/nea/ledger/modules/ExternalDataProvider.kt | 43 +++++ .../moe/nea/ledger/modules/EyedropsDetection.kt | 35 ++++ .../moe/nea/ledger/modules/ForgeDetection.kt | 48 +++++ .../moe/nea/ledger/modules/GambleDetection.kt | 62 +++++++ .../moe/nea/ledger/modules/GodPotionDetection.kt | 35 ++++ .../nea/ledger/modules/GodPotionMixinDetection.kt | 38 ++++ .../kotlin/moe/nea/ledger/modules/KatDetection.kt | 95 ++++++++++ .../moe/nea/ledger/modules/KuudraChestDetection.kt | 45 +++++ .../nea/ledger/modules/MineshaftCorpseDetection.kt | 81 ++++++++ .../moe/nea/ledger/modules/MinionDetection.kt | 61 ++++++ .../kotlin/moe/nea/ledger/modules/NpcDetection.kt | 111 +++++++++++ .../kotlin/moe/nea/ledger/modules/UpdateChecker.kt | 167 +++++++++++++++++ .../moe/nea/ledger/modules/VisitorDetection.kt | 87 +++++++++ .../moe/nea/ledger/utils/BorderedTextTracker.kt | 41 +++++ .../main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt | 52 ++++++ .../main/kotlin/moe/nea/ledger/utils/GsonUtil.kt | 10 + .../moe/nea/ledger/utils/MinecraftExecutor.kt | 10 + .../kotlin/moe/nea/ledger/utils/NoSideEffects.kt | 4 + .../kotlin/moe/nea/ledger/utils/network/Request.kt | 40 ++++ .../moe/nea/ledger/utils/network/RequestUtil.kt | 63 +++++++ .../moe/nea/ledger/utils/network/Response.kt | 19 ++ .../nea/ledger/utils/telemetry/BooleanContext.kt | 10 + .../moe/nea/ledger/utils/telemetry/CommonKeys.kt | 9 + .../moe/nea/ledger/utils/telemetry/Context.kt | 57 ++++++ .../moe/nea/ledger/utils/telemetry/ContextValue.kt | 70 +++++++ .../nea/ledger/utils/telemetry/EventRecorder.kt | 9 + .../utils/telemetry/ExceptionContextValue.kt | 39 ++++ .../ledger/utils/telemetry/JsonElementContext.kt | 9 + .../ledger/utils/telemetry/LoggingEventRecorder.kt | 25 +++ .../nea/ledger/utils/telemetry/RecordedEvent.kt | 5 + .../moe/nea/ledger/utils/telemetry/Severity.kt | 8 + .../kotlin/moe/nea/ledger/utils/telemetry/Span.kt | 146 +++++++++++++++ .../nea/ledger/utils/telemetry/StringContext.kt | 11 ++ mod/src/main/resources/ledgerkeystore.jks | Bin 0 -> 104393 bytes mod/src/main/resources/mcmod.info | 18 ++ mod/src/main/resources/mixins.moneyledger.json | 7 + .../test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt | 17 ++ settings.gradle.kts | 1 + .../nea/ledger/init/AutoDiscoveryMixinPlugin.java | 193 ------------------- .../moe/nea/ledger/mixin/AccessorGuiEditSign.java | 12 -- .../moe/nea/ledger/mixin/MouseClickEventPatch.java | 18 -- .../mixin/OnInitializationCompletePatch.java | 18 -- .../mixin/devenv/RegisterModResourcesPatch.java | 66 ------- src/main/kotlin/moe/nea/ledger/ConfigCommand.kt | 31 ---- src/main/kotlin/moe/nea/ledger/DebouncedValue.kt | 38 ---- src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt | 34 ---- src/main/kotlin/moe/nea/ledger/DevUtil.kt | 7 - src/main/kotlin/moe/nea/ledger/ExpiringValue.kt | 30 --- src/main/kotlin/moe/nea/ledger/ItemChange.kt | 84 --------- src/main/kotlin/moe/nea/ledger/ItemId.kt | 35 ---- src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt | 188 ------------------- src/main/kotlin/moe/nea/ledger/ItemUtil.kt | 90 --------- src/main/kotlin/moe/nea/ledger/Ledger.kt | 205 --------------------- src/main/kotlin/moe/nea/ledger/LedgerEntry.kt | 29 --- src/main/kotlin/moe/nea/ledger/LedgerLogger.kt | 136 -------------- src/main/kotlin/moe/nea/ledger/LogChatCommand.kt | 27 --- src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt | 22 --- src/main/kotlin/moe/nea/ledger/NumberUtil.kt | 117 ------------ src/main/kotlin/moe/nea/ledger/QueryCommand.kt | 197 -------------------- src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt | 29 --- .../kotlin/moe/nea/ledger/TelemetryProvider.kt | 66 ------- src/main/kotlin/moe/nea/ledger/TransactionType.kt | 35 ---- src/main/kotlin/moe/nea/ledger/TriggerCommand.kt | 34 ---- .../kotlin/moe/nea/ledger/config/DebugOptions.kt | 13 -- .../kotlin/moe/nea/ledger/config/LedgerConfig.kt | 35 ---- .../kotlin/moe/nea/ledger/config/MainOptions.kt | 27 --- .../nea/ledger/config/SynchronizationOptions.kt | 11 -- src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt | 17 -- .../kotlin/moe/nea/ledger/config/UpdateUiMarker.kt | 6 - .../kotlin/moe/nea/ledger/database/DBLogEntry.kt | 24 --- .../kotlin/moe/nea/ledger/database/DBUpgrade.kt | 68 ------- .../kotlin/moe/nea/ledger/database/Database.kt | 57 ------ .../kotlin/moe/nea/ledger/database/Upgrades.kt | 20 -- src/main/kotlin/moe/nea/ledger/database/schema.dot | 23 --- .../moe/nea/ledger/events/BeforeGuiAction.kt | 11 -- .../kotlin/moe/nea/ledger/events/ChatReceived.kt | 15 -- .../moe/nea/ledger/events/ExtraSupplyIdEvent.kt | 12 -- .../kotlin/moe/nea/ledger/events/GuiClickEvent.kt | 9 - .../nea/ledger/events/InitializationComplete.kt | 6 - .../nea/ledger/events/RegistrationFinishedEvent.kt | 7 - .../moe/nea/ledger/events/SupplyDebugInfo.kt | 10 - .../kotlin/moe/nea/ledger/events/TriggerEvent.kt | 7 - .../kotlin/moe/nea/ledger/events/WorldLoadEvent.kt | 5 - .../moe/nea/ledger/events/WorldSwitchEvent.kt | 6 - .../ledger/modules/AccessorySwapperDetection.kt | 34 ---- .../moe/nea/ledger/modules/AllowanceDetection.kt | 37 ---- .../nea/ledger/modules/AuctionHouseDetection.kt | 143 -------------- .../kotlin/moe/nea/ledger/modules/BankDetection.kt | 49 ----- .../moe/nea/ledger/modules/BazaarDetection.kt | 58 ------ .../moe/nea/ledger/modules/BazaarOrderDetection.kt | 95 ---------- .../kotlin/moe/nea/ledger/modules/BitsDetection.kt | 62 ------- .../moe/nea/ledger/modules/BitsShopDetection.kt | 66 ------- .../moe/nea/ledger/modules/ChestDetection.kt | 48 ----- .../ledger/modules/DragonEyePlacementDetection.kt | 47 ----- .../nea/ledger/modules/DragonSacrificeDetection.kt | 72 -------- .../nea/ledger/modules/DungeonChestDetection.kt | 95 ---------- .../moe/nea/ledger/modules/ExternalDataProvider.kt | 43 ----- .../moe/nea/ledger/modules/EyedropsDetection.kt | 35 ---- .../moe/nea/ledger/modules/ForgeDetection.kt | 48 ----- .../moe/nea/ledger/modules/GambleDetection.kt | 62 ------- .../moe/nea/ledger/modules/GodPotionDetection.kt | 35 ---- .../nea/ledger/modules/GodPotionMixinDetection.kt | 38 ---- .../kotlin/moe/nea/ledger/modules/KatDetection.kt | 95 ---------- .../moe/nea/ledger/modules/KuudraChestDetection.kt | 45 ----- .../nea/ledger/modules/MineshaftCorpseDetection.kt | 81 -------- .../moe/nea/ledger/modules/MinionDetection.kt | 61 ------ .../kotlin/moe/nea/ledger/modules/NpcDetection.kt | 111 ----------- .../kotlin/moe/nea/ledger/modules/UpdateChecker.kt | 167 ----------------- .../moe/nea/ledger/modules/VisitorDetection.kt | 87 --------- .../moe/nea/ledger/utils/BorderedTextTracker.kt | 41 ----- src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt | 52 ------ src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt | 10 - .../moe/nea/ledger/utils/MinecraftExecutor.kt | 10 - .../kotlin/moe/nea/ledger/utils/NoSideEffects.kt | 4 - .../kotlin/moe/nea/ledger/utils/network/Request.kt | 40 ---- .../moe/nea/ledger/utils/network/RequestUtil.kt | 63 ------- .../moe/nea/ledger/utils/network/Response.kt | 19 -- .../nea/ledger/utils/telemetry/BooleanContext.kt | 10 - .../moe/nea/ledger/utils/telemetry/CommonKeys.kt | 9 - .../moe/nea/ledger/utils/telemetry/Context.kt | 57 ------ .../moe/nea/ledger/utils/telemetry/ContextValue.kt | 70 ------- .../nea/ledger/utils/telemetry/EventRecorder.kt | 9 - .../utils/telemetry/ExceptionContextValue.kt | 39 ---- .../ledger/utils/telemetry/JsonElementContext.kt | 9 - .../ledger/utils/telemetry/LoggingEventRecorder.kt | 25 --- .../nea/ledger/utils/telemetry/RecordedEvent.kt | 5 - .../moe/nea/ledger/utils/telemetry/Severity.kt | 8 - .../kotlin/moe/nea/ledger/utils/telemetry/Span.kt | 146 --------------- .../nea/ledger/utils/telemetry/StringContext.kt | 11 -- src/main/resources/ledgerkeystore.jks | Bin 104393 -> 0 bytes src/main/resources/mcmod.info | 18 -- src/main/resources/mixins.moneyledger.json | 7 - src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt | 17 -- 201 files changed, 4728 insertions(+), 4717 deletions(-) create mode 100644 build-src/src/main/kotlin/helpers.kt delete mode 100644 ledger-rules.pro delete mode 100644 log4j2.xml create mode 100644 mod/build.gradle.kts create mode 100644 mod/gradle.properties create mode 100644 mod/ledger-rules.pro create mode 100644 mod/log4j2.xml create mode 100644 mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java create mode 100644 mod/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java create mode 100644 mod/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java create mode 100644 mod/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java create mode 100644 mod/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/DevUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ItemChange.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ItemId.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ItemUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/Ledger.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/TransactionType.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/database/Database.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/database/schema.dot create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt create mode 100644 mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt create mode 100644 mod/src/main/resources/ledgerkeystore.jks create mode 100644 mod/src/main/resources/mcmod.info create mode 100644 mod/src/main/resources/mixins.moneyledger.json create mode 100644 mod/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt delete mode 100644 src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java delete mode 100644 src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java delete mode 100644 src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java delete mode 100644 src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java delete mode 100644 src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java delete mode 100644 src/main/kotlin/moe/nea/ledger/ConfigCommand.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/DebouncedValue.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/DevUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ExpiringValue.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ItemChange.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ItemId.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ItemUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/Ledger.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/LedgerEntry.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/LedgerLogger.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/LogChatCommand.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/NumberUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/QueryCommand.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/TransactionType.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/TriggerCommand.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/MainOptions.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/database/Database.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/database/Upgrades.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/database/schema.dot delete mode 100644 src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/network/Request.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/network/Response.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt delete mode 100644 src/main/resources/ledgerkeystore.jks delete mode 100644 src/main/resources/mcmod.info delete mode 100644 src/main/resources/mixins.moneyledger.json delete mode 100644 src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7765a6c..a9eee2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,18 +24,18 @@ jobs: - name: Show build directory if: runner.debug == '1' run: | - ls -lahR build + ls -lahR mod/build - name: Upload built mod JAR uses: actions/upload-artifact@v4.3.0 with: name: mod-jar - path: build/libs/*.jar + path: mod/build/libs/*.jar - name: Upload partial JARs if: runner.debug == '1' uses: actions/upload-artifact@v4.3.0 with: name: extras - path: build/badjars/*.jar + path: mod/build/badjars/*.jar release: runs-on: ubuntu-latest needs: gradle diff --git a/build-src/src/main/kotlin/helpers.kt b/build-src/src/main/kotlin/helpers.kt new file mode 100644 index 0000000..5afef4f --- /dev/null +++ b/build-src/src/main/kotlin/helpers.kt @@ -0,0 +1,10 @@ +import org.gradle.api.plugins.ExtensionAware +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.findByType + + +inline fun ExtensionAware.configureIf(crossinline block: T.() -> Unit) { + if (extensions.findByType() != null) { + extensions.configure { block() } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index c5ee6d5..b4583f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,4 @@ import com.github.gmazzo.buildconfig.BuildConfigExtension -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import proguard.gradle.ProGuardTask import java.io.ByteArrayOutputStream buildscript { @@ -13,16 +11,11 @@ buildscript { } plugins { - idea - java - id("gg.essential.loom") version "1.6.+" - id("dev.architectury.architectury-pack200") version "0.1.3" - id("com.github.johnrengelman.shadow") version "8.1.1" - id("com.github.gmazzo.buildconfig") version "5.5.0" - kotlin("jvm") version "2.0.20" + kotlin("jvm") version "2.0.20" apply false + id("com.github.gmazzo.buildconfig") version "5.5.0" apply false id("ledger-globals") - id("ledger-repo") } + allprojects { apply(plugin = "ledger-globals") } @@ -36,179 +29,16 @@ fun cmd(vararg args: String): String { return baos.toByteArray().decodeToString().trim() } - -val baseGroup: String by project -val mcVersion: String by project val gitVersion = cmd("git", "rev-parse", "--short", "HEAD") val fullVersion = project.property("mod_version").toString() val versionName: String = "$fullVersion-$gitVersion" -val mixinGroup = "$baseGroup.mixin" allprojects { version = versionName -} -val modid: String by project - -// Toolchains: -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(8)) -} - -// Minecraft configuration: -loom { - forge { - pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) - mixinConfig("mixins.$modid.json") - } - log4jConfigs.from(file("log4j2.xml")) - runConfigs { - "client" { - property("ledger.bonusresourcemod", sourceSets.main.get().output.resourcesDir!!.absolutePath) - property("mixin.debug", "true") - programArgs("--tweakClass", "org.spongepowered.asm.launch.MixinTweaker") - programArgs("--tweakClass", "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker") + afterEvaluate { + configureIf { + buildConfigField("VERSION", versionName) + buildConfigField("FULL_VERSION", fullVersion) + buildConfigField("GIT_COMMIT", gitVersion) } - remove(getByName("server")) - } - mixin.useLegacyMixinAp.set(false) -} - -// TODO: Add an extra shadow configuration for optimizable jars -//val optShadowImpl: Configuration by configurations.creating { -// -//} - -val shadowImpl: Configuration by configurations.creating { - configurations.implementation.get().extendsFrom(this) -} - -dependencies { - minecraft("com.mojang:minecraft:1.8.9") - mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") - forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") - - shadowImpl(kotlin("stdlib-jdk8")) - implementation("org.jspecify:jspecify:1.0.0") - - shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") { - isTransitive = false - } - - shadowImpl("org.xerial:sqlite-jdbc:3.45.3.0") - shadowImpl("org.notenoughupdates.moulconfig:legacy:3.0.0-beta.9") - shadowImpl("io.azam.ulidj:ulidj:1.0.4") - shadowImpl(project(":dependency-injection")) - shadowImpl(project(":database:core")) - shadowImpl("moe.nea:libautoupdate:1.3.1") { - exclude(module = "gson") } - runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.1") - testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") -} - -// Tasks: - -// Delete default shadow configuration -tasks.shadowJar { - doFirst { error("Incorrect shadow JAR built!") } } - -tasks.downloadRepo { - hash.set("dcf1dbc") -} - -val generateItemIds by tasks.register("generateItemIds", GenerateItemIds::class) { - repoHash.set(tasks.downloadRepo.get().hash) - packageName.set("moe.nea.ledger.gen") - outputDirectory.set(layout.buildDirectory.dir("generated/sources/itemIds")) - repoFiles.set(tasks.downloadRepo.get().outputDirectory) -} -sourceSets.main { - java.srcDir(generateItemIds) -} - -tasks.withType { - archiveBaseName.set(modid) - manifest.attributes.run { - this["FMLCorePluginContainsFMLMod"] = "true" - this["ForceLoadAsMod"] = "true" - - // If you don't want mixins, remove these lines - this["TweakClass"] = "org.spongepowered.asm.launch.MixinTweaker" - this["MixinConfigs"] = "mixins.$modid.json" - } -} - -tasks.processResources { - inputs.property("version", project.version) - inputs.property("mcversion", mcVersion) - inputs.property("modid", modid) - inputs.property("basePackage", baseGroup) - - filesMatching(listOf("mcmod.info", "mixins.$modid.json")) { - expand(inputs.properties) - } -} - - -val proguardOutJar = project.layout.buildDirectory.file("badjars/stripped.jar") -val proguard = tasks.register("proguard", ProGuardTask::class) { - dependsOn(tasks.jar) - injars(tasks.jar.map { it.archiveFile }) - outjars(proguardOutJar) - configuration(file("ledger-rules.pro")) - val libJava = javaToolchains.launcherFor(java.toolchain) - .get() - .metadata.installationPath.file("jre/lib/rt.jar") - libraryjars(libJava) - libraryjars(configurations.compileClasspath) -} - -val shadowJar2 = tasks.register("shadowJar2", ShadowJar::class) { - destinationDirectory.set(layout.buildDirectory.dir("badjars")) - archiveClassifier.set("all-dev") - from(proguardOutJar) - dependsOn(proguard) - configurations = listOf(shadowImpl) - relocate("moe.nea.libautoupdate", "moe.nea.ledger.deps.libautoupdate") - relocate("io.github.notenoughupdates.moulconfig", "moe.nea.ledger.deps.moulconfig") - relocate("io.azam.ulidj", "moe.nea.ledger.deps.ulid") - mergeServiceFiles() - exclude( - // Signatures - "META-INF/INDEX.LIST", - "META-INF/*.SF", - "META-INF/*.DSA", - "META-INF/*.RSA", - "module-info.class", - - "META-INF/*.kotlin_module", - "META-INF/versions/**" - ) -} -tasks.remapJar { - archiveClassifier.set("") - inputFile.set(shadowJar2.flatMap { it.archiveFile }) -} - -tasks.jar { - archiveClassifier.set("without-deps") - destinationDirectory.set(layout.buildDirectory.dir("badjars")) -} - -tasks.assemble.get().dependsOn(tasks.remapJar) - -inline fun ExtensionAware.configureIf(crossinline block: T.() -> Unit) { - if (extensions.findByType() != null) { - extensions.configure { block() } - } -} - -allprojects { - configureIf { - packageName("moe.nea.ledger.gen") - buildConfigField("VERSION", versionName) - buildConfigField("FULL_VERSION", fullVersion) - buildConfigField("GIT_COMMIT", gitVersion) - } -} - diff --git a/gradle.properties b/gradle.properties index 38adc2e..6e2027c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,3 @@ -loom.platform=forge -org.gradle.jvmargs=-Xmx2g +org.gradle.jvmargs = -Xmx2g baseGroup = moe.nea.ledger -mcVersion = 1.8.9 -modid = moneyledger mod_version = 2.0.0 diff --git a/ledger-rules.pro b/ledger-rules.pro deleted file mode 100644 index 32cd337..0000000 --- a/ledger-rules.pro +++ /dev/null @@ -1,4 +0,0 @@ --keep class !moe.nea.ledger.gen.** {*;} --dontobfuscate --assumenosideeffects class ** { @moe.nea.ledger.utils.NoSideEffects ; } -#-dontoptimize diff --git a/log4j2.xml b/log4j2.xml deleted file mode 100644 index af9b1b7..0000000 --- a/log4j2.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mod/build.gradle.kts b/mod/build.gradle.kts new file mode 100644 index 0000000..10f01e1 --- /dev/null +++ b/mod/build.gradle.kts @@ -0,0 +1,170 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import proguard.gradle.ProGuardTask + +plugins { + idea + java + id("gg.essential.loom") version "1.6.+" + id("dev.architectury.architectury-pack200") version "0.1.3" + id("com.github.johnrengelman.shadow") version "8.1.1" + id("com.github.gmazzo.buildconfig") + kotlin("jvm") + id("ledger-repo") +} +val baseGroup: String by project +val mcVersion: String by project +val mixinGroup = "$baseGroup.mixin" +val modid: String by project + +// Toolchains: +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(8)) +} + +// Minecraft configuration: +loom { + forge { + pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) + mixinConfig("mixins.$modid.json") + } + log4jConfigs.from(file("log4j2.xml")) + runConfigs { + "client" { + property("ledger.bonusresourcemod", sourceSets.main.get().output.resourcesDir!!.absolutePath) + property("mixin.debug", "true") + programArgs("--tweakClass", "org.spongepowered.asm.launch.MixinTweaker") + programArgs("--tweakClass", "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker") + } + remove(getByName("server")) + } + mixin.useLegacyMixinAp.set(false) +} + +// TODO: Add an extra shadow configuration for optimizable jars +//val optShadowImpl: Configuration by configurations.creating { +// +//} + +val shadowImpl: Configuration by configurations.creating { + configurations.implementation.get().extendsFrom(this) +} + +dependencies { + minecraft("com.mojang:minecraft:1.8.9") + mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") + forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") + + shadowImpl(kotlin("stdlib-jdk8")) + implementation("org.jspecify:jspecify:1.0.0") + + shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") { + isTransitive = false + } + + shadowImpl("org.xerial:sqlite-jdbc:3.45.3.0") + shadowImpl("org.notenoughupdates.moulconfig:legacy:3.0.0-beta.9") + shadowImpl("io.azam.ulidj:ulidj:1.0.4") + shadowImpl(project(":dependency-injection")) + shadowImpl(project(":database:core")) + shadowImpl("moe.nea:libautoupdate:1.3.1") { + exclude(module = "gson") + } + runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.1") + testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") +} + +// Tasks: + +// Delete default shadow configuration +tasks.shadowJar { + doFirst { error("Incorrect shadow JAR built!") } +} + +tasks.downloadRepo { + hash.set("dcf1dbc") +} + +val generateItemIds by tasks.register("generateItemIds", GenerateItemIds::class) { + repoHash.set(tasks.downloadRepo.get().hash) + packageName.set("moe.nea.ledger.gen") + outputDirectory.set(layout.buildDirectory.dir("generated/sources/itemIds")) + repoFiles.set(tasks.downloadRepo.get().outputDirectory) +} +sourceSets.main { + java.srcDir(generateItemIds) +} +tasks.withType { + archiveBaseName.set(modid) +} +tasks.withType { + manifest.attributes.run { + this["FMLCorePluginContainsFMLMod"] = "true" + this["ForceLoadAsMod"] = "true" + this["TweakClass"] = "org.spongepowered.asm.launch.MixinTweaker" + this["MixinConfigs"] = "mixins.$modid.json" + } +} + +tasks.processResources { + inputs.property("version", project.version) + inputs.property("mcversion", mcVersion) + inputs.property("modid", modid) + inputs.property("basePackage", baseGroup) + + filesMatching(listOf("mcmod.info", "mixins.$modid.json")) { + expand(inputs.properties) + } +} + + +val proguardOutJar = project.layout.buildDirectory.file("badjars/stripped.jar") +val proguard = tasks.register("proguard", ProGuardTask::class) { + dependsOn(tasks.jar) + injars(tasks.jar.map { it.archiveFile }) + outjars(proguardOutJar) + configuration(file("ledger-rules.pro")) + val libJava = javaToolchains.launcherFor(java.toolchain) + .get() + .metadata.installationPath.file("jre/lib/rt.jar") + libraryjars(libJava) + libraryjars(configurations.compileClasspath) +} + +val shadowJar2 = tasks.register("shadowJar2", ShadowJar::class) { + destinationDirectory.set(layout.buildDirectory.dir("badjars")) + archiveClassifier.set("all-dev") + from(proguardOutJar) + dependsOn(proguard) + configurations = listOf(shadowImpl) + relocate("moe.nea.libautoupdate", "moe.nea.ledger.deps.libautoupdate") + relocate("io.github.notenoughupdates.moulconfig", "moe.nea.ledger.deps.moulconfig") + relocate("io.azam.ulidj", "moe.nea.ledger.deps.ulid") + mergeServiceFiles() + exclude( + // Signatures + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + "module-info.class", + + "META-INF/*.kotlin_module", + "META-INF/versions/**" + ) +} +tasks.remapJar { + archiveClassifier.set("") + inputFile.set(shadowJar2.flatMap { it.archiveFile }) +} + +tasks.jar { + archiveClassifier.set("without-deps") + destinationDirectory.set(layout.buildDirectory.dir("badjars")) +} + +tasks.assemble.get().dependsOn(tasks.remapJar) + +buildConfig { + packageName("moe.nea.ledger.gen") +} + diff --git a/mod/gradle.properties b/mod/gradle.properties new file mode 100644 index 0000000..28f0604 --- /dev/null +++ b/mod/gradle.properties @@ -0,0 +1,3 @@ +loom.platform = forge +mcVersion = 1.8.9 +modid = moneyledger diff --git a/mod/ledger-rules.pro b/mod/ledger-rules.pro new file mode 100644 index 0000000..32cd337 --- /dev/null +++ b/mod/ledger-rules.pro @@ -0,0 +1,4 @@ +-keep class !moe.nea.ledger.gen.** {*;} +-dontobfuscate +-assumenosideeffects class ** { @moe.nea.ledger.utils.NoSideEffects ; } +#-dontoptimize diff --git a/mod/log4j2.xml b/mod/log4j2.xml new file mode 100644 index 0000000..af9b1b7 --- /dev/null +++ b/mod/log4j2.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java b/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java new file mode 100644 index 0000000..56841b5 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java @@ -0,0 +1,193 @@ +package moe.nea.ledger.init; + +import net.minecraft.launchwrapper.Launch; +import org.spongepowered.asm.lib.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * A mixin plugin to automatically discover all mixins in the current JAR. + *

+ * This mixin plugin automatically scans your entire JAR (or class directory, in case of an in-IDE launch) for classes inside of your + * mixin package and registers those. It does this recursively for sub packages of the mixin package as well. This means you will need + * to only have mixin classes inside of your mixin package, which is good style anyway. + * + * @author Linnea Gräf + */ +public class AutoDiscoveryMixinPlugin implements IMixinConfigPlugin { + private static final List mixinPlugins = new ArrayList<>(); + + public static List getMixinPlugins() { + return mixinPlugins; + } + + private String mixinPackage; + + @Override + public void onLoad(String mixinPackage) { + this.mixinPackage = mixinPackage; + mixinPlugins.add(this); + } + + /** + * Resolves the base class root for a given class URL. This resolves either the JAR root, or the class file root. + * In either case the return value of this + the class name will resolve back to the original class url, or to other + * class urls for other classes. + */ + public URL getBaseUrlForClassUrl(URL classUrl) { + String string = classUrl.toString(); + if (classUrl.getProtocol().equals("jar")) { + try { + return new URL(string.substring(4).split("!")[0]); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + if (string.endsWith(".class")) { + try { + return new URL(string.replace("\\", "/") + .replace(getClass().getCanonicalName() + .replace(".", "/") + ".class", "")); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + return classUrl; + } + + /** + * Get the package that contains all the mixins. This value is set by mixin itself using {@link #onLoad}. + */ + public String getMixinPackage() { + return mixinPackage; + } + + /** + * Get the path inside the class root to the mixin package + */ + public String getMixinBaseDir() { + return mixinPackage.replace(".", "/"); + } + + /** + * A list of all discovered mixins. + */ + private List mixins = null; + + /** + * Try to add mixin class ot the mixins based on the filepath inside of the class root. + * Removes the {@code .class} file suffix, as well as the base mixin package. + *

This method cannot be called after mixin initialization.

+ * + * @param className the name or path of a class to be registered as a mixin. + */ + public void tryAddMixinClass(String className) { + String norm = (className.endsWith(".class") ? className.substring(0, className.length() - ".class".length()) : className) + .replace("\\", "/") + .replace("/", "."); + if (norm.startsWith(getMixinPackage() + ".") && !norm.endsWith(".")) { + mixins.add(norm.substring(getMixinPackage().length() + 1)); + } + } + + /** + * Search through the JAR or class directory to find mixins contained in {@link #getMixinPackage()} + */ + @Override + public List getMixins() { + if (mixins != null) return mixins; + System.out.println("Trying to discover mixins"); + mixins = new ArrayList<>(); + URL classUrl = getClass().getProtectionDomain().getCodeSource().getLocation(); + System.out.println("Found classes at " + classUrl); + Path file; + try { + file = Paths.get(getBaseUrlForClassUrl(classUrl).toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + System.out.println("Base directory found at " + file); + if (Files.isDirectory(file)) { + walkDir(file); + } else { + walkJar(file); + } + System.out.println("Found mixins: " + mixins); + + if (!(Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) { + mixins.removeIf(it -> it.contains("devenv")); + } + + return mixins; + } + + /** + * Search through directory for mixin classes based on {@link #getMixinBaseDir}. + * + * @param classRoot The root directory in which classes are stored for the default package. + */ + private void walkDir(Path classRoot) { + System.out.println("Trying to find mixins from directory"); + try (Stream classes = Files.walk(classRoot.resolve(getMixinBaseDir()))) { + classes.map(it -> classRoot.relativize(it).toString()) + .forEach(this::tryAddMixinClass); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Read through a JAR file, trying to find all mixins inside. + */ + private void walkJar(Path file) { + System.out.println("Trying to find mixins from jar file"); + try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(file))) { + ZipEntry next; + while ((next = zis.getNextEntry()) != null) { + tryAddMixinClass(next.getName()); + zis.closeEntry(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } +} diff --git a/mod/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java b/mod/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java new file mode 100644 index 0000000..52b8911 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java @@ -0,0 +1,12 @@ +package moe.nea.ledger.mixin; + +import net.minecraft.client.gui.inventory.GuiEditSign; +import net.minecraft.tileentity.TileEntitySign; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(GuiEditSign.class) +public interface AccessorGuiEditSign { + @Accessor("tileSign") + TileEntitySign getTileEntity_ledger(); +} diff --git a/mod/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java b/mod/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java new file mode 100644 index 0000000..4e6e360 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java @@ -0,0 +1,18 @@ +package moe.nea.ledger.mixin; + +import moe.nea.ledger.events.GuiClickEvent; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import net.minecraftforge.common.MinecraftForge; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiContainer.class) +public class MouseClickEventPatch { + @Inject(method = "handleMouseClick", at = @At("HEAD")) + private void onHandleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType, CallbackInfo ci) { + MinecraftForge.EVENT_BUS.post(new GuiClickEvent(slotIn, slotId, clickedButton, clickType)); + } +} diff --git a/mod/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java b/mod/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java new file mode 100644 index 0000000..fc9afb7 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java @@ -0,0 +1,18 @@ +package moe.nea.ledger.mixin; + +import moe.nea.ledger.events.InitializationComplete; +import net.minecraft.client.Minecraft; +import net.minecraftforge.common.MinecraftForge; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class OnInitializationCompletePatch { + + @Inject(method = "startGame", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/FMLClientHandler;onInitializationComplete()V")) + private void onInitComplete(CallbackInfo ci) { + MinecraftForge.EVENT_BUS.post(new InitializationComplete()); + } +} diff --git a/mod/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java b/mod/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java new file mode 100644 index 0000000..88e8364 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java @@ -0,0 +1,66 @@ +package moe.nea.ledger.mixin.devenv; + +import com.google.common.eventbus.EventBus; +import net.minecraftforge.fml.client.FMLFileResourcePack; +import net.minecraftforge.fml.common.DummyModContainer; +import net.minecraftforge.fml.common.LoadController; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.ModMetadata; +import net.minecraftforge.fml.common.discovery.ASMDataTable; +import net.minecraftforge.fml.common.discovery.ContainerType; +import net.minecraftforge.fml.common.discovery.ModCandidate; +import net.minecraftforge.fml.common.discovery.ModDiscoverer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +@Mixin(value = ModDiscoverer.class, remap = false) +public class RegisterModResourcesPatch { + @Shadow + private List candidates; + + @Inject(method = "identifyMods", at = @At("HEAD"), remap = false) + private void addCandidate(CallbackInfoReturnable> cir) { + String bonusResourceMod = System.getProperty("ledger.bonusresourcemod"); + if (bonusResourceMod == null) return; + File file = new File(bonusResourceMod); + if (!file.isDirectory()) return; + ModMetadata modMetadata = new ModMetadata(); + modMetadata.modId = "ledger-bonus"; + modMetadata.name = "Ledger Bonus Resources"; + modMetadata.autogenerated = true; + ModContainer container = new DummyModContainer(modMetadata) { + @Override + public Object getMod() { + return new Object(); + } + + @Override + public boolean registerBus(EventBus bus, LoadController controller) { + return true; + } + + @Override + public File getSource() { + return file; + } + + @Override + public Class getCustomResourcePackClass() { + return FMLFileResourcePack.class; + } + }; + candidates.add(new ModCandidate(file, file, ContainerType.DIR) { + @Override + public List explore(ASMDataTable table) { + return Collections.singletonList(container); + } + }); + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt new file mode 100644 index 0000000..5b964c8 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt @@ -0,0 +1,31 @@ +package moe.nea.ledger + +import io.github.notenoughupdates.moulconfig.common.IMinecraft +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender + +class ConfigCommand : CommandBase() { + override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { + return true + } + + override fun getCommandName(): String { + return "ledgerconfig" + } + + override fun getCommandUsage(sender: ICommandSender?): String { + return "" + } + + override fun processCommand(sender: ICommandSender?, args: Array) { + val editor = Ledger.managedConfig.getEditor() + editor.search(args.joinToString(" ")) + Ledger.runLater { + IMinecraft.instance.openWrappedScreen(editor) + } + } + + override fun getCommandAliases(): List { + return listOf("moneyledger") + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt b/mod/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt new file mode 100644 index 0000000..66fba8d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt @@ -0,0 +1,38 @@ +package moe.nea.ledger + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +class DebouncedValue(private val value: T) { + companion object { + fun farFuture(): DebouncedValue { + val value = DebouncedValue(Unit) + value.take() + @Suppress("UNCHECKED_CAST") + return value as DebouncedValue + } + } + + val lastSeenAt = System.nanoTime() + val age get() = (System.nanoTime() - lastSeenAt).nanoseconds + var taken = false + private set + + fun get(debounce: Duration): T? { + return if (!taken && age >= debounce) value + else null + } + + fun replace(): T? { + return consume(0.seconds) + } + + fun consume(debounce: Duration): T? { + return get(debounce)?.also { take() } + } + + fun take() { + taken = true + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt new file mode 100644 index 0000000..bab0a78 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt @@ -0,0 +1,34 @@ +package moe.nea.ledger + +import moe.nea.ledger.events.SupplyDebugInfo +import moe.nea.ledger.utils.di.Inject +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender +import net.minecraftforge.common.MinecraftForge + +class DebugDataCommand : CommandBase() { + + override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { + return true + } + + override fun getCommandName(): String { + return "ledgerdebug" + } + + override fun getCommandUsage(sender: ICommandSender?): String { + return "" + } + + @Inject + lateinit var logger: LedgerLogger + + override fun processCommand(sender: ICommandSender?, args: Array?) { + val debugInfo = SupplyDebugInfo() + MinecraftForge.EVENT_BUS.post(debugInfo) + logger.printOut("Collected debug info:") + debugInfo.data.forEach { + logger.printOut("${it.first}: ${it.second}") + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/DevUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/DevUtil.kt new file mode 100644 index 0000000..d0dd653 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/DevUtil.kt @@ -0,0 +1,7 @@ +package moe.nea.ledger + +import net.minecraft.launchwrapper.Launch + +object DevUtil { + val isDevEnv = Launch.blackboard["fml.deobfuscatedEnvironment"] as Boolean +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt b/mod/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt new file mode 100644 index 0000000..b50b14e --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt @@ -0,0 +1,30 @@ +package moe.nea.ledger + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds + +class ExpiringValue(private val value: T) { + val lastSeenAt: Long = System.nanoTime() + val age get() = (System.nanoTime() - lastSeenAt).nanoseconds + var taken = false + private set + + fun get(expiry: Duration): T? { + return if (!taken && age < expiry) value + else null + } + + companion object { + fun empty(): ExpiringValue { + val value = ExpiringValue(Unit) + value.take() + @Suppress("UNCHECKED_CAST") + return value as ExpiringValue + } + } + + fun consume(expiry: Duration): T? = get(expiry)?.also { take() } + fun take() { + taken = true + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ItemChange.kt b/mod/src/main/kotlin/moe/nea/ledger/ItemChange.kt new file mode 100644 index 0000000..fda709c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ItemChange.kt @@ -0,0 +1,84 @@ +package moe.nea.ledger + +import moe.nea.ledger.database.DBItemEntry +import moe.nea.ledger.database.ResultRow +import net.minecraft.event.HoverEvent +import net.minecraft.util.ChatComponentText +import net.minecraft.util.ChatStyle +import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.IChatComponent + +data class ItemChange( + val itemId: ItemId, + val count: Double, + val direction: ChangeDirection, +) { + fun formatChat(): IChatComponent { + return ChatComponentText(" ") + .appendSibling(direction.chatFormat) + .appendText(" ") + .appendSibling(ChatComponentText("$count").setChatStyle(ChatStyle().setColor(EnumChatFormatting.WHITE))) + .appendSibling(ChatComponentText("x").setChatStyle(ChatStyle().setColor(EnumChatFormatting.DARK_GRAY))) + .appendText(" ") + .appendSibling(ChatComponentText(itemId.string).setChatStyle(ChatStyle().setParentStyle(ChatStyle().setColor( + EnumChatFormatting.WHITE)))) + } + + enum class ChangeDirection { + GAINED, + TRANSFORM, + SYNC, + CATALYST, + LOST; + + val chatFormat by lazy { formatChat0() } + private fun formatChat0(): IChatComponent { + val (text, color) = when (this) { + GAINED -> "+" to EnumChatFormatting.GREEN + TRANSFORM -> "~" to EnumChatFormatting.YELLOW + SYNC -> "=" to EnumChatFormatting.BLUE + CATALYST -> "*" to EnumChatFormatting.DARK_PURPLE + LOST -> "-" to EnumChatFormatting.RED + } + return ChatComponentText(text) + .setChatStyle( + ChatStyle() + .setColor(color) + .setChatHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, + ChatComponentText(name).setChatStyle(ChatStyle().setColor(color))))) + } + } + + companion object { + fun gainCoins(number: Double): ItemChange { + return gain(ItemId.COINS, number) + } + + fun unpair(direction: ChangeDirection, pair: Pair): ItemChange { + return ItemChange(pair.first, pair.second, direction) + } + + fun unpairGain(pair: Pair) = unpair(ChangeDirection.GAINED, pair) + fun unpairLose(pair: Pair) = unpair(ChangeDirection.LOST, pair) + + fun gain(itemId: ItemId, amount: Number): ItemChange { + return ItemChange(itemId, amount.toDouble(), ChangeDirection.GAINED) + } + + fun lose(itemId: ItemId, amount: Number): ItemChange { + return ItemChange(itemId, amount.toDouble(), ChangeDirection.LOST) + } + + fun loseCoins(number: Double): ItemChange { + return lose(ItemId.COINS, number) + } + + fun from(result: ResultRow): ItemChange { + return ItemChange( + result[DBItemEntry.itemId], + result[DBItemEntry.size], + result[DBItemEntry.mode], + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ItemId.kt b/mod/src/main/kotlin/moe/nea/ledger/ItemId.kt new file mode 100644 index 0000000..8211cd3 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ItemId.kt @@ -0,0 +1,35 @@ +package moe.nea.ledger + +import moe.nea.ledger.utils.NoSideEffects + +data class ItemId( + val string: String +) { + @NoSideEffects + fun singleItem(): Pair { + return withStackSize(1) + } + + @NoSideEffects + fun withStackSize(size: Number): Pair { + return Pair(this, size.toDouble()) + } + + + companion object { + + @JvmStatic + @NoSideEffects + fun forName(string: String) = ItemId(string) + fun skill(skill: String) = ItemId("SKYBLOCK_SKILL_$skill") + + val GARDEN = skill("GARDEN") + val FARMING = skill("FARMING") + + + val COINS = ItemId("SKYBLOCK_COIN") + val GEMSTONE_POWDER = ItemId("SKYBLOCK_POWDER_GEMSTONE") + val MITHRIL_POWDER = ItemId("SKYBLOCK_POWDER_MITHRIL") + val NIL = ItemId("SKYBLOCK_NIL") + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt new file mode 100644 index 0000000..0bacf32 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt @@ -0,0 +1,188 @@ +package moe.nea.ledger + +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ExtraSupplyIdEvent +import moe.nea.ledger.events.RegistrationFinishedEvent +import moe.nea.ledger.events.SupplyDebugInfo +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.modules.ExternalDataProvider +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.client.event.GuiScreenEvent +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.input.Mouse + +class ItemIdProvider { + + @SubscribeEvent + fun onMouseInput(event: GuiScreenEvent.MouseInputEvent.Pre) { + if (Mouse.getEventButton() == -1) return + MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) + } + + @SubscribeEvent + fun onKeyInput(event: GuiScreenEvent.KeyboardInputEvent.Pre) { + MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) + } + + private val knownNames = mutableMapOf() + + fun createLookupTagFromDisplayName(itemName: String): String { + return itemName.unformattedString().trim().lowercase() + } + + fun saveKnownItem(itemName: String, itemId: ItemId) { + knownNames[createLookupTagFromDisplayName(itemName)] = itemId + } + + @SubscribeEvent + fun onDataLoaded(event: ExternalDataProvider.DataLoaded) { + event.provider.itemNames.forEach { (itemId, itemName) -> + saveKnownItem(itemName, ItemId(itemId)) + } + } + + @SubscribeEvent + fun onRegistrationFinished(event: RegistrationFinishedEvent) { + MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(::saveKnownItem)) + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun savePlayerInventoryIds(event: BeforeGuiAction) { + val player = Minecraft.getMinecraft().thePlayer ?: return + val inventory = player.inventory ?: return + inventory.mainInventory?.forEach { saveFromSlot(it) } + inventory.armorInventory?.forEach { saveFromSlot(it) } + } + + @SubscribeEvent + fun onDebugData(event: SupplyDebugInfo) { + event.record("knownItemNames", knownNames.size) + } + + fun saveFromSlot(stack: ItemStack?, preprocessName: (String) -> String = { it }) { + if (stack == null) return + val nbt = stack.tagCompound ?: NBTTagCompound() + val display = nbt.getCompoundTag("display") + var name = display.getString("Name").unformattedString() + name = preprocessName(name) + name = name.trim() + val id = stack.getInternalId() + if (id != null && name.isNotBlank()) { + saveKnownItem(name, id) + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun saveChestInventoryIds(event: BeforeGuiAction) { + val slots = event.chestSlots ?: return + val chestName = slots.lowerChestInventory.name.unformattedString() + val isOrderMenu = chestName == "Your Bazaar Orders" || chestName == "Co-op Bazaar Orders" + val preprocessor: (String) -> String = if (isOrderMenu) { + { it.removePrefix("BUY ").removePrefix("SELL ") } + } else { + { it } + } + slots.inventorySlots.forEach { + saveFromSlot(it?.stack, preprocessor) + } + } + + // TODO: make use of colour + fun findForName(name: String, fallbackToGenerated: Boolean = true): ItemId? { + var id = knownNames[createLookupTagFromDisplayName(name)] + if (id == null && fallbackToGenerated) { + id = generateName(name) + } + return id + } + + fun generateName(name: String): ItemId { + return ItemId(name.uppercase().replace(" ", "_")) + } + + private val coinRegex = "(?$SHORT_NUMBER_PATTERN) Coins?".toPattern() + private val stackedItemRegex = "(?.*) x(?$SHORT_NUMBER_PATTERN)".toPattern() + private val reverseStackedItemRegex = "(?$SHORT_NUMBER_PATTERN)x (?.*)".toPattern() + private val essenceRegex = "(?.*) Essence x(?$SHORT_NUMBER_PATTERN)".toPattern() + private val numberedItemRegex = "(?$SHORT_NUMBER_PATTERN) (?.*)".toPattern() + + fun findCostItemsFromSpan(lore: List): List> { + return lore.iterator().asSequence() + .dropWhile { it.unformattedString() != "Cost" }.drop(1) + .takeWhile { it != "" } + .map { findStackableItemByName(it) ?: Pair(ItemId.NIL, 1.0) } + .toList() + } + + private val etherialRewardPattern = "\\+(?${SHORT_NUMBER_PATTERN})x? (?.*)".toPattern() + + fun findStackableItemByName(name: String, fallbackToGenerated: Boolean = false): Pair? { + val properName = name.unformattedString().trim() + if (properName == "FREE" || properName == "This Chest is Free!") { + return Pair(ItemId.COINS, 0.0) + } + coinRegex.useMatcher(properName) { + return Pair(ItemId.COINS, parseShortNumber(group("amount"))) + } + etherialRewardPattern.useMatcher(properName) { + val id = when (val id = group("what")) { + "Copper" -> ItemIds.SKYBLOCK_COPPER + "Bits" -> ItemIds.SKYBLOCK_BIT + "Garden Experience" -> ItemId.GARDEN + "Farming XP" -> ItemId.FARMING + "Gold Essence" -> ItemIds.ESSENCE_GOLD + "Gemstone Powder" -> ItemId.GEMSTONE_POWDER + "Mithril Powder" -> ItemId.MITHRIL_POWDER + "Pelts" -> ItemIds.SKYBLOCK_PELT + "Fine Flour" -> ItemIds.FINE_FLOUR + else -> { + id.ifDropLast(" Experience") { + ItemId.skill(generateName(it).string) + } ?: id.ifDropLast(" XP") { + ItemId.skill(generateName(it).string) + } ?: id.ifDropLast(" Powder") { + ItemId("SKYBLOCK_POWDER_${generateName(it).string}") + } ?: id.ifDropLast(" Essence") { + ItemId("ESSENCE_${generateName(it).string}") + } ?: generateName(id) + } + } + return Pair(id, parseShortNumber(group("amount"))) + } + essenceRegex.useMatcher(properName) { + return Pair(ItemId("ESSENCE_${group("essence").uppercase()}"), + parseShortNumber(group("count"))) + } + stackedItemRegex.useMatcher(properName) { + val item = findForName(group("name"), fallbackToGenerated) + if (item != null) { + val count = parseShortNumber(group("count")) + return Pair(item, count) + } + } + reverseStackedItemRegex.useMatcher(properName) { + val item = findForName(group("name"), fallbackToGenerated) + if (item != null) { + val count = parseShortNumber(group("count")) + return Pair(item, count) + } + } + numberedItemRegex.useMatcher(properName) { + val item = findForName(group("what"), fallbackToGenerated) + if (item != null) { + val count = parseShortNumber(group("count")) + return Pair(item, count) + } + } + + return findForName(properName, fallbackToGenerated)?.let { Pair(it, 1.0) } + } + + fun getKnownItemIds(): Collection { + return knownNames.values + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ItemUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/ItemUtil.kt new file mode 100644 index 0000000..a3d8162 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ItemUtil.kt @@ -0,0 +1,90 @@ +package moe.nea.ledger + +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound + + +fun ItemStack.getExtraAttributes(): NBTTagCompound { + val nbt = this.tagCompound ?: return NBTTagCompound() + return nbt.getCompoundTag("ExtraAttributes") +} + +fun ItemStack?.getInternalId(): ItemId? { + if (this == null) return null + val extraAttributes = getExtraAttributes() + var id = extraAttributes.getString("id") + id = id.takeIf { it.isNotBlank() } + if (id == "PET") { + id = getPetId() ?: id + } + if (id == "ENCHANTED_BOOK") { + id = getEnchanments().entries.singleOrNull()?.let { + "${it.key};${it.value}".uppercase() + } + } + return id?.let(::ItemId) +} + +fun ItemStack.getEnchanments(): Map { + val enchantments = getExtraAttributes().getCompoundTag("enchantments") + return enchantments.keySet.associateWith { enchantments.getInteger(it) } +} + +class PetInfo { + var type: String? = null + var tier: String? = null +} + +fun ItemStack.getPetId(): String? { + val petInfoStr = getExtraAttributes().getString("petInfo") + val petInfo = runCatching { + Ledger.gson.fromJson(petInfoStr, + PetInfo::class.java) + }.getOrNull() // TODO: error reporting to sentry + if (petInfo?.type == null || petInfo.tier == null) return null + return petInfo.type + ";" + rarityToIndex(petInfo.tier ?: "") +} + +fun rarityToIndex(rarity: String): Int { + return when (rarity) { + "COMMON" -> 0 + "UNCOMMON" -> 1 + "RARE" -> 2 + "EPIC" -> 3 + "LEGENDARY" -> 4 + "MYTHIC" -> 5 + else -> -1 + } +} + +fun ItemStack.getLore(): List { + val nbt = this.tagCompound ?: NBTTagCompound() + val extraAttributes = nbt.getCompoundTag("display") + val lore = extraAttributes.getTagList("Lore", 8) + return (0 until lore.tagCount()).map { lore.getStringTagAt(it) } +} + + +fun IInventory.asIterable(): Iterable = object : Iterable { + override fun iterator(): Iterator { + return object : Iterator { + var i = 0 + override fun hasNext(): Boolean { + return i < this@asIterable.sizeInventory + } + + override fun next(): ItemStack? { + if (!hasNext()) throw NoSuchElementException("$i is out of range for inventory ${this@asIterable}") + return this@asIterable.getStackInSlot(i++) + } + } + } +} + +fun ItemStack.getDisplayNameU(): String { + val nbt = this.tagCompound ?: NBTTagCompound() + val extraAttributes = nbt.getCompoundTag("display") + return extraAttributes.getString("Name") +} + diff --git a/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt b/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt new file mode 100644 index 0000000..bc667e4 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt @@ -0,0 +1,205 @@ +package moe.nea.ledger + +import com.google.gson.Gson +import io.github.notenoughupdates.moulconfig.Config +import io.github.notenoughupdates.moulconfig.managed.ManagedConfig +import moe.nea.ledger.config.LedgerConfig +import moe.nea.ledger.config.UpdateUi +import moe.nea.ledger.config.UpdateUiMarker +import moe.nea.ledger.database.Database +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.LateWorldLoadEvent +import moe.nea.ledger.events.RegistrationFinishedEvent +import moe.nea.ledger.events.WorldSwitchEvent +import moe.nea.ledger.gen.BuildConfig +import moe.nea.ledger.modules.AccessorySwapperDetection +import moe.nea.ledger.modules.AllowanceDetection +import moe.nea.ledger.modules.AuctionHouseDetection +import moe.nea.ledger.modules.BankDetection +import moe.nea.ledger.modules.BazaarDetection +import moe.nea.ledger.modules.BazaarOrderDetection +import moe.nea.ledger.modules.BitsDetection +import moe.nea.ledger.modules.BitsShopDetection +import moe.nea.ledger.modules.DragonEyePlacementDetection +import moe.nea.ledger.modules.`DragonSacrificeDetection` +import moe.nea.ledger.modules.DungeonChestDetection +import moe.nea.ledger.modules.ExternalDataProvider +import moe.nea.ledger.modules.EyedropsDetection +import moe.nea.ledger.modules.ForgeDetection +import moe.nea.ledger.modules.GambleDetection +import moe.nea.ledger.modules.GodPotionDetection +import moe.nea.ledger.modules.GodPotionMixinDetection +import moe.nea.ledger.modules.KatDetection +import moe.nea.ledger.modules.KuudraChestDetection +import moe.nea.ledger.modules.MineshaftCorpseDetection +import moe.nea.ledger.modules.MinionDetection +import moe.nea.ledger.modules.NpcDetection +import moe.nea.ledger.modules.UpdateChecker +import moe.nea.ledger.modules.VisitorDetection +import moe.nea.ledger.utils.ErrorUtil +import moe.nea.ledger.utils.MinecraftExecutor +import moe.nea.ledger.utils.di.DI +import moe.nea.ledger.utils.di.DIProvider +import moe.nea.ledger.utils.network.RequestUtil +import net.minecraft.client.Minecraft +import net.minecraft.command.ICommand +import net.minecraftforge.client.ClientCommandHandler +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.event.entity.EntityJoinWorldEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.event.FMLInitializationEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import org.apache.logging.log4j.LogManager +import java.io.File +import java.util.concurrent.ConcurrentLinkedQueue + +@Mod(modid = "ledger", useMetadata = true, version = BuildConfig.VERSION) +class Ledger { + /* + You have withdrawn 1M coins! You now have 518M coins in your account! + You have deposited 519M coins! You now have 519M coins in your account! + + // ORDERS: + + [Bazaar] Buy Order Setup! 160x Wheat for 720.0 coins. + [Bazaar] Claimed 160x Wheat worth 720.0 coins bought for 4.5 each! + + [Bazaar] Sell Offer Setup! 160x Wheat for 933.4 coins. + [Bazaar] Claimed 34,236,799 coins from selling 176x Hyper Catalyst at 196,741 each! + + // INSTABUY: + + [Bazaar] Bought 64x Wheat for 377.6 coins! + [Bazaar] Sold 64x Wheat for 268.8 coins! + + // AUCTION HOUSE: + + You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction! + You purchased 2x Walnut for 69 coins! + You purchased ◆ Ice Rune I for 4,000 coins! + + // NPC + + // You bought Cactus x32 for 465.6 Coins! + // You sold Cactus x1 for 3 Coins! + // You bought back Potato x3 for 9 Coins! + + TODO: TRADING, FORGE, VISITORS / COPPER, CORPSES ÖFFNEN, HIGH / LOW GAMBLES, MINION ITEMS (maybe inferno refuel) + TODO: PET LEVELING COSTS AT FANN, SLAYER / MOB DROPS, SLAYER START COST + */ + companion object { + val dataFolder = File("money-ledger").apply { mkdirs() } + val logger = LogManager.getLogger("MoneyLedger") + val managedConfig = ManagedConfig.create(File("config/money-ledger/config.json"), LedgerConfig::class.java) { + checkExpose = false + customProcessor { option, ann -> + UpdateUi(option) + } + } + val gson = Gson() + private val tickQueue = ConcurrentLinkedQueue() + fun runLater(runnable: Runnable) { + tickQueue.add(runnable) + } + + val di = DI() + } + + @Mod.EventHandler + fun init(event: FMLInitializationEvent) { + logger.info("Initializing ledger") + + TelemetryProvider.setupFor(di) + di.registerSingleton(this) + di.registerSingleton(Minecraft.getMinecraft()) + di.registerSingleton(gson) + di.register(LedgerConfig::class.java, DIProvider { managedConfig.instance }) + di.register(Config::class.java, DIProvider.fromInheritance(LedgerConfig::class.java)) + di.registerInjectableClasses( + AccessorySwapperDetection::class.java, + AllowanceDetection::class.java, + AuctionHouseDetection::class.java, + BankDetection::class.java, + BazaarDetection::class.java, + BazaarOrderDetection::class.java, + BitsDetection::class.java, + BitsShopDetection::class.java, + ConfigCommand::class.java, + Database::class.java, + DebugDataCommand::class.java, + DragonEyePlacementDetection::class.java, + DragonSacrificeDetection::class.java, + DungeonChestDetection::class.java, + ErrorUtil::class.java, + ExternalDataProvider::class.java, + EyedropsDetection::class.java, + ForgeDetection::class.java, + GambleDetection::class.java, + GodPotionDetection::class.java, + GodPotionMixinDetection::class.java, + ItemIdProvider::class.java, + KatDetection::class.java, + KuudraChestDetection::class.java, + LedgerLogger::class.java, + LogChatCommand::class.java, + MinecraftExecutor::class.java, + MineshaftCorpseDetection::class.java, + MinionDetection::class.java, + NpcDetection::class.java, + QueryCommand::class.java, + RequestUtil::class.java, + TriggerCommand::class.java, + UpdateChecker::class.java, + VisitorDetection::class.java, + ) + val errorUtil = di.provide() + errorUtil.catch { + di.instantiateAll() + di.getAllInstances().forEach(MinecraftForge.EVENT_BUS::register) + di.getAllInstances().filterIsInstance() + .forEach { ClientCommandHandler.instance.registerCommand(it) } + } + + errorUtil.catch { + di.provide().loadAndUpgrade() + } + + MinecraftForge.EVENT_BUS.post(RegistrationFinishedEvent()) + } + + var lastJoin = -1L + + @SubscribeEvent + fun worldSwitchEvent(event: EntityJoinWorldEvent) { + if (event.entity == Minecraft.getMinecraft().thePlayer) { + lastJoin = System.currentTimeMillis() + MinecraftForge.EVENT_BUS.post(WorldSwitchEvent()) + } + } + + @SubscribeEvent + fun tickEvent(event: ClientTickEvent) { + if (event.phase == TickEvent.Phase.END + && lastJoin > 0 + && System.currentTimeMillis() - lastJoin > 10_000 + && Minecraft.getMinecraft().thePlayer != null + ) { + lastJoin = -1 + MinecraftForge.EVENT_BUS.post(LateWorldLoadEvent()) + } + while (true) { + val queued = tickQueue.poll() ?: break + queued.run() + } + } + + @SubscribeEvent(receiveCanceled = true, priority = EventPriority.HIGHEST) + fun onChat(event: ClientChatReceivedEvent) { + if (event.type != 2.toByte()) + MinecraftForge.EVENT_BUS.post(ChatReceived(event)) + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt b/mod/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt new file mode 100644 index 0000000..d4a3932 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt @@ -0,0 +1,29 @@ +package moe.nea.ledger + +import com.google.gson.JsonObject +import moe.nea.ledger.gen.ItemIds +import java.time.Instant +import java.util.UUID + +data class LedgerEntry( + val transactionType: TransactionType, + val timestamp: Instant, + val items: List, +) { + fun intoJson(profileId: UUID?): JsonObject { + val coinAmount = items.find { it.itemId == ItemId.COINS || it.itemId == ItemIds.SKYBLOCK_BIT }?.count + val nonCoins = items.find { it.itemId != ItemId.COINS && it.itemId != ItemIds.SKYBLOCK_BIT } + return JsonObject().apply { + addProperty("transactionType", transactionType.name) + addProperty("timestamp", timestamp.toEpochMilli().toString()) + addProperty("totalTransactionValue", coinAmount) + addProperty("itemId", nonCoins?.itemId?.string ?: "") + addProperty("itemAmount", nonCoins?.count ?: 0.0) + addProperty("profileId", profileId.toString()) + addProperty( + "playerId", + MCUUIDUtil.getPlayerUUID().toString() + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/mod/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt new file mode 100644 index 0000000..6049aa2 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt @@ -0,0 +1,136 @@ +package moe.nea.ledger + +import com.google.gson.Gson +import com.google.gson.JsonArray +import moe.nea.ledger.database.DBItemEntry +import moe.nea.ledger.database.DBLogEntry +import moe.nea.ledger.database.Database +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.utils.ULIDWrapper +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.Minecraft +import net.minecraft.util.ChatComponentText +import net.minecraft.util.IChatComponent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.UUID + +class LedgerLogger { + fun printOut(text: String) = printOut(ChatComponentText(text)) + fun printOut(comp: IChatComponent) { + Minecraft.getMinecraft().ingameGUI?.chatGUI?.printChatMessage(comp) + } + + val profileIdPattern = + "Profile ID: (?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})".toPattern() + + var currentProfile: UUID? = null + + var shouldLog by Ledger.managedConfig.instance.debug::logEntries + + @Inject + lateinit var database: Database + + @SubscribeEvent + fun onProfileSwitch(event: ChatReceived) { + profileIdPattern.useMatcher(event.message) { + currentProfile = UUID.fromString(group("profile")) + } + } + + + fun printToChat(entry: LedgerEntry) { + val items = entry.items.joinToString("\n§e") { " - ${it.direction} ${it.count}x ${it.itemId}" } + printOut( + """ + §e================= TRANSACTION START + §eTYPE: §a${entry.transactionType} + §eTIMESTAMP: §a${entry.timestamp} + §e%s + §ePROFILE: §a${currentProfile} + §e================= TRANSACTION END + """.trimIndent().replace("%s", items) + ) + } + + val entries = JsonArray() + var hasRecentlyMerged = false + var lastMergeTime = System.currentTimeMillis() + + fun doMerge() { + val allFiles = folder.listFiles()?.toList() ?: emptyList() + val mergedJson = allFiles + .filter { it.name != "merged.json" && it.extension == "json" } + .sortedDescending() + .map { it.readText().trim().removePrefix("[").removeSuffix("]") } + .joinToString(",", "[", "]") + folder.resolve("merged.json").writeText(mergedJson) + hasRecentlyMerged = true + } + + init { + Runtime.getRuntime().addShutdownHook(Thread { doMerge() }) + } + + @SubscribeEvent + fun onTick(event: ClientTickEvent) { + if (!hasRecentlyMerged && (System.currentTimeMillis() - lastMergeTime) > 60_000L) { + lastMergeTime = System.currentTimeMillis() + doMerge() + } + } + + fun logEntry(entry: LedgerEntry) { + if (shouldLog) + printToChat(entry) + Ledger.logger.info("Logging entry of type ${entry.transactionType}") + val transactionId = ULIDWrapper.createULIDAt(entry.timestamp) + DBLogEntry.insert(database.connection) { + it[DBLogEntry.profileId] = currentProfile ?: MCUUIDUtil.NIL_UUID + it[DBLogEntry.playerId] = MCUUIDUtil.getPlayerUUID() + it[DBLogEntry.type] = entry.transactionType + it[DBLogEntry.transactionId] = transactionId + } + entry.items.forEach { change -> + DBItemEntry.insert(database.connection) { + it[DBItemEntry.transactionId] = transactionId + it[DBItemEntry.mode] = change.direction + it[DBItemEntry.size] = change.count + it[DBItemEntry.itemId] = change.itemId + } + } + entries.add(entry.intoJson(currentProfile)) + commit() + } + + fun commit() { + try { + hasRecentlyMerged = false + file.writeText(gson.toJson(entries)) + } catch (ex: Exception) { + Ledger.logger.error("Could not save file", ex) + } + } + + val gson = Gson() + + val folder = Ledger.dataFolder + val file: File = run { + val date = SimpleDateFormat("yyyy.MM.dd").format(Date()) + + generateSequence(0) { it + 1 } + .map { + if (it == 0) + folder.resolve("$date.json") + else + folder.resolve("$date-$it.json") + } + .filter { !it.exists() } + .first() + } +} + + diff --git a/mod/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt new file mode 100644 index 0000000..90b2545 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt @@ -0,0 +1,27 @@ +package moe.nea.ledger + +import moe.nea.ledger.utils.di.Inject +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender + +class LogChatCommand : CommandBase() { + @Inject + lateinit var logger: LedgerLogger + + override fun getCommandName(): String { + return "ledgerlogchat" + } + + override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { + return true + } + + override fun getCommandUsage(sender: ICommandSender?): String { + return "" + } + + override fun processCommand(sender: ICommandSender?, args: Array?) { + logger.shouldLog = !logger.shouldLog + logger.printOut("§eLedger logging toggled " + (if (logger.shouldLog) "§aon" else "§coff") + "§e.") + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt new file mode 100644 index 0000000..79068cc --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt @@ -0,0 +1,22 @@ +package moe.nea.ledger + +import com.mojang.util.UUIDTypeAdapter +import net.minecraft.client.Minecraft +import java.util.UUID + +object MCUUIDUtil { + + fun parseDashlessUuid(string: String) = UUIDTypeAdapter.fromString(string) + val NIL_UUID = UUID(0L, 0L) + fun getPlayerUUID(): UUID { + val currentUUID = Minecraft.getMinecraft().thePlayer?.uniqueID + ?: Minecraft.getMinecraft().session?.playerID?.let(::parseDashlessUuid) + ?: lastKnownUUID + lastKnownUUID = currentUUID + return currentUUID + } + + + private var lastKnownUUID: UUID = NIL_UUID + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt new file mode 100644 index 0000000..438f342 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt @@ -0,0 +1,117 @@ +package moe.nea.ledger + +import net.minecraft.event.ClickEvent +import net.minecraft.event.HoverEvent +import net.minecraft.util.ChatComponentText +import net.minecraft.util.ChatStyle +import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.IChatComponent +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder +import java.time.temporal.ChronoField +import java.util.regex.Matcher +import java.util.regex.Pattern + +// language=regexp +val SHORT_NUMBER_PATTERN = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?" + +// language=regexp +val ROMAN_NUMBER_PATTERN = "[IVXLCDM]+" + +val romanNumbers = mapOf( + 'I' to 1, + 'V' to 5, + 'X' to 10, + 'L' to 50, + 'C' to 100, + 'D' to 500, + 'M' to 1000 +) + +fun parseRomanNumber(string: String): Int { + var smallestSeenSoFar = Int.MAX_VALUE + var lastSeenOfSmallest = 0 + var amount = 0 + for (c in string) { + val cV = romanNumbers[c]!! + if (cV == smallestSeenSoFar) { + lastSeenOfSmallest++ + amount += cV + } else if (cV < smallestSeenSoFar) { + smallestSeenSoFar = cV + amount += cV + lastSeenOfSmallest = 1 + } else { + amount -= lastSeenOfSmallest * smallestSeenSoFar * 2 + smallestSeenSoFar = cV + amount += cV + lastSeenOfSmallest = 1 + } + } + return amount +} + +val siScalars = mapOf( + 'k' to 1_000.0, + 'K' to 1_000.0, + 'm' to 1_000_000.0, + 'M' to 1_000_000.0, + 'b' to 1_000_000_000.0, + 'B' to 1_000_000_000.0, +) + +fun parseShortNumber(string: String): Double { + var k = string.replace(",", "") + val scalar = k.last() + var scalarMultiplier = siScalars[scalar] + if (scalarMultiplier == null) { + scalarMultiplier = 1.0 + } else { + k = k.dropLast(1) + } + return k.toDouble() * scalarMultiplier +} + +fun Pattern.matches(string: String): Boolean = matcher(string).matches() +inline fun Pattern.useMatcher(string: String, block: Matcher.() -> T): T? = + matcher(string).takeIf { it.matches() }?.let(block) + +fun String.ifDropLast(suffix: String, block: (String) -> T): T? { + if (endsWith(suffix)) { + return block(dropLast(suffix.length)) + } + return null +} + +fun String.unformattedString(): String = replace("§.".toRegex(), "") + +val timeFormat: DateTimeFormatter = DateTimeFormatterBuilder() + .appendValue(ChronoField.DAY_OF_MONTH, 2) + .appendLiteral(".") + .appendValue(ChronoField.MONTH_OF_YEAR, 2) + .appendLiteral(".") + .appendValue(ChronoField.YEAR, 4) + .appendLiteral(" ") + .appendValue(ChronoField.HOUR_OF_DAY, 2) + .appendLiteral(":") + .appendValue(ChronoField.MINUTE_OF_HOUR, 2) + .appendLiteral(":") + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .toFormatter() + +fun Instant.formatChat(): IChatComponent { + val text = ChatComponentText( + LocalDateTime.ofInstant(this, ZoneId.systemDefault()).format(timeFormat) + ) + text.setChatStyle( + ChatStyle() + .setChatClickEvent( + ClickEvent(ClickEvent.Action.OPEN_URL, "https://time.is/${this.epochSecond}")) + .setChatHoverEvent( + HoverEvent(HoverEvent.Action.SHOW_TEXT, ChatComponentText("Click to show on time.is"))) + .setColor(EnumChatFormatting.AQUA)) + return text +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt new file mode 100644 index 0000000..19bd5d0 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt @@ -0,0 +1,197 @@ +package moe.nea.ledger + +import moe.nea.ledger.database.sql.ANDExpression +import moe.nea.ledger.database.sql.BooleanExpression +import moe.nea.ledger.database.sql.Clause +import moe.nea.ledger.database.DBItemEntry +import moe.nea.ledger.database.DBLogEntry +import moe.nea.ledger.database.Database +import moe.nea.ledger.utils.ULIDWrapper +import moe.nea.ledger.utils.di.Inject +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender +import net.minecraft.util.BlockPos +import net.minecraft.util.ChatComponentText +import net.minecraft.util.ChatStyle +import net.minecraft.util.EnumChatFormatting + +class QueryCommand : CommandBase() { + override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { + return true + } + + override fun getCommandName(): String { + return "ledger" + } + + override fun getCommandUsage(sender: ICommandSender?): String { + return "" + } + + override fun getCommandAliases(): List { + return listOf("lgq") + } + + @Inject + lateinit var logger: LedgerLogger + + override fun processCommand(sender: ICommandSender, args: Array) { + if (args.isEmpty()) { + logger.printOut("§eHere is how you can look up transactions:") + logger.printOut("") + logger.printOut("§f- §e/ledger withitem %POTATO%") + logger.printOut(" §aLook up transactions involving potatoes!") + logger.printOut("§f- §e/ledger withitem ENCHANTED_POTATO") + logger.printOut(" §aLook up transactions involving just enchanted potatoes!") + logger.printOut("§f- §e/ledger withitem %POTATO% withitem %CARROT%") + logger.printOut(" §aLook up transactions involving potatoes or carrots!") + logger.printOut("§f- §e/ledger withtype AUCTION_SOLD") + logger.printOut(" §aLook up transactions of sold auctions!") + logger.printOut("§f- §e/ledger withtype AUCTION_SOLD withitem CRIMSON%") + logger.printOut(" §aLook up sold auctions involving crimson armor pieces!") + logger.printOut("") + logger.printOut("§eFilters of the same type apply using §aOR§e and loggers of different types apply using §aAND§e.") + logger.printOut("§eYou can use % as a wildcard!") + return + } + val p = parseArgs(args) + when (p) { + is ParseResult.Success -> { + executeQuery(p) + } + + is ParseResult.UnknownFilter -> { + logger.printOut("§cUnknown filter name ${p.start}. Available filter names are: ${mFilters.keys.joinToString()}") + } + + is ParseResult.MissingArg -> { + logger.printOut("§cFilter ${p.filterM.name} is missing an argument.") + } + } + } + + override fun addTabCompletionOptions( + sender: ICommandSender, + args: Array, + pos: BlockPos + ): MutableList? { + when (val p = parseArgs(args)) { + is ParseResult.MissingArg -> return null + is ParseResult.Success -> return p.lastFilterM.tabComplete(args.last()) + is ParseResult.UnknownFilter -> return getListOfStringsMatchingLastWord(args, mFilters.keys) + } + } + + @Inject + lateinit var database: Database + private fun executeQuery(parse: ParseResult.Success) { + val grouped = parse.filters + val query = DBLogEntry.from(database.connection) + .select(DBLogEntry.type, DBLogEntry.transactionId) + .join(DBItemEntry, on = Clause { column(DBLogEntry.transactionId) eq column(DBItemEntry.transactionId) }) + for (value in grouped.values) { + query.where(ANDExpression(value)) + } + query.limit(80u) + val dedup = mutableSetOf() + query.forEach { + val type = it[DBLogEntry.type] + val transactionId = it[DBLogEntry.transactionId] + if (!dedup.add(transactionId)) { + return@forEach + } + val timestamp = transactionId.getTimestamp() + val items = DBItemEntry.selectAll(database.connection) + .where(Clause { column(DBItemEntry.transactionId) eq string(transactionId.wrapped) }) + .map { ItemChange.from(it) } + val text = ChatComponentText("") + .setChatStyle(ChatStyle().setColor(EnumChatFormatting.YELLOW)) + .appendSibling( + ChatComponentText(type.name) + .setChatStyle(ChatStyle().setColor(EnumChatFormatting.GREEN)) + ) + .appendText(" on ") + .appendSibling(timestamp.formatChat()) + .appendText("\n") + .appendSibling( + ChatComponentText(transactionId.wrapped).setChatStyle(ChatStyle().setColor(EnumChatFormatting.DARK_GRAY)) + ) + for (item in items) { + text.appendText("\n") + .appendSibling(item.formatChat()) + } + text.appendText("\n") + logger.printOut(text) + } + } + + sealed interface ParseResult { + data class UnknownFilter(val start: String) : ParseResult + data class MissingArg(val filterM: FilterM) : ParseResult + data class Success(val lastFilterM: FilterM, val filters: Map>) : ParseResult + } + + fun parseArgs(args: Array): ParseResult { + require(args.isNotEmpty()) + val arr = args.iterator() + val filters = mutableMapOf>() + var lastFilterM: FilterM? = null + while (arr.hasNext()) { + val filterName = arr.next() + val filterM = mFilters[filterName] + if (filterM == null) { + return ParseResult.UnknownFilter(filterName) + } + if (!arr.hasNext()) { + return ParseResult.MissingArg(filterM) + } + filters.getOrPut(filterM, ::mutableListOf).add(filterM.getFilter(arr.next())) + lastFilterM = filterM + } + return ParseResult.Success(lastFilterM!!, filters) + } + + + val mFilters = listOf(TypeFilter, ItemFilter).associateBy { it.name } + + object TypeFilter : FilterM { + override val name: String + get() = "withtype" + + override fun getFilter(text: String): BooleanExpression { + val preparedText = "%" + text.trim('%') + "%" + return Clause { column(DBLogEntry.type) like preparedText } + } + + override fun tabComplete(partialArg: String): MutableList { + return TransactionType.entries.asSequence().map { it.name }.filter { partialArg in it }.toMutableList() + } + } + + object ItemFilter : FilterM { + override val name: String + get() = "withitem" + + private val itemIdProvider = Ledger.di.provide() // TODO: close this escape hatch + override fun getFilter(text: String): BooleanExpression { + return Clause { column(DBItemEntry.itemId) like text } + } + + override fun tabComplete(partialArg: String): MutableList? { + return itemIdProvider.getKnownItemIds() + .asSequence() + .map { it.string } + .filter { partialArg in it } + .take(100) + .toMutableList() + } + } + + interface FilterM { + val name: String + fun getFilter(text: String): BooleanExpression + fun tabComplete(partialArg: String): MutableList? +// fun tabCompleteFilter() TODO + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt new file mode 100644 index 0000000..783664b --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt @@ -0,0 +1,29 @@ +package moe.nea.ledger + +import net.minecraft.client.Minecraft +import net.minecraft.scoreboard.ScorePlayerTeam + +object ScoreboardUtil { + + val sidebarSlot = 1 + fun getScoreboardStrings(): List { + val scoreboard = Minecraft.getMinecraft().theWorld.scoreboard + val objective = scoreboard.getObjectiveInDisplaySlot(sidebarSlot) + val scoreList = scoreboard.getSortedScores(objective).take(15) + .map { + ScorePlayerTeam.formatPlayerName(scoreboard.getPlayersTeam(it.playerName), it.playerName) + } + .map { stripAlien(it) } + .reversed() + return scoreList + } + + fun stripAlien(string: String): String { + val sb = StringBuilder() + for (c in string) { + if (Minecraft.getMinecraft().fontRendererObj.getCharWidth(c) > 0 || c == '§') + sb.append(c) + } + return sb.toString() + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt new file mode 100644 index 0000000..d9c7108 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt @@ -0,0 +1,66 @@ +package moe.nea.ledger + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import moe.nea.ledger.gen.BuildConfig +import moe.nea.ledger.utils.di.DI +import moe.nea.ledger.utils.di.DIProvider +import moe.nea.ledger.utils.telemetry.CommonKeys +import moe.nea.ledger.utils.telemetry.ContextValue +import moe.nea.ledger.utils.telemetry.EventRecorder +import moe.nea.ledger.utils.telemetry.JsonElementContext +import moe.nea.ledger.utils.telemetry.LoggingEventRecorder +import moe.nea.ledger.utils.telemetry.Span +import net.minecraft.client.Minecraft +import net.minecraft.util.Session +import net.minecraftforge.fml.common.Loader + +object TelemetryProvider { + fun injectTo(di: DI) { + di.register( + EventRecorder::class.java, + if (DevUtil.isDevEnv) DIProvider.singeleton(LoggingEventRecorder(Ledger.logger, true)) + else DIProvider.singeleton( + LoggingEventRecorder(Ledger.logger, false)) // TODO: replace with upload to server + ) + } + + val USER = "minecraft_user" + val MINECRAFT_VERSION = "minecraft_version" + val MODS = "mods" + + class MinecraftUser(val session: Session) : ContextValue { + override fun serialize(): JsonElement { + val obj = JsonObject() + obj.addProperty("uuid", session.playerID) + obj.addProperty("name", session.username) + return obj + } + } + + fun setupDefaultSpan() { + val sp = Span.current() + sp.add(USER, MinecraftUser(Minecraft.getMinecraft().session)) + sp.add(MINECRAFT_VERSION, ContextValue.compound( + "static" to "1.8.9", + "rt" to Minecraft.getMinecraft().version, + )) + val mods = JsonArray() + Loader.instance().activeModList.map { + val obj = JsonObject() + obj.addProperty("id", it.modId) + obj.addProperty("version", it.version) + obj.addProperty("displayVersion", it.displayVersion) + obj + }.forEach(mods::add) + sp.add(MODS, JsonElementContext(mods)) + sp.add(CommonKeys.VERSION, ContextValue.string(BuildConfig.FULL_VERSION)) + sp.add(CommonKeys.COMMIT_VERSION, ContextValue.string(BuildConfig.GIT_COMMIT)) + } + + fun setupFor(di: DI) { + injectTo(di) + setupDefaultSpan() + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/TransactionType.kt b/mod/src/main/kotlin/moe/nea/ledger/TransactionType.kt new file mode 100644 index 0000000..33c633d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/TransactionType.kt @@ -0,0 +1,35 @@ +package moe.nea.ledger + +enum class TransactionType { + ACCESSORIES_SWAPPING, + ALLOWANCE_GAIN, + AUCTION_BOUGHT, + AUCTION_LISTING_CHARGE, + AUCTION_SOLD, + AUTOMERCHANT_PROFIT_COLLECT, + BANK_DEPOSIT, + BANK_WITHDRAW, + BAZAAR_BUY_INSTANT, + BAZAAR_BUY_ORDER, + BAZAAR_SELL_INSTANT, + BAZAAR_SELL_ORDER, + BITS_PURSE_STATUS, + BOOSTER_COOKIE_ATE, + CAPSAICIN_EYEDROPS_USED, + COMMUNITY_SHOP_BUY, + CORPSE_DESECRATED, + DIE_ROLLED, + DRACONIC_SACRIFICE, + DUNGEON_CHEST_OPEN, + FORGED, + GOD_POTION_DRANK, + GOD_POTION_MIXIN_DRANK, + KAT_TIMESKIP, + KAT_UPGRADE, + KISMET_REROLL, + KUUDRA_CHEST_OPEN, + NPC_BUY, + NPC_SELL, + VISITOR_BARGAIN, + WYRM_EVOKED, +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt new file mode 100644 index 0000000..c97627d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt @@ -0,0 +1,34 @@ +package moe.nea.ledger + +import moe.nea.ledger.events.TriggerEvent +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender +import net.minecraft.event.ClickEvent +import net.minecraft.util.ChatComponentText +import net.minecraftforge.common.MinecraftForge + +class TriggerCommand : CommandBase() { + fun getTriggerCommandLine(trigger: String): ClickEvent { + return ClickEvent(ClickEvent.Action.RUN_COMMAND, "/${commandName} $trigger") + } + + override fun getCommandName(): String { + return "__ledgertrigger" + } + + override fun getCommandUsage(sender: ICommandSender?): String { + return "" + } + + override fun processCommand(sender: ICommandSender, args: Array) { + val event = TriggerEvent(args.joinToString(" ")) + MinecraftForge.EVENT_BUS.post(event) + if (!event.isCanceled) + sender.addChatMessage(ChatComponentText("§cCould not find the given trigger. This is an internal command for ledger.")) + } + + override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { + return true + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt b/mod/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt new file mode 100644 index 0000000..fd5ed3d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt @@ -0,0 +1,13 @@ +package moe.nea.ledger.config + +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption + +class DebugOptions { + @ConfigOption(name = "Log entries to chat", + desc = "Appends all logged entries into the chat as they are logged. This option does not persist on restarts.") + @Transient + @ConfigEditorBoolean + @JvmField + var logEntries = false +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt b/mod/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt new file mode 100644 index 0000000..91ee5c1 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt @@ -0,0 +1,35 @@ +package moe.nea.ledger.config + +import io.github.notenoughupdates.moulconfig.Config +import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour +import io.github.notenoughupdates.moulconfig.annotations.Category +import io.github.notenoughupdates.moulconfig.processor.ProcessedOption +import moe.nea.ledger.Ledger + +class LedgerConfig : Config() { + override fun getTitle(): String { + return "§6Ledger §7- §6Hypixel SkyBlock data logger §7by §anea89o" + } + + override fun saveNow() { + super.saveNow() + Ledger.managedConfig.saveToFile() + } + + override fun getDescriptionBehaviour(option: ProcessedOption?): DescriptionRendereringBehaviour { + return DescriptionRendereringBehaviour.EXPAND_PANEL + } + + @Category(name = "Ledger", desc = "") + @JvmField + val main = MainOptions() + + @Category(name = "Synchronization", desc = "") + @JvmField + val synchronization = SynchronizationOptions() + + @Category(name = "Debug", desc = "") + @JvmField + val debug = DebugOptions() + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt b/mod/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt new file mode 100644 index 0000000..1efa970 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt @@ -0,0 +1,27 @@ +package moe.nea.ledger.config + +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption + +class MainOptions { + @ConfigOption(name = "Marker for Update UI", desc = "_") + @JvmField + @UpdateUiMarker + @Transient + var testOption = Unit + + @ConfigOption(name = "Check for Updates", desc = "Automatically check for updates on startup") + @ConfigEditorDropdown + @JvmField + var updateCheck = UpdateCheckBehaviour.SEMI + + enum class UpdateCheckBehaviour(val label: String) { + SEMI("Semi-Automatic"), + FULL("Full-Automatic"), + NONE("Don't check"); + + override fun toString(): String { + return label + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt b/mod/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt new file mode 100644 index 0000000..b8c740b --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt @@ -0,0 +1,11 @@ +package moe.nea.ledger.config + +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption + +class SynchronizationOptions { + @ConfigOption(name = "Test Option", desc = "Test Description") + @ConfigEditorBoolean + @JvmField + var testOption = false +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt b/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt new file mode 100644 index 0000000..86ccbf7 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt @@ -0,0 +1,17 @@ +package moe.nea.ledger.config + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent +import io.github.notenoughupdates.moulconfig.gui.component.TextComponent +import io.github.notenoughupdates.moulconfig.gui.editors.ComponentEditor +import io.github.notenoughupdates.moulconfig.processor.ProcessedOption +import moe.nea.ledger.Ledger + +class UpdateUi(option: ProcessedOption) : ComponentEditor(option) { + val delegate by lazy {// TODO + TextComponent("Hier könnte ihre werbung stehen") + } + + override fun getDelegate(): GuiComponent { + return delegate + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt b/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt new file mode 100644 index 0000000..7a0466a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.config + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD) +annotation class UpdateUiMarker { +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt b/mod/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt new file mode 100644 index 0000000..b162c6f --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt @@ -0,0 +1,24 @@ +package moe.nea.ledger.database + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.TransactionType +import moe.nea.ledger.database.columns.DBDouble +import moe.nea.ledger.database.columns.DBEnum +import moe.nea.ledger.database.columns.DBString +import moe.nea.ledger.database.columns.DBUlid +import moe.nea.ledger.database.columns.DBUuid + +object DBLogEntry : Table("LogEntry") { + val transactionId = column("transactionId", DBUlid) + val type = column("type", DBEnum()) + val profileId = column("profileId", DBUuid) + val playerId = column("playerId", DBUuid) +} + +object DBItemEntry : Table("ItemEntry") { + val transactionId = column("transactionId", DBUlid) // TODO: add foreign keys + val mode = column("mode", DBEnum()) + val itemId = column("item", DBString.mapped(ItemId::string, ::ItemId)) + val size = column("size", DBDouble) +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt b/mod/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt new file mode 100644 index 0000000..7d1782a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt @@ -0,0 +1,68 @@ +package moe.nea.ledger.database + +import java.sql.Connection + +interface DBUpgrade { + val toVersion: Long + val fromVersion get() = toVersion - 1 + fun performUpgrade(connection: Connection) + + companion object { + + fun performUpgrades( + connection: Connection, + upgrades: Iterable, + ) { + for (upgrade in upgrades) { + upgrade.performUpgrade(connection) + } + } + + fun performUpgradeChain( + connection: Connection, + from: Long, to: Long, + upgrades: Iterable, + afterEach: (newVersion: Long) -> Unit, + ) { + val table = buildLookup(upgrades) + for (version in (from + 1)..(to)) { + val currentUpgrades = table[version] ?: listOf() + println("Scheduled ${currentUpgrades.size} upgrades to reach DB version $version") + performUpgrades(connection, currentUpgrades) + afterEach(version) + } + } + + fun buildLookup(upgrades: Iterable): Map> { + return upgrades.groupBy { it.toVersion } + } + + fun createTable(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade { + require(columns.all { it in table.columns }) + return of("Create table ${table}", to) { + table.createIfNotExists(it, columns.toList()) + } + } + + fun addColumns(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade { + return of("Add columns to table $table", to) { + table.alterTableAddColumns(it, columns.toList()) + } + } + + fun of(name: String, to: Long, block: (Connection) -> Unit): DBUpgrade { + return object : DBUpgrade { + override val toVersion: Long + get() = to + + override fun performUpgrade(connection: Connection) { + block(connection) + } + + override fun toString(): String { + return name + } + } + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/database/Database.kt b/mod/src/main/kotlin/moe/nea/ledger/database/Database.kt new file mode 100644 index 0000000..025888c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/database/Database.kt @@ -0,0 +1,57 @@ +package moe.nea.ledger.database + +import moe.nea.ledger.Ledger +import moe.nea.ledger.database.columns.DBString +import java.sql.Connection +import java.sql.DriverManager + +class Database { + lateinit var connection: Connection + + object MetaTable : Table("LedgerMeta") { + val key = column("key", DBString) + val value = column("value", DBString) + + init { + unique(key) + } + } + + data class MetaKey(val name: String) { + companion object { + val DATABASE_VERSION = MetaKey("databaseVersion") + val LAST_LAUNCH = MetaKey("lastLaunch") + } + } + + fun setMetaKey(key: MetaKey, value: String) { + MetaTable.insert(connection, Table.OnConflict.REPLACE) { + it[MetaTable.key] = key.name + it[MetaTable.value] = value + } + } + + val databaseVersion: Long = 1 + + fun loadAndUpgrade() { + connection = DriverManager.getConnection("jdbc:sqlite:${Ledger.dataFolder.resolve("database.db")}") + MetaTable.createIfNotExists(connection) + val meta = MetaTable.selectAll(connection).associate { MetaKey(it[MetaTable.key]) to it[MetaTable.value] } + val lastLaunch = meta[MetaKey.LAST_LAUNCH]?.toLong() ?: 0L + println("Last launch $lastLaunch") + setMetaKey(MetaKey.LAST_LAUNCH, System.currentTimeMillis().toString()) + + val oldVersion = meta[MetaKey.DATABASE_VERSION]?.toLong() ?: -1 + println("Old Database Version: $oldVersion; Current version: $databaseVersion") + if (oldVersion > databaseVersion) + error("Outdated software. Database is newer than me!") + // TODO: create a backup if there is a db version upgrade happening + DBUpgrade.performUpgradeChain( + connection, oldVersion, databaseVersion, + Upgrades().upgrades + ) { version -> + setMetaKey(MetaKey.DATABASE_VERSION, version.toString()) + } + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt b/mod/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt new file mode 100644 index 0000000..e83abe7 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt @@ -0,0 +1,20 @@ +package moe.nea.ledger.database + +class Upgrades { + val upgrades = mutableListOf() + + fun add(upgrade: DBUpgrade) = upgrades.add(upgrade) + + init { + add(DBUpgrade.createTable( + 0, DBLogEntry, + DBLogEntry.type, DBLogEntry.playerId, DBLogEntry.profileId, + DBLogEntry.transactionId)) + add(DBUpgrade.createTable( + 0, DBItemEntry, + DBItemEntry.itemId, DBItemEntry.size, DBItemEntry.mode, DBItemEntry.transactionId + )) + } + + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/database/schema.dot b/mod/src/main/kotlin/moe/nea/ledger/database/schema.dot new file mode 100644 index 0000000..d932f6a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/database/schema.dot @@ -0,0 +1,23 @@ +digraph { + node [shape=plain]; + rankdir=LR; + entry [label=< + + + + + + +
Log Entry
playerId
profileId
timestamp
Type
+ >]; + item [label=< + + + + + + +
Item Stack
Transaction
Item ID
Count
Transfer Direction
+ >]; +// item:transaction -> entry; +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt b/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt new file mode 100644 index 0000000..098912a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt @@ -0,0 +1,11 @@ +package moe.nea.ledger.events + +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.Event + +data class BeforeGuiAction(val gui: GuiScreen) : Event() { + val chest = gui as? GuiChest + val chestSlots = chest?.inventorySlots as ContainerChest? +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt b/mod/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt new file mode 100644 index 0000000..a352c27 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt @@ -0,0 +1,15 @@ +package moe.nea.ledger.events + +import moe.nea.ledger.unformattedString +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.Event +import java.time.Instant + +data class ChatReceived( + val message: String, + val timestamp: Instant = Instant.now() +) : Event() { + constructor(event: ClientChatReceivedEvent) : this( + event.message.unformattedText.unformattedString().trimEnd() + ) +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt new file mode 100644 index 0000000..d040961 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt @@ -0,0 +1,12 @@ +package moe.nea.ledger.events + +import moe.nea.ledger.ItemId +import net.minecraftforge.fml.common.eventhandler.Event + +class ExtraSupplyIdEvent( + private val store: (String, ItemId) -> Unit +) : Event() { + fun store(name: String, id: ItemId) { + store.invoke(name, id) + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt new file mode 100644 index 0000000..9e057dd --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt @@ -0,0 +1,9 @@ +package moe.nea.ledger.events + +import net.minecraft.inventory.Slot +import net.minecraftforge.fml.common.eventhandler.Event + +data class GuiClickEvent( + val slotIn: Slot?, val slotId: Int, val clickedButton: Int, val clickType: Int +) : Event() { +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt b/mod/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt new file mode 100644 index 0000000..d917039 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class InitializationComplete : Event() { +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt new file mode 100644 index 0000000..d36e0c7 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt @@ -0,0 +1,7 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class RegistrationFinishedEvent : Event() { + +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt b/mod/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt new file mode 100644 index 0000000..cab0a20 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt @@ -0,0 +1,10 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class SupplyDebugInfo : Event() { // TODO: collect this in the event recorder + val data = mutableListOf>() + fun record(key: String, value: Any) { + data.add(key to value) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt new file mode 100644 index 0000000..3751f43 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt @@ -0,0 +1,7 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Cancelable +import net.minecraftforge.fml.common.eventhandler.Event + +@Cancelable +data class TriggerEvent(val action: String) : Event() diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt new file mode 100644 index 0000000..d60f3a4 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt @@ -0,0 +1,5 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class LateWorldLoadEvent : Event() diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt new file mode 100644 index 0000000..22a97f7 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class WorldSwitchEvent : Event() { +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt new file mode 100644 index 0000000..1c228ff --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt @@ -0,0 +1,34 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class AccessorySwapperDetection { + + val swapperUsed = "Swapped .* enrichments to .*!".toPattern() + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onChat(event: ChatReceived) { + swapperUsed.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + TransactionType.ACCESSORIES_SWAPPING, + event.timestamp, + listOf( + ItemChange.lose(ItemIds.TALISMAN_ENRICHMENT_SWAPPER, 1) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt new file mode 100644 index 0000000..cd48d45 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt @@ -0,0 +1,37 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class AllowanceDetection { + + val allowancePattern = + Pattern.compile("ALLOWANCE! You earned (?$SHORT_NUMBER_PATTERN) coins!") + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onAllowanceGain(event: ChatReceived) { + allowancePattern.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + TransactionType.ALLOWANCE_GAIN, + event.timestamp, + listOf( + ItemChange.gainCoins(parseShortNumber(group("coins"))), + ) + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt new file mode 100644 index 0000000..d02095d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt @@ -0,0 +1,143 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ExpiringValue +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern +import kotlin.time.Duration.Companion.seconds + +class AuctionHouseDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + data class LastViewedItem( + val count: Int, + val id: ItemId, + ) + /* + You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction! + You collected 60,000 coins from selling Walnut to [MVP++] Alea1337 in an auction! + You purchased 2x Walnut for 69 coins! + You purchased ◆ Ice Rune I for 4,000 coins! + */ + + val createAuctionScreen = "Confirm( BIN)? Auction".toPattern() + val auctionCreationCostPattern = "Cost: (?$SHORT_NUMBER_PATTERN) coins?".toPattern() + + val auctionCreatedChatPattern = "(BIN )?Auction started for .*".toPattern() + + var lastCreationCost: ExpiringValue = ExpiringValue.empty() + + @SubscribeEvent + fun onCreateAuctionClick(event: BeforeGuiAction) { + val slots = event.chestSlots ?: return + if (!createAuctionScreen.asPredicate().test(slots.lowerChestInventory.name)) return + val auctionSlot = slots.lowerChestInventory.getStackInSlot(9 + 2) ?: return + val creationCost = auctionSlot.getLore().firstNotNullOfOrNull { + auctionCreationCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")) } + } + if (creationCost != null) { + lastCreationCost = ExpiringValue(creationCost) + } + } + + @SubscribeEvent + fun onCreateAuctionChat(event: ChatReceived) { + auctionCreatedChatPattern.useMatcher(event.message) { + lastCreationCost.consume(3.seconds)?.let { cost -> + ledger.logEntry(LedgerEntry( + TransactionType.AUCTION_LISTING_CHARGE, + event.timestamp, + listOf(ItemChange.loseCoins(cost)) + )) + } + } + } + + val collectSold = + Pattern.compile("You collected (?$SHORT_NUMBER_PATTERN) coins? from selling (?.*) to (?.*) in an auction!") + val purchased = + Pattern.compile("You purchased (?:(?[0-9]+)x )?(?.*) for (?$SHORT_NUMBER_PATTERN) coins!") + var lastViewedItems: MutableList = mutableListOf() + + @SubscribeEvent + fun onEvent(event: ChatReceived) { + collectSold.useMatcher(event.message) { + val lastViewedItem = lastViewedItems.removeLastOrNull() + ledger.logEntry( + LedgerEntry( + TransactionType.AUCTION_SOLD, + event.timestamp, + listOfNotNull( + ItemChange.gainCoins(parseShortNumber(group("coins"))), + lastViewedItem?.let { ItemChange.lose(it.id, it.count) } + ), + ) + ) + } + purchased.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.AUCTION_BOUGHT, + event.timestamp, + listOf( + ItemChange.loseCoins(parseShortNumber(group("coins"))), + ItemChange.gain( + ids.findForName(group("what")) ?: ItemId.NIL, + group("amount")?.toInt() ?: 1 + ) + ) + ) + ) + } + } + + @SubscribeEvent + fun onBeforeAuctionCollected(event: BeforeGuiAction) { + val chest = (event.gui as? GuiChest) ?: return + val slots = chest.inventorySlots as ContainerChest + val name = slots.lowerChestInventory.displayName.unformattedText.unformattedString() + + if (name == "BIN Auction View" || name == "Auction View") { + handleCollectSingleAuctionView(slots) + } + if (name == "Manage Auctions") { + handleCollectMultipleAuctionsView(slots) + } + } + + private fun handleCollectMultipleAuctionsView(slots: ContainerChest) { + lastViewedItems = + (0 until slots.lowerChestInventory.sizeInventory) + .mapNotNull { slots.lowerChestInventory.getStackInSlot(it) } + .filter { + it.getLore().contains("§7Status: §aSold!") // BINs + || it.getLore().contains("§7Status: §aEnded!") // Auctions + } + .mapNotNull { LastViewedItem(it.stackSize, it.getInternalId() ?: return@mapNotNull null) } + .toMutableList() + } + + + fun handleCollectSingleAuctionView(slots: ContainerChest) { + val soldItem = slots.lowerChestInventory.getStackInSlot(9 + 4) ?: return + val id = soldItem.getInternalId() ?: return + val count = soldItem.stackSize + lastViewedItems = mutableListOf(LastViewedItem(count, id)) + } + + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt new file mode 100644 index 0000000..928d30c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt @@ -0,0 +1,49 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + + +class BankDetection @Inject constructor(val ledger: LedgerLogger) { + val withdrawPattern = + Pattern.compile("^(You have withdrawn|Withdrew) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:left in the account!|in your account!)$") + val depositPattern = + Pattern.compile("^(?:You have deposited|Deposited) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:in your account!|in the account!)$") + + @SubscribeEvent + fun onChat(event: ChatReceived) { + withdrawPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BANK_WITHDRAW, + event.timestamp, + listOf(ItemChange(ItemId.COINS, + parseShortNumber(group("amount")), + ItemChange.ChangeDirection.TRANSFORM)), + ) + ) + } + depositPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BANK_DEPOSIT, + event.timestamp, + listOf(ItemChange(ItemId.COINS, + parseShortNumber(group("amount")), + ItemChange.ChangeDirection.TRANSFORM)), + ) + ) + } + } + +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt new file mode 100644 index 0000000..0f1fc2c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt @@ -0,0 +1,58 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class BazaarDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val instaBuyPattern = + Pattern.compile("\\[Bazaar\\] Bought (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") + val instaSellPattern = + Pattern.compile("\\[Bazaar\\] Sold (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") + + + @SubscribeEvent + fun onInstSellChat(event: ChatReceived) { + instaBuyPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BAZAAR_BUY_INSTANT, + event.timestamp, + listOf( + ItemChange.loseCoins(parseShortNumber(group("coins"))), + ItemChange.gain( + ids.findForName(group("what")) ?: ItemId.NIL, + parseShortNumber(group("count")) + ) + ) + ) + ) + } + instaSellPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BAZAAR_SELL_INSTANT, + event.timestamp, + listOf( + ItemChange.gainCoins(parseShortNumber(group("coins"))), + ItemChange.lose( + ids.findForName(group("what")) ?: ItemId.NIL, + parseShortNumber(group("count")) + ) + ), + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt new file mode 100644 index 0000000..330ee1d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt @@ -0,0 +1,95 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.mixin.AccessorGuiEditSign +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.gui.inventory.GuiEditSign +import net.minecraftforge.client.event.GuiScreenEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class BazaarOrderDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val buyOrderClaimed = + Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN)x (?.*) worth (?$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!") + val sellOrderClaimed = + Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN) coins? from selling (?$SHORT_NUMBER_PATTERN)x (?.*) at $SHORT_NUMBER_PATTERN each!") + val orderFlipped = + Pattern.compile("\\[Bazaar] Order Flipped! (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins? of total expected profit.") + val previousPricePattern = + Pattern.compile("(?$SHORT_NUMBER_PATTERN)/u") + var lastFlippedPreviousPrice = 0.0 + + @SubscribeEvent + fun detectSignFlip(event: GuiScreenEvent.InitGuiEvent) { + val gui = event.gui + if (gui !is GuiEditSign) return + gui as AccessorGuiEditSign + val text = gui.tileEntity_ledger.signText + if (text[2].unformattedText != "Previous price:") return + previousPricePattern.useMatcher(text[3].unformattedText) { + lastFlippedPreviousPrice = parseShortNumber(group("price")) + } + } + + @SubscribeEvent + fun detectBuyOrders(event: ChatReceived) { + orderFlipped.useMatcher(event.message) { + val amount = parseShortNumber(group("amount")).toInt() + ledger.logEntry( + LedgerEntry( + TransactionType.BAZAAR_BUY_ORDER, + event.timestamp, + listOf( + ItemChange.loseCoins(lastFlippedPreviousPrice * amount), + ItemChange.gain( + ids.findForName(group("what")) ?: ItemId.NIL, + amount, + ) + ) + ) + ) + } + buyOrderClaimed.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BAZAAR_BUY_ORDER, + event.timestamp, + listOf( + ItemChange.loseCoins(parseShortNumber(group("coins"))), + ItemChange.gain( + ids.findForName(group("what")) ?: ItemId.NIL, + parseShortNumber(group("amount")) + ) + ), + ) + ) + } + sellOrderClaimed.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.BAZAAR_SELL_ORDER, + event.timestamp, + listOf( + ItemChange.gainCoins( + parseShortNumber(group("coins")) + ), + ItemChange.lose( + ids.findForName(group("what")) ?: ItemId.NIL, + parseShortNumber(group("amount")), + ) + ), + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt new file mode 100644 index 0000000..44a0050 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt @@ -0,0 +1,62 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.LateWorldLoadEvent +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.ScoreboardUtil +import moe.nea.ledger.TransactionType +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class BitsDetection @Inject constructor(val ledger: LedgerLogger) { + + var lastBits = -1 + + val bitScoreboardRegex = "Bits: (?$SHORT_NUMBER_PATTERN)".toPattern() + + @SubscribeEvent + fun onWorldSwitch(event: LateWorldLoadEvent) { + ScoreboardUtil.getScoreboardStrings().forEach { + bitScoreboardRegex.useMatcher(it.unformattedString()) { + val bits = parseShortNumber(group("purse")).toInt() + if (lastBits != bits) { + ledger.logEntry( + LedgerEntry( + TransactionType.BITS_PURSE_STATUS, + Instant.now(), + listOf( + ItemChange(ItemIds.SKYBLOCK_BIT, bits.toDouble(), ItemChange.ChangeDirection.SYNC) + ) + ) + ) + lastBits = bits + } + return + } + } + } + + @SubscribeEvent + fun onEvent(event: ChatReceived) { + if (event.message.startsWith("You consumed a Booster Cookie!")) { + ledger.logEntry( + LedgerEntry( + TransactionType.BOOSTER_COOKIE_ATE, + Instant.now(), + listOf( + ItemChange.lose(ItemIds.BOOSTER_COOKIE, 1) + ) + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt new file mode 100644 index 0000000..553bebf --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt @@ -0,0 +1,66 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class BitsShopDetection @Inject constructor(val ledger: LedgerLogger) { + + + data class BitShopEntry( + val id: ItemId, + val stackSize: Int, + val bitPrice: Int, + val timestamp: Long = System.currentTimeMillis() + ) + + var lastClickedBitShopItem: BitShopEntry? = null + var bitCostPattern = "(?$SHORT_NUMBER_PATTERN) Bits".toPattern() + + @SubscribeEvent + fun recordLastBitPrice(event: GuiClickEvent) { + val slot = event.slotIn ?: return + val name = slot.inventory.displayName.unformattedText.unformattedString() + if (name != "Community Shop" && !name.startsWith("Bits Shop")) + return + val stack = slot.stack ?: return + val id = stack.getInternalId() ?: return + val bitPrice = stack.getLore() + .firstNotNullOfOrNull { bitCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")).toInt() } } + ?: return + lastClickedBitShopItem = BitShopEntry(id, stack.stackSize, bitPrice) + } + + @SubscribeEvent + fun onChat(event: ChatReceived) { + if (event.message.startsWith("You bought ")) { + val lastBit = lastClickedBitShopItem ?: return + if (System.currentTimeMillis() - lastBit.timestamp > 5000) return + ledger.logEntry( + LedgerEntry( + TransactionType.COMMUNITY_SHOP_BUY, + Instant.now(), + listOf( + ItemChange.lose(ItemIds.SKYBLOCK_BIT, lastBit.bitPrice.toDouble()), + ItemChange.gain(lastBit.id, lastBit.stackSize) + ) + ) + ) + } + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt new file mode 100644 index 0000000..cca02e1 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt @@ -0,0 +1,48 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.unformattedString +import moe.nea.ledger.utils.di.Inject +import net.minecraft.init.Blocks +import net.minecraft.inventory.Slot +import net.minecraft.item.Item +import java.time.Instant + +abstract class ChestDetection { + data class ChestCost( + val diff: List, + val timestamp: Instant, + ) + + @Inject + lateinit var itemIdProvider: ItemIdProvider + fun scrapeChestReward(rewardSlot: Slot): ChestCost? { + val inventory = rewardSlot.inventory + if (!inventory.displayName.unformattedText.unformattedString() + .endsWith(" Chest") + ) return null + val rewardStack = rewardSlot.stack ?: return null + val name = rewardStack.getDisplayNameU() + if (name != "§aOpen Reward Chest") return null + val lore = rewardStack.getLore() + val cost = itemIdProvider.findCostItemsFromSpan(lore) + val gain = (9..18) + .mapNotNull { inventory.getStackInSlot(it) } + .filter { it.item != Item.getItemFromBlock(Blocks.stained_glass_pane) } + .map { + it.getInternalId()?.withStackSize(it.stackSize) + ?: itemIdProvider.findStackableItemByName(it.displayName) + ?: ItemId.NIL.withStackSize(it.stackSize) + } + return ChestCost( + cost.map { ItemChange.lose(it.first, it.second) } + gain.map { ItemChange.gain(it.first, it.second) }, + Instant.now() + ) + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt new file mode 100644 index 0000000..e389ffb --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt @@ -0,0 +1,47 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.WorldSwitchEvent +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class DragonEyePlacementDetection { + val eyePlaced = "☬ You placed a Summoning Eye!( Brace yourselves!)? \\(./.\\)".toPattern() +//☬ You placed a Summoning Eye! Brace yourselves! (8/8) + var eyeCount = 0 + + @SubscribeEvent + fun onWorldSwap(event: WorldSwitchEvent) { + eyeCount = 0 + } + + @SubscribeEvent + fun onRetrieveEye(event: ChatReceived) { + if (event.message == "You recovered a Summoning Eye!") { + eyeCount-- + } + eyePlaced.useMatcher(event.message) { + eyeCount++ + } + if (event.message == "Your Sleeping Eyes have been awoken by the magic of the Dragon. They are now Remnants of the Eye!") { + logger.logEntry(LedgerEntry( + TransactionType.WYRM_EVOKED, + event.timestamp, + listOf( + ItemChange.lose(ItemIds.SUMMONING_EYE, eyeCount) + ) + )) + eyeCount = 0 + } + } + + @Inject + lateinit var logger: LedgerLogger +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt new file mode 100644 index 0000000..574cfcf --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt @@ -0,0 +1,72 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.DebouncedValue +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import kotlin.time.Duration.Companion.seconds + +class DragonSacrificeDetection { + //SACRIFICE! You turned Holy Dragon Boots into 30 Dragon Essence! + //BONUS LOOT! You also received 17x Holy Dragon Fragment from your sacrifice! + @Inject + lateinit var itemIdProvider: ItemIdProvider + + @Inject + lateinit var logger: LedgerLogger + + val sacrificePattern = + "SACRIFICE! You turned (?.*) into (?$SHORT_NUMBER_PATTERN) Dragon Essence!".toPattern() + val bonusLootPattern = "BONUS LOOT! You also received (?.*) from your sacrifice!".toPattern() + + var lastSacrifice: DebouncedValue = DebouncedValue.farFuture() + + + @SubscribeEvent + fun onChat(event: ChatReceived) { + sacrificePattern.useMatcher(event.message) { + val sacrifice = itemIdProvider.findForName(group("sacrifice")) ?: return + val lootEssence = parseShortNumber(group("amount")) + consume(lastSacrifice.replace()) + lastSacrifice = DebouncedValue(LedgerEntry( + TransactionType.DRACONIC_SACRIFICE, + event.timestamp, + listOf( + ItemChange.lose(sacrifice, 1), + ItemChange.gain(ItemIds.ESSENCE_DRAGON, lootEssence) + ) + )) + } + bonusLootPattern.useMatcher(event.message) { + val bonusItem = itemIdProvider.findStackableItemByName( + group("bonus"), true + ) ?: return + lastSacrifice.replace()?.let { + consume( + it.copy(items = it.items + ItemChange.unpairGain(bonusItem)) + ) + } + } + } + + @SubscribeEvent + fun onTick(event: TickEvent) { + consume(lastSacrifice.consume(4.seconds)) + } + + fun consume(entry: LedgerEntry?) { + if (entry != null) + logger.logEntry(entry) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt new file mode 100644 index 0000000..e747be9 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt @@ -0,0 +1,95 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ExpiringValue +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.ExtraSupplyIdEvent +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant +import java.util.concurrent.locks.ReentrantLock +import kotlin.time.Duration.Companion.seconds + +class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) : ChestDetection() { + + @SubscribeEvent + fun onKismetClick(event: GuiClickEvent) { + val slot = event.slotIn ?: return + if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return + val stack = slot.stack ?: return + if (stack.getDisplayNameU() == "§aReroll Chest") { + logger.logEntry( + LedgerEntry( + TransactionType.KISMET_REROLL, + Instant.now(), + listOf( + ItemChange.lose(ItemIds.KISMET_FEATHER, 1) + ) + ) + ) + } + } + + + var lastOpenedChest = ExpiringValue.empty() + + @SubscribeEvent + fun supplyExtraIds(event: ExtraSupplyIdEvent) { + event.store("Dungeon Chest Key", ItemIds.DUNGEON_CHEST_KEY) + event.store("Kismet Feather", ItemIds.KISMET_FEATHER) + } + + @SubscribeEvent + fun onRewardChestClick(event: GuiClickEvent) { + lastOpenedChest = ExpiringValue(scrapeChestReward(event.slotIn ?: return) ?: return) + } + + class Mutex(defaultValue: T) { + private var value: T = defaultValue + val lock = ReentrantLock() + + fun getUnsafeLockedValue(): T { + if (!lock.isHeldByCurrentThread) + error("Accessed unsafe locked value, without holding the lock.") + return value + } + + fun withLock(func: (T) -> R): R { + lock.lockInterruptibly() + try { + val ret = func(value) + if (ret === value) { + error("Please don't smuggle out the locked value. If this is unintentional, please append a `Unit` instruction to the end of your `withLock` call: `.withLock { /* your existing code */; Unit }`.") + } + return ret + } finally { + lock.unlock() + } + } + } + + val rewardMessage = " (WOOD|GOLD|DIAMOND|EMERALD|OBSIDIAN|BEDROCK) CHEST REWARDS".toPattern() + + @SubscribeEvent + fun onChatMessage(event: ChatReceived) { + if (event.message == "You don't have that many coins in the bank!") { + lastOpenedChest.take() + } + rewardMessage.useMatcher(event.message) { + val chest = lastOpenedChest.consume(3.seconds) ?: return + logger.logEntry(LedgerEntry( + TransactionType.DUNGEON_CHEST_OPEN, + chest.timestamp, + chest.diff, + )) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt new file mode 100644 index 0000000..93bb453 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt @@ -0,0 +1,43 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.InitializationComplete +import moe.nea.ledger.events.SupplyDebugInfo +import moe.nea.ledger.utils.GsonUtil +import moe.nea.ledger.utils.di.Inject +import moe.nea.ledger.utils.network.Request +import moe.nea.ledger.utils.network.RequestUtil +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.Event +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.concurrent.CompletableFuture + +class ExternalDataProvider @Inject constructor( + val requestUtil: RequestUtil +) { + + fun createAuxillaryDataRequest(path: String): Request { + return requestUtil.createRequest("https://github.com/nea89o/ledger-auxiliary-data/raw/refs/heads/master/$path") + } + + private val itemNameFuture: CompletableFuture> = CompletableFuture.supplyAsync { + val request = createAuxillaryDataRequest("data/item_names.json") + val response = request.execute(requestUtil) + val nameMap = response.json(GsonUtil.typeToken>()) + return@supplyAsync nameMap + } + + lateinit var itemNames: Map + + class DataLoaded(val provider: ExternalDataProvider) : Event() + + @SubscribeEvent + fun onDebugData(debugInfo: SupplyDebugInfo) { + debugInfo.record("externalItemsLoaded", itemNameFuture.isDone && !itemNameFuture.isCompletedExceptionally) + } + + @SubscribeEvent + fun onInitComplete(event: InitializationComplete) { + itemNames = itemNameFuture.join() + MinecraftForge.EVENT_BUS.post(DataLoaded(this)) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt new file mode 100644 index 0000000..04dbe80 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt @@ -0,0 +1,35 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class EyedropsDetection { + + val capsaicinEyedropsUsed = "You applied the eyedrops on the minion and ran out!".toPattern() + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onChat(event: ChatReceived) { + capsaicinEyedropsUsed.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + TransactionType.CAPSAICIN_EYEDROPS_USED, + event.timestamp, + listOf( + ItemChange.lose(ItemIds.CAPSAICIN_EYEDROPS_NO_CHARGES, 1) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt new file mode 100644 index 0000000..95811ed --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt @@ -0,0 +1,48 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.getInternalId +import moe.nea.ledger.matches +import moe.nea.ledger.unformattedString +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class ForgeDetection { + val furnaceSlot = 9 + 4 + val furnaceName = "Forge Slot.*".toPattern() + + @SubscribeEvent + fun onClick(event: GuiClickEvent) { + val slot = event.slotIn ?: return + val clickedItem = slot.stack ?: return + if (clickedItem.displayName.unformattedString() != "Confirm") return + val furnaceSlotName = slot.inventory.getStackInSlot(furnaceSlot)?.displayName?.unformattedString() ?: return + if (!furnaceName.matches(furnaceSlotName)) + return + val cl = (0 until slot.inventory.sizeInventory - 9) + .mapNotNull { + val stack = slot.inventory.getStackInSlot(it) ?: return@mapNotNull null + val x = it % 9 + if (x == 4) return@mapNotNull null + ItemChange( + stack.getInternalId() ?: return@mapNotNull null, + stack.stackSize.toDouble(), + if (x < 4) ItemChange.ChangeDirection.LOST else ItemChange.ChangeDirection.GAINED + ) + } + logger.logEntry(LedgerEntry( + TransactionType.FORGED, + Instant.now(), + cl, + )) + } + + @Inject + lateinit var logger: LedgerLogger + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt new file mode 100644 index 0000000..a8f79c1 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt @@ -0,0 +1,62 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class GambleDetection { + + val dieRolled = + "Your (?High Class )?Archfiend Dice rolled a (?[1-7])!.*" + .toPattern() + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onChat(event: ChatReceived) { + dieRolled.useMatcher(event.message) { + val isLowClass = group("isHighClass").isNullOrBlank() + val item = if (isLowClass) ItemIds.ARCHFIEND_DICE else ItemIds.HIGH_CLASS_ARCHFIEND_DICE + val face = group("face") + val rollCost = if (isLowClass) 666_000.0 else 6_600_000.0 + if (face == "7") { + logger.logEntry(LedgerEntry( + TransactionType.DIE_ROLLED, + event.timestamp, + listOf( + ItemChange.lose(item, 1), + ItemChange.loseCoins(rollCost), + ItemChange.gain(ItemIds.DYE_ARCHFIEND, 1), + ) + )) + } else if (face == "6") { + logger.logEntry(LedgerEntry( + TransactionType.DIE_ROLLED, + event.timestamp, + listOf( + ItemChange.lose(item, 1), + ItemChange.loseCoins(rollCost), + ItemChange.gainCoins(if (isLowClass) 15_000_000.0 else 100_000_000.0), + ) + )) + } else { + logger.logEntry(LedgerEntry( + TransactionType.DIE_ROLLED, + event.timestamp, + listOf( + ItemChange(item, 1.0, ItemChange.ChangeDirection.CATALYST), + ItemChange.loseCoins(rollCost), + ) + )) + } + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt new file mode 100644 index 0000000..ae86519 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt @@ -0,0 +1,35 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class GodPotionDetection { + + val godPotionDrank = "(SIP|SLURP|GULP|CHUGALUG)! The God Potion grants you powers for .*!".toPattern() + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onChat(event: ChatReceived) { + godPotionDrank.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + TransactionType.GOD_POTION_DRANK, + event.timestamp, + listOf( + ItemChange.lose(ItemIds.GOD_POTION_2, 1) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt new file mode 100644 index 0000000..b96a24a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt @@ -0,0 +1,38 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class GodPotionMixinDetection { + + val godPotionMixinDrank = "SCHLURP! The (effects of the )?(?.*?) (grants you effects|have been extended by) .*! They will pause if your God Potion expires.".toPattern() + + @Inject + lateinit var logger: LedgerLogger + + @Inject + lateinit var itemIdProvider: ItemIdProvider + + @SubscribeEvent + fun onChat(event: ChatReceived) { + godPotionMixinDrank.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + TransactionType.GOD_POTION_MIXIN_DRANK, + event.timestamp, + listOf( + ItemChange.lose(itemIdProvider.findForName(group("what")) ?: ItemId.NIL, 1) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt new file mode 100644 index 0000000..eda5aba --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt @@ -0,0 +1,95 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + + +class KatDetection { + @Inject + lateinit var log: LedgerLogger + + @Inject + lateinit var itemIdProvider: ItemIdProvider + + val giftNameToIdMap = mapOf( + "flower" to ItemId("KAT_FLOWER"), + "bouquet" to ItemId("KAT_BOUQUET"), + ) + val katGift = "\\[NPC\\] Kat: A (?.*)\\? For me\\? How sweet!".toPattern() + + @SubscribeEvent + fun onChat(event: ChatReceived) { + katGift.useMatcher(event.message) { + val giftName = group("gift") + val giftId = giftNameToIdMap[giftName] + log.logEntry(LedgerEntry( + TransactionType.KAT_TIMESKIP, + event.timestamp, + listOf( + ItemChange.lose(giftId ?: ItemId.NIL, 1) + ) + )) + } + } + + val confirmSlot = 9 + 9 + 4 + val petSlot = 9 + 4 + + data class PetUpgrade( + val beforePetId: ItemId, + val cost: List> + ) + + var lastPetUpgradeScheduled: PetUpgrade? = null + + @SubscribeEvent + fun onClick(event: BeforeGuiAction) { + val slots = event.chestSlots ?: return + val petItem = slots.lowerChestInventory.getStackInSlot(petSlot) ?: return + val beforePetId = petItem.getInternalId() ?: return + val confirmItem = slots.lowerChestInventory.getStackInSlot(confirmSlot) ?: return + val lore = confirmItem.getLore() + val cost = itemIdProvider.findCostItemsFromSpan(lore) + lastPetUpgradeScheduled = PetUpgrade(beforePetId, cost) + } + + val petUpgradeDialogue = "\\[NPC\\] Kat: I'll get your (?.*) upgraded to (?.*) in no time!".toPattern() + fun upgradePetTier(itemId: ItemId): ItemId { + val str = itemId.string.split(";", limit = 2) + if (str.size == 2) { + val (type, tier) = str + val tierT = tier.toIntOrNull() + if (tierT != null) + return ItemId(type + ";" + (tierT + 1)) + } + return itemId + } + + @SubscribeEvent + fun onPetUpgrade(event: ChatReceived) { + petUpgradeDialogue.useMatcher(event.message) { + val upgrade = lastPetUpgradeScheduled ?: return + lastPetUpgradeScheduled = null + log.logEntry(LedgerEntry( + TransactionType.KAT_UPGRADE, + event.timestamp, + listOf( + ItemChange.lose(upgrade.beforePetId, 1), + ItemChange.gain(upgradePetTier(upgrade.beforePetId), 1), + ) + upgrade.cost.map { ItemChange.lose(it.first, it.second) }, + )) + } + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt new file mode 100644 index 0000000..e0e9322 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt @@ -0,0 +1,45 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.getInternalId +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.Minecraft +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class KuudraChestDetection : ChestDetection() { + // TODO: extra essence for kuudra pet (how?), item SALVAGE detection + // TODO: save uuid along side item id + + val kuudraKeyPattern = "KUUDRA_.*_TIER_KEY".toPattern() + + @Inject + lateinit var log: LedgerLogger + + @Inject + lateinit var minecraft: Minecraft + fun hasKey(keyItem: ItemId): Boolean { + val p = minecraft.thePlayer ?: return false + return p.inventory.mainInventory.any { it?.getInternalId() == keyItem } + } + + @SubscribeEvent + fun onRewardChestClick(event: GuiClickEvent) { + val diffs = scrapeChestReward(event.slotIn ?: return) ?: return + val requiredKey = diffs.diff.find { + it.direction == ItemChange.ChangeDirection.LOST && kuudraKeyPattern.asPredicate().test(it.itemId.string) + }?.itemId + if (requiredKey != null && !hasKey(requiredKey)) { + return + } + log.logEntry(LedgerEntry( + TransactionType.KUUDRA_CHEST_OPEN, + diffs.timestamp, + diffs.diff, + )) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt new file mode 100644 index 0000000..60b06ae --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt @@ -0,0 +1,81 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.matches +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.BorderedTextTracker +import moe.nea.ledger.utils.di.Inject + +class MineshaftCorpseDetection : BorderedTextTracker() { + /* +[23:39:47] [Client thread/INFO]: [CHAT] §r§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§b§l§r§9§lLAPIS §r§b§lCORPSE LOOT! §r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a§lREWARDS§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§5+100 HOTM Experience§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a§r§aGreen Goblin Egg§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§9Enchanted Glacite §r§8x2§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§9☠ Fine Onyx Gemstone§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a☠ Flawed Onyx Gemstone §r§8x20§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a☘ Flawed Peridot Gemstone §r§8x40§r +[23:39:47] [Client thread/INFO]: [CHAT] §r §r§bGlacite Powder §r§8x500§r +[23:39:47] [Client thread/INFO]: [CHAT] §e[SkyHanni] Profit for §9Lapis Corpse§e: §678k§r +[23:39:47] [Client thread/INFO]: [CHAT] §r§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬§r + */ + + val corpseEnterMessage = " (?.*) CORPSE LOOT!".toPattern() + + override fun shouldEnter(event: ChatReceived): Boolean { + return corpseEnterMessage.matches(event.message) + } + + override fun shouldExit(event: ChatReceived): Boolean { + return genericBorderExit.matches(event.message) + } + + override fun onBorderedTextFinished(enclosed: List) { + val rewards = enclosed.asSequence() + .dropWhile { it.message != " REWARDS" } + .drop(1) + .mapNotNull { + itemIdProvider.findStackableItemByName(it.message, true) + } + .map { ItemChange.unpairGain(it) } + .toMutableList() + val introMessage = enclosed.first() + val corpseTyp = corpseEnterMessage.useMatcher(introMessage.message) { + group("corpseKind") + }!! + val keyTyp = corpseNameToKey[corpseTyp] + if (keyTyp == null) { + errorUtil.reportAdHoc("Unknown corpse type $corpseTyp") + } else if (keyTyp != ItemId.NIL) { + rewards.add(ItemChange.lose(keyTyp, 1)) + } + logger.logEntry( + LedgerEntry( + TransactionType.CORPSE_DESECRATED, + introMessage.timestamp, + rewards + ) + ) + } + + val corpseNameToKey = mapOf( + "LAPIS" to ItemId.NIL, + "VANGUARD" to ItemId("SKELETON_KEY"), + "UMBER" to ItemId("UMBER_KEY"), + "TUNGSTEN" to ItemId("TUNGSTEN_KEY"), + ) + + @Inject + lateinit var logger: LedgerLogger + + @Inject + lateinit var itemIdProvider: ItemIdProvider +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt new file mode 100644 index 0000000..6999c7f --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt @@ -0,0 +1,61 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ExpiringValue +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.ROMAN_NUMBER_PATTERN +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseRomanNumber +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant +import kotlin.time.Duration.Companion.seconds + +class MinionDetection @Inject constructor(val ledger: LedgerLogger) { + // §aYou received §r§6367,516.8 coins§r§a! + val hopperCollectPattern = "You received (?$SHORT_NUMBER_PATTERN) coins?!".toPattern() + val minionNamePattern = "(?.*) Minion (?$ROMAN_NUMBER_PATTERN)".toPattern() + + var lastOpenedMinion = ExpiringValue.empty() + + @SubscribeEvent + fun onBeforeClaim(event: BeforeGuiAction) { + val container = event.gui as? GuiChest ?: return + val inv = (container.inventorySlots as ContainerChest).lowerChestInventory + val invName = inv.displayName.unformattedText.unformattedString() + minionNamePattern.useMatcher(invName) { + val name = group("name") + val level = parseRomanNumber(group("level")) + lastOpenedMinion = ExpiringValue( + ItemId(name.uppercase().replace(" ", "_") + .replace("MINION", "GENERATOR") + "_" + level)) + } + } + + + @SubscribeEvent + fun onChat(event: ChatReceived) { + hopperCollectPattern.useMatcher(event.message) { + val minionName = lastOpenedMinion.consume(3.seconds) + ledger.logEntry(LedgerEntry( + TransactionType.AUTOMERCHANT_PROFIT_COLLECT, + Instant.now(), + listOf( + ItemChange.gainCoins(parseShortNumber(group("amount"))), + ItemChange(minionName ?: ItemId.NIL, 1.0, ItemChange.ChangeDirection.CATALYST) + ), + )) + } + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt new file mode 100644 index 0000000..95b8aa5 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt @@ -0,0 +1,111 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.asIterable +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.ExtraSupplyIdEvent +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.ErrorUtil +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class NpcDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val npcBuyPattern = + Pattern.compile("You bought (back )?(?.*?) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins?!") + val npcSellPattern = + Pattern.compile("You sold (?.*) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins?!") + + // You bought InfiniDirt™ Wand! + // You bought Prismapump x4! + val npcBuyWithItemPattern = + "You bought (?.*?)!".toPattern() + var storedPurchases = mutableMapOf>() + + @SubscribeEvent + fun onClick(event: BeforeGuiAction) { + (event.chestSlots?.lowerChestInventory?.asIterable() ?: listOf()) + .filterNotNull().forEach { + val name = it.getDisplayNameU().unformattedString() + val id = it.getInternalId() ?: return@forEach + val count = it.stackSize + val cost = ids.findCostItemsFromSpan(it.getLore()) + storedPurchases[name] = listOf(ItemChange.gain(id, count)) + cost.map { ItemChange.unpairLose(it) } + } + } + + @SubscribeEvent + fun addChocolate(event: ExtraSupplyIdEvent) { + event.store("Chocolate", ItemId("SKYBLOCK_CHOCOLATE")) + } + + @Inject + lateinit var errorUtil: ErrorUtil + + @SubscribeEvent + fun onBarteredItemBought(event: ChatReceived) { + npcBuyWithItemPattern.useMatcher(event.message) { + val changes = storedPurchases[group("what")] + if (changes == null) { + errorUtil.reportAdHoc("Item bought for items without associated cost") + } + storedPurchases.clear() + ledger.logEntry( + LedgerEntry( + TransactionType.NPC_BUY, + event.timestamp, + changes ?: listOf() + ) + ) + } + } + + @SubscribeEvent + fun onNpcBuy(event: ChatReceived) { + npcBuyPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.NPC_BUY, + event.timestamp, + listOf( + ItemChange.loseCoins( + parseShortNumber(group("coins")), + ), + ItemChange.gain( + ids.findForName(group("what")) ?: ItemId.NIL, + group("count")?.let(::parseShortNumber) ?: 1, + ) + ) + ) + ) + } + npcSellPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + TransactionType.NPC_SELL, + event.timestamp, + listOf( + ItemChange.gainCoins(parseShortNumber(group("coins"))), + ItemChange.lose( + ids.findForName(group("what")) ?: ItemId.NIL, + group("count")?.let(::parseShortNumber)?.toInt() ?: 1, + ) + ) + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt new file mode 100644 index 0000000..0d89ca1 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt @@ -0,0 +1,167 @@ +package moe.nea.ledger.modules + +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive +import moe.nea.ledger.DevUtil +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TriggerCommand +import moe.nea.ledger.config.LedgerConfig +import moe.nea.ledger.config.MainOptions +import moe.nea.ledger.events.RegistrationFinishedEvent +import moe.nea.ledger.events.TriggerEvent +import moe.nea.ledger.gen.BuildConfig +import moe.nea.ledger.utils.ErrorUtil +import moe.nea.ledger.utils.MinecraftExecutor +import moe.nea.ledger.utils.di.Inject +import moe.nea.ledger.utils.network.RequestUtil +import moe.nea.libautoupdate.CurrentVersion +import moe.nea.libautoupdate.GithubReleaseUpdateData +import moe.nea.libautoupdate.GithubReleaseUpdateSource +import moe.nea.libautoupdate.PotentialUpdate +import moe.nea.libautoupdate.UpdateContext +import moe.nea.libautoupdate.UpdateData +import moe.nea.libautoupdate.UpdateTarget +import moe.nea.libautoupdate.UpdateUtils +import net.minecraft.util.ChatComponentText +import net.minecraft.util.ChatStyle +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.concurrent.CompletableFuture + +class UpdateChecker @Inject constructor( + val errorUtil: ErrorUtil, + val requestUtil: RequestUtil, +) { + + @Inject + lateinit var minecraftExecutor: MinecraftExecutor + + val updater = UpdateContext( + NightlyAwareGithubUpdateSource("nea89o", "LocalTransactionLedger"), + if (DevUtil.isDevEnv) UpdateTarget { listOf() } + else UpdateTarget.deleteAndSaveInTheSameFolder(UpdateChecker::class.java), + object : CurrentVersion { + override fun display(): String { + return BuildConfig.VERSION + } + + override fun isOlderThan(element: JsonElement?): Boolean { + if (element !is JsonPrimitive || !element.isString) return true + val newHash = element.asString // TODO: change once i support non nightly update streams + val length = minOf(newHash.length, BuildConfig.GIT_COMMIT.length) + if (newHash.substring(0, length).equals(BuildConfig.GIT_COMMIT.substring(0, length), ignoreCase = true)) + return false + return true + } + + + override fun toString(): String { + return "{gitversion:${BuildConfig.GIT_COMMIT}, version:${BuildConfig.FULL_VERSION}}" + } + }, + "ledger" + ) + + class NightlyAwareGithubUpdateSource(owner: String, repository: String) : + GithubReleaseUpdateSource(owner, repository) { + override fun selectUpdate(updateStream: String, releases: List): UpdateData? { + if (updateStream == "nightly") { + return findAsset(releases.find { it.tagName == "nightly" }) + } + return super.selectUpdate(updateStream, releases.filter { it.tagName != "nightly" }) + } + + val releaseRegex = "commit: `(?[a-f0-9]+)`".toPattern() + + override fun findAsset(release: GithubRelease?): UpdateData? { + val update = super.findAsset(release) as GithubReleaseUpdateData? ?: return null + return GithubReleaseUpdateData( + update.versionName, + releaseRegex.matcher(update.releaseDescription) + .takeIf { it.find() } + ?.run { group("hash") } + ?.let(::JsonPrimitive) + ?: update.versionNumber, + update.sha256, + update.download, + update.releaseDescription, + update.targetCommittish, + update.createdAt, + update.publishedAt, + update.htmlUrl + ) + } + } + + init { + UpdateUtils.patchConnection { + this.requestUtil.enhanceConnection(it) + } + } + + var latestUpdate: PotentialUpdate? = null + var hasNotified = false + + @SubscribeEvent + fun onStartup(event: RegistrationFinishedEvent) { + if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.NONE) return + launchUpdateCheck() + } + + fun launchUpdateCheck() { + errorUtil.listenToFuture( + updater.checkUpdate("nightly") + .thenAcceptAsync( + { + latestUpdate = it + informAboutUpdates(it) + }, minecraftExecutor) + ) + } + + @Inject + lateinit var config: LedgerConfig + + @Inject + lateinit var triggerCommand: TriggerCommand + + val installTrigger = "execute-download" + + @Inject + lateinit var logger: LedgerLogger + fun informAboutUpdates(potentialUpdate: PotentialUpdate) { + if (hasNotified) return + hasNotified = true + if (!potentialUpdate.isUpdateAvailable) return + logger.printOut( + ChatComponentText("§aThere is a new update for LocalTransactionLedger. Click here to automatically download and install it.") + .setChatStyle(ChatStyle().setChatClickEvent(triggerCommand.getTriggerCommandLine(installTrigger)))) + if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.FULL) { + downloadUpdate() + } + } + + var updateFuture: CompletableFuture? = null + + fun downloadUpdate() { + val l = latestUpdate ?: return + if (updateFuture != null) return + // TODO: inject into findAsset to overwrite the tag id with the commit id + logger.printOut("§aTrying to download ledger update ${l.update.versionName}") + updateFuture = + latestUpdate?.launchUpdate() + ?.thenAcceptAsync( + { + logger.printOut("§aLedger update downloaded. It will automatically apply after your next restart.") + }, minecraftExecutor) + ?.let(errorUtil::listenToFuture) + } + + @SubscribeEvent + fun onTrigger(event: TriggerEvent) { + if (event.action == installTrigger) { + event.isCanceled = true + downloadUpdate() + } + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt new file mode 100644 index 0000000..f457ae4 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt @@ -0,0 +1,87 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ExtraSupplyIdEvent +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class VisitorDetection { + @Inject + lateinit var logger: LedgerLogger + + @Inject + lateinit var idProvider: ItemIdProvider + + @SubscribeEvent + fun parseFromItem(event: GuiClickEvent) { + val stack = event.slotIn?.stack ?: return + + val displayName = stack.getDisplayNameU() + if (displayName != "§aAccept Offer") return + val lore = stack.getLore() + if (!lore.contains("§eClick to give!")) return + + val rewards = lore + .asSequence() + .dropWhile { it != "§7Rewards:" }.drop(1) + .takeWhile { it != "" } + .mapNotNull { parseGardenLoreLine(it) } + .map { ItemChange.gain(it.first, it.second) } + .toList() + + val cost = lore + .asSequence() + .dropWhile { it != "§7Items Required:" }.drop(1) + .takeWhile { it != "" } + .mapNotNull { parseGardenLoreLine(it) } + .map { ItemChange.lose(it.first, it.second) } + .toList() + + logger.logEntry(LedgerEntry( + TransactionType.VISITOR_BARGAIN, + Instant.now(), + cost + rewards + )) + } + + private fun parseGardenLoreLine(rewardLine: String): Pair? { + val f = rewardLine.unformattedString().trim() + return idProvider.findStackableItemByName(f, true) + } + + + @SubscribeEvent + fun supplyNames(event: ExtraSupplyIdEvent) { + event.store("Carrot", ItemId("CARROT_ITEM")) + event.store("Potato", ItemId("POTATO_ITEM")) + event.store("Jacob's Ticket", ItemId("JACOBS_TICKET")) + event.store("Cocoa Beans", ItemId("INK_SACK:3")) + event.store("Enchanted Cocoa Beans", ItemId("ENCHANTED_COCOA")) + event.store("Enchanted Red Mushroom Block", ItemId("ENCHANTED_HUGE_MUSHROOM_2")) + event.store("Enchanted Brown Mushroom Block", ItemId("ENCHANTED_HUGE_MUSHROOM_1")) + event.store("Nether Wart", ItemId("NETHER_STALK")) + event.store("Enchanted Nether Wart", ItemId("ENCHANTED_NETHER_STALK")) + event.store("Mutant Nether Wart", ItemId("MUTANT_NETHER_STALK")) + event.store("Jack o' Lantern", ItemId("JACK_O_LANTERN")) + event.store("Cactus Green", ItemId("INK_SACK:2")) + event.store("Hay Bale", ItemId("HAY_BLOCK")) + event.store("Rabbit's Foot", ItemId("RABBIT_FOOT")) + event.store("Raw Porkchop", ItemId("PORK")) + event.store("Raw Rabbit", ItemId("RABBIT")) + event.store("White Wool", ItemId("WOOL")) + event.store("Copper Dye", ItemId("DYE_COPPER")) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt new file mode 100644 index 0000000..9e621e8 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt @@ -0,0 +1,41 @@ +package moe.nea.ledger.utils + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +abstract class BorderedTextTracker { + + val genericBorderExit = "▬{10,}".toPattern() + + @Inject + lateinit var errorUtil: ErrorUtil + var stack: MutableList? = null + + + @SubscribeEvent + fun receiveText(event: ChatReceived) { + if (stack != null && shouldExit(event)) { + exit() + return + } + if (shouldEnter(event)) { + if (stack != null) { + errorUtil.reportAdHoc("Double entered a bordered message") + exit() + } + stack = mutableListOf() + } + stack?.add(event) + } + + private fun exit() { + onBorderedTextFinished(stack!!) + stack = null + } + + abstract fun shouldEnter(event: ChatReceived): Boolean + abstract fun shouldExit(event: ChatReceived): Boolean + abstract fun onBorderedTextFinished(enclosed: List) + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt new file mode 100644 index 0000000..e0c83f9 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt @@ -0,0 +1,52 @@ +package moe.nea.ledger.utils + +import moe.nea.ledger.utils.di.Inject +import moe.nea.ledger.utils.telemetry.ContextValue +import moe.nea.ledger.utils.telemetry.EventRecorder +import moe.nea.ledger.utils.telemetry.Span +import java.util.concurrent.CompletableFuture + +class ErrorUtil { + + @Inject + lateinit var reporter: EventRecorder + + fun reportAdHoc(message: String) { + report(Exception(message), message) + + } + + fun report(exception: Throwable, message: String?) { + Span.current().recordException(reporter, exception, message) + } + + fun Result.getOrReport(): T? { + val exc = exceptionOrNull() + if (exc != null) { + report(exc, null) + } + return getOrNull() + } + + fun > listenToFuture(t: T): T { + t.handle { ignored, exception -> + if (exception != null) + report(exception, "Uncaught exception in completable future") + } + return t + } + + inline fun catch( + vararg pairs: Pair, + crossinline function: () -> T + ): T? { + return Span.current().enterWith(*pairs) { + try { + return@enterWith function() + } catch (ex: Exception) { + report(ex, null) + return@enterWith null + } + } + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt new file mode 100644 index 0000000..d3c1f6e --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt @@ -0,0 +1,10 @@ +package moe.nea.ledger.utils + +import com.google.gson.reflect.TypeToken + +object GsonUtil { + inline fun typeToken(): TypeToken { + return object : TypeToken() {} + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt new file mode 100644 index 0000000..affd86c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt @@ -0,0 +1,10 @@ +package moe.nea.ledger.utils + +import net.minecraft.client.Minecraft +import java.util.concurrent.Executor + +class MinecraftExecutor : Executor { + override fun execute(command: Runnable) { + Minecraft.getMinecraft().addScheduledTask(command) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt new file mode 100644 index 0000000..9b0e7a3 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt @@ -0,0 +1,4 @@ +package moe.nea.ledger.utils + +@Retention(AnnotationRetention.BINARY) +annotation class NoSideEffects diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt new file mode 100644 index 0000000..ddf2fcc --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt @@ -0,0 +1,40 @@ +package moe.nea.ledger.utils.network + +import com.google.gson.JsonElement +import java.net.URL + +data class Request( + val url: URL, + val method: Method, + val body: String?, + val headers: Map, +) { + enum class Method { + GET, POST + } + + enum class MediaType(val text: String) { + JSON("application/json"), + TEXT("text/plain"), + HTML("text/html"), + ANY("*/*"), + } + + fun withHeaders(map: Map): Request { + // TODO: enforce caselessness? + return this.copy(headers = headers + map) + } + + fun post() = copy(method = Method.POST) + fun get() = copy(method = Method.GET) + + fun json(element: JsonElement) = copy( + headers = headers + mapOf("content-type" to "application/json"), + body = element.toString()) + + fun accept(request: MediaType) = withHeaders(mapOf("accept" to request.text)) + + fun acceptJson() = accept(MediaType.JSON) + + fun execute(requestUtil: RequestUtil) = requestUtil.executeRequest(this) +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt new file mode 100644 index 0000000..a49c65a --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt @@ -0,0 +1,63 @@ +package moe.nea.ledger.utils.network + +import moe.nea.ledger.utils.ErrorUtil +import moe.nea.ledger.utils.di.Inject +import java.net.URL +import java.net.URLConnection +import java.security.KeyStore +import java.util.zip.GZIPInputStream +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManagerFactory + +class RequestUtil @Inject constructor(val errorUtil: ErrorUtil) { + + private fun createSSLContext(): SSLContext? = errorUtil.catch { + val keyStorePath = RequestUtil::class.java.getResourceAsStream("/ledgerkeystore.jks") + ?: error("Could not locate keystore") + val keyStore = KeyStore.getInstance("JKS") + keyStore.load(keyStorePath, "neuneu".toCharArray()) + val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + kmf.init(keyStore, null) + tmf.init(keyStore) + val ctx = SSLContext.getInstance("TLS") + ctx.init(kmf.keyManagers, tmf.trustManagers, null) + return@catch ctx + } + + val sslContext = createSSLContext() + + fun enhanceConnection(connection: URLConnection) { + if (connection is HttpsURLConnection && sslContext != null) { + connection.sslSocketFactory = sslContext.socketFactory + } + } + + fun createRequest(url: String) = createRequest(URL(url)) + fun createRequest(url: URL) = Request(url, Request.Method.GET, null, mapOf()) + + fun executeRequest(request: Request): Response { + val connection = request.url.openConnection() + enhanceConnection(connection) + connection.setRequestProperty("accept-encoding", "gzip") + request.headers.forEach { (k, v) -> + connection.setRequestProperty(k, v) + } + if (request.body != null) { + connection.getOutputStream().write(request.body.encodeToByteArray()) + connection.getOutputStream().close() + } + var stream = connection.getInputStream() + if (connection.contentEncoding == "gzip") { + stream = GZIPInputStream(stream) + } + val text = stream.bufferedReader().readText() + stream.close() + // Do NOT call connection.disconnect() to allow for connection reuse + return Response(request, text, connection.headerFields) + } + + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt new file mode 100644 index 0000000..daae7f7 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt @@ -0,0 +1,19 @@ +package moe.nea.ledger.utils.network + +import com.google.gson.reflect.TypeToken +import moe.nea.ledger.Ledger + +data class Response( + val source: Request, + // TODO: allow other body processors, to avoid loading everything as strings + val response: String, + val headers: Map>, +) { + fun json(typ: TypeToken): T { + return Ledger.gson.fromJson(response, typ.type) + } + + fun json(clazz: Class): T { + return Ledger.gson.fromJson(response, clazz) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt new file mode 100644 index 0000000..5f4ccdf --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt @@ -0,0 +1,10 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive + +class BooleanContext(val boolean: Boolean) : ContextValue { + override fun serialize(): JsonElement { + return JsonPrimitive(boolean) + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt new file mode 100644 index 0000000..004ae9c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt @@ -0,0 +1,9 @@ +package moe.nea.ledger.utils.telemetry + +object CommonKeys { + val EVENT_MESSAGE = "event_message" + val EXCEPTION = "event_exception" + val COMMIT_VERSION = "version_commit" + val VERSION = "version" + val PHASE = "phase" // TODO: add a sort of "manual" stacktrace with designated function phases +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt new file mode 100644 index 0000000..3c30a52 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt @@ -0,0 +1,57 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonObject + +class Context(val data: MutableMap = mutableMapOf()) : ContextValue.Collatable { + + inline fun getT(key: String): T? { + return get(key) as? T + } + + fun get(key: String): ContextValue? { + return data[key] + } + + fun add(key: String, value: ContextValue) { + data[key] = value + } + + @Suppress("NOTHING_TO_INLINE") + private inline fun > cope( + left: ContextValue.Collatable, + right: ContextValue + ): ContextValue { + return try { + left.combineWith(right as T) + } catch (ex: Exception) { + // TODO: cope with this better + right + } + } + + override fun combineWith(overrides: Context): Context { + val copy = data.toMutableMap() + for ((key, overrideValue) in overrides.data) { + copy.merge(key, overrideValue) { old, new -> + if (old is ContextValue.Collatable<*>) { + cope(old, new) + } else { + new + } + } + } + return Context(copy) + } + + override fun actualize(): Context { + return this + } + + override fun serialize(): JsonObject { + val obj = JsonObject() + data.forEach { (k, v) -> + obj.add(k, v.serialize()) + } + return obj + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt new file mode 100644 index 0000000..b5891fc --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt @@ -0,0 +1,70 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonElement +import com.google.gson.JsonObject + +interface ContextValue { + companion object { + fun > lazyCollatable(value: () -> Collatable): Collatable { + return LazyCollatable(value) + } + + fun lazy(value: () -> ContextValue): ContextValue { + return object : ContextValue { + val value by kotlin.lazy(value) + override fun serialize(): JsonElement { + return this.value.serialize() + } + } + } + + fun bool(boolean: Boolean): ContextValue { + return BooleanContext(boolean) + } + + fun string(message: String): ContextValue { + return StringContext(message) + } + + fun jsonObject(vararg pairs: Pair): ContextValue { + val obj = JsonObject() + for ((l, r) in pairs) { + obj.add(l, r) + } + return JsonElementContext(obj) + } + + fun compound(vararg pairs: Pair): ContextValue { + val obj = JsonObject() + for ((l, r) in pairs) { + obj.addProperty(l, r) + } + // TODO: should this be its own class? + return JsonElementContext(obj) + } + } + + // TODO: allow other serialization formats + fun serialize(): JsonElement + interface Collatable> : ContextValue { + fun combineWith(overrides: T): T + fun actualize(): T + } + + private class LazyCollatable>( + provider: () -> Collatable, + ) : Collatable { + val value by kotlin.lazy(provider) + override fun actualize(): T { + return value.actualize() + } + + override fun combineWith(overrides: T): T { + return value.combineWith(overrides) + } + + override fun serialize(): JsonElement { + return value.serialize() + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt new file mode 100644 index 0000000..28b1ab5 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt @@ -0,0 +1,9 @@ +package moe.nea.ledger.utils.telemetry + +interface EventRecorder { + companion object { + var instance: EventRecorder? = null + } + + fun record(event: RecordedEvent) +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt new file mode 100644 index 0000000..96b70ec --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt @@ -0,0 +1,39 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject + +class ExceptionContextValue(val exception: Throwable) : ContextValue { + val stackTrace by lazy { + exception.stackTraceToString() + } + + override fun serialize(): JsonElement { + val jsonObject = JsonObject() + jsonObject.addProperty("exception_stackTrace", stackTrace) + jsonObject.add("exception_structure", walkExceptions(exception, 6)) + return jsonObject + } + + private fun walkExceptions(exception: Throwable, searchDepth: Int): JsonElement { + val obj = JsonObject() + obj.addProperty("class", exception.javaClass.name) + obj.addProperty("message", exception.message) + // TODO: allow exceptions to implement an "extra info" interface + if (searchDepth > 0) { + val cause = exception.cause + if (cause != null && cause !== exception) { + obj.add("cause", walkExceptions(cause, searchDepth - 1)) + } + val suppressions = JsonArray() + for (suppressedException in exception.suppressedExceptions) { + suppressions.add(walkExceptions(suppressedException, searchDepth - 1)) + } + if (suppressions.size() > 0) { + obj.add("suppressions", suppressions) + } + } + return obj + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt new file mode 100644 index 0000000..1601f56 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt @@ -0,0 +1,9 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonElement + +class JsonElementContext(val element: JsonElement) : ContextValue { + override fun serialize(): JsonElement { + return element + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt new file mode 100644 index 0000000..82a76ed --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt @@ -0,0 +1,25 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.GsonBuilder +import org.apache.logging.log4j.Logger + +class LoggingEventRecorder( + val logger: Logger, + val logJson: Boolean +) : EventRecorder { + companion object { + private val gson = GsonBuilder().setPrettyPrinting().create() + } + + override fun record(event: RecordedEvent) { + val exc = event.context.getT(CommonKeys.EXCEPTION) + var message = "Event Recorded: " + event.context.getT(CommonKeys.EVENT_MESSAGE)?.message + if (logJson) { + message += "\n" + gson.toJson(event.context.serialize()) + } + if (exc != null) + logger.error(message, exc.exception) + else + logger.warn(message) + } +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt new file mode 100644 index 0000000..346417d --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt @@ -0,0 +1,5 @@ +package moe.nea.ledger.utils.telemetry + +class RecordedEvent(val context: Context) { + +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt new file mode 100644 index 0000000..e9a3b79 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt @@ -0,0 +1,8 @@ +package moe.nea.ledger.utils.telemetry + +enum class Severity { + INFO, + WARN, + ERROR, + CRITICAL, +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt new file mode 100644 index 0000000..0d680a9 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt @@ -0,0 +1,146 @@ +package moe.nea.ledger.utils.telemetry + +class Span(val parent: Span?) : AutoCloseable { + companion object { + private val _current = object : InheritableThreadLocal() { + override fun initialValue(): Span { + return Span(null) + } + + override fun childValue(parentValue: Span?): Span { + return parentValue?.forkNewRoot() ?: initialValue() + } + } + + fun current(): Span { + return _current.get() + } + } + + private val data = Context() + + // TODO : replace string key with a SpanKey class + fun add(key: String, value: ContextValue) { + data.add(key, value) + } + + /** + * Create a sub span, and [enter] it, with the given values. + */ + fun enterWith(vararg pairs: Pair, block: Span.() -> T): T { + return enter().use { span -> + pairs.forEach { (k, value) -> + span.add(k, value) + } + block(span) + } + } + + /** + * Create a sub span, to attach some additional context, without modifying the [current] at all. + */ + fun forkWith(vararg pairs: Pair): Span { + val newSpan = fork() + for ((key, value) in pairs) { + if (value == null) continue + newSpan.add(key, value) + } + return newSpan + } + + /** + * Create a sub span, to which additional context can be added. This context will receive updates from its parent, + * and will be set as the [current]. To return to the parent, either call [exit] on the child. Or use inside of a + * [use] block. + */ + fun enter(): Span { + require(_current.get() == this) + return fork().enterSelf() + } + + /** + * Force [enter] this span, without creating a subspan. This bypasses checks like parent / child being the [current]. + */ + fun enterSelf(): Span { + _current.set(this) + return this + } + + /** + * Creates a temporary sub span, to which additional context can be added. This context will receive updates from + * its parent, but will not be set as the [current]. + */ + fun fork(): Span { + return Span(this) + } + + /** + * Create a new root span, that will not receive any updates from the current span, but will have all the same + * context keys associated. + */ + fun forkNewRoot(): Span { + val newRoot = Span(null) + newRoot.data.data.putAll(collectContext().data) + return newRoot + } + + /** + * Collect the context, including all parent context + */ + fun collectContext(): Context { + if (parent != null) + return data.combineWith(parent.collectContext()) + return data + } + + /** + * Exit an [entered][enter] span, returning back to the parent context, and discard any current keys. + */ + fun exit() { + require(parent != null) + require(_current.get() == this) + _current.set(parent) + } + + /** + * [AutoCloseable] implementation for [exit] + */ + override fun close() { + return exit() + } + + /** + * Record an empty event given the context. This indicates nothing except for "I was here". + * @see recordMessageEvent + * @see recordException + */ + fun recordEmptyTrace(recorder: EventRecorder) { + recorder.record(RecordedEvent(collectContext())) + } + + /** + * Record a message with the key `"event_message"` to the recorder + */ + fun recordMessageEvent( + recorder: EventRecorder, + message: String + ) { + forkWith(CommonKeys.EVENT_MESSAGE to ContextValue.string(message)) + .recordEmptyTrace(recorder) + } + + /** + * Record an exception to the recorder + */ + fun recordException( + recorder: EventRecorder, + exception: Throwable, + message: String? = null + ) { + forkWith( + CommonKeys.EVENT_MESSAGE to message?.let(ContextValue::string), + CommonKeys.EXCEPTION to ExceptionContextValue(exception), + ).recordEmptyTrace(recorder) + } + +} \ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt new file mode 100644 index 0000000..2d33075 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt @@ -0,0 +1,11 @@ +package moe.nea.ledger.utils.telemetry + +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive + +class StringContext(val message: String) : ContextValue { + override fun serialize(): JsonElement { + return JsonPrimitive(message) + } + +} diff --git a/mod/src/main/resources/ledgerkeystore.jks b/mod/src/main/resources/ledgerkeystore.jks new file mode 100644 index 0000000..b71185a Binary files /dev/null and b/mod/src/main/resources/ledgerkeystore.jks differ diff --git a/mod/src/main/resources/mcmod.info b/mod/src/main/resources/mcmod.info new file mode 100644 index 0000000..fdeffd8 --- /dev/null +++ b/mod/src/main/resources/mcmod.info @@ -0,0 +1,18 @@ +[ + { + "modid": "${modid}", + "name": "Simple Ledger Mod", + "description": "Export all your activities into a file", + "version": "${version}", + "mcversion": "${mcversion}", + "url": "https://github.com/nea89o/LocalTransactionLedger/", + "updateUrl": "", + "authorList": [ + "nea89" + ], + "credits": "", + "logoFile": "", + "screenshots": [], + "dependencies": [] + } +] \ No newline at end of file diff --git a/mod/src/main/resources/mixins.moneyledger.json b/mod/src/main/resources/mixins.moneyledger.json new file mode 100644 index 0000000..fa6482e --- /dev/null +++ b/mod/src/main/resources/mixins.moneyledger.json @@ -0,0 +1,7 @@ +{ + "package": "${basePackage}.mixin", + "plugin": "${basePackage}.init.AutoDiscoveryMixinPlugin", + "minVersion": "0.7", + "compatibilityLevel": "JAVA_8", + "__comment": "You do not need to manually register mixins in this template. Check the auto discovery mixin plugin for more info." +} diff --git a/mod/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt b/mod/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt new file mode 100644 index 0000000..4068a42 --- /dev/null +++ b/mod/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt @@ -0,0 +1,17 @@ +package moe.nea.ledger + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class NumberUtilKtTest { + @Test + fun parseRomanNumberTest() { + assertEquals(4, parseRomanNumber("IV")) + assertEquals(1, parseRomanNumber("I")) + assertEquals(14, parseRomanNumber("XIV")) + assertEquals(3, parseRomanNumber("III")) + assertEquals(8, parseRomanNumber("IIX")) + assertEquals(500, parseRomanNumber("DM")) + assertEquals(2024, parseRomanNumber("MMXXIV")) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index ea25ef5..0390203 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,4 +29,5 @@ rootProject.name = "ledger" include("dependency-injection") include("database:core") include("basetypes") +include("mod") includeBuild("build-src") diff --git a/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java b/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java deleted file mode 100644 index 56841b5..0000000 --- a/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java +++ /dev/null @@ -1,193 +0,0 @@ -package moe.nea.ledger.init; - -import net.minecraft.launchwrapper.Launch; -import org.spongepowered.asm.lib.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * A mixin plugin to automatically discover all mixins in the current JAR. - *

- * This mixin plugin automatically scans your entire JAR (or class directory, in case of an in-IDE launch) for classes inside of your - * mixin package and registers those. It does this recursively for sub packages of the mixin package as well. This means you will need - * to only have mixin classes inside of your mixin package, which is good style anyway. - * - * @author Linnea Gräf - */ -public class AutoDiscoveryMixinPlugin implements IMixinConfigPlugin { - private static final List mixinPlugins = new ArrayList<>(); - - public static List getMixinPlugins() { - return mixinPlugins; - } - - private String mixinPackage; - - @Override - public void onLoad(String mixinPackage) { - this.mixinPackage = mixinPackage; - mixinPlugins.add(this); - } - - /** - * Resolves the base class root for a given class URL. This resolves either the JAR root, or the class file root. - * In either case the return value of this + the class name will resolve back to the original class url, or to other - * class urls for other classes. - */ - public URL getBaseUrlForClassUrl(URL classUrl) { - String string = classUrl.toString(); - if (classUrl.getProtocol().equals("jar")) { - try { - return new URL(string.substring(4).split("!")[0]); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - if (string.endsWith(".class")) { - try { - return new URL(string.replace("\\", "/") - .replace(getClass().getCanonicalName() - .replace(".", "/") + ".class", "")); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - return classUrl; - } - - /** - * Get the package that contains all the mixins. This value is set by mixin itself using {@link #onLoad}. - */ - public String getMixinPackage() { - return mixinPackage; - } - - /** - * Get the path inside the class root to the mixin package - */ - public String getMixinBaseDir() { - return mixinPackage.replace(".", "/"); - } - - /** - * A list of all discovered mixins. - */ - private List mixins = null; - - /** - * Try to add mixin class ot the mixins based on the filepath inside of the class root. - * Removes the {@code .class} file suffix, as well as the base mixin package. - *

This method cannot be called after mixin initialization.

- * - * @param className the name or path of a class to be registered as a mixin. - */ - public void tryAddMixinClass(String className) { - String norm = (className.endsWith(".class") ? className.substring(0, className.length() - ".class".length()) : className) - .replace("\\", "/") - .replace("/", "."); - if (norm.startsWith(getMixinPackage() + ".") && !norm.endsWith(".")) { - mixins.add(norm.substring(getMixinPackage().length() + 1)); - } - } - - /** - * Search through the JAR or class directory to find mixins contained in {@link #getMixinPackage()} - */ - @Override - public List getMixins() { - if (mixins != null) return mixins; - System.out.println("Trying to discover mixins"); - mixins = new ArrayList<>(); - URL classUrl = getClass().getProtectionDomain().getCodeSource().getLocation(); - System.out.println("Found classes at " + classUrl); - Path file; - try { - file = Paths.get(getBaseUrlForClassUrl(classUrl).toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - System.out.println("Base directory found at " + file); - if (Files.isDirectory(file)) { - walkDir(file); - } else { - walkJar(file); - } - System.out.println("Found mixins: " + mixins); - - if (!(Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) { - mixins.removeIf(it -> it.contains("devenv")); - } - - return mixins; - } - - /** - * Search through directory for mixin classes based on {@link #getMixinBaseDir}. - * - * @param classRoot The root directory in which classes are stored for the default package. - */ - private void walkDir(Path classRoot) { - System.out.println("Trying to find mixins from directory"); - try (Stream classes = Files.walk(classRoot.resolve(getMixinBaseDir()))) { - classes.map(it -> classRoot.relativize(it).toString()) - .forEach(this::tryAddMixinClass); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Read through a JAR file, trying to find all mixins inside. - */ - private void walkJar(Path file) { - System.out.println("Trying to find mixins from jar file"); - try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(file))) { - ZipEntry next; - while ((next = zis.getNextEntry()) != null) { - tryAddMixinClass(next.getName()); - zis.closeEntry(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return true; - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } -} diff --git a/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java b/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java deleted file mode 100644 index 52b8911..0000000 --- a/src/main/java/moe/nea/ledger/mixin/AccessorGuiEditSign.java +++ /dev/null @@ -1,12 +0,0 @@ -package moe.nea.ledger.mixin; - -import net.minecraft.client.gui.inventory.GuiEditSign; -import net.minecraft.tileentity.TileEntitySign; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(GuiEditSign.class) -public interface AccessorGuiEditSign { - @Accessor("tileSign") - TileEntitySign getTileEntity_ledger(); -} diff --git a/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java b/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java deleted file mode 100644 index 4e6e360..0000000 --- a/src/main/java/moe/nea/ledger/mixin/MouseClickEventPatch.java +++ /dev/null @@ -1,18 +0,0 @@ -package moe.nea.ledger.mixin; - -import moe.nea.ledger.events.GuiClickEvent; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.Slot; -import net.minecraftforge.common.MinecraftForge; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(GuiContainer.class) -public class MouseClickEventPatch { - @Inject(method = "handleMouseClick", at = @At("HEAD")) - private void onHandleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType, CallbackInfo ci) { - MinecraftForge.EVENT_BUS.post(new GuiClickEvent(slotIn, slotId, clickedButton, clickType)); - } -} diff --git a/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java b/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java deleted file mode 100644 index fc9afb7..0000000 --- a/src/main/java/moe/nea/ledger/mixin/OnInitializationCompletePatch.java +++ /dev/null @@ -1,18 +0,0 @@ -package moe.nea.ledger.mixin; - -import moe.nea.ledger.events.InitializationComplete; -import net.minecraft.client.Minecraft; -import net.minecraftforge.common.MinecraftForge; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Minecraft.class) -public class OnInitializationCompletePatch { - - @Inject(method = "startGame", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/FMLClientHandler;onInitializationComplete()V")) - private void onInitComplete(CallbackInfo ci) { - MinecraftForge.EVENT_BUS.post(new InitializationComplete()); - } -} diff --git a/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java b/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java deleted file mode 100644 index 88e8364..0000000 --- a/src/main/java/moe/nea/ledger/mixin/devenv/RegisterModResourcesPatch.java +++ /dev/null @@ -1,66 +0,0 @@ -package moe.nea.ledger.mixin.devenv; - -import com.google.common.eventbus.EventBus; -import net.minecraftforge.fml.client.FMLFileResourcePack; -import net.minecraftforge.fml.common.DummyModContainer; -import net.minecraftforge.fml.common.LoadController; -import net.minecraftforge.fml.common.ModContainer; -import net.minecraftforge.fml.common.ModMetadata; -import net.minecraftforge.fml.common.discovery.ASMDataTable; -import net.minecraftforge.fml.common.discovery.ContainerType; -import net.minecraftforge.fml.common.discovery.ModCandidate; -import net.minecraftforge.fml.common.discovery.ModDiscoverer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -@Mixin(value = ModDiscoverer.class, remap = false) -public class RegisterModResourcesPatch { - @Shadow - private List candidates; - - @Inject(method = "identifyMods", at = @At("HEAD"), remap = false) - private void addCandidate(CallbackInfoReturnable> cir) { - String bonusResourceMod = System.getProperty("ledger.bonusresourcemod"); - if (bonusResourceMod == null) return; - File file = new File(bonusResourceMod); - if (!file.isDirectory()) return; - ModMetadata modMetadata = new ModMetadata(); - modMetadata.modId = "ledger-bonus"; - modMetadata.name = "Ledger Bonus Resources"; - modMetadata.autogenerated = true; - ModContainer container = new DummyModContainer(modMetadata) { - @Override - public Object getMod() { - return new Object(); - } - - @Override - public boolean registerBus(EventBus bus, LoadController controller) { - return true; - } - - @Override - public File getSource() { - return file; - } - - @Override - public Class getCustomResourcePackClass() { - return FMLFileResourcePack.class; - } - }; - candidates.add(new ModCandidate(file, file, ContainerType.DIR) { - @Override - public List explore(ASMDataTable table) { - return Collections.singletonList(container); - } - }); - } -} diff --git a/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt b/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt deleted file mode 100644 index 5b964c8..0000000 --- a/src/main/kotlin/moe/nea/ledger/ConfigCommand.kt +++ /dev/null @@ -1,31 +0,0 @@ -package moe.nea.ledger - -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender - -class ConfigCommand : CommandBase() { - override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { - return true - } - - override fun getCommandName(): String { - return "ledgerconfig" - } - - override fun getCommandUsage(sender: ICommandSender?): String { - return "" - } - - override fun processCommand(sender: ICommandSender?, args: Array) { - val editor = Ledger.managedConfig.getEditor() - editor.search(args.joinToString(" ")) - Ledger.runLater { - IMinecraft.instance.openWrappedScreen(editor) - } - } - - override fun getCommandAliases(): List { - return listOf("moneyledger") - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt b/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt deleted file mode 100644 index 66fba8d..0000000 --- a/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt +++ /dev/null @@ -1,38 +0,0 @@ -package moe.nea.ledger - -import kotlin.time.Duration -import kotlin.time.Duration.Companion.nanoseconds -import kotlin.time.Duration.Companion.seconds - -class DebouncedValue(private val value: T) { - companion object { - fun farFuture(): DebouncedValue { - val value = DebouncedValue(Unit) - value.take() - @Suppress("UNCHECKED_CAST") - return value as DebouncedValue - } - } - - val lastSeenAt = System.nanoTime() - val age get() = (System.nanoTime() - lastSeenAt).nanoseconds - var taken = false - private set - - fun get(debounce: Duration): T? { - return if (!taken && age >= debounce) value - else null - } - - fun replace(): T? { - return consume(0.seconds) - } - - fun consume(debounce: Duration): T? { - return get(debounce)?.also { take() } - } - - fun take() { - taken = true - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt b/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt deleted file mode 100644 index bab0a78..0000000 --- a/src/main/kotlin/moe/nea/ledger/DebugDataCommand.kt +++ /dev/null @@ -1,34 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.events.SupplyDebugInfo -import moe.nea.ledger.utils.di.Inject -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraftforge.common.MinecraftForge - -class DebugDataCommand : CommandBase() { - - override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { - return true - } - - override fun getCommandName(): String { - return "ledgerdebug" - } - - override fun getCommandUsage(sender: ICommandSender?): String { - return "" - } - - @Inject - lateinit var logger: LedgerLogger - - override fun processCommand(sender: ICommandSender?, args: Array?) { - val debugInfo = SupplyDebugInfo() - MinecraftForge.EVENT_BUS.post(debugInfo) - logger.printOut("Collected debug info:") - debugInfo.data.forEach { - logger.printOut("${it.first}: ${it.second}") - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/DevUtil.kt b/src/main/kotlin/moe/nea/ledger/DevUtil.kt deleted file mode 100644 index d0dd653..0000000 --- a/src/main/kotlin/moe/nea/ledger/DevUtil.kt +++ /dev/null @@ -1,7 +0,0 @@ -package moe.nea.ledger - -import net.minecraft.launchwrapper.Launch - -object DevUtil { - val isDevEnv = Launch.blackboard["fml.deobfuscatedEnvironment"] as Boolean -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt b/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt deleted file mode 100644 index b50b14e..0000000 --- a/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt +++ /dev/null @@ -1,30 +0,0 @@ -package moe.nea.ledger - -import kotlin.time.Duration -import kotlin.time.Duration.Companion.nanoseconds - -class ExpiringValue(private val value: T) { - val lastSeenAt: Long = System.nanoTime() - val age get() = (System.nanoTime() - lastSeenAt).nanoseconds - var taken = false - private set - - fun get(expiry: Duration): T? { - return if (!taken && age < expiry) value - else null - } - - companion object { - fun empty(): ExpiringValue { - val value = ExpiringValue(Unit) - value.take() - @Suppress("UNCHECKED_CAST") - return value as ExpiringValue - } - } - - fun consume(expiry: Duration): T? = get(expiry)?.also { take() } - fun take() { - taken = true - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ItemChange.kt b/src/main/kotlin/moe/nea/ledger/ItemChange.kt deleted file mode 100644 index fda709c..0000000 --- a/src/main/kotlin/moe/nea/ledger/ItemChange.kt +++ /dev/null @@ -1,84 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.database.DBItemEntry -import moe.nea.ledger.database.ResultRow -import net.minecraft.event.HoverEvent -import net.minecraft.util.ChatComponentText -import net.minecraft.util.ChatStyle -import net.minecraft.util.EnumChatFormatting -import net.minecraft.util.IChatComponent - -data class ItemChange( - val itemId: ItemId, - val count: Double, - val direction: ChangeDirection, -) { - fun formatChat(): IChatComponent { - return ChatComponentText(" ") - .appendSibling(direction.chatFormat) - .appendText(" ") - .appendSibling(ChatComponentText("$count").setChatStyle(ChatStyle().setColor(EnumChatFormatting.WHITE))) - .appendSibling(ChatComponentText("x").setChatStyle(ChatStyle().setColor(EnumChatFormatting.DARK_GRAY))) - .appendText(" ") - .appendSibling(ChatComponentText(itemId.string).setChatStyle(ChatStyle().setParentStyle(ChatStyle().setColor( - EnumChatFormatting.WHITE)))) - } - - enum class ChangeDirection { - GAINED, - TRANSFORM, - SYNC, - CATALYST, - LOST; - - val chatFormat by lazy { formatChat0() } - private fun formatChat0(): IChatComponent { - val (text, color) = when (this) { - GAINED -> "+" to EnumChatFormatting.GREEN - TRANSFORM -> "~" to EnumChatFormatting.YELLOW - SYNC -> "=" to EnumChatFormatting.BLUE - CATALYST -> "*" to EnumChatFormatting.DARK_PURPLE - LOST -> "-" to EnumChatFormatting.RED - } - return ChatComponentText(text) - .setChatStyle( - ChatStyle() - .setColor(color) - .setChatHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, - ChatComponentText(name).setChatStyle(ChatStyle().setColor(color))))) - } - } - - companion object { - fun gainCoins(number: Double): ItemChange { - return gain(ItemId.COINS, number) - } - - fun unpair(direction: ChangeDirection, pair: Pair): ItemChange { - return ItemChange(pair.first, pair.second, direction) - } - - fun unpairGain(pair: Pair) = unpair(ChangeDirection.GAINED, pair) - fun unpairLose(pair: Pair) = unpair(ChangeDirection.LOST, pair) - - fun gain(itemId: ItemId, amount: Number): ItemChange { - return ItemChange(itemId, amount.toDouble(), ChangeDirection.GAINED) - } - - fun lose(itemId: ItemId, amount: Number): ItemChange { - return ItemChange(itemId, amount.toDouble(), ChangeDirection.LOST) - } - - fun loseCoins(number: Double): ItemChange { - return lose(ItemId.COINS, number) - } - - fun from(result: ResultRow): ItemChange { - return ItemChange( - result[DBItemEntry.itemId], - result[DBItemEntry.size], - result[DBItemEntry.mode], - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ItemId.kt b/src/main/kotlin/moe/nea/ledger/ItemId.kt deleted file mode 100644 index 8211cd3..0000000 --- a/src/main/kotlin/moe/nea/ledger/ItemId.kt +++ /dev/null @@ -1,35 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.utils.NoSideEffects - -data class ItemId( - val string: String -) { - @NoSideEffects - fun singleItem(): Pair { - return withStackSize(1) - } - - @NoSideEffects - fun withStackSize(size: Number): Pair { - return Pair(this, size.toDouble()) - } - - - companion object { - - @JvmStatic - @NoSideEffects - fun forName(string: String) = ItemId(string) - fun skill(skill: String) = ItemId("SKYBLOCK_SKILL_$skill") - - val GARDEN = skill("GARDEN") - val FARMING = skill("FARMING") - - - val COINS = ItemId("SKYBLOCK_COIN") - val GEMSTONE_POWDER = ItemId("SKYBLOCK_POWDER_GEMSTONE") - val MITHRIL_POWDER = ItemId("SKYBLOCK_POWDER_MITHRIL") - val NIL = ItemId("SKYBLOCK_NIL") - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt deleted file mode 100644 index 0bacf32..0000000 --- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt +++ /dev/null @@ -1,188 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.events.BeforeGuiAction -import moe.nea.ledger.events.ExtraSupplyIdEvent -import moe.nea.ledger.events.RegistrationFinishedEvent -import moe.nea.ledger.events.SupplyDebugInfo -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.modules.ExternalDataProvider -import net.minecraft.client.Minecraft -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound -import net.minecraftforge.client.event.GuiScreenEvent -import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import org.lwjgl.input.Mouse - -class ItemIdProvider { - - @SubscribeEvent - fun onMouseInput(event: GuiScreenEvent.MouseInputEvent.Pre) { - if (Mouse.getEventButton() == -1) return - MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) - } - - @SubscribeEvent - fun onKeyInput(event: GuiScreenEvent.KeyboardInputEvent.Pre) { - MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) - } - - private val knownNames = mutableMapOf() - - fun createLookupTagFromDisplayName(itemName: String): String { - return itemName.unformattedString().trim().lowercase() - } - - fun saveKnownItem(itemName: String, itemId: ItemId) { - knownNames[createLookupTagFromDisplayName(itemName)] = itemId - } - - @SubscribeEvent - fun onDataLoaded(event: ExternalDataProvider.DataLoaded) { - event.provider.itemNames.forEach { (itemId, itemName) -> - saveKnownItem(itemName, ItemId(itemId)) - } - } - - @SubscribeEvent - fun onRegistrationFinished(event: RegistrationFinishedEvent) { - MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(::saveKnownItem)) - } - - @SubscribeEvent(priority = EventPriority.HIGH) - fun savePlayerInventoryIds(event: BeforeGuiAction) { - val player = Minecraft.getMinecraft().thePlayer ?: return - val inventory = player.inventory ?: return - inventory.mainInventory?.forEach { saveFromSlot(it) } - inventory.armorInventory?.forEach { saveFromSlot(it) } - } - - @SubscribeEvent - fun onDebugData(event: SupplyDebugInfo) { - event.record("knownItemNames", knownNames.size) - } - - fun saveFromSlot(stack: ItemStack?, preprocessName: (String) -> String = { it }) { - if (stack == null) return - val nbt = stack.tagCompound ?: NBTTagCompound() - val display = nbt.getCompoundTag("display") - var name = display.getString("Name").unformattedString() - name = preprocessName(name) - name = name.trim() - val id = stack.getInternalId() - if (id != null && name.isNotBlank()) { - saveKnownItem(name, id) - } - } - - @SubscribeEvent(priority = EventPriority.HIGH) - fun saveChestInventoryIds(event: BeforeGuiAction) { - val slots = event.chestSlots ?: return - val chestName = slots.lowerChestInventory.name.unformattedString() - val isOrderMenu = chestName == "Your Bazaar Orders" || chestName == "Co-op Bazaar Orders" - val preprocessor: (String) -> String = if (isOrderMenu) { - { it.removePrefix("BUY ").removePrefix("SELL ") } - } else { - { it } - } - slots.inventorySlots.forEach { - saveFromSlot(it?.stack, preprocessor) - } - } - - // TODO: make use of colour - fun findForName(name: String, fallbackToGenerated: Boolean = true): ItemId? { - var id = knownNames[createLookupTagFromDisplayName(name)] - if (id == null && fallbackToGenerated) { - id = generateName(name) - } - return id - } - - fun generateName(name: String): ItemId { - return ItemId(name.uppercase().replace(" ", "_")) - } - - private val coinRegex = "(?$SHORT_NUMBER_PATTERN) Coins?".toPattern() - private val stackedItemRegex = "(?.*) x(?$SHORT_NUMBER_PATTERN)".toPattern() - private val reverseStackedItemRegex = "(?$SHORT_NUMBER_PATTERN)x (?.*)".toPattern() - private val essenceRegex = "(?.*) Essence x(?$SHORT_NUMBER_PATTERN)".toPattern() - private val numberedItemRegex = "(?$SHORT_NUMBER_PATTERN) (?.*)".toPattern() - - fun findCostItemsFromSpan(lore: List): List> { - return lore.iterator().asSequence() - .dropWhile { it.unformattedString() != "Cost" }.drop(1) - .takeWhile { it != "" } - .map { findStackableItemByName(it) ?: Pair(ItemId.NIL, 1.0) } - .toList() - } - - private val etherialRewardPattern = "\\+(?${SHORT_NUMBER_PATTERN})x? (?.*)".toPattern() - - fun findStackableItemByName(name: String, fallbackToGenerated: Boolean = false): Pair? { - val properName = name.unformattedString().trim() - if (properName == "FREE" || properName == "This Chest is Free!") { - return Pair(ItemId.COINS, 0.0) - } - coinRegex.useMatcher(properName) { - return Pair(ItemId.COINS, parseShortNumber(group("amount"))) - } - etherialRewardPattern.useMatcher(properName) { - val id = when (val id = group("what")) { - "Copper" -> ItemIds.SKYBLOCK_COPPER - "Bits" -> ItemIds.SKYBLOCK_BIT - "Garden Experience" -> ItemId.GARDEN - "Farming XP" -> ItemId.FARMING - "Gold Essence" -> ItemIds.ESSENCE_GOLD - "Gemstone Powder" -> ItemId.GEMSTONE_POWDER - "Mithril Powder" -> ItemId.MITHRIL_POWDER - "Pelts" -> ItemIds.SKYBLOCK_PELT - "Fine Flour" -> ItemIds.FINE_FLOUR - else -> { - id.ifDropLast(" Experience") { - ItemId.skill(generateName(it).string) - } ?: id.ifDropLast(" XP") { - ItemId.skill(generateName(it).string) - } ?: id.ifDropLast(" Powder") { - ItemId("SKYBLOCK_POWDER_${generateName(it).string}") - } ?: id.ifDropLast(" Essence") { - ItemId("ESSENCE_${generateName(it).string}") - } ?: generateName(id) - } - } - return Pair(id, parseShortNumber(group("amount"))) - } - essenceRegex.useMatcher(properName) { - return Pair(ItemId("ESSENCE_${group("essence").uppercase()}"), - parseShortNumber(group("count"))) - } - stackedItemRegex.useMatcher(properName) { - val item = findForName(group("name"), fallbackToGenerated) - if (item != null) { - val count = parseShortNumber(group("count")) - return Pair(item, count) - } - } - reverseStackedItemRegex.useMatcher(properName) { - val item = findForName(group("name"), fallbackToGenerated) - if (item != null) { - val count = parseShortNumber(group("count")) - return Pair(item, count) - } - } - numberedItemRegex.useMatcher(properName) { - val item = findForName(group("what"), fallbackToGenerated) - if (item != null) { - val count = parseShortNumber(group("count")) - return Pair(item, count) - } - } - - return findForName(properName, fallbackToGenerated)?.let { Pair(it, 1.0) } - } - - fun getKnownItemIds(): Collection { - return knownNames.values - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt deleted file mode 100644 index a3d8162..0000000 --- a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt +++ /dev/null @@ -1,90 +0,0 @@ -package moe.nea.ledger - -import net.minecraft.inventory.IInventory -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound - - -fun ItemStack.getExtraAttributes(): NBTTagCompound { - val nbt = this.tagCompound ?: return NBTTagCompound() - return nbt.getCompoundTag("ExtraAttributes") -} - -fun ItemStack?.getInternalId(): ItemId? { - if (this == null) return null - val extraAttributes = getExtraAttributes() - var id = extraAttributes.getString("id") - id = id.takeIf { it.isNotBlank() } - if (id == "PET") { - id = getPetId() ?: id - } - if (id == "ENCHANTED_BOOK") { - id = getEnchanments().entries.singleOrNull()?.let { - "${it.key};${it.value}".uppercase() - } - } - return id?.let(::ItemId) -} - -fun ItemStack.getEnchanments(): Map { - val enchantments = getExtraAttributes().getCompoundTag("enchantments") - return enchantments.keySet.associateWith { enchantments.getInteger(it) } -} - -class PetInfo { - var type: String? = null - var tier: String? = null -} - -fun ItemStack.getPetId(): String? { - val petInfoStr = getExtraAttributes().getString("petInfo") - val petInfo = runCatching { - Ledger.gson.fromJson(petInfoStr, - PetInfo::class.java) - }.getOrNull() // TODO: error reporting to sentry - if (petInfo?.type == null || petInfo.tier == null) return null - return petInfo.type + ";" + rarityToIndex(petInfo.tier ?: "") -} - -fun rarityToIndex(rarity: String): Int { - return when (rarity) { - "COMMON" -> 0 - "UNCOMMON" -> 1 - "RARE" -> 2 - "EPIC" -> 3 - "LEGENDARY" -> 4 - "MYTHIC" -> 5 - else -> -1 - } -} - -fun ItemStack.getLore(): List { - val nbt = this.tagCompound ?: NBTTagCompound() - val extraAttributes = nbt.getCompoundTag("display") - val lore = extraAttributes.getTagList("Lore", 8) - return (0 until lore.tagCount()).map { lore.getStringTagAt(it) } -} - - -fun IInventory.asIterable(): Iterable = object : Iterable { - override fun iterator(): Iterator { - return object : Iterator { - var i = 0 - override fun hasNext(): Boolean { - return i < this@asIterable.sizeInventory - } - - override fun next(): ItemStack? { - if (!hasNext()) throw NoSuchElementException("$i is out of range for inventory ${this@asIterable}") - return this@asIterable.getStackInSlot(i++) - } - } - } -} - -fun ItemStack.getDisplayNameU(): String { - val nbt = this.tagCompound ?: NBTTagCompound() - val extraAttributes = nbt.getCompoundTag("display") - return extraAttributes.getString("Name") -} - diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt deleted file mode 100644 index bc667e4..0000000 --- a/src/main/kotlin/moe/nea/ledger/Ledger.kt +++ /dev/null @@ -1,205 +0,0 @@ -package moe.nea.ledger - -import com.google.gson.Gson -import io.github.notenoughupdates.moulconfig.Config -import io.github.notenoughupdates.moulconfig.managed.ManagedConfig -import moe.nea.ledger.config.LedgerConfig -import moe.nea.ledger.config.UpdateUi -import moe.nea.ledger.config.UpdateUiMarker -import moe.nea.ledger.database.Database -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.LateWorldLoadEvent -import moe.nea.ledger.events.RegistrationFinishedEvent -import moe.nea.ledger.events.WorldSwitchEvent -import moe.nea.ledger.gen.BuildConfig -import moe.nea.ledger.modules.AccessorySwapperDetection -import moe.nea.ledger.modules.AllowanceDetection -import moe.nea.ledger.modules.AuctionHouseDetection -import moe.nea.ledger.modules.BankDetection -import moe.nea.ledger.modules.BazaarDetection -import moe.nea.ledger.modules.BazaarOrderDetection -import moe.nea.ledger.modules.BitsDetection -import moe.nea.ledger.modules.BitsShopDetection -import moe.nea.ledger.modules.DragonEyePlacementDetection -import moe.nea.ledger.modules.`DragonSacrificeDetection` -import moe.nea.ledger.modules.DungeonChestDetection -import moe.nea.ledger.modules.ExternalDataProvider -import moe.nea.ledger.modules.EyedropsDetection -import moe.nea.ledger.modules.ForgeDetection -import moe.nea.ledger.modules.GambleDetection -import moe.nea.ledger.modules.GodPotionDetection -import moe.nea.ledger.modules.GodPotionMixinDetection -import moe.nea.ledger.modules.KatDetection -import moe.nea.ledger.modules.KuudraChestDetection -import moe.nea.ledger.modules.MineshaftCorpseDetection -import moe.nea.ledger.modules.MinionDetection -import moe.nea.ledger.modules.NpcDetection -import moe.nea.ledger.modules.UpdateChecker -import moe.nea.ledger.modules.VisitorDetection -import moe.nea.ledger.utils.ErrorUtil -import moe.nea.ledger.utils.MinecraftExecutor -import moe.nea.ledger.utils.di.DI -import moe.nea.ledger.utils.di.DIProvider -import moe.nea.ledger.utils.network.RequestUtil -import net.minecraft.client.Minecraft -import net.minecraft.command.ICommand -import net.minecraftforge.client.ClientCommandHandler -import net.minecraftforge.client.event.ClientChatReceivedEvent -import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.event.entity.EntityJoinWorldEvent -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.common.event.FMLInitializationEvent -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent -import org.apache.logging.log4j.LogManager -import java.io.File -import java.util.concurrent.ConcurrentLinkedQueue - -@Mod(modid = "ledger", useMetadata = true, version = BuildConfig.VERSION) -class Ledger { - /* - You have withdrawn 1M coins! You now have 518M coins in your account! - You have deposited 519M coins! You now have 519M coins in your account! - - // ORDERS: - - [Bazaar] Buy Order Setup! 160x Wheat for 720.0 coins. - [Bazaar] Claimed 160x Wheat worth 720.0 coins bought for 4.5 each! - - [Bazaar] Sell Offer Setup! 160x Wheat for 933.4 coins. - [Bazaar] Claimed 34,236,799 coins from selling 176x Hyper Catalyst at 196,741 each! - - // INSTABUY: - - [Bazaar] Bought 64x Wheat for 377.6 coins! - [Bazaar] Sold 64x Wheat for 268.8 coins! - - // AUCTION HOUSE: - - You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction! - You purchased 2x Walnut for 69 coins! - You purchased ◆ Ice Rune I for 4,000 coins! - - // NPC - - // You bought Cactus x32 for 465.6 Coins! - // You sold Cactus x1 for 3 Coins! - // You bought back Potato x3 for 9 Coins! - - TODO: TRADING, FORGE, VISITORS / COPPER, CORPSES ÖFFNEN, HIGH / LOW GAMBLES, MINION ITEMS (maybe inferno refuel) - TODO: PET LEVELING COSTS AT FANN, SLAYER / MOB DROPS, SLAYER START COST - */ - companion object { - val dataFolder = File("money-ledger").apply { mkdirs() } - val logger = LogManager.getLogger("MoneyLedger") - val managedConfig = ManagedConfig.create(File("config/money-ledger/config.json"), LedgerConfig::class.java) { - checkExpose = false - customProcessor { option, ann -> - UpdateUi(option) - } - } - val gson = Gson() - private val tickQueue = ConcurrentLinkedQueue() - fun runLater(runnable: Runnable) { - tickQueue.add(runnable) - } - - val di = DI() - } - - @Mod.EventHandler - fun init(event: FMLInitializationEvent) { - logger.info("Initializing ledger") - - TelemetryProvider.setupFor(di) - di.registerSingleton(this) - di.registerSingleton(Minecraft.getMinecraft()) - di.registerSingleton(gson) - di.register(LedgerConfig::class.java, DIProvider { managedConfig.instance }) - di.register(Config::class.java, DIProvider.fromInheritance(LedgerConfig::class.java)) - di.registerInjectableClasses( - AccessorySwapperDetection::class.java, - AllowanceDetection::class.java, - AuctionHouseDetection::class.java, - BankDetection::class.java, - BazaarDetection::class.java, - BazaarOrderDetection::class.java, - BitsDetection::class.java, - BitsShopDetection::class.java, - ConfigCommand::class.java, - Database::class.java, - DebugDataCommand::class.java, - DragonEyePlacementDetection::class.java, - DragonSacrificeDetection::class.java, - DungeonChestDetection::class.java, - ErrorUtil::class.java, - ExternalDataProvider::class.java, - EyedropsDetection::class.java, - ForgeDetection::class.java, - GambleDetection::class.java, - GodPotionDetection::class.java, - GodPotionMixinDetection::class.java, - ItemIdProvider::class.java, - KatDetection::class.java, - KuudraChestDetection::class.java, - LedgerLogger::class.java, - LogChatCommand::class.java, - MinecraftExecutor::class.java, - MineshaftCorpseDetection::class.java, - MinionDetection::class.java, - NpcDetection::class.java, - QueryCommand::class.java, - RequestUtil::class.java, - TriggerCommand::class.java, - UpdateChecker::class.java, - VisitorDetection::class.java, - ) - val errorUtil = di.provide() - errorUtil.catch { - di.instantiateAll() - di.getAllInstances().forEach(MinecraftForge.EVENT_BUS::register) - di.getAllInstances().filterIsInstance() - .forEach { ClientCommandHandler.instance.registerCommand(it) } - } - - errorUtil.catch { - di.provide().loadAndUpgrade() - } - - MinecraftForge.EVENT_BUS.post(RegistrationFinishedEvent()) - } - - var lastJoin = -1L - - @SubscribeEvent - fun worldSwitchEvent(event: EntityJoinWorldEvent) { - if (event.entity == Minecraft.getMinecraft().thePlayer) { - lastJoin = System.currentTimeMillis() - MinecraftForge.EVENT_BUS.post(WorldSwitchEvent()) - } - } - - @SubscribeEvent - fun tickEvent(event: ClientTickEvent) { - if (event.phase == TickEvent.Phase.END - && lastJoin > 0 - && System.currentTimeMillis() - lastJoin > 10_000 - && Minecraft.getMinecraft().thePlayer != null - ) { - lastJoin = -1 - MinecraftForge.EVENT_BUS.post(LateWorldLoadEvent()) - } - while (true) { - val queued = tickQueue.poll() ?: break - queued.run() - } - } - - @SubscribeEvent(receiveCanceled = true, priority = EventPriority.HIGHEST) - fun onChat(event: ClientChatReceivedEvent) { - if (event.type != 2.toByte()) - MinecraftForge.EVENT_BUS.post(ChatReceived(event)) - } -} diff --git a/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt b/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt deleted file mode 100644 index d4a3932..0000000 --- a/src/main/kotlin/moe/nea/ledger/LedgerEntry.kt +++ /dev/null @@ -1,29 +0,0 @@ -package moe.nea.ledger - -import com.google.gson.JsonObject -import moe.nea.ledger.gen.ItemIds -import java.time.Instant -import java.util.UUID - -data class LedgerEntry( - val transactionType: TransactionType, - val timestamp: Instant, - val items: List, -) { - fun intoJson(profileId: UUID?): JsonObject { - val coinAmount = items.find { it.itemId == ItemId.COINS || it.itemId == ItemIds.SKYBLOCK_BIT }?.count - val nonCoins = items.find { it.itemId != ItemId.COINS && it.itemId != ItemIds.SKYBLOCK_BIT } - return JsonObject().apply { - addProperty("transactionType", transactionType.name) - addProperty("timestamp", timestamp.toEpochMilli().toString()) - addProperty("totalTransactionValue", coinAmount) - addProperty("itemId", nonCoins?.itemId?.string ?: "") - addProperty("itemAmount", nonCoins?.count ?: 0.0) - addProperty("profileId", profileId.toString()) - addProperty( - "playerId", - MCUUIDUtil.getPlayerUUID().toString() - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt deleted file mode 100644 index 6049aa2..0000000 --- a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt +++ /dev/null @@ -1,136 +0,0 @@ -package moe.nea.ledger - -import com.google.gson.Gson -import com.google.gson.JsonArray -import moe.nea.ledger.database.DBItemEntry -import moe.nea.ledger.database.DBLogEntry -import moe.nea.ledger.database.Database -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.utils.ULIDWrapper -import moe.nea.ledger.utils.di.Inject -import net.minecraft.client.Minecraft -import net.minecraft.util.ChatComponentText -import net.minecraft.util.IChatComponent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent -import java.io.File -import java.text.SimpleDateFormat -import java.util.Date -import java.util.UUID - -class LedgerLogger { - fun printOut(text: String) = printOut(ChatComponentText(text)) - fun printOut(comp: IChatComponent) { - Minecraft.getMinecraft().ingameGUI?.chatGUI?.printChatMessage(comp) - } - - val profileIdPattern = - "Profile ID: (?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})".toPattern() - - var currentProfile: UUID? = null - - var shouldLog by Ledger.managedConfig.instance.debug::logEntries - - @Inject - lateinit var database: Database - - @SubscribeEvent - fun onProfileSwitch(event: ChatReceived) { - profileIdPattern.useMatcher(event.message) { - currentProfile = UUID.fromString(group("profile")) - } - } - - - fun printToChat(entry: LedgerEntry) { - val items = entry.items.joinToString("\n§e") { " - ${it.direction} ${it.count}x ${it.itemId}" } - printOut( - """ - §e================= TRANSACTION START - §eTYPE: §a${entry.transactionType} - §eTIMESTAMP: §a${entry.timestamp} - §e%s - §ePROFILE: §a${currentProfile} - §e================= TRANSACTION END - """.trimIndent().replace("%s", items) - ) - } - - val entries = JsonArray() - var hasRecentlyMerged = false - var lastMergeTime = System.currentTimeMillis() - - fun doMerge() { - val allFiles = folder.listFiles()?.toList() ?: emptyList() - val mergedJson = allFiles - .filter { it.name != "merged.json" && it.extension == "json" } - .sortedDescending() - .map { it.readText().trim().removePrefix("[").removeSuffix("]") } - .joinToString(",", "[", "]") - folder.resolve("merged.json").writeText(mergedJson) - hasRecentlyMerged = true - } - - init { - Runtime.getRuntime().addShutdownHook(Thread { doMerge() }) - } - - @SubscribeEvent - fun onTick(event: ClientTickEvent) { - if (!hasRecentlyMerged && (System.currentTimeMillis() - lastMergeTime) > 60_000L) { - lastMergeTime = System.currentTimeMillis() - doMerge() - } - } - - fun logEntry(entry: LedgerEntry) { - if (shouldLog) - printToChat(entry) - Ledger.logger.info("Logging entry of type ${entry.transactionType}") - val transactionId = ULIDWrapper.createULIDAt(entry.timestamp) - DBLogEntry.insert(database.connection) { - it[DBLogEntry.profileId] = currentProfile ?: MCUUIDUtil.NIL_UUID - it[DBLogEntry.playerId] = MCUUIDUtil.getPlayerUUID() - it[DBLogEntry.type] = entry.transactionType - it[DBLogEntry.transactionId] = transactionId - } - entry.items.forEach { change -> - DBItemEntry.insert(database.connection) { - it[DBItemEntry.transactionId] = transactionId - it[DBItemEntry.mode] = change.direction - it[DBItemEntry.size] = change.count - it[DBItemEntry.itemId] = change.itemId - } - } - entries.add(entry.intoJson(currentProfile)) - commit() - } - - fun commit() { - try { - hasRecentlyMerged = false - file.writeText(gson.toJson(entries)) - } catch (ex: Exception) { - Ledger.logger.error("Could not save file", ex) - } - } - - val gson = Gson() - - val folder = Ledger.dataFolder - val file: File = run { - val date = SimpleDateFormat("yyyy.MM.dd").format(Date()) - - generateSequence(0) { it + 1 } - .map { - if (it == 0) - folder.resolve("$date.json") - else - folder.resolve("$date-$it.json") - } - .filter { !it.exists() } - .first() - } -} - - diff --git a/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt b/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt deleted file mode 100644 index 90b2545..0000000 --- a/src/main/kotlin/moe/nea/ledger/LogChatCommand.kt +++ /dev/null @@ -1,27 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.utils.di.Inject -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender - -class LogChatCommand : CommandBase() { - @Inject - lateinit var logger: LedgerLogger - - override fun getCommandName(): String { - return "ledgerlogchat" - } - - override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { - return true - } - - override fun getCommandUsage(sender: ICommandSender?): String { - return "" - } - - override fun processCommand(sender: ICommandSender?, args: Array?) { - logger.shouldLog = !logger.shouldLog - logger.printOut("§eLedger logging toggled " + (if (logger.shouldLog) "§aon" else "§coff") + "§e.") - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt b/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt deleted file mode 100644 index 79068cc..0000000 --- a/src/main/kotlin/moe/nea/ledger/MCUUIDUtil.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.nea.ledger - -import com.mojang.util.UUIDTypeAdapter -import net.minecraft.client.Minecraft -import java.util.UUID - -object MCUUIDUtil { - - fun parseDashlessUuid(string: String) = UUIDTypeAdapter.fromString(string) - val NIL_UUID = UUID(0L, 0L) - fun getPlayerUUID(): UUID { - val currentUUID = Minecraft.getMinecraft().thePlayer?.uniqueID - ?: Minecraft.getMinecraft().session?.playerID?.let(::parseDashlessUuid) - ?: lastKnownUUID - lastKnownUUID = currentUUID - return currentUUID - } - - - private var lastKnownUUID: UUID = NIL_UUID - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/NumberUtil.kt b/src/main/kotlin/moe/nea/ledger/NumberUtil.kt deleted file mode 100644 index 438f342..0000000 --- a/src/main/kotlin/moe/nea/ledger/NumberUtil.kt +++ /dev/null @@ -1,117 +0,0 @@ -package moe.nea.ledger - -import net.minecraft.event.ClickEvent -import net.minecraft.event.HoverEvent -import net.minecraft.util.ChatComponentText -import net.minecraft.util.ChatStyle -import net.minecraft.util.EnumChatFormatting -import net.minecraft.util.IChatComponent -import java.time.Instant -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeFormatterBuilder -import java.time.temporal.ChronoField -import java.util.regex.Matcher -import java.util.regex.Pattern - -// language=regexp -val SHORT_NUMBER_PATTERN = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?" - -// language=regexp -val ROMAN_NUMBER_PATTERN = "[IVXLCDM]+" - -val romanNumbers = mapOf( - 'I' to 1, - 'V' to 5, - 'X' to 10, - 'L' to 50, - 'C' to 100, - 'D' to 500, - 'M' to 1000 -) - -fun parseRomanNumber(string: String): Int { - var smallestSeenSoFar = Int.MAX_VALUE - var lastSeenOfSmallest = 0 - var amount = 0 - for (c in string) { - val cV = romanNumbers[c]!! - if (cV == smallestSeenSoFar) { - lastSeenOfSmallest++ - amount += cV - } else if (cV < smallestSeenSoFar) { - smallestSeenSoFar = cV - amount += cV - lastSeenOfSmallest = 1 - } else { - amount -= lastSeenOfSmallest * smallestSeenSoFar * 2 - smallestSeenSoFar = cV - amount += cV - lastSeenOfSmallest = 1 - } - } - return amount -} - -val siScalars = mapOf( - 'k' to 1_000.0, - 'K' to 1_000.0, - 'm' to 1_000_000.0, - 'M' to 1_000_000.0, - 'b' to 1_000_000_000.0, - 'B' to 1_000_000_000.0, -) - -fun parseShortNumber(string: String): Double { - var k = string.replace(",", "") - val scalar = k.last() - var scalarMultiplier = siScalars[scalar] - if (scalarMultiplier == null) { - scalarMultiplier = 1.0 - } else { - k = k.dropLast(1) - } - return k.toDouble() * scalarMultiplier -} - -fun Pattern.matches(string: String): Boolean = matcher(string).matches() -inline fun Pattern.useMatcher(string: String, block: Matcher.() -> T): T? = - matcher(string).takeIf { it.matches() }?.let(block) - -fun String.ifDropLast(suffix: String, block: (String) -> T): T? { - if (endsWith(suffix)) { - return block(dropLast(suffix.length)) - } - return null -} - -fun String.unformattedString(): String = replace("§.".toRegex(), "") - -val timeFormat: DateTimeFormatter = DateTimeFormatterBuilder() - .appendValue(ChronoField.DAY_OF_MONTH, 2) - .appendLiteral(".") - .appendValue(ChronoField.MONTH_OF_YEAR, 2) - .appendLiteral(".") - .appendValue(ChronoField.YEAR, 4) - .appendLiteral(" ") - .appendValue(ChronoField.HOUR_OF_DAY, 2) - .appendLiteral(":") - .appendValue(ChronoField.MINUTE_OF_HOUR, 2) - .appendLiteral(":") - .appendValue(ChronoField.SECOND_OF_MINUTE, 2) - .toFormatter() - -fun Instant.formatChat(): IChatComponent { - val text = ChatComponentText( - LocalDateTime.ofInstant(this, ZoneId.systemDefault()).format(timeFormat) - ) - text.setChatStyle( - ChatStyle() - .setChatClickEvent( - ClickEvent(ClickEvent.Action.OPEN_URL, "https://time.is/${this.epochSecond}")) - .setChatHoverEvent( - HoverEvent(HoverEvent.Action.SHOW_TEXT, ChatComponentText("Click to show on time.is"))) - .setColor(EnumChatFormatting.AQUA)) - return text -} diff --git a/src/main/kotlin/moe/nea/ledger/QueryCommand.kt b/src/main/kotlin/moe/nea/ledger/QueryCommand.kt deleted file mode 100644 index 19bd5d0..0000000 --- a/src/main/kotlin/moe/nea/ledger/QueryCommand.kt +++ /dev/null @@ -1,197 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.database.sql.ANDExpression -import moe.nea.ledger.database.sql.BooleanExpression -import moe.nea.ledger.database.sql.Clause -import moe.nea.ledger.database.DBItemEntry -import moe.nea.ledger.database.DBLogEntry -import moe.nea.ledger.database.Database -import moe.nea.ledger.utils.ULIDWrapper -import moe.nea.ledger.utils.di.Inject -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.util.BlockPos -import net.minecraft.util.ChatComponentText -import net.minecraft.util.ChatStyle -import net.minecraft.util.EnumChatFormatting - -class QueryCommand : CommandBase() { - override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { - return true - } - - override fun getCommandName(): String { - return "ledger" - } - - override fun getCommandUsage(sender: ICommandSender?): String { - return "" - } - - override fun getCommandAliases(): List { - return listOf("lgq") - } - - @Inject - lateinit var logger: LedgerLogger - - override fun processCommand(sender: ICommandSender, args: Array) { - if (args.isEmpty()) { - logger.printOut("§eHere is how you can look up transactions:") - logger.printOut("") - logger.printOut("§f- §e/ledger withitem %POTATO%") - logger.printOut(" §aLook up transactions involving potatoes!") - logger.printOut("§f- §e/ledger withitem ENCHANTED_POTATO") - logger.printOut(" §aLook up transactions involving just enchanted potatoes!") - logger.printOut("§f- §e/ledger withitem %POTATO% withitem %CARROT%") - logger.printOut(" §aLook up transactions involving potatoes or carrots!") - logger.printOut("§f- §e/ledger withtype AUCTION_SOLD") - logger.printOut(" §aLook up transactions of sold auctions!") - logger.printOut("§f- §e/ledger withtype AUCTION_SOLD withitem CRIMSON%") - logger.printOut(" §aLook up sold auctions involving crimson armor pieces!") - logger.printOut("") - logger.printOut("§eFilters of the same type apply using §aOR§e and loggers of different types apply using §aAND§e.") - logger.printOut("§eYou can use % as a wildcard!") - return - } - val p = parseArgs(args) - when (p) { - is ParseResult.Success -> { - executeQuery(p) - } - - is ParseResult.UnknownFilter -> { - logger.printOut("§cUnknown filter name ${p.start}. Available filter names are: ${mFilters.keys.joinToString()}") - } - - is ParseResult.MissingArg -> { - logger.printOut("§cFilter ${p.filterM.name} is missing an argument.") - } - } - } - - override fun addTabCompletionOptions( - sender: ICommandSender, - args: Array, - pos: BlockPos - ): MutableList? { - when (val p = parseArgs(args)) { - is ParseResult.MissingArg -> return null - is ParseResult.Success -> return p.lastFilterM.tabComplete(args.last()) - is ParseResult.UnknownFilter -> return getListOfStringsMatchingLastWord(args, mFilters.keys) - } - } - - @Inject - lateinit var database: Database - private fun executeQuery(parse: ParseResult.Success) { - val grouped = parse.filters - val query = DBLogEntry.from(database.connection) - .select(DBLogEntry.type, DBLogEntry.transactionId) - .join(DBItemEntry, on = Clause { column(DBLogEntry.transactionId) eq column(DBItemEntry.transactionId) }) - for (value in grouped.values) { - query.where(ANDExpression(value)) - } - query.limit(80u) - val dedup = mutableSetOf() - query.forEach { - val type = it[DBLogEntry.type] - val transactionId = it[DBLogEntry.transactionId] - if (!dedup.add(transactionId)) { - return@forEach - } - val timestamp = transactionId.getTimestamp() - val items = DBItemEntry.selectAll(database.connection) - .where(Clause { column(DBItemEntry.transactionId) eq string(transactionId.wrapped) }) - .map { ItemChange.from(it) } - val text = ChatComponentText("") - .setChatStyle(ChatStyle().setColor(EnumChatFormatting.YELLOW)) - .appendSibling( - ChatComponentText(type.name) - .setChatStyle(ChatStyle().setColor(EnumChatFormatting.GREEN)) - ) - .appendText(" on ") - .appendSibling(timestamp.formatChat()) - .appendText("\n") - .appendSibling( - ChatComponentText(transactionId.wrapped).setChatStyle(ChatStyle().setColor(EnumChatFormatting.DARK_GRAY)) - ) - for (item in items) { - text.appendText("\n") - .appendSibling(item.formatChat()) - } - text.appendText("\n") - logger.printOut(text) - } - } - - sealed interface ParseResult { - data class UnknownFilter(val start: String) : ParseResult - data class MissingArg(val filterM: FilterM) : ParseResult - data class Success(val lastFilterM: FilterM, val filters: Map>) : ParseResult - } - - fun parseArgs(args: Array): ParseResult { - require(args.isNotEmpty()) - val arr = args.iterator() - val filters = mutableMapOf>() - var lastFilterM: FilterM? = null - while (arr.hasNext()) { - val filterName = arr.next() - val filterM = mFilters[filterName] - if (filterM == null) { - return ParseResult.UnknownFilter(filterName) - } - if (!arr.hasNext()) { - return ParseResult.MissingArg(filterM) - } - filters.getOrPut(filterM, ::mutableListOf).add(filterM.getFilter(arr.next())) - lastFilterM = filterM - } - return ParseResult.Success(lastFilterM!!, filters) - } - - - val mFilters = listOf(TypeFilter, ItemFilter).associateBy { it.name } - - object TypeFilter : FilterM { - override val name: String - get() = "withtype" - - override fun getFilter(text: String): BooleanExpression { - val preparedText = "%" + text.trim('%') + "%" - return Clause { column(DBLogEntry.type) like preparedText } - } - - override fun tabComplete(partialArg: String): MutableList { - return TransactionType.entries.asSequence().map { it.name }.filter { partialArg in it }.toMutableList() - } - } - - object ItemFilter : FilterM { - override val name: String - get() = "withitem" - - private val itemIdProvider = Ledger.di.provide() // TODO: close this escape hatch - override fun getFilter(text: String): BooleanExpression { - return Clause { column(DBItemEntry.itemId) like text } - } - - override fun tabComplete(partialArg: String): MutableList? { - return itemIdProvider.getKnownItemIds() - .asSequence() - .map { it.string } - .filter { partialArg in it } - .take(100) - .toMutableList() - } - } - - interface FilterM { - val name: String - fun getFilter(text: String): BooleanExpression - fun tabComplete(partialArg: String): MutableList? -// fun tabCompleteFilter() TODO - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt b/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt deleted file mode 100644 index 783664b..0000000 --- a/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt +++ /dev/null @@ -1,29 +0,0 @@ -package moe.nea.ledger - -import net.minecraft.client.Minecraft -import net.minecraft.scoreboard.ScorePlayerTeam - -object ScoreboardUtil { - - val sidebarSlot = 1 - fun getScoreboardStrings(): List { - val scoreboard = Minecraft.getMinecraft().theWorld.scoreboard - val objective = scoreboard.getObjectiveInDisplaySlot(sidebarSlot) - val scoreList = scoreboard.getSortedScores(objective).take(15) - .map { - ScorePlayerTeam.formatPlayerName(scoreboard.getPlayersTeam(it.playerName), it.playerName) - } - .map { stripAlien(it) } - .reversed() - return scoreList - } - - fun stripAlien(string: String): String { - val sb = StringBuilder() - for (c in string) { - if (Minecraft.getMinecraft().fontRendererObj.getCharWidth(c) > 0 || c == '§') - sb.append(c) - } - return sb.toString() - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt b/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt deleted file mode 100644 index d9c7108..0000000 --- a/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt +++ /dev/null @@ -1,66 +0,0 @@ -package moe.nea.ledger - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import moe.nea.ledger.gen.BuildConfig -import moe.nea.ledger.utils.di.DI -import moe.nea.ledger.utils.di.DIProvider -import moe.nea.ledger.utils.telemetry.CommonKeys -import moe.nea.ledger.utils.telemetry.ContextValue -import moe.nea.ledger.utils.telemetry.EventRecorder -import moe.nea.ledger.utils.telemetry.JsonElementContext -import moe.nea.ledger.utils.telemetry.LoggingEventRecorder -import moe.nea.ledger.utils.telemetry.Span -import net.minecraft.client.Minecraft -import net.minecraft.util.Session -import net.minecraftforge.fml.common.Loader - -object TelemetryProvider { - fun injectTo(di: DI) { - di.register( - EventRecorder::class.java, - if (DevUtil.isDevEnv) DIProvider.singeleton(LoggingEventRecorder(Ledger.logger, true)) - else DIProvider.singeleton( - LoggingEventRecorder(Ledger.logger, false)) // TODO: replace with upload to server - ) - } - - val USER = "minecraft_user" - val MINECRAFT_VERSION = "minecraft_version" - val MODS = "mods" - - class MinecraftUser(val session: Session) : ContextValue { - override fun serialize(): JsonElement { - val obj = JsonObject() - obj.addProperty("uuid", session.playerID) - obj.addProperty("name", session.username) - return obj - } - } - - fun setupDefaultSpan() { - val sp = Span.current() - sp.add(USER, MinecraftUser(Minecraft.getMinecraft().session)) - sp.add(MINECRAFT_VERSION, ContextValue.compound( - "static" to "1.8.9", - "rt" to Minecraft.getMinecraft().version, - )) - val mods = JsonArray() - Loader.instance().activeModList.map { - val obj = JsonObject() - obj.addProperty("id", it.modId) - obj.addProperty("version", it.version) - obj.addProperty("displayVersion", it.displayVersion) - obj - }.forEach(mods::add) - sp.add(MODS, JsonElementContext(mods)) - sp.add(CommonKeys.VERSION, ContextValue.string(BuildConfig.FULL_VERSION)) - sp.add(CommonKeys.COMMIT_VERSION, ContextValue.string(BuildConfig.GIT_COMMIT)) - } - - fun setupFor(di: DI) { - injectTo(di) - setupDefaultSpan() - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/TransactionType.kt b/src/main/kotlin/moe/nea/ledger/TransactionType.kt deleted file mode 100644 index 33c633d..0000000 --- a/src/main/kotlin/moe/nea/ledger/TransactionType.kt +++ /dev/null @@ -1,35 +0,0 @@ -package moe.nea.ledger - -enum class TransactionType { - ACCESSORIES_SWAPPING, - ALLOWANCE_GAIN, - AUCTION_BOUGHT, - AUCTION_LISTING_CHARGE, - AUCTION_SOLD, - AUTOMERCHANT_PROFIT_COLLECT, - BANK_DEPOSIT, - BANK_WITHDRAW, - BAZAAR_BUY_INSTANT, - BAZAAR_BUY_ORDER, - BAZAAR_SELL_INSTANT, - BAZAAR_SELL_ORDER, - BITS_PURSE_STATUS, - BOOSTER_COOKIE_ATE, - CAPSAICIN_EYEDROPS_USED, - COMMUNITY_SHOP_BUY, - CORPSE_DESECRATED, - DIE_ROLLED, - DRACONIC_SACRIFICE, - DUNGEON_CHEST_OPEN, - FORGED, - GOD_POTION_DRANK, - GOD_POTION_MIXIN_DRANK, - KAT_TIMESKIP, - KAT_UPGRADE, - KISMET_REROLL, - KUUDRA_CHEST_OPEN, - NPC_BUY, - NPC_SELL, - VISITOR_BARGAIN, - WYRM_EVOKED, -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt b/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt deleted file mode 100644 index c97627d..0000000 --- a/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt +++ /dev/null @@ -1,34 +0,0 @@ -package moe.nea.ledger - -import moe.nea.ledger.events.TriggerEvent -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.event.ClickEvent -import net.minecraft.util.ChatComponentText -import net.minecraftforge.common.MinecraftForge - -class TriggerCommand : CommandBase() { - fun getTriggerCommandLine(trigger: String): ClickEvent { - return ClickEvent(ClickEvent.Action.RUN_COMMAND, "/${commandName} $trigger") - } - - override fun getCommandName(): String { - return "__ledgertrigger" - } - - override fun getCommandUsage(sender: ICommandSender?): String { - return "" - } - - override fun processCommand(sender: ICommandSender, args: Array) { - val event = TriggerEvent(args.joinToString(" ")) - MinecraftForge.EVENT_BUS.post(event) - if (!event.isCanceled) - sender.addChatMessage(ChatComponentText("§cCould not find the given trigger. This is an internal command for ledger.")) - } - - override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean { - return true - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt b/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt deleted file mode 100644 index fd5ed3d..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/DebugOptions.kt +++ /dev/null @@ -1,13 +0,0 @@ -package moe.nea.ledger.config - -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean -import io.github.notenoughupdates.moulconfig.annotations.ConfigOption - -class DebugOptions { - @ConfigOption(name = "Log entries to chat", - desc = "Appends all logged entries into the chat as they are logged. This option does not persist on restarts.") - @Transient - @ConfigEditorBoolean - @JvmField - var logEntries = false -} diff --git a/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt b/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt deleted file mode 100644 index 91ee5c1..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt +++ /dev/null @@ -1,35 +0,0 @@ -package moe.nea.ledger.config - -import io.github.notenoughupdates.moulconfig.Config -import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour -import io.github.notenoughupdates.moulconfig.annotations.Category -import io.github.notenoughupdates.moulconfig.processor.ProcessedOption -import moe.nea.ledger.Ledger - -class LedgerConfig : Config() { - override fun getTitle(): String { - return "§6Ledger §7- §6Hypixel SkyBlock data logger §7by §anea89o" - } - - override fun saveNow() { - super.saveNow() - Ledger.managedConfig.saveToFile() - } - - override fun getDescriptionBehaviour(option: ProcessedOption?): DescriptionRendereringBehaviour { - return DescriptionRendereringBehaviour.EXPAND_PANEL - } - - @Category(name = "Ledger", desc = "") - @JvmField - val main = MainOptions() - - @Category(name = "Synchronization", desc = "") - @JvmField - val synchronization = SynchronizationOptions() - - @Category(name = "Debug", desc = "") - @JvmField - val debug = DebugOptions() - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt b/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt deleted file mode 100644 index 1efa970..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt +++ /dev/null @@ -1,27 +0,0 @@ -package moe.nea.ledger.config - -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown -import io.github.notenoughupdates.moulconfig.annotations.ConfigOption - -class MainOptions { - @ConfigOption(name = "Marker for Update UI", desc = "_") - @JvmField - @UpdateUiMarker - @Transient - var testOption = Unit - - @ConfigOption(name = "Check for Updates", desc = "Automatically check for updates on startup") - @ConfigEditorDropdown - @JvmField - var updateCheck = UpdateCheckBehaviour.SEMI - - enum class UpdateCheckBehaviour(val label: String) { - SEMI("Semi-Automatic"), - FULL("Full-Automatic"), - NONE("Don't check"); - - override fun toString(): String { - return label - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt b/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt deleted file mode 100644 index b8c740b..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/SynchronizationOptions.kt +++ /dev/null @@ -1,11 +0,0 @@ -package moe.nea.ledger.config - -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean -import io.github.notenoughupdates.moulconfig.annotations.ConfigOption - -class SynchronizationOptions { - @ConfigOption(name = "Test Option", desc = "Test Description") - @ConfigEditorBoolean - @JvmField - var testOption = false -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt b/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt deleted file mode 100644 index 86ccbf7..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt +++ /dev/null @@ -1,17 +0,0 @@ -package moe.nea.ledger.config - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.component.TextComponent -import io.github.notenoughupdates.moulconfig.gui.editors.ComponentEditor -import io.github.notenoughupdates.moulconfig.processor.ProcessedOption -import moe.nea.ledger.Ledger - -class UpdateUi(option: ProcessedOption) : ComponentEditor(option) { - val delegate by lazy {// TODO - TextComponent("Hier könnte ihre werbung stehen") - } - - override fun getDelegate(): GuiComponent { - return delegate - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt b/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt deleted file mode 100644 index 7a0466a..0000000 --- a/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt +++ /dev/null @@ -1,6 +0,0 @@ -package moe.nea.ledger.config - -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FIELD) -annotation class UpdateUiMarker { -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt b/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt deleted file mode 100644 index b162c6f..0000000 --- a/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt +++ /dev/null @@ -1,24 +0,0 @@ -package moe.nea.ledger.database - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.TransactionType -import moe.nea.ledger.database.columns.DBDouble -import moe.nea.ledger.database.columns.DBEnum -import moe.nea.ledger.database.columns.DBString -import moe.nea.ledger.database.columns.DBUlid -import moe.nea.ledger.database.columns.DBUuid - -object DBLogEntry : Table("LogEntry") { - val transactionId = column("transactionId", DBUlid) - val type = column("type", DBEnum()) - val profileId = column("profileId", DBUuid) - val playerId = column("playerId", DBUuid) -} - -object DBItemEntry : Table("ItemEntry") { - val transactionId = column("transactionId", DBUlid) // TODO: add foreign keys - val mode = column("mode", DBEnum()) - val itemId = column("item", DBString.mapped(ItemId::string, ::ItemId)) - val size = column("size", DBDouble) -} diff --git a/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt b/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt deleted file mode 100644 index 7d1782a..0000000 --- a/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt +++ /dev/null @@ -1,68 +0,0 @@ -package moe.nea.ledger.database - -import java.sql.Connection - -interface DBUpgrade { - val toVersion: Long - val fromVersion get() = toVersion - 1 - fun performUpgrade(connection: Connection) - - companion object { - - fun performUpgrades( - connection: Connection, - upgrades: Iterable, - ) { - for (upgrade in upgrades) { - upgrade.performUpgrade(connection) - } - } - - fun performUpgradeChain( - connection: Connection, - from: Long, to: Long, - upgrades: Iterable, - afterEach: (newVersion: Long) -> Unit, - ) { - val table = buildLookup(upgrades) - for (version in (from + 1)..(to)) { - val currentUpgrades = table[version] ?: listOf() - println("Scheduled ${currentUpgrades.size} upgrades to reach DB version $version") - performUpgrades(connection, currentUpgrades) - afterEach(version) - } - } - - fun buildLookup(upgrades: Iterable): Map> { - return upgrades.groupBy { it.toVersion } - } - - fun createTable(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade { - require(columns.all { it in table.columns }) - return of("Create table ${table}", to) { - table.createIfNotExists(it, columns.toList()) - } - } - - fun addColumns(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade { - return of("Add columns to table $table", to) { - table.alterTableAddColumns(it, columns.toList()) - } - } - - fun of(name: String, to: Long, block: (Connection) -> Unit): DBUpgrade { - return object : DBUpgrade { - override val toVersion: Long - get() = to - - override fun performUpgrade(connection: Connection) { - block(connection) - } - - override fun toString(): String { - return name - } - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/database/Database.kt b/src/main/kotlin/moe/nea/ledger/database/Database.kt deleted file mode 100644 index 025888c..0000000 --- a/src/main/kotlin/moe/nea/ledger/database/Database.kt +++ /dev/null @@ -1,57 +0,0 @@ -package moe.nea.ledger.database - -import moe.nea.ledger.Ledger -import moe.nea.ledger.database.columns.DBString -import java.sql.Connection -import java.sql.DriverManager - -class Database { - lateinit var connection: Connection - - object MetaTable : Table("LedgerMeta") { - val key = column("key", DBString) - val value = column("value", DBString) - - init { - unique(key) - } - } - - data class MetaKey(val name: String) { - companion object { - val DATABASE_VERSION = MetaKey("databaseVersion") - val LAST_LAUNCH = MetaKey("lastLaunch") - } - } - - fun setMetaKey(key: MetaKey, value: String) { - MetaTable.insert(connection, Table.OnConflict.REPLACE) { - it[MetaTable.key] = key.name - it[MetaTable.value] = value - } - } - - val databaseVersion: Long = 1 - - fun loadAndUpgrade() { - connection = DriverManager.getConnection("jdbc:sqlite:${Ledger.dataFolder.resolve("database.db")}") - MetaTable.createIfNotExists(connection) - val meta = MetaTable.selectAll(connection).associate { MetaKey(it[MetaTable.key]) to it[MetaTable.value] } - val lastLaunch = meta[MetaKey.LAST_LAUNCH]?.toLong() ?: 0L - println("Last launch $lastLaunch") - setMetaKey(MetaKey.LAST_LAUNCH, System.currentTimeMillis().toString()) - - val oldVersion = meta[MetaKey.DATABASE_VERSION]?.toLong() ?: -1 - println("Old Database Version: $oldVersion; Current version: $databaseVersion") - if (oldVersion > databaseVersion) - error("Outdated software. Database is newer than me!") - // TODO: create a backup if there is a db version upgrade happening - DBUpgrade.performUpgradeChain( - connection, oldVersion, databaseVersion, - Upgrades().upgrades - ) { version -> - setMetaKey(MetaKey.DATABASE_VERSION, version.toString()) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt b/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt deleted file mode 100644 index e83abe7..0000000 --- a/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt +++ /dev/null @@ -1,20 +0,0 @@ -package moe.nea.ledger.database - -class Upgrades { - val upgrades = mutableListOf() - - fun add(upgrade: DBUpgrade) = upgrades.add(upgrade) - - init { - add(DBUpgrade.createTable( - 0, DBLogEntry, - DBLogEntry.type, DBLogEntry.playerId, DBLogEntry.profileId, - DBLogEntry.transactionId)) - add(DBUpgrade.createTable( - 0, DBItemEntry, - DBItemEntry.itemId, DBItemEntry.size, DBItemEntry.mode, DBItemEntry.transactionId - )) - } - - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/database/schema.dot b/src/main/kotlin/moe/nea/ledger/database/schema.dot deleted file mode 100644 index d932f6a..0000000 --- a/src/main/kotlin/moe/nea/ledger/database/schema.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph { - node [shape=plain]; - rankdir=LR; - entry [label=< - - - - - - -
Log Entry
playerId
profileId
timestamp
Type
- >]; - item [label=< - - - - - - -
Item Stack
Transaction
Item ID
Count
Transfer Direction
- >]; -// item:transaction -> entry; -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt b/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt deleted file mode 100644 index 098912a..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt +++ /dev/null @@ -1,11 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest -import net.minecraftforge.fml.common.eventhandler.Event - -data class BeforeGuiAction(val gui: GuiScreen) : Event() { - val chest = gui as? GuiChest - val chestSlots = chest?.inventorySlots as ContainerChest? -} diff --git a/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt b/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt deleted file mode 100644 index a352c27..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt +++ /dev/null @@ -1,15 +0,0 @@ -package moe.nea.ledger.events - -import moe.nea.ledger.unformattedString -import net.minecraftforge.client.event.ClientChatReceivedEvent -import net.minecraftforge.fml.common.eventhandler.Event -import java.time.Instant - -data class ChatReceived( - val message: String, - val timestamp: Instant = Instant.now() -) : Event() { - constructor(event: ClientChatReceivedEvent) : this( - event.message.unformattedText.unformattedString().trimEnd() - ) -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt b/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt deleted file mode 100644 index d040961..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt +++ /dev/null @@ -1,12 +0,0 @@ -package moe.nea.ledger.events - -import moe.nea.ledger.ItemId -import net.minecraftforge.fml.common.eventhandler.Event - -class ExtraSupplyIdEvent( - private val store: (String, ItemId) -> Unit -) : Event() { - fun store(name: String, id: ItemId) { - store.invoke(name, id) - } -} diff --git a/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt b/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt deleted file mode 100644 index 9e057dd..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt +++ /dev/null @@ -1,9 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraft.inventory.Slot -import net.minecraftforge.fml.common.eventhandler.Event - -data class GuiClickEvent( - val slotIn: Slot?, val slotId: Int, val clickedButton: Int, val clickType: Int -) : Event() { -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt b/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt deleted file mode 100644 index d917039..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/InitializationComplete.kt +++ /dev/null @@ -1,6 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Event - -class InitializationComplete : Event() { -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt b/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt deleted file mode 100644 index d36e0c7..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/RegistrationFinishedEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Event - -class RegistrationFinishedEvent : Event() { - -} diff --git a/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt b/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt deleted file mode 100644 index cab0a20..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/SupplyDebugInfo.kt +++ /dev/null @@ -1,10 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Event - -class SupplyDebugInfo : Event() { // TODO: collect this in the event recorder - val data = mutableListOf>() - fun record(key: String, value: Any) { - data.add(key to value) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt b/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt deleted file mode 100644 index 3751f43..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Cancelable -import net.minecraftforge.fml.common.eventhandler.Event - -@Cancelable -data class TriggerEvent(val action: String) : Event() diff --git a/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt b/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt deleted file mode 100644 index d60f3a4..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt +++ /dev/null @@ -1,5 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Event - -class LateWorldLoadEvent : Event() diff --git a/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt b/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt deleted file mode 100644 index 22a97f7..0000000 --- a/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt +++ /dev/null @@ -1,6 +0,0 @@ -package moe.nea.ledger.events - -import net.minecraftforge.fml.common.eventhandler.Event - -class WorldSwitchEvent : Event() { -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt deleted file mode 100644 index 1c228ff..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt +++ /dev/null @@ -1,34 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class AccessorySwapperDetection { - - val swapperUsed = "Swapped .* enrichments to .*!".toPattern() - - @Inject - lateinit var logger: LedgerLogger - - @SubscribeEvent - fun onChat(event: ChatReceived) { - swapperUsed.useMatcher(event.message) { - logger.logEntry( - LedgerEntry( - TransactionType.ACCESSORIES_SWAPPING, - event.timestamp, - listOf( - ItemChange.lose(ItemIds.TALISMAN_ENRICHMENT_SWAPPER, 1) - ) - ) - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt deleted file mode 100644 index cd48d45..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt +++ /dev/null @@ -1,37 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class AllowanceDetection { - - val allowancePattern = - Pattern.compile("ALLOWANCE! You earned (?$SHORT_NUMBER_PATTERN) coins!") - - @Inject - lateinit var logger: LedgerLogger - - @SubscribeEvent - fun onAllowanceGain(event: ChatReceived) { - allowancePattern.useMatcher(event.message) { - logger.logEntry( - LedgerEntry( - TransactionType.ALLOWANCE_GAIN, - event.timestamp, - listOf( - ItemChange.gainCoins(parseShortNumber(group("coins"))), - ) - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt deleted file mode 100644 index d02095d..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt +++ /dev/null @@ -1,143 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ExpiringValue -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.BeforeGuiAction -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.getInternalId -import moe.nea.ledger.getLore -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern -import kotlin.time.Duration.Companion.seconds - -class AuctionHouseDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { - data class LastViewedItem( - val count: Int, - val id: ItemId, - ) - /* - You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction! - You collected 60,000 coins from selling Walnut to [MVP++] Alea1337 in an auction! - You purchased 2x Walnut for 69 coins! - You purchased ◆ Ice Rune I for 4,000 coins! - */ - - val createAuctionScreen = "Confirm( BIN)? Auction".toPattern() - val auctionCreationCostPattern = "Cost: (?$SHORT_NUMBER_PATTERN) coins?".toPattern() - - val auctionCreatedChatPattern = "(BIN )?Auction started for .*".toPattern() - - var lastCreationCost: ExpiringValue = ExpiringValue.empty() - - @SubscribeEvent - fun onCreateAuctionClick(event: BeforeGuiAction) { - val slots = event.chestSlots ?: return - if (!createAuctionScreen.asPredicate().test(slots.lowerChestInventory.name)) return - val auctionSlot = slots.lowerChestInventory.getStackInSlot(9 + 2) ?: return - val creationCost = auctionSlot.getLore().firstNotNullOfOrNull { - auctionCreationCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")) } - } - if (creationCost != null) { - lastCreationCost = ExpiringValue(creationCost) - } - } - - @SubscribeEvent - fun onCreateAuctionChat(event: ChatReceived) { - auctionCreatedChatPattern.useMatcher(event.message) { - lastCreationCost.consume(3.seconds)?.let { cost -> - ledger.logEntry(LedgerEntry( - TransactionType.AUCTION_LISTING_CHARGE, - event.timestamp, - listOf(ItemChange.loseCoins(cost)) - )) - } - } - } - - val collectSold = - Pattern.compile("You collected (?$SHORT_NUMBER_PATTERN) coins? from selling (?.*) to (?.*) in an auction!") - val purchased = - Pattern.compile("You purchased (?:(?[0-9]+)x )?(?.*) for (?$SHORT_NUMBER_PATTERN) coins!") - var lastViewedItems: MutableList = mutableListOf() - - @SubscribeEvent - fun onEvent(event: ChatReceived) { - collectSold.useMatcher(event.message) { - val lastViewedItem = lastViewedItems.removeLastOrNull() - ledger.logEntry( - LedgerEntry( - TransactionType.AUCTION_SOLD, - event.timestamp, - listOfNotNull( - ItemChange.gainCoins(parseShortNumber(group("coins"))), - lastViewedItem?.let { ItemChange.lose(it.id, it.count) } - ), - ) - ) - } - purchased.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.AUCTION_BOUGHT, - event.timestamp, - listOf( - ItemChange.loseCoins(parseShortNumber(group("coins"))), - ItemChange.gain( - ids.findForName(group("what")) ?: ItemId.NIL, - group("amount")?.toInt() ?: 1 - ) - ) - ) - ) - } - } - - @SubscribeEvent - fun onBeforeAuctionCollected(event: BeforeGuiAction) { - val chest = (event.gui as? GuiChest) ?: return - val slots = chest.inventorySlots as ContainerChest - val name = slots.lowerChestInventory.displayName.unformattedText.unformattedString() - - if (name == "BIN Auction View" || name == "Auction View") { - handleCollectSingleAuctionView(slots) - } - if (name == "Manage Auctions") { - handleCollectMultipleAuctionsView(slots) - } - } - - private fun handleCollectMultipleAuctionsView(slots: ContainerChest) { - lastViewedItems = - (0 until slots.lowerChestInventory.sizeInventory) - .mapNotNull { slots.lowerChestInventory.getStackInSlot(it) } - .filter { - it.getLore().contains("§7Status: §aSold!") // BINs - || it.getLore().contains("§7Status: §aEnded!") // Auctions - } - .mapNotNull { LastViewedItem(it.stackSize, it.getInternalId() ?: return@mapNotNull null) } - .toMutableList() - } - - - fun handleCollectSingleAuctionView(slots: ContainerChest) { - val soldItem = slots.lowerChestInventory.getStackInSlot(9 + 4) ?: return - val id = soldItem.getInternalId() ?: return - val count = soldItem.stackSize - lastViewedItems = mutableListOf(LastViewedItem(count, id)) - } - - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt deleted file mode 100644 index 928d30c..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt +++ /dev/null @@ -1,49 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - - -class BankDetection @Inject constructor(val ledger: LedgerLogger) { - val withdrawPattern = - Pattern.compile("^(You have withdrawn|Withdrew) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:left in the account!|in your account!)$") - val depositPattern = - Pattern.compile("^(?:You have deposited|Deposited) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:in your account!|in the account!)$") - - @SubscribeEvent - fun onChat(event: ChatReceived) { - withdrawPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BANK_WITHDRAW, - event.timestamp, - listOf(ItemChange(ItemId.COINS, - parseShortNumber(group("amount")), - ItemChange.ChangeDirection.TRANSFORM)), - ) - ) - } - depositPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BANK_DEPOSIT, - event.timestamp, - listOf(ItemChange(ItemId.COINS, - parseShortNumber(group("amount")), - ItemChange.ChangeDirection.TRANSFORM)), - ) - ) - } - } - -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt deleted file mode 100644 index 0f1fc2c..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt +++ /dev/null @@ -1,58 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class BazaarDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { - - val instaBuyPattern = - Pattern.compile("\\[Bazaar\\] Bought (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") - val instaSellPattern = - Pattern.compile("\\[Bazaar\\] Sold (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") - - - @SubscribeEvent - fun onInstSellChat(event: ChatReceived) { - instaBuyPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BAZAAR_BUY_INSTANT, - event.timestamp, - listOf( - ItemChange.loseCoins(parseShortNumber(group("coins"))), - ItemChange.gain( - ids.findForName(group("what")) ?: ItemId.NIL, - parseShortNumber(group("count")) - ) - ) - ) - ) - } - instaSellPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BAZAAR_SELL_INSTANT, - event.timestamp, - listOf( - ItemChange.gainCoins(parseShortNumber(group("coins"))), - ItemChange.lose( - ids.findForName(group("what")) ?: ItemId.NIL, - parseShortNumber(group("count")) - ) - ), - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt deleted file mode 100644 index 330ee1d..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt +++ /dev/null @@ -1,95 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.mixin.AccessorGuiEditSign -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraft.client.gui.inventory.GuiEditSign -import net.minecraftforge.client.event.GuiScreenEvent -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class BazaarOrderDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { - - val buyOrderClaimed = - Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN)x (?.*) worth (?$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!") - val sellOrderClaimed = - Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN) coins? from selling (?$SHORT_NUMBER_PATTERN)x (?.*) at $SHORT_NUMBER_PATTERN each!") - val orderFlipped = - Pattern.compile("\\[Bazaar] Order Flipped! (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins? of total expected profit.") - val previousPricePattern = - Pattern.compile("(?$SHORT_NUMBER_PATTERN)/u") - var lastFlippedPreviousPrice = 0.0 - - @SubscribeEvent - fun detectSignFlip(event: GuiScreenEvent.InitGuiEvent) { - val gui = event.gui - if (gui !is GuiEditSign) return - gui as AccessorGuiEditSign - val text = gui.tileEntity_ledger.signText - if (text[2].unformattedText != "Previous price:") return - previousPricePattern.useMatcher(text[3].unformattedText) { - lastFlippedPreviousPrice = parseShortNumber(group("price")) - } - } - - @SubscribeEvent - fun detectBuyOrders(event: ChatReceived) { - orderFlipped.useMatcher(event.message) { - val amount = parseShortNumber(group("amount")).toInt() - ledger.logEntry( - LedgerEntry( - TransactionType.BAZAAR_BUY_ORDER, - event.timestamp, - listOf( - ItemChange.loseCoins(lastFlippedPreviousPrice * amount), - ItemChange.gain( - ids.findForName(group("what")) ?: ItemId.NIL, - amount, - ) - ) - ) - ) - } - buyOrderClaimed.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BAZAAR_BUY_ORDER, - event.timestamp, - listOf( - ItemChange.loseCoins(parseShortNumber(group("coins"))), - ItemChange.gain( - ids.findForName(group("what")) ?: ItemId.NIL, - parseShortNumber(group("amount")) - ) - ), - ) - ) - } - sellOrderClaimed.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.BAZAAR_SELL_ORDER, - event.timestamp, - listOf( - ItemChange.gainCoins( - parseShortNumber(group("coins")) - ), - ItemChange.lose( - ids.findForName(group("what")) ?: ItemId.NIL, - parseShortNumber(group("amount")), - ) - ), - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt deleted file mode 100644 index 44a0050..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt +++ /dev/null @@ -1,62 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.LateWorldLoadEvent -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.ScoreboardUtil -import moe.nea.ledger.TransactionType -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant - -class BitsDetection @Inject constructor(val ledger: LedgerLogger) { - - var lastBits = -1 - - val bitScoreboardRegex = "Bits: (?$SHORT_NUMBER_PATTERN)".toPattern() - - @SubscribeEvent - fun onWorldSwitch(event: LateWorldLoadEvent) { - ScoreboardUtil.getScoreboardStrings().forEach { - bitScoreboardRegex.useMatcher(it.unformattedString()) { - val bits = parseShortNumber(group("purse")).toInt() - if (lastBits != bits) { - ledger.logEntry( - LedgerEntry( - TransactionType.BITS_PURSE_STATUS, - Instant.now(), - listOf( - ItemChange(ItemIds.SKYBLOCK_BIT, bits.toDouble(), ItemChange.ChangeDirection.SYNC) - ) - ) - ) - lastBits = bits - } - return - } - } - } - - @SubscribeEvent - fun onEvent(event: ChatReceived) { - if (event.message.startsWith("You consumed a Booster Cookie!")) { - ledger.logEntry( - LedgerEntry( - TransactionType.BOOSTER_COOKIE_ATE, - Instant.now(), - listOf( - ItemChange.lose(ItemIds.BOOSTER_COOKIE, 1) - ) - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt deleted file mode 100644 index 553bebf..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt +++ /dev/null @@ -1,66 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.GuiClickEvent -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.getInternalId -import moe.nea.ledger.getLore -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant - -class BitsShopDetection @Inject constructor(val ledger: LedgerLogger) { - - - data class BitShopEntry( - val id: ItemId, - val stackSize: Int, - val bitPrice: Int, - val timestamp: Long = System.currentTimeMillis() - ) - - var lastClickedBitShopItem: BitShopEntry? = null - var bitCostPattern = "(?$SHORT_NUMBER_PATTERN) Bits".toPattern() - - @SubscribeEvent - fun recordLastBitPrice(event: GuiClickEvent) { - val slot = event.slotIn ?: return - val name = slot.inventory.displayName.unformattedText.unformattedString() - if (name != "Community Shop" && !name.startsWith("Bits Shop")) - return - val stack = slot.stack ?: return - val id = stack.getInternalId() ?: return - val bitPrice = stack.getLore() - .firstNotNullOfOrNull { bitCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")).toInt() } } - ?: return - lastClickedBitShopItem = BitShopEntry(id, stack.stackSize, bitPrice) - } - - @SubscribeEvent - fun onChat(event: ChatReceived) { - if (event.message.startsWith("You bought ")) { - val lastBit = lastClickedBitShopItem ?: return - if (System.currentTimeMillis() - lastBit.timestamp > 5000) return - ledger.logEntry( - LedgerEntry( - TransactionType.COMMUNITY_SHOP_BUY, - Instant.now(), - listOf( - ItemChange.lose(ItemIds.SKYBLOCK_BIT, lastBit.bitPrice.toDouble()), - ItemChange.gain(lastBit.id, lastBit.stackSize) - ) - ) - ) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt deleted file mode 100644 index cca02e1..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt +++ /dev/null @@ -1,48 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.getDisplayNameU -import moe.nea.ledger.getInternalId -import moe.nea.ledger.getLore -import moe.nea.ledger.unformattedString -import moe.nea.ledger.utils.di.Inject -import net.minecraft.init.Blocks -import net.minecraft.inventory.Slot -import net.minecraft.item.Item -import java.time.Instant - -abstract class ChestDetection { - data class ChestCost( - val diff: List, - val timestamp: Instant, - ) - - @Inject - lateinit var itemIdProvider: ItemIdProvider - fun scrapeChestReward(rewardSlot: Slot): ChestCost? { - val inventory = rewardSlot.inventory - if (!inventory.displayName.unformattedText.unformattedString() - .endsWith(" Chest") - ) return null - val rewardStack = rewardSlot.stack ?: return null - val name = rewardStack.getDisplayNameU() - if (name != "§aOpen Reward Chest") return null - val lore = rewardStack.getLore() - val cost = itemIdProvider.findCostItemsFromSpan(lore) - val gain = (9..18) - .mapNotNull { inventory.getStackInSlot(it) } - .filter { it.item != Item.getItemFromBlock(Blocks.stained_glass_pane) } - .map { - it.getInternalId()?.withStackSize(it.stackSize) - ?: itemIdProvider.findStackableItemByName(it.displayName) - ?: ItemId.NIL.withStackSize(it.stackSize) - } - return ChestCost( - cost.map { ItemChange.lose(it.first, it.second) } + gain.map { ItemChange.gain(it.first, it.second) }, - Instant.now() - ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt deleted file mode 100644 index e389ffb..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt +++ /dev/null @@ -1,47 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.WorldSwitchEvent -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class DragonEyePlacementDetection { - val eyePlaced = "☬ You placed a Summoning Eye!( Brace yourselves!)? \\(./.\\)".toPattern() -//☬ You placed a Summoning Eye! Brace yourselves! (8/8) - var eyeCount = 0 - - @SubscribeEvent - fun onWorldSwap(event: WorldSwitchEvent) { - eyeCount = 0 - } - - @SubscribeEvent - fun onRetrieveEye(event: ChatReceived) { - if (event.message == "You recovered a Summoning Eye!") { - eyeCount-- - } - eyePlaced.useMatcher(event.message) { - eyeCount++ - } - if (event.message == "Your Sleeping Eyes have been awoken by the magic of the Dragon. They are now Remnants of the Eye!") { - logger.logEntry(LedgerEntry( - TransactionType.WYRM_EVOKED, - event.timestamp, - listOf( - ItemChange.lose(ItemIds.SUMMONING_EYE, eyeCount) - ) - )) - eyeCount = 0 - } - } - - @Inject - lateinit var logger: LedgerLogger -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt deleted file mode 100644 index 574cfcf..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt +++ /dev/null @@ -1,72 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.DebouncedValue -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent -import kotlin.time.Duration.Companion.seconds - -class DragonSacrificeDetection { - //SACRIFICE! You turned Holy Dragon Boots into 30 Dragon Essence! - //BONUS LOOT! You also received 17x Holy Dragon Fragment from your sacrifice! - @Inject - lateinit var itemIdProvider: ItemIdProvider - - @Inject - lateinit var logger: LedgerLogger - - val sacrificePattern = - "SACRIFICE! You turned (?.*) into (?$SHORT_NUMBER_PATTERN) Dragon Essence!".toPattern() - val bonusLootPattern = "BONUS LOOT! You also received (?.*) from your sacrifice!".toPattern() - - var lastSacrifice: DebouncedValue = DebouncedValue.farFuture() - - - @SubscribeEvent - fun onChat(event: ChatReceived) { - sacrificePattern.useMatcher(event.message) { - val sacrifice = itemIdProvider.findForName(group("sacrifice")) ?: return - val lootEssence = parseShortNumber(group("amount")) - consume(lastSacrifice.replace()) - lastSacrifice = DebouncedValue(LedgerEntry( - TransactionType.DRACONIC_SACRIFICE, - event.timestamp, - listOf( - ItemChange.lose(sacrifice, 1), - ItemChange.gain(ItemIds.ESSENCE_DRAGON, lootEssence) - ) - )) - } - bonusLootPattern.useMatcher(event.message) { - val bonusItem = itemIdProvider.findStackableItemByName( - group("bonus"), true - ) ?: return - lastSacrifice.replace()?.let { - consume( - it.copy(items = it.items + ItemChange.unpairGain(bonusItem)) - ) - } - } - } - - @SubscribeEvent - fun onTick(event: TickEvent) { - consume(lastSacrifice.consume(4.seconds)) - } - - fun consume(entry: LedgerEntry?) { - if (entry != null) - logger.logEntry(entry) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt deleted file mode 100644 index e747be9..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt +++ /dev/null @@ -1,95 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ExpiringValue -import moe.nea.ledger.ItemChange -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.ExtraSupplyIdEvent -import moe.nea.ledger.events.GuiClickEvent -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.getDisplayNameU -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant -import java.util.concurrent.locks.ReentrantLock -import kotlin.time.Duration.Companion.seconds - -class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) : ChestDetection() { - - @SubscribeEvent - fun onKismetClick(event: GuiClickEvent) { - val slot = event.slotIn ?: return - if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return - val stack = slot.stack ?: return - if (stack.getDisplayNameU() == "§aReroll Chest") { - logger.logEntry( - LedgerEntry( - TransactionType.KISMET_REROLL, - Instant.now(), - listOf( - ItemChange.lose(ItemIds.KISMET_FEATHER, 1) - ) - ) - ) - } - } - - - var lastOpenedChest = ExpiringValue.empty() - - @SubscribeEvent - fun supplyExtraIds(event: ExtraSupplyIdEvent) { - event.store("Dungeon Chest Key", ItemIds.DUNGEON_CHEST_KEY) - event.store("Kismet Feather", ItemIds.KISMET_FEATHER) - } - - @SubscribeEvent - fun onRewardChestClick(event: GuiClickEvent) { - lastOpenedChest = ExpiringValue(scrapeChestReward(event.slotIn ?: return) ?: return) - } - - class Mutex(defaultValue: T) { - private var value: T = defaultValue - val lock = ReentrantLock() - - fun getUnsafeLockedValue(): T { - if (!lock.isHeldByCurrentThread) - error("Accessed unsafe locked value, without holding the lock.") - return value - } - - fun withLock(func: (T) -> R): R { - lock.lockInterruptibly() - try { - val ret = func(value) - if (ret === value) { - error("Please don't smuggle out the locked value. If this is unintentional, please append a `Unit` instruction to the end of your `withLock` call: `.withLock { /* your existing code */; Unit }`.") - } - return ret - } finally { - lock.unlock() - } - } - } - - val rewardMessage = " (WOOD|GOLD|DIAMOND|EMERALD|OBSIDIAN|BEDROCK) CHEST REWARDS".toPattern() - - @SubscribeEvent - fun onChatMessage(event: ChatReceived) { - if (event.message == "You don't have that many coins in the bank!") { - lastOpenedChest.take() - } - rewardMessage.useMatcher(event.message) { - val chest = lastOpenedChest.consume(3.seconds) ?: return - logger.logEntry(LedgerEntry( - TransactionType.DUNGEON_CHEST_OPEN, - chest.timestamp, - chest.diff, - )) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt b/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt deleted file mode 100644 index 93bb453..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt +++ /dev/null @@ -1,43 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.events.InitializationComplete -import moe.nea.ledger.events.SupplyDebugInfo -import moe.nea.ledger.utils.GsonUtil -import moe.nea.ledger.utils.di.Inject -import moe.nea.ledger.utils.network.Request -import moe.nea.ledger.utils.network.RequestUtil -import net.minecraftforge.common.MinecraftForge -import net.minecraftforge.fml.common.eventhandler.Event -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.concurrent.CompletableFuture - -class ExternalDataProvider @Inject constructor( - val requestUtil: RequestUtil -) { - - fun createAuxillaryDataRequest(path: String): Request { - return requestUtil.createRequest("https://github.com/nea89o/ledger-auxiliary-data/raw/refs/heads/master/$path") - } - - private val itemNameFuture: CompletableFuture> = CompletableFuture.supplyAsync { - val request = createAuxillaryDataRequest("data/item_names.json") - val response = request.execute(requestUtil) - val nameMap = response.json(GsonUtil.typeToken>()) - return@supplyAsync nameMap - } - - lateinit var itemNames: Map - - class DataLoaded(val provider: ExternalDataProvider) : Event() - - @SubscribeEvent - fun onDebugData(debugInfo: SupplyDebugInfo) { - debugInfo.record("externalItemsLoaded", itemNameFuture.isDone && !itemNameFuture.isCompletedExceptionally) - } - - @SubscribeEvent - fun onInitComplete(event: InitializationComplete) { - itemNames = itemNameFuture.join() - MinecraftForge.EVENT_BUS.post(DataLoaded(this)) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt deleted file mode 100644 index 04dbe80..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt +++ /dev/null @@ -1,35 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class EyedropsDetection { - - val capsaicinEyedropsUsed = "You applied the eyedrops on the minion and ran out!".toPattern() - - @Inject - lateinit var logger: LedgerLogger - - @SubscribeEvent - fun onChat(event: ChatReceived) { - capsaicinEyedropsUsed.useMatcher(event.message) { - logger.logEntry( - LedgerEntry( - TransactionType.CAPSAICIN_EYEDROPS_USED, - event.timestamp, - listOf( - ItemChange.lose(ItemIds.CAPSAICIN_EYEDROPS_NO_CHARGES, 1) - ) - ) - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt deleted file mode 100644 index 95811ed..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt +++ /dev/null @@ -1,48 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.GuiClickEvent -import moe.nea.ledger.getInternalId -import moe.nea.ledger.matches -import moe.nea.ledger.unformattedString -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant - -class ForgeDetection { - val furnaceSlot = 9 + 4 - val furnaceName = "Forge Slot.*".toPattern() - - @SubscribeEvent - fun onClick(event: GuiClickEvent) { - val slot = event.slotIn ?: return - val clickedItem = slot.stack ?: return - if (clickedItem.displayName.unformattedString() != "Confirm") return - val furnaceSlotName = slot.inventory.getStackInSlot(furnaceSlot)?.displayName?.unformattedString() ?: return - if (!furnaceName.matches(furnaceSlotName)) - return - val cl = (0 until slot.inventory.sizeInventory - 9) - .mapNotNull { - val stack = slot.inventory.getStackInSlot(it) ?: return@mapNotNull null - val x = it % 9 - if (x == 4) return@mapNotNull null - ItemChange( - stack.getInternalId() ?: return@mapNotNull null, - stack.stackSize.toDouble(), - if (x < 4) ItemChange.ChangeDirection.LOST else ItemChange.ChangeDirection.GAINED - ) - } - logger.logEntry(LedgerEntry( - TransactionType.FORGED, - Instant.now(), - cl, - )) - } - - @Inject - lateinit var logger: LedgerLogger - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt deleted file mode 100644 index a8f79c1..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt +++ /dev/null @@ -1,62 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class GambleDetection { - - val dieRolled = - "Your (?High Class )?Archfiend Dice rolled a (?[1-7])!.*" - .toPattern() - - @Inject - lateinit var logger: LedgerLogger - - @SubscribeEvent - fun onChat(event: ChatReceived) { - dieRolled.useMatcher(event.message) { - val isLowClass = group("isHighClass").isNullOrBlank() - val item = if (isLowClass) ItemIds.ARCHFIEND_DICE else ItemIds.HIGH_CLASS_ARCHFIEND_DICE - val face = group("face") - val rollCost = if (isLowClass) 666_000.0 else 6_600_000.0 - if (face == "7") { - logger.logEntry(LedgerEntry( - TransactionType.DIE_ROLLED, - event.timestamp, - listOf( - ItemChange.lose(item, 1), - ItemChange.loseCoins(rollCost), - ItemChange.gain(ItemIds.DYE_ARCHFIEND, 1), - ) - )) - } else if (face == "6") { - logger.logEntry(LedgerEntry( - TransactionType.DIE_ROLLED, - event.timestamp, - listOf( - ItemChange.lose(item, 1), - ItemChange.loseCoins(rollCost), - ItemChange.gainCoins(if (isLowClass) 15_000_000.0 else 100_000_000.0), - ) - )) - } else { - logger.logEntry(LedgerEntry( - TransactionType.DIE_ROLLED, - event.timestamp, - listOf( - ItemChange(item, 1.0, ItemChange.ChangeDirection.CATALYST), - ItemChange.loseCoins(rollCost), - ) - )) - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt deleted file mode 100644 index ae86519..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt +++ /dev/null @@ -1,35 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.gen.ItemIds -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class GodPotionDetection { - - val godPotionDrank = "(SIP|SLURP|GULP|CHUGALUG)! The God Potion grants you powers for .*!".toPattern() - - @Inject - lateinit var logger: LedgerLogger - - @SubscribeEvent - fun onChat(event: ChatReceived) { - godPotionDrank.useMatcher(event.message) { - logger.logEntry( - LedgerEntry( - TransactionType.GOD_POTION_DRANK, - event.timestamp, - listOf( - ItemChange.lose(ItemIds.GOD_POTION_2, 1) - ) - ) - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt deleted file mode 100644 index b96a24a..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt +++ /dev/null @@ -1,38 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class GodPotionMixinDetection { - - val godPotionMixinDrank = "SCHLURP! The (effects of the )?(?.*?) (grants you effects|have been extended by) .*! They will pause if your God Potion expires.".toPattern() - - @Inject - lateinit var logger: LedgerLogger - - @Inject - lateinit var itemIdProvider: ItemIdProvider - - @SubscribeEvent - fun onChat(event: ChatReceived) { - godPotionMixinDrank.useMatcher(event.message) { - logger.logEntry( - LedgerEntry( - TransactionType.GOD_POTION_MIXIN_DRANK, - event.timestamp, - listOf( - ItemChange.lose(itemIdProvider.findForName(group("what")) ?: ItemId.NIL, 1) - ) - ) - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt deleted file mode 100644 index eda5aba..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt +++ /dev/null @@ -1,95 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.BeforeGuiAction -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.getInternalId -import moe.nea.ledger.getLore -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - - -class KatDetection { - @Inject - lateinit var log: LedgerLogger - - @Inject - lateinit var itemIdProvider: ItemIdProvider - - val giftNameToIdMap = mapOf( - "flower" to ItemId("KAT_FLOWER"), - "bouquet" to ItemId("KAT_BOUQUET"), - ) - val katGift = "\\[NPC\\] Kat: A (?.*)\\? For me\\? How sweet!".toPattern() - - @SubscribeEvent - fun onChat(event: ChatReceived) { - katGift.useMatcher(event.message) { - val giftName = group("gift") - val giftId = giftNameToIdMap[giftName] - log.logEntry(LedgerEntry( - TransactionType.KAT_TIMESKIP, - event.timestamp, - listOf( - ItemChange.lose(giftId ?: ItemId.NIL, 1) - ) - )) - } - } - - val confirmSlot = 9 + 9 + 4 - val petSlot = 9 + 4 - - data class PetUpgrade( - val beforePetId: ItemId, - val cost: List> - ) - - var lastPetUpgradeScheduled: PetUpgrade? = null - - @SubscribeEvent - fun onClick(event: BeforeGuiAction) { - val slots = event.chestSlots ?: return - val petItem = slots.lowerChestInventory.getStackInSlot(petSlot) ?: return - val beforePetId = petItem.getInternalId() ?: return - val confirmItem = slots.lowerChestInventory.getStackInSlot(confirmSlot) ?: return - val lore = confirmItem.getLore() - val cost = itemIdProvider.findCostItemsFromSpan(lore) - lastPetUpgradeScheduled = PetUpgrade(beforePetId, cost) - } - - val petUpgradeDialogue = "\\[NPC\\] Kat: I'll get your (?.*) upgraded to (?.*) in no time!".toPattern() - fun upgradePetTier(itemId: ItemId): ItemId { - val str = itemId.string.split(";", limit = 2) - if (str.size == 2) { - val (type, tier) = str - val tierT = tier.toIntOrNull() - if (tierT != null) - return ItemId(type + ";" + (tierT + 1)) - } - return itemId - } - - @SubscribeEvent - fun onPetUpgrade(event: ChatReceived) { - petUpgradeDialogue.useMatcher(event.message) { - val upgrade = lastPetUpgradeScheduled ?: return - lastPetUpgradeScheduled = null - log.logEntry(LedgerEntry( - TransactionType.KAT_UPGRADE, - event.timestamp, - listOf( - ItemChange.lose(upgrade.beforePetId, 1), - ItemChange.gain(upgradePetTier(upgrade.beforePetId), 1), - ) + upgrade.cost.map { ItemChange.lose(it.first, it.second) }, - )) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt deleted file mode 100644 index e0e9322..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt +++ /dev/null @@ -1,45 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.GuiClickEvent -import moe.nea.ledger.getInternalId -import moe.nea.ledger.utils.di.Inject -import net.minecraft.client.Minecraft -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class KuudraChestDetection : ChestDetection() { - // TODO: extra essence for kuudra pet (how?), item SALVAGE detection - // TODO: save uuid along side item id - - val kuudraKeyPattern = "KUUDRA_.*_TIER_KEY".toPattern() - - @Inject - lateinit var log: LedgerLogger - - @Inject - lateinit var minecraft: Minecraft - fun hasKey(keyItem: ItemId): Boolean { - val p = minecraft.thePlayer ?: return false - return p.inventory.mainInventory.any { it?.getInternalId() == keyItem } - } - - @SubscribeEvent - fun onRewardChestClick(event: GuiClickEvent) { - val diffs = scrapeChestReward(event.slotIn ?: return) ?: return - val requiredKey = diffs.diff.find { - it.direction == ItemChange.ChangeDirection.LOST && kuudraKeyPattern.asPredicate().test(it.itemId.string) - }?.itemId - if (requiredKey != null && !hasKey(requiredKey)) { - return - } - log.logEntry(LedgerEntry( - TransactionType.KUUDRA_CHEST_OPEN, - diffs.timestamp, - diffs.diff, - )) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt deleted file mode 100644 index 60b06ae..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt +++ /dev/null @@ -1,81 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.matches -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.BorderedTextTracker -import moe.nea.ledger.utils.di.Inject - -class MineshaftCorpseDetection : BorderedTextTracker() { - /* -[23:39:47] [Client thread/INFO]: [CHAT] §r§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§b§l§r§9§lLAPIS §r§b§lCORPSE LOOT! §r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a§lREWARDS§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§5+100 HOTM Experience§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a§r§aGreen Goblin Egg§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§9Enchanted Glacite §r§8x2§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§9☠ Fine Onyx Gemstone§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a☠ Flawed Onyx Gemstone §r§8x20§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§a☘ Flawed Peridot Gemstone §r§8x40§r -[23:39:47] [Client thread/INFO]: [CHAT] §r §r§bGlacite Powder §r§8x500§r -[23:39:47] [Client thread/INFO]: [CHAT] §e[SkyHanni] Profit for §9Lapis Corpse§e: §678k§r -[23:39:47] [Client thread/INFO]: [CHAT] §r§a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬§r - */ - - val corpseEnterMessage = " (?.*) CORPSE LOOT!".toPattern() - - override fun shouldEnter(event: ChatReceived): Boolean { - return corpseEnterMessage.matches(event.message) - } - - override fun shouldExit(event: ChatReceived): Boolean { - return genericBorderExit.matches(event.message) - } - - override fun onBorderedTextFinished(enclosed: List) { - val rewards = enclosed.asSequence() - .dropWhile { it.message != " REWARDS" } - .drop(1) - .mapNotNull { - itemIdProvider.findStackableItemByName(it.message, true) - } - .map { ItemChange.unpairGain(it) } - .toMutableList() - val introMessage = enclosed.first() - val corpseTyp = corpseEnterMessage.useMatcher(introMessage.message) { - group("corpseKind") - }!! - val keyTyp = corpseNameToKey[corpseTyp] - if (keyTyp == null) { - errorUtil.reportAdHoc("Unknown corpse type $corpseTyp") - } else if (keyTyp != ItemId.NIL) { - rewards.add(ItemChange.lose(keyTyp, 1)) - } - logger.logEntry( - LedgerEntry( - TransactionType.CORPSE_DESECRATED, - introMessage.timestamp, - rewards - ) - ) - } - - val corpseNameToKey = mapOf( - "LAPIS" to ItemId.NIL, - "VANGUARD" to ItemId("SKELETON_KEY"), - "UMBER" to ItemId("UMBER_KEY"), - "TUNGSTEN" to ItemId("TUNGSTEN_KEY"), - ) - - @Inject - lateinit var logger: LedgerLogger - - @Inject - lateinit var itemIdProvider: ItemIdProvider -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt deleted file mode 100644 index 6999c7f..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt +++ /dev/null @@ -1,61 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ExpiringValue -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.ROMAN_NUMBER_PATTERN -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.BeforeGuiAction -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.parseRomanNumber -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant -import kotlin.time.Duration.Companion.seconds - -class MinionDetection @Inject constructor(val ledger: LedgerLogger) { - // §aYou received §r§6367,516.8 coins§r§a! - val hopperCollectPattern = "You received (?$SHORT_NUMBER_PATTERN) coins?!".toPattern() - val minionNamePattern = "(?.*) Minion (?$ROMAN_NUMBER_PATTERN)".toPattern() - - var lastOpenedMinion = ExpiringValue.empty() - - @SubscribeEvent - fun onBeforeClaim(event: BeforeGuiAction) { - val container = event.gui as? GuiChest ?: return - val inv = (container.inventorySlots as ContainerChest).lowerChestInventory - val invName = inv.displayName.unformattedText.unformattedString() - minionNamePattern.useMatcher(invName) { - val name = group("name") - val level = parseRomanNumber(group("level")) - lastOpenedMinion = ExpiringValue( - ItemId(name.uppercase().replace(" ", "_") - .replace("MINION", "GENERATOR") + "_" + level)) - } - } - - - @SubscribeEvent - fun onChat(event: ChatReceived) { - hopperCollectPattern.useMatcher(event.message) { - val minionName = lastOpenedMinion.consume(3.seconds) - ledger.logEntry(LedgerEntry( - TransactionType.AUTOMERCHANT_PROFIT_COLLECT, - Instant.now(), - listOf( - ItemChange.gainCoins(parseShortNumber(group("amount"))), - ItemChange(minionName ?: ItemId.NIL, 1.0, ItemChange.ChangeDirection.CATALYST) - ), - )) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt deleted file mode 100644 index 95b8aa5..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt +++ /dev/null @@ -1,111 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.asIterable -import moe.nea.ledger.events.BeforeGuiAction -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.events.ExtraSupplyIdEvent -import moe.nea.ledger.getDisplayNameU -import moe.nea.ledger.getInternalId -import moe.nea.ledger.getLore -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.ErrorUtil -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class NpcDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { - - val npcBuyPattern = - Pattern.compile("You bought (back )?(?.*?) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins?!") - val npcSellPattern = - Pattern.compile("You sold (?.*) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins?!") - - // You bought InfiniDirt™ Wand! - // You bought Prismapump x4! - val npcBuyWithItemPattern = - "You bought (?.*?)!".toPattern() - var storedPurchases = mutableMapOf>() - - @SubscribeEvent - fun onClick(event: BeforeGuiAction) { - (event.chestSlots?.lowerChestInventory?.asIterable() ?: listOf()) - .filterNotNull().forEach { - val name = it.getDisplayNameU().unformattedString() - val id = it.getInternalId() ?: return@forEach - val count = it.stackSize - val cost = ids.findCostItemsFromSpan(it.getLore()) - storedPurchases[name] = listOf(ItemChange.gain(id, count)) + cost.map { ItemChange.unpairLose(it) } - } - } - - @SubscribeEvent - fun addChocolate(event: ExtraSupplyIdEvent) { - event.store("Chocolate", ItemId("SKYBLOCK_CHOCOLATE")) - } - - @Inject - lateinit var errorUtil: ErrorUtil - - @SubscribeEvent - fun onBarteredItemBought(event: ChatReceived) { - npcBuyWithItemPattern.useMatcher(event.message) { - val changes = storedPurchases[group("what")] - if (changes == null) { - errorUtil.reportAdHoc("Item bought for items without associated cost") - } - storedPurchases.clear() - ledger.logEntry( - LedgerEntry( - TransactionType.NPC_BUY, - event.timestamp, - changes ?: listOf() - ) - ) - } - } - - @SubscribeEvent - fun onNpcBuy(event: ChatReceived) { - npcBuyPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.NPC_BUY, - event.timestamp, - listOf( - ItemChange.loseCoins( - parseShortNumber(group("coins")), - ), - ItemChange.gain( - ids.findForName(group("what")) ?: ItemId.NIL, - group("count")?.let(::parseShortNumber) ?: 1, - ) - ) - ) - ) - } - npcSellPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - TransactionType.NPC_SELL, - event.timestamp, - listOf( - ItemChange.gainCoins(parseShortNumber(group("coins"))), - ItemChange.lose( - ids.findForName(group("what")) ?: ItemId.NIL, - group("count")?.let(::parseShortNumber)?.toInt() ?: 1, - ) - ) - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt b/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt deleted file mode 100644 index 0d89ca1..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt +++ /dev/null @@ -1,167 +0,0 @@ -package moe.nea.ledger.modules - -import com.google.gson.JsonElement -import com.google.gson.JsonPrimitive -import moe.nea.ledger.DevUtil -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.TriggerCommand -import moe.nea.ledger.config.LedgerConfig -import moe.nea.ledger.config.MainOptions -import moe.nea.ledger.events.RegistrationFinishedEvent -import moe.nea.ledger.events.TriggerEvent -import moe.nea.ledger.gen.BuildConfig -import moe.nea.ledger.utils.ErrorUtil -import moe.nea.ledger.utils.MinecraftExecutor -import moe.nea.ledger.utils.di.Inject -import moe.nea.ledger.utils.network.RequestUtil -import moe.nea.libautoupdate.CurrentVersion -import moe.nea.libautoupdate.GithubReleaseUpdateData -import moe.nea.libautoupdate.GithubReleaseUpdateSource -import moe.nea.libautoupdate.PotentialUpdate -import moe.nea.libautoupdate.UpdateContext -import moe.nea.libautoupdate.UpdateData -import moe.nea.libautoupdate.UpdateTarget -import moe.nea.libautoupdate.UpdateUtils -import net.minecraft.util.ChatComponentText -import net.minecraft.util.ChatStyle -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.concurrent.CompletableFuture - -class UpdateChecker @Inject constructor( - val errorUtil: ErrorUtil, - val requestUtil: RequestUtil, -) { - - @Inject - lateinit var minecraftExecutor: MinecraftExecutor - - val updater = UpdateContext( - NightlyAwareGithubUpdateSource("nea89o", "LocalTransactionLedger"), - if (DevUtil.isDevEnv) UpdateTarget { listOf() } - else UpdateTarget.deleteAndSaveInTheSameFolder(UpdateChecker::class.java), - object : CurrentVersion { - override fun display(): String { - return BuildConfig.VERSION - } - - override fun isOlderThan(element: JsonElement?): Boolean { - if (element !is JsonPrimitive || !element.isString) return true - val newHash = element.asString // TODO: change once i support non nightly update streams - val length = minOf(newHash.length, BuildConfig.GIT_COMMIT.length) - if (newHash.substring(0, length).equals(BuildConfig.GIT_COMMIT.substring(0, length), ignoreCase = true)) - return false - return true - } - - - override fun toString(): String { - return "{gitversion:${BuildConfig.GIT_COMMIT}, version:${BuildConfig.FULL_VERSION}}" - } - }, - "ledger" - ) - - class NightlyAwareGithubUpdateSource(owner: String, repository: String) : - GithubReleaseUpdateSource(owner, repository) { - override fun selectUpdate(updateStream: String, releases: List): UpdateData? { - if (updateStream == "nightly") { - return findAsset(releases.find { it.tagName == "nightly" }) - } - return super.selectUpdate(updateStream, releases.filter { it.tagName != "nightly" }) - } - - val releaseRegex = "commit: `(?[a-f0-9]+)`".toPattern() - - override fun findAsset(release: GithubRelease?): UpdateData? { - val update = super.findAsset(release) as GithubReleaseUpdateData? ?: return null - return GithubReleaseUpdateData( - update.versionName, - releaseRegex.matcher(update.releaseDescription) - .takeIf { it.find() } - ?.run { group("hash") } - ?.let(::JsonPrimitive) - ?: update.versionNumber, - update.sha256, - update.download, - update.releaseDescription, - update.targetCommittish, - update.createdAt, - update.publishedAt, - update.htmlUrl - ) - } - } - - init { - UpdateUtils.patchConnection { - this.requestUtil.enhanceConnection(it) - } - } - - var latestUpdate: PotentialUpdate? = null - var hasNotified = false - - @SubscribeEvent - fun onStartup(event: RegistrationFinishedEvent) { - if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.NONE) return - launchUpdateCheck() - } - - fun launchUpdateCheck() { - errorUtil.listenToFuture( - updater.checkUpdate("nightly") - .thenAcceptAsync( - { - latestUpdate = it - informAboutUpdates(it) - }, minecraftExecutor) - ) - } - - @Inject - lateinit var config: LedgerConfig - - @Inject - lateinit var triggerCommand: TriggerCommand - - val installTrigger = "execute-download" - - @Inject - lateinit var logger: LedgerLogger - fun informAboutUpdates(potentialUpdate: PotentialUpdate) { - if (hasNotified) return - hasNotified = true - if (!potentialUpdate.isUpdateAvailable) return - logger.printOut( - ChatComponentText("§aThere is a new update for LocalTransactionLedger. Click here to automatically download and install it.") - .setChatStyle(ChatStyle().setChatClickEvent(triggerCommand.getTriggerCommandLine(installTrigger)))) - if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.FULL) { - downloadUpdate() - } - } - - var updateFuture: CompletableFuture? = null - - fun downloadUpdate() { - val l = latestUpdate ?: return - if (updateFuture != null) return - // TODO: inject into findAsset to overwrite the tag id with the commit id - logger.printOut("§aTrying to download ledger update ${l.update.versionName}") - updateFuture = - latestUpdate?.launchUpdate() - ?.thenAcceptAsync( - { - logger.printOut("§aLedger update downloaded. It will automatically apply after your next restart.") - }, minecraftExecutor) - ?.let(errorUtil::listenToFuture) - } - - @SubscribeEvent - fun onTrigger(event: TriggerEvent) { - if (event.action == installTrigger) { - event.isCanceled = true - downloadUpdate() - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt deleted file mode 100644 index f457ae4..0000000 --- a/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt +++ /dev/null @@ -1,87 +0,0 @@ -package moe.nea.ledger.modules - -import moe.nea.ledger.ItemChange -import moe.nea.ledger.ItemId -import moe.nea.ledger.ItemIdProvider -import moe.nea.ledger.LedgerEntry -import moe.nea.ledger.LedgerLogger -import moe.nea.ledger.SHORT_NUMBER_PATTERN -import moe.nea.ledger.TransactionType -import moe.nea.ledger.events.ExtraSupplyIdEvent -import moe.nea.ledger.events.GuiClickEvent -import moe.nea.ledger.getDisplayNameU -import moe.nea.ledger.getLore -import moe.nea.ledger.parseShortNumber -import moe.nea.ledger.unformattedString -import moe.nea.ledger.useMatcher -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.time.Instant - -class VisitorDetection { - @Inject - lateinit var logger: LedgerLogger - - @Inject - lateinit var idProvider: ItemIdProvider - - @SubscribeEvent - fun parseFromItem(event: GuiClickEvent) { - val stack = event.slotIn?.stack ?: return - - val displayName = stack.getDisplayNameU() - if (displayName != "§aAccept Offer") return - val lore = stack.getLore() - if (!lore.contains("§eClick to give!")) return - - val rewards = lore - .asSequence() - .dropWhile { it != "§7Rewards:" }.drop(1) - .takeWhile { it != "" } - .mapNotNull { parseGardenLoreLine(it) } - .map { ItemChange.gain(it.first, it.second) } - .toList() - - val cost = lore - .asSequence() - .dropWhile { it != "§7Items Required:" }.drop(1) - .takeWhile { it != "" } - .mapNotNull { parseGardenLoreLine(it) } - .map { ItemChange.lose(it.first, it.second) } - .toList() - - logger.logEntry(LedgerEntry( - TransactionType.VISITOR_BARGAIN, - Instant.now(), - cost + rewards - )) - } - - private fun parseGardenLoreLine(rewardLine: String): Pair? { - val f = rewardLine.unformattedString().trim() - return idProvider.findStackableItemByName(f, true) - } - - - @SubscribeEvent - fun supplyNames(event: ExtraSupplyIdEvent) { - event.store("Carrot", ItemId("CARROT_ITEM")) - event.store("Potato", ItemId("POTATO_ITEM")) - event.store("Jacob's Ticket", ItemId("JACOBS_TICKET")) - event.store("Cocoa Beans", ItemId("INK_SACK:3")) - event.store("Enchanted Cocoa Beans", ItemId("ENCHANTED_COCOA")) - event.store("Enchanted Red Mushroom Block", ItemId("ENCHANTED_HUGE_MUSHROOM_2")) - event.store("Enchanted Brown Mushroom Block", ItemId("ENCHANTED_HUGE_MUSHROOM_1")) - event.store("Nether Wart", ItemId("NETHER_STALK")) - event.store("Enchanted Nether Wart", ItemId("ENCHANTED_NETHER_STALK")) - event.store("Mutant Nether Wart", ItemId("MUTANT_NETHER_STALK")) - event.store("Jack o' Lantern", ItemId("JACK_O_LANTERN")) - event.store("Cactus Green", ItemId("INK_SACK:2")) - event.store("Hay Bale", ItemId("HAY_BLOCK")) - event.store("Rabbit's Foot", ItemId("RABBIT_FOOT")) - event.store("Raw Porkchop", ItemId("PORK")) - event.store("Raw Rabbit", ItemId("RABBIT")) - event.store("White Wool", ItemId("WOOL")) - event.store("Copper Dye", ItemId("DYE_COPPER")) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt b/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt deleted file mode 100644 index 9e621e8..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/BorderedTextTracker.kt +++ /dev/null @@ -1,41 +0,0 @@ -package moe.nea.ledger.utils - -import moe.nea.ledger.events.ChatReceived -import moe.nea.ledger.utils.di.Inject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -abstract class BorderedTextTracker { - - val genericBorderExit = "▬{10,}".toPattern() - - @Inject - lateinit var errorUtil: ErrorUtil - var stack: MutableList? = null - - - @SubscribeEvent - fun receiveText(event: ChatReceived) { - if (stack != null && shouldExit(event)) { - exit() - return - } - if (shouldEnter(event)) { - if (stack != null) { - errorUtil.reportAdHoc("Double entered a bordered message") - exit() - } - stack = mutableListOf() - } - stack?.add(event) - } - - private fun exit() { - onBorderedTextFinished(stack!!) - stack = null - } - - abstract fun shouldEnter(event: ChatReceived): Boolean - abstract fun shouldExit(event: ChatReceived): Boolean - abstract fun onBorderedTextFinished(enclosed: List) - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt deleted file mode 100644 index e0c83f9..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt +++ /dev/null @@ -1,52 +0,0 @@ -package moe.nea.ledger.utils - -import moe.nea.ledger.utils.di.Inject -import moe.nea.ledger.utils.telemetry.ContextValue -import moe.nea.ledger.utils.telemetry.EventRecorder -import moe.nea.ledger.utils.telemetry.Span -import java.util.concurrent.CompletableFuture - -class ErrorUtil { - - @Inject - lateinit var reporter: EventRecorder - - fun reportAdHoc(message: String) { - report(Exception(message), message) - - } - - fun report(exception: Throwable, message: String?) { - Span.current().recordException(reporter, exception, message) - } - - fun Result.getOrReport(): T? { - val exc = exceptionOrNull() - if (exc != null) { - report(exc, null) - } - return getOrNull() - } - - fun > listenToFuture(t: T): T { - t.handle { ignored, exception -> - if (exception != null) - report(exception, "Uncaught exception in completable future") - } - return t - } - - inline fun catch( - vararg pairs: Pair, - crossinline function: () -> T - ): T? { - return Span.current().enterWith(*pairs) { - try { - return@enterWith function() - } catch (ex: Exception) { - report(ex, null) - return@enterWith null - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt deleted file mode 100644 index d3c1f6e..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt +++ /dev/null @@ -1,10 +0,0 @@ -package moe.nea.ledger.utils - -import com.google.gson.reflect.TypeToken - -object GsonUtil { - inline fun typeToken(): TypeToken { - return object : TypeToken() {} - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt b/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt deleted file mode 100644 index affd86c..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt +++ /dev/null @@ -1,10 +0,0 @@ -package moe.nea.ledger.utils - -import net.minecraft.client.Minecraft -import java.util.concurrent.Executor - -class MinecraftExecutor : Executor { - override fun execute(command: Runnable) { - Minecraft.getMinecraft().addScheduledTask(command) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt b/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt deleted file mode 100644 index 9b0e7a3..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/NoSideEffects.kt +++ /dev/null @@ -1,4 +0,0 @@ -package moe.nea.ledger.utils - -@Retention(AnnotationRetention.BINARY) -annotation class NoSideEffects diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt b/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt deleted file mode 100644 index ddf2fcc..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt +++ /dev/null @@ -1,40 +0,0 @@ -package moe.nea.ledger.utils.network - -import com.google.gson.JsonElement -import java.net.URL - -data class Request( - val url: URL, - val method: Method, - val body: String?, - val headers: Map, -) { - enum class Method { - GET, POST - } - - enum class MediaType(val text: String) { - JSON("application/json"), - TEXT("text/plain"), - HTML("text/html"), - ANY("*/*"), - } - - fun withHeaders(map: Map): Request { - // TODO: enforce caselessness? - return this.copy(headers = headers + map) - } - - fun post() = copy(method = Method.POST) - fun get() = copy(method = Method.GET) - - fun json(element: JsonElement) = copy( - headers = headers + mapOf("content-type" to "application/json"), - body = element.toString()) - - fun accept(request: MediaType) = withHeaders(mapOf("accept" to request.text)) - - fun acceptJson() = accept(MediaType.JSON) - - fun execute(requestUtil: RequestUtil) = requestUtil.executeRequest(this) -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt deleted file mode 100644 index a49c65a..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt +++ /dev/null @@ -1,63 +0,0 @@ -package moe.nea.ledger.utils.network - -import moe.nea.ledger.utils.ErrorUtil -import moe.nea.ledger.utils.di.Inject -import java.net.URL -import java.net.URLConnection -import java.security.KeyStore -import java.util.zip.GZIPInputStream -import javax.net.ssl.HttpsURLConnection -import javax.net.ssl.KeyManagerFactory -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManagerFactory - -class RequestUtil @Inject constructor(val errorUtil: ErrorUtil) { - - private fun createSSLContext(): SSLContext? = errorUtil.catch { - val keyStorePath = RequestUtil::class.java.getResourceAsStream("/ledgerkeystore.jks") - ?: error("Could not locate keystore") - val keyStore = KeyStore.getInstance("JKS") - keyStore.load(keyStorePath, "neuneu".toCharArray()) - val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) - val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - kmf.init(keyStore, null) - tmf.init(keyStore) - val ctx = SSLContext.getInstance("TLS") - ctx.init(kmf.keyManagers, tmf.trustManagers, null) - return@catch ctx - } - - val sslContext = createSSLContext() - - fun enhanceConnection(connection: URLConnection) { - if (connection is HttpsURLConnection && sslContext != null) { - connection.sslSocketFactory = sslContext.socketFactory - } - } - - fun createRequest(url: String) = createRequest(URL(url)) - fun createRequest(url: URL) = Request(url, Request.Method.GET, null, mapOf()) - - fun executeRequest(request: Request): Response { - val connection = request.url.openConnection() - enhanceConnection(connection) - connection.setRequestProperty("accept-encoding", "gzip") - request.headers.forEach { (k, v) -> - connection.setRequestProperty(k, v) - } - if (request.body != null) { - connection.getOutputStream().write(request.body.encodeToByteArray()) - connection.getOutputStream().close() - } - var stream = connection.getInputStream() - if (connection.contentEncoding == "gzip") { - stream = GZIPInputStream(stream) - } - val text = stream.bufferedReader().readText() - stream.close() - // Do NOT call connection.disconnect() to allow for connection reuse - return Response(request, text, connection.headerFields) - } - - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt b/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt deleted file mode 100644 index daae7f7..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt +++ /dev/null @@ -1,19 +0,0 @@ -package moe.nea.ledger.utils.network - -import com.google.gson.reflect.TypeToken -import moe.nea.ledger.Ledger - -data class Response( - val source: Request, - // TODO: allow other body processors, to avoid loading everything as strings - val response: String, - val headers: Map>, -) { - fun json(typ: TypeToken): T { - return Ledger.gson.fromJson(response, typ.type) - } - - fun json(clazz: Class): T { - return Ledger.gson.fromJson(response, clazz) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt deleted file mode 100644 index 5f4ccdf..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/BooleanContext.kt +++ /dev/null @@ -1,10 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonElement -import com.google.gson.JsonPrimitive - -class BooleanContext(val boolean: Boolean) : ContextValue { - override fun serialize(): JsonElement { - return JsonPrimitive(boolean) - } -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt deleted file mode 100644 index 004ae9c..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/CommonKeys.kt +++ /dev/null @@ -1,9 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -object CommonKeys { - val EVENT_MESSAGE = "event_message" - val EXCEPTION = "event_exception" - val COMMIT_VERSION = "version_commit" - val VERSION = "version" - val PHASE = "phase" // TODO: add a sort of "manual" stacktrace with designated function phases -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt deleted file mode 100644 index 3c30a52..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Context.kt +++ /dev/null @@ -1,57 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonObject - -class Context(val data: MutableMap = mutableMapOf()) : ContextValue.Collatable { - - inline fun getT(key: String): T? { - return get(key) as? T - } - - fun get(key: String): ContextValue? { - return data[key] - } - - fun add(key: String, value: ContextValue) { - data[key] = value - } - - @Suppress("NOTHING_TO_INLINE") - private inline fun > cope( - left: ContextValue.Collatable, - right: ContextValue - ): ContextValue { - return try { - left.combineWith(right as T) - } catch (ex: Exception) { - // TODO: cope with this better - right - } - } - - override fun combineWith(overrides: Context): Context { - val copy = data.toMutableMap() - for ((key, overrideValue) in overrides.data) { - copy.merge(key, overrideValue) { old, new -> - if (old is ContextValue.Collatable<*>) { - cope(old, new) - } else { - new - } - } - } - return Context(copy) - } - - override fun actualize(): Context { - return this - } - - override fun serialize(): JsonObject { - val obj = JsonObject() - data.forEach { (k, v) -> - obj.add(k, v.serialize()) - } - return obj - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt deleted file mode 100644 index b5891fc..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ContextValue.kt +++ /dev/null @@ -1,70 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonElement -import com.google.gson.JsonObject - -interface ContextValue { - companion object { - fun > lazyCollatable(value: () -> Collatable): Collatable { - return LazyCollatable(value) - } - - fun lazy(value: () -> ContextValue): ContextValue { - return object : ContextValue { - val value by kotlin.lazy(value) - override fun serialize(): JsonElement { - return this.value.serialize() - } - } - } - - fun bool(boolean: Boolean): ContextValue { - return BooleanContext(boolean) - } - - fun string(message: String): ContextValue { - return StringContext(message) - } - - fun jsonObject(vararg pairs: Pair): ContextValue { - val obj = JsonObject() - for ((l, r) in pairs) { - obj.add(l, r) - } - return JsonElementContext(obj) - } - - fun compound(vararg pairs: Pair): ContextValue { - val obj = JsonObject() - for ((l, r) in pairs) { - obj.addProperty(l, r) - } - // TODO: should this be its own class? - return JsonElementContext(obj) - } - } - - // TODO: allow other serialization formats - fun serialize(): JsonElement - interface Collatable> : ContextValue { - fun combineWith(overrides: T): T - fun actualize(): T - } - - private class LazyCollatable>( - provider: () -> Collatable, - ) : Collatable { - val value by kotlin.lazy(provider) - override fun actualize(): T { - return value.actualize() - } - - override fun combineWith(overrides: T): T { - return value.combineWith(overrides) - } - - override fun serialize(): JsonElement { - return value.serialize() - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt deleted file mode 100644 index 28b1ab5..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/EventRecorder.kt +++ /dev/null @@ -1,9 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -interface EventRecorder { - companion object { - var instance: EventRecorder? = null - } - - fun record(event: RecordedEvent) -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt deleted file mode 100644 index 96b70ec..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt +++ /dev/null @@ -1,39 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject - -class ExceptionContextValue(val exception: Throwable) : ContextValue { - val stackTrace by lazy { - exception.stackTraceToString() - } - - override fun serialize(): JsonElement { - val jsonObject = JsonObject() - jsonObject.addProperty("exception_stackTrace", stackTrace) - jsonObject.add("exception_structure", walkExceptions(exception, 6)) - return jsonObject - } - - private fun walkExceptions(exception: Throwable, searchDepth: Int): JsonElement { - val obj = JsonObject() - obj.addProperty("class", exception.javaClass.name) - obj.addProperty("message", exception.message) - // TODO: allow exceptions to implement an "extra info" interface - if (searchDepth > 0) { - val cause = exception.cause - if (cause != null && cause !== exception) { - obj.add("cause", walkExceptions(cause, searchDepth - 1)) - } - val suppressions = JsonArray() - for (suppressedException in exception.suppressedExceptions) { - suppressions.add(walkExceptions(suppressedException, searchDepth - 1)) - } - if (suppressions.size() > 0) { - obj.add("suppressions", suppressions) - } - } - return obj - } -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt deleted file mode 100644 index 1601f56..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/JsonElementContext.kt +++ /dev/null @@ -1,9 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonElement - -class JsonElementContext(val element: JsonElement) : ContextValue { - override fun serialize(): JsonElement { - return element - } -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt deleted file mode 100644 index 82a76ed..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/LoggingEventRecorder.kt +++ /dev/null @@ -1,25 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.GsonBuilder -import org.apache.logging.log4j.Logger - -class LoggingEventRecorder( - val logger: Logger, - val logJson: Boolean -) : EventRecorder { - companion object { - private val gson = GsonBuilder().setPrettyPrinting().create() - } - - override fun record(event: RecordedEvent) { - val exc = event.context.getT(CommonKeys.EXCEPTION) - var message = "Event Recorded: " + event.context.getT(CommonKeys.EVENT_MESSAGE)?.message - if (logJson) { - message += "\n" + gson.toJson(event.context.serialize()) - } - if (exc != null) - logger.error(message, exc.exception) - else - logger.warn(message) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt deleted file mode 100644 index 346417d..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/RecordedEvent.kt +++ /dev/null @@ -1,5 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -class RecordedEvent(val context: Context) { - -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt deleted file mode 100644 index e9a3b79..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Severity.kt +++ /dev/null @@ -1,8 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -enum class Severity { - INFO, - WARN, - ERROR, - CRITICAL, -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt deleted file mode 100644 index 0d680a9..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt +++ /dev/null @@ -1,146 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -class Span(val parent: Span?) : AutoCloseable { - companion object { - private val _current = object : InheritableThreadLocal() { - override fun initialValue(): Span { - return Span(null) - } - - override fun childValue(parentValue: Span?): Span { - return parentValue?.forkNewRoot() ?: initialValue() - } - } - - fun current(): Span { - return _current.get() - } - } - - private val data = Context() - - // TODO : replace string key with a SpanKey class - fun add(key: String, value: ContextValue) { - data.add(key, value) - } - - /** - * Create a sub span, and [enter] it, with the given values. - */ - fun enterWith(vararg pairs: Pair, block: Span.() -> T): T { - return enter().use { span -> - pairs.forEach { (k, value) -> - span.add(k, value) - } - block(span) - } - } - - /** - * Create a sub span, to attach some additional context, without modifying the [current] at all. - */ - fun forkWith(vararg pairs: Pair): Span { - val newSpan = fork() - for ((key, value) in pairs) { - if (value == null) continue - newSpan.add(key, value) - } - return newSpan - } - - /** - * Create a sub span, to which additional context can be added. This context will receive updates from its parent, - * and will be set as the [current]. To return to the parent, either call [exit] on the child. Or use inside of a - * [use] block. - */ - fun enter(): Span { - require(_current.get() == this) - return fork().enterSelf() - } - - /** - * Force [enter] this span, without creating a subspan. This bypasses checks like parent / child being the [current]. - */ - fun enterSelf(): Span { - _current.set(this) - return this - } - - /** - * Creates a temporary sub span, to which additional context can be added. This context will receive updates from - * its parent, but will not be set as the [current]. - */ - fun fork(): Span { - return Span(this) - } - - /** - * Create a new root span, that will not receive any updates from the current span, but will have all the same - * context keys associated. - */ - fun forkNewRoot(): Span { - val newRoot = Span(null) - newRoot.data.data.putAll(collectContext().data) - return newRoot - } - - /** - * Collect the context, including all parent context - */ - fun collectContext(): Context { - if (parent != null) - return data.combineWith(parent.collectContext()) - return data - } - - /** - * Exit an [entered][enter] span, returning back to the parent context, and discard any current keys. - */ - fun exit() { - require(parent != null) - require(_current.get() == this) - _current.set(parent) - } - - /** - * [AutoCloseable] implementation for [exit] - */ - override fun close() { - return exit() - } - - /** - * Record an empty event given the context. This indicates nothing except for "I was here". - * @see recordMessageEvent - * @see recordException - */ - fun recordEmptyTrace(recorder: EventRecorder) { - recorder.record(RecordedEvent(collectContext())) - } - - /** - * Record a message with the key `"event_message"` to the recorder - */ - fun recordMessageEvent( - recorder: EventRecorder, - message: String - ) { - forkWith(CommonKeys.EVENT_MESSAGE to ContextValue.string(message)) - .recordEmptyTrace(recorder) - } - - /** - * Record an exception to the recorder - */ - fun recordException( - recorder: EventRecorder, - exception: Throwable, - message: String? = null - ) { - forkWith( - CommonKeys.EVENT_MESSAGE to message?.let(ContextValue::string), - CommonKeys.EXCEPTION to ExceptionContextValue(exception), - ).recordEmptyTrace(recorder) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt deleted file mode 100644 index 2d33075..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/StringContext.kt +++ /dev/null @@ -1,11 +0,0 @@ -package moe.nea.ledger.utils.telemetry - -import com.google.gson.JsonElement -import com.google.gson.JsonPrimitive - -class StringContext(val message: String) : ContextValue { - override fun serialize(): JsonElement { - return JsonPrimitive(message) - } - -} diff --git a/src/main/resources/ledgerkeystore.jks b/src/main/resources/ledgerkeystore.jks deleted file mode 100644 index b71185a..0000000 Binary files a/src/main/resources/ledgerkeystore.jks and /dev/null differ diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index fdeffd8..0000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "modid": "${modid}", - "name": "Simple Ledger Mod", - "description": "Export all your activities into a file", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "https://github.com/nea89o/LocalTransactionLedger/", - "updateUrl": "", - "authorList": [ - "nea89" - ], - "credits": "", - "logoFile": "", - "screenshots": [], - "dependencies": [] - } -] \ No newline at end of file diff --git a/src/main/resources/mixins.moneyledger.json b/src/main/resources/mixins.moneyledger.json deleted file mode 100644 index fa6482e..0000000 --- a/src/main/resources/mixins.moneyledger.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "package": "${basePackage}.mixin", - "plugin": "${basePackage}.init.AutoDiscoveryMixinPlugin", - "minVersion": "0.7", - "compatibilityLevel": "JAVA_8", - "__comment": "You do not need to manually register mixins in this template. Check the auto discovery mixin plugin for more info." -} diff --git a/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt b/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt deleted file mode 100644 index 4068a42..0000000 --- a/src/test/kotlin/moe/nea/ledger/NumberUtilKtTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package moe.nea.ledger - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class NumberUtilKtTest { - @Test - fun parseRomanNumberTest() { - assertEquals(4, parseRomanNumber("IV")) - assertEquals(1, parseRomanNumber("I")) - assertEquals(14, parseRomanNumber("XIV")) - assertEquals(3, parseRomanNumber("III")) - assertEquals(8, parseRomanNumber("IIX")) - assertEquals(500, parseRomanNumber("DM")) - assertEquals(2024, parseRomanNumber("MMXXIV")) - } -} \ No newline at end of file -- cgit