From 6cf27edd12d52b67cd4ec7c8c6245e09eaa04be3 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:15:45 +0100 Subject: [PATCH 01/24] Add move button in operations --- .../widgets/components/app/entry_node.dart | 49 ++++++++++--------- app/lib/widgets/inspector/operations.dart | 23 +++++++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/app/lib/widgets/components/app/entry_node.dart b/app/lib/widgets/components/app/entry_node.dart index 5345e3d23e..c09252c27e 100644 --- a/app/lib/widgets/components/app/entry_node.dart +++ b/app/lib/widgets/components/app/entry_node.dart @@ -156,6 +156,31 @@ Future pathSelector( return completer.future; } +void moveEntryToSelectingPage(PassingRef ref, String entryId) { + final from = ref.read(entryPageIdProvider(entryId)); + if (from == null) return; + + final entryType = ref.read(entryTypeProvider(entryId)); + if (entryType == null) return; + + final pageType = ref.read(entryBlueprintPageTypeProvider(entryType)); + + ref.read(searchProvider.notifier).asBuilder() + ..pageType(pageType) + ..fetchPage( + onSelect: (page) { + ref.read(bookProvider.notifier).moveEntry(entryId, from, page.pageName); + return true; + }, + ) + ..fetchAddPage( + onAdded: (page) { + ref.read(bookProvider.notifier).moveEntry(entryId, from, page.pageName); + }, + ) + ..open(); +} + class _EntryNode extends HookConsumerWidget { const _EntryNode({ required this.id, @@ -202,28 +227,6 @@ class _EntryNode extends HookConsumerWidget { await page.linkWithDuplicate(ref, id, path); } - void _moveEntry(BuildContext context, PassingRef ref) { - final from = ref.read(entryPageIdProvider(id)); - if (from == null) return; - - final pageType = ref.read(entryBlueprintPageTypeProvider(type)); - - ref.read(searchProvider.notifier).asBuilder() - ..pageType(pageType) - ..fetchPage( - onSelect: (page) { - ref.read(bookProvider.notifier).moveEntry(id, from, page.pageName); - return true; - }, - ) - ..fetchAddPage( - onAdded: (page) { - ref.read(bookProvider.notifier).moveEntry(id, from, page.pageName); - }, - ) - ..open(); - } - void _deleteEntry(BuildContext context, PassingRef ref) { final page = ref.read(currentPageProvider); if (page == null) return; @@ -268,7 +271,7 @@ class _EntryNode extends HookConsumerWidget { title: "Move to ...", icon: TWIcons.moveEntry, color: Colors.blueAccent, - onTap: () => _moveEntry(context, ref.passing), + onTap: () => moveEntryToSelectingPage(ref.passing, id), ), ContextMenuTile.divider(), ContextMenuTile.button( diff --git a/app/lib/widgets/inspector/operations.dart b/app/lib/widgets/inspector/operations.dart index 89c3f9c95c..ade9026163 100644 --- a/app/lib/widgets/inspector/operations.dart +++ b/app/lib/widgets/inspector/operations.dart @@ -54,6 +54,8 @@ class Operations extends HookConsumerWidget { _LinkWithDuplicate(paths: linkableDuplicatePaths), const SizedBox(height: 8), ], + const _MoveEntry(), + const SizedBox(height: 8), const _DeleteEntry(), const SizedBox(height: 8), ], @@ -113,6 +115,27 @@ class _LinkWithDuplicate extends HookConsumerWidget { } } +class _MoveEntry extends HookConsumerWidget { + const _MoveEntry(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return FilledButton.icon( + onPressed: () { + final entryId = ref.read(inspectingEntryIdProvider); + if (entryId.isNullOrEmpty) return; + moveEntryToSelectingPage( + ref.passing, + entryId!, + ); + }, + icon: const Iconify(TWIcons.moveEntry), + label: const Text("Move Entry"), + color: Theme.of(context).colorScheme.primary, + ); + } +} + class _DeleteEntry extends HookConsumerWidget { const _DeleteEntry(); From 9b839e4024e8eaf6aef07490787c6a2b815b0932 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:15:58 +0100 Subject: [PATCH 02/24] Upgrade blinding cinematic --- .../cinematic/BlindingCinematicEntry.kt | 44 ++++++------------- .../entry/cinematic/SimpleCinematicAction.kt | 12 ++++- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/BlindingCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/BlindingCinematicEntry.kt index 614aef0af3..8e1bb51769 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/BlindingCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/BlindingCinematicEntry.kt @@ -1,5 +1,7 @@ package me.gabber235.typewriter.entries.cinematic +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChangeGameState +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow import me.gabber235.typewriter.adapters.Colors import me.gabber235.typewriter.adapters.Entry import me.gabber235.typewriter.adapters.modifiers.Segments @@ -8,22 +10,15 @@ import me.gabber235.typewriter.entry.cinematic.SimpleCinematicAction import me.gabber235.typewriter.entry.entries.CinematicAction import me.gabber235.typewriter.entry.entries.CinematicEntry import me.gabber235.typewriter.entry.entries.Segment -import me.gabber235.typewriter.utils.EffectStateProvider -import me.gabber235.typewriter.utils.PlayerState -import me.gabber235.typewriter.utils.ThreadType.SYNC -import me.gabber235.typewriter.utils.restore -import me.gabber235.typewriter.utils.state +import me.gabber235.typewriter.extensions.packetevents.sendPacketTo import org.bukkit.entity.Player -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType.BLINDNESS @Entry("blinding_cinematic", "Blind the player so the screen looks black", Colors.CYAN, "heroicons-solid:eye-off") /** * The `Blinding Cinematic` entry is used to blind the player so the screen looks black. * * ## How could this be used? - * When starting a cinematic, if you have a [Camera Cinematic Entry](./camera_cinematic) - * where you wait for a few frames to get it loading in. + * Make the screen look black for the player during a cinematic. */ class BlindingCinematicEntry( override val id: String, @@ -48,40 +43,27 @@ data class BlindingSegment( class BlindingCinematicAction( private val player: Player, - entry: BlindingCinematicEntry, + private val entry: BlindingCinematicEntry, ) : SimpleCinematicAction() { - - private var state: PlayerState? = null - override val segments: List = entry.segments - override suspend fun startSegment(segment: BlindingSegment) { - super.startSegment(segment) - state = player.state(EffectStateProvider(BLINDNESS)) + override suspend fun tickSegment(segment: BlindingSegment, frame: Int) { + super.tickSegment(segment, frame) - SYNC.switchContext { - player.addPotionEffect(PotionEffect(BLINDNESS, 10000000, 1, false, false, false)) - } + val packet = WrapperPlayServerChangeGameState(WrapperPlayServerChangeGameState.Reason.WIN_GAME, 1f) + packet.sendPacketTo(player) } override suspend fun stopSegment(segment: BlindingSegment) { super.stopSegment(segment) - restoreState() + val packet = WrapperPlayServerCloseWindow(0) + packet.sendPacketTo(player) } - private suspend fun restoreState() { - val state = state ?: return - this.state = null - SYNC.switchContext { - player.restore(state) - } - } override suspend fun teardown() { super.teardown() - - if (state != null) { - restoreState() - } + val packet = WrapperPlayServerCloseWindow(0) + packet.sendPacketTo(player) } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/SimpleCinematicAction.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/SimpleCinematicAction.kt index ecc89be809..786687ca3b 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/SimpleCinematicAction.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/SimpleCinematicAction.kt @@ -12,12 +12,17 @@ abstract class SimpleCinematicAction : CinematicAction { super.tick(frame) val segment = segments activeSegmentAt frame - if (segment == previousSegment) + if (segment == previousSegment) { + segment?.let { tickSegment(it, frame) } return + } previousSegment?.let { stopSegment(it) } - segment?.let { startSegment(it) } + segment?.let { + startSegment(it) + tickSegment(it, frame) + } } override fun canFinish(frame: Int): Boolean = segments canFinishAt frame @@ -30,5 +35,8 @@ abstract class SimpleCinematicAction : CinematicAction { previousSegment = null } + protected open suspend fun tickSegment(segment: S, frame: Int) { + } + abstract val segments: List } \ No newline at end of file From fd89e87e429dd48a0f324244ce4a29dbff88e3cd Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:16:13 +0100 Subject: [PATCH 03/24] Add potion effect cinematic entry --- .../cinematic/PotionEffectCinematicEntry.kt | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt new file mode 100644 index 0000000000..bfc3c71a48 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt @@ -0,0 +1,112 @@ +package me.gabber235.typewriter.entries.cinematic + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.Segments +import me.gabber235.typewriter.entry.Criteria +import me.gabber235.typewriter.entry.cinematic.SimpleCinematicAction +import me.gabber235.typewriter.entry.entries.CinematicAction +import me.gabber235.typewriter.entry.entries.CinematicEntry +import me.gabber235.typewriter.entry.entries.Segment +import me.gabber235.typewriter.utils.EffectStateProvider +import me.gabber235.typewriter.utils.PlayerState +import me.gabber235.typewriter.utils.ThreadType.SYNC +import me.gabber235.typewriter.utils.restore +import me.gabber235.typewriter.utils.state +import org.bukkit.entity.Player +import org.bukkit.potion.PotionEffect +import org.bukkit.potion.PotionEffectType + +@Entry( + "potion_effect_cinematic", + "Apply different potion effects to the player during a cinematic", + Colors.CYAN, + "heroicons-solid:status-offline" +) +/** + * The `PotionEffectCinematicEntry` is used to apply different potion effects to the player during a cinematic. + * + * ## How could this be used? + * This can be used to dynamically apply effects like blindness, slowness, etc., at different times + * during a cinematic, enhancing the storytelling or gameplay experience. + */ +class PotionEffectCinematicEntry( + override val id: String = "", + override val name: String = "", + override val criteria: List = emptyList(), + @Segments(icon = "heroicons-solid:status-offline") + val segments: List = emptyList() +) : CinematicEntry { + override fun createSimulated(player: Player): CinematicAction? = null + override fun create(player: Player): CinematicAction { + return PotionEffectCinematicAction( + player, + this + ) + } +} + +data class PotionEffectSegment( + override val startFrame: Int = 0, + override val endFrame: Int = 0, + @Help("The type of potion effect to apply") + val potionEffectType: PotionEffectType = PotionEffectType.BLINDNESS, + @Help("The strength of the potion effect") + val strength: Int = 1, + @Help("Whether the potion effect should be ambient") + val ambient: Boolean = false, + @Help("Whether the potion effect should have particles") + val particles: Boolean = false, + @Help("Whether the potion effect should display an icon") + val icon: Boolean = false, +) : Segment + +class PotionEffectCinematicAction( + private val player: Player, + entry: PotionEffectCinematicEntry +) : SimpleCinematicAction() { + + private var state: PlayerState? = null + + override val segments: List = entry.segments + + override suspend fun startSegment(segment: PotionEffectSegment) { + super.startSegment(segment) + state = player.state(EffectStateProvider(segment.potionEffectType)) + + SYNC.switchContext { + player.addPotionEffect( + PotionEffect( + segment.potionEffectType, + 10000000, + segment.strength, + segment.ambient, + segment.particles, + segment.icon + ) + ) + } + } + + override suspend fun stopSegment(segment: PotionEffectSegment) { + super.stopSegment(segment) + restoreState() + } + + private suspend fun restoreState() { + val state = state ?: return + this.state = null + SYNC.switchContext { + player.restore(state) + } + } + + override suspend fun teardown() { + super.teardown() + + if (state != null) { + restoreState() + } + } +} From eee6adca45d820b95ac93212ea0a3bd7b3ffc82d Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:16:20 +0100 Subject: [PATCH 04/24] Add screen shake cinematic --- .../cinematic/ScreenShakeCinematicEntry.kt | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ScreenShakeCinematicEntry.kt diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ScreenShakeCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ScreenShakeCinematicEntry.kt new file mode 100644 index 0000000000..d97fe292ef --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ScreenShakeCinematicEntry.kt @@ -0,0 +1,65 @@ +package me.gabber235.typewriter.entries.cinematic + +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerHurtAnimation +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.Segments +import me.gabber235.typewriter.entry.Criteria +import me.gabber235.typewriter.entry.entries.* +import me.gabber235.typewriter.extensions.packetevents.sendPacketTo +import org.bukkit.entity.Player +import kotlin.random.Random + +@Entry( + "screen_shake_cinematic", + "Shake the screen", + Colors.CYAN, + "ant-design:shake-outlined" +) +/** + * The `Screen Shake Cinematic` entry is used to shake the screen. + * + * ## How could this be used? + * It could be used to simulate an earthquake or a sudden impact. + */ +class ScreenShakeCinematicEntry( + override val id: String = "", + override val name: String = "", + override val criteria: List = emptyList(), + @Segments(icon = "ant-design:shake-outlined") + val segments: List +) : CinematicEntry { + override fun create(player: Player): CinematicAction { + return ScreenShakeCinematicAction( + player, + this + ) + } +} + +data class ScreenShakeSegment( + override val startFrame: Int, + override val endFrame: Int, + @Help("The amount of frames to wait before the next shake.") + val frameDelay: Int, +) : Segment + +class ScreenShakeCinematicAction( + private val player: Player, + private val entry: ScreenShakeCinematicEntry, +) : CinematicAction { + + override suspend fun tick(frame: Int) { + super.tick(frame) + + val segment = (entry.segments activeSegmentAt frame) ?: return + val baseFrame = frame - segment.startFrame + if (segment.frameDelay > 0 && baseFrame % segment.frameDelay != 0) return + + val packet = WrapperPlayServerHurtAnimation(player.entityId, Random.nextFloat() * 360 - 180) + packet.sendPacketTo(player) + } + + override fun canFinish(frame: Int): Boolean = entry.segments canFinishAt frame +} \ No newline at end of file From bd6def96a97066ff386cba2e062180606f3f8027 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:17:58 +0100 Subject: [PATCH 05/24] Change icon --- .../typewriter/entries/cinematic/PotionEffectCinematicEntry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt index bfc3c71a48..fc7b43b7d5 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/PotionEffectCinematicEntry.kt @@ -22,7 +22,7 @@ import org.bukkit.potion.PotionEffectType "potion_effect_cinematic", "Apply different potion effects to the player during a cinematic", Colors.CYAN, - "heroicons-solid:status-offline" + "fa6-solid:flask-vial" ) /** * The `PotionEffectCinematicEntry` is used to apply different potion effects to the player during a cinematic. From 8836a57c9e48b7e989dd4841bf24f69171cc2e40 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:30:42 +0100 Subject: [PATCH 06/24] Add trailing comma --- app/lib/models/entry.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/lib/models/entry.dart b/app/lib/models/entry.dart index bfab1442b2..4679c4d5d5 100644 --- a/app/lib/models/entry.dart +++ b/app/lib/models/entry.dart @@ -11,7 +11,10 @@ part "entry.g.dart"; @riverpod EntryDefinition? entryDefinition( - EntryDefinitionRef ref, String pageId, String entryId) { + EntryDefinitionRef ref, + String pageId, + String entryId, +) { final entry = ref.watch(entryProvider(pageId, entryId)); final adapterEntry = ref.watch(entryBlueprintProvider(entry?.type ?? "")); if (entry == null || adapterEntry == null) { From 9220b7047f5ed349a6e59f26b6bda50b10e17f71 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 09:59:59 +0100 Subject: [PATCH 07/24] When no group is specified. Assume the group is the player --- .../me/gabber235/typewriter/entry/entries/FactEntry.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt index 34fe546c06..238925128e 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt @@ -1,5 +1,6 @@ package me.gabber235.typewriter.entry.entries +import lirand.api.extensions.server.server import me.gabber235.typewriter.adapters.Tags import me.gabber235.typewriter.adapters.modifiers.Help import me.gabber235.typewriter.adapters.modifiers.MultiLine @@ -10,6 +11,7 @@ import me.gabber235.typewriter.facts.FactDatabase import me.gabber235.typewriter.facts.FactId import org.bukkit.entity.Player import org.koin.java.KoinJavaComponent.get +import java.util.* @Tags("fact") interface FactEntry : StaticEntry { @@ -43,7 +45,12 @@ interface ReadableFactEntry : FactEntry { } fun readForGroup(groupId: GroupId): FactData { - val entry = group.get() ?: return FactData(0) + val entry = group.get() + if (entry == null) { + // If no group entry is set, we assume that the player is the group for backwards compatibility + val player = server.getPlayer(UUID.fromString(groupId.id)) ?: return FactData(0) + return readSinglePlayer(player) + } val group = entry.group(groupId) return group.players.map { readSinglePlayer(it) }.let { FactData(it.sumOf(FactData::value)) } } From cb2b8a37c00d4c2816df28a41552f8fe9f4c5c53 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 11:29:48 +0100 Subject: [PATCH 08/24] Update adventure --- adapters/BasicAdapter/build.gradle.kts | 6 +++--- adapters/CitizensAdapter/build.gradle.kts | 6 +++--- adapters/CombatLogXAdapter/build.gradle.kts | 6 +++--- adapters/FancyNpcsAdapter/build.gradle.kts | 6 +++--- adapters/MythicMobsAdapter/build.gradle.kts | 6 +++--- adapters/RPGRegionsAdapter/build.gradle.kts | 6 +++--- adapters/SuperiorSkyblockAdapter/build.gradle.kts | 6 +++--- adapters/VaultAdapter/build.gradle.kts | 6 +++--- adapters/WorldGuardAdapter/build.gradle.kts | 6 +++--- adapters/ZNPCsPlusAdapter/build.gradle.kts | 6 +++--- plugin/build.gradle.kts | 10 +++++----- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/adapters/BasicAdapter/build.gradle.kts b/adapters/BasicAdapter/build.gradle.kts index 45bd87d552..b436991e8e 100644 --- a/adapters/BasicAdapter/build.gradle.kts +++ b/adapters/BasicAdapter/build.gradle.kts @@ -30,8 +30,8 @@ dependencies { compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") compileOnly("com.github.Tofaa2.EntityLib:EntityLib:1.2.4-SNAPSHOT") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("org.geysermc.floodgate:api:2.2.0-SNAPSHOT") @@ -55,7 +55,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/CitizensAdapter/build.gradle.kts b/adapters/CitizensAdapter/build.gradle.kts index 6ca0bdbecd..2519ff3895 100644 --- a/adapters/CitizensAdapter/build.gradle.kts +++ b/adapters/CitizensAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") compileOnly("io.insert-koin:koin-core:3.4.0") // External dependencies @@ -60,7 +60,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/CombatLogXAdapter/build.gradle.kts b/adapters/CombatLogXAdapter/build.gradle.kts index 4970ec813d..1a25395239 100644 --- a/adapters/CombatLogXAdapter/build.gradle.kts +++ b/adapters/CombatLogXAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("com.github.sirblobman.api:core:2.9-SNAPSHOT") @@ -56,7 +56,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/FancyNpcsAdapter/build.gradle.kts b/adapters/FancyNpcsAdapter/build.gradle.kts index ed12f3e4e4..2a1ceb6434 100644 --- a/adapters/FancyNpcsAdapter/build.gradle.kts +++ b/adapters/FancyNpcsAdapter/build.gradle.kts @@ -31,8 +31,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("de.oliver:FancyNpcs:2.0.7") @@ -56,7 +56,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/MythicMobsAdapter/build.gradle.kts b/adapters/MythicMobsAdapter/build.gradle.kts index ee2084bdb7..0d60513f04 100644 --- a/adapters/MythicMobsAdapter/build.gradle.kts +++ b/adapters/MythicMobsAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("io.lumine:Mythic-Dist:5.2.1") @@ -57,7 +57,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/RPGRegionsAdapter/build.gradle.kts b/adapters/RPGRegionsAdapter/build.gradle.kts index daeab037d8..5e7a40c9b9 100644 --- a/adapters/RPGRegionsAdapter/build.gradle.kts +++ b/adapters/RPGRegionsAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("net.islandearth.rpgregions:api:1.4.4") // 1.4.4 @@ -57,7 +57,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/SuperiorSkyblockAdapter/build.gradle.kts b/adapters/SuperiorSkyblockAdapter/build.gradle.kts index 8c2b3767f1..8771ceb477 100644 --- a/adapters/SuperiorSkyblockAdapter/build.gradle.kts +++ b/adapters/SuperiorSkyblockAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("com.bgsoftware:SuperiorSkyblockAPI:1.11.1") @@ -57,7 +57,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/VaultAdapter/build.gradle.kts b/adapters/VaultAdapter/build.gradle.kts index 32c385e1cb..4246bc2cdd 100644 --- a/adapters/VaultAdapter/build.gradle.kts +++ b/adapters/VaultAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") @@ -57,7 +57,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/WorldGuardAdapter/build.gradle.kts b/adapters/WorldGuardAdapter/build.gradle.kts index a458f85d8b..c47822b2fb 100644 --- a/adapters/WorldGuardAdapter/build.gradle.kts +++ b/adapters/WorldGuardAdapter/build.gradle.kts @@ -32,8 +32,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.6-SNAPSHOT") @@ -57,7 +57,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/adapters/ZNPCsPlusAdapter/build.gradle.kts b/adapters/ZNPCsPlusAdapter/build.gradle.kts index 250a618e66..838c77e641 100644 --- a/adapters/ZNPCsPlusAdapter/build.gradle.kts +++ b/adapters/ZNPCsPlusAdapter/build.gradle.kts @@ -31,8 +31,8 @@ dependencies { // Already included in the TypeWriter plugin compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") // External dependencies compileOnly("lol.pyr:znpcsplus-api:2.0.0-SNAPSHOT") @@ -56,7 +56,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(buildDir.resolve("generated-sources/templates/kotlin/main")) + into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) } sourceSets { diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index e2626a06c0..fba027b736 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -60,11 +60,11 @@ dependencies { compileOnly("com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.11.0") compileOnly("com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:2.11.0") - compileOnly("net.kyori:adventure-api:4.13.1") - compileOnly("net.kyori:adventure-text-minimessage:4.13.1") - compileOnly("net.kyori:adventure-text-serializer-plain:4.13.1") - compileOnly("net.kyori:adventure-text-serializer-legacy:4.13.1") - compileOnly("net.kyori:adventure-text-serializer-gson:4.13.1") + compileOnly("net.kyori:adventure-api:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") + compileOnly("net.kyori:adventure-text-serializer-plain:4.15.0") + compileOnly("net.kyori:adventure-text-serializer-legacy:4.15.0") + compileOnly("net.kyori:adventure-text-serializer-gson:4.15.0") compileOnly("com.mojang:brigadier:1.0.18") compileOnly("me.clip:placeholderapi:2.11.3") compileOnly("com.google.code.gson:gson:2.10.1") From 20f46bbef9c73e7f1f5d5c896259e541e59d7c4d Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 12:54:49 +0100 Subject: [PATCH 09/24] Fix build gradles --- adapters/BasicAdapter/build.gradle.kts | 11 ++++++++--- adapters/CitizensAdapter/build.gradle.kts | 12 +++++++++++- adapters/CombatLogXAdapter/build.gradle.kts | 12 +++++++++++- adapters/FancyNpcsAdapter/build.gradle.kts | 12 +++++++++++- adapters/MythicMobsAdapter/build.gradle.kts | 12 +++++++++++- adapters/RPGRegionsAdapter/build.gradle.kts | 12 +++++++++++- adapters/SuperiorSkyblockAdapter/build.gradle.kts | 12 +++++++++++- adapters/VaultAdapter/build.gradle.kts | 12 +++++++++++- adapters/WorldGuardAdapter/build.gradle.kts | 12 +++++++++++- adapters/ZNPCsPlusAdapter/build.gradle.kts | 12 +++++++++++- plugin/build.gradle.kts | 11 ++++++++--- 11 files changed, 115 insertions(+), 15 deletions(-) diff --git a/adapters/BasicAdapter/build.gradle.kts b/adapters/BasicAdapter/build.gradle.kts index b436991e8e..f7e6da3800 100644 --- a/adapters/BasicAdapter/build.gradle.kts +++ b/adapters/BasicAdapter/build.gradle.kts @@ -48,6 +48,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -55,7 +62,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -85,8 +92,6 @@ task("buildRelease") group = "build" description = "Builds the jar and renames it" - - doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) diff --git a/adapters/CitizensAdapter/build.gradle.kts b/adapters/CitizensAdapter/build.gradle.kts index 2519ff3895..308f2f6522 100644 --- a/adapters/CitizensAdapter/build.gradle.kts +++ b/adapters/CitizensAdapter/build.gradle.kts @@ -53,6 +53,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -60,7 +67,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -92,6 +99,9 @@ task("buildRelease") doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/CombatLogXAdapter/build.gradle.kts b/adapters/CombatLogXAdapter/build.gradle.kts index 1a25395239..bdec71fda0 100644 --- a/adapters/CombatLogXAdapter/build.gradle.kts +++ b/adapters/CombatLogXAdapter/build.gradle.kts @@ -49,6 +49,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -56,7 +63,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -88,6 +95,9 @@ task("buildRelease") { doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/FancyNpcsAdapter/build.gradle.kts b/adapters/FancyNpcsAdapter/build.gradle.kts index 2a1ceb6434..082353f489 100644 --- a/adapters/FancyNpcsAdapter/build.gradle.kts +++ b/adapters/FancyNpcsAdapter/build.gradle.kts @@ -49,6 +49,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -56,7 +63,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -91,6 +98,9 @@ task("buildRelease") doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/MythicMobsAdapter/build.gradle.kts b/adapters/MythicMobsAdapter/build.gradle.kts index 0d60513f04..70c187f352 100644 --- a/adapters/MythicMobsAdapter/build.gradle.kts +++ b/adapters/MythicMobsAdapter/build.gradle.kts @@ -50,6 +50,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -57,7 +64,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -89,6 +96,9 @@ task("buildRelease") { doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/RPGRegionsAdapter/build.gradle.kts b/adapters/RPGRegionsAdapter/build.gradle.kts index 5e7a40c9b9..3d1aec248f 100644 --- a/adapters/RPGRegionsAdapter/build.gradle.kts +++ b/adapters/RPGRegionsAdapter/build.gradle.kts @@ -50,6 +50,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -57,7 +64,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -89,6 +96,9 @@ task("buildRelease") { doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/SuperiorSkyblockAdapter/build.gradle.kts b/adapters/SuperiorSkyblockAdapter/build.gradle.kts index 8771ceb477..41900a8e06 100644 --- a/adapters/SuperiorSkyblockAdapter/build.gradle.kts +++ b/adapters/SuperiorSkyblockAdapter/build.gradle.kts @@ -50,6 +50,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -57,7 +64,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -89,6 +96,9 @@ task("buildRelease") doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/VaultAdapter/build.gradle.kts b/adapters/VaultAdapter/build.gradle.kts index 4246bc2cdd..ba084e0005 100644 --- a/adapters/VaultAdapter/build.gradle.kts +++ b/adapters/VaultAdapter/build.gradle.kts @@ -50,6 +50,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -57,7 +64,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -89,6 +96,9 @@ task("buildRelease") { doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/WorldGuardAdapter/build.gradle.kts b/adapters/WorldGuardAdapter/build.gradle.kts index c47822b2fb..21dfca2e08 100644 --- a/adapters/WorldGuardAdapter/build.gradle.kts +++ b/adapters/WorldGuardAdapter/build.gradle.kts @@ -50,6 +50,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -57,7 +64,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -89,6 +96,9 @@ task("buildRelease") { doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/adapters/ZNPCsPlusAdapter/build.gradle.kts b/adapters/ZNPCsPlusAdapter/build.gradle.kts index 838c77e641..164e9f16f1 100644 --- a/adapters/ZNPCsPlusAdapter/build.gradle.kts +++ b/adapters/ZNPCsPlusAdapter/build.gradle.kts @@ -49,6 +49,13 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } val copyTemplates by tasks.registering(Copy::class) { @@ -56,7 +63,7 @@ val copyTemplates by tasks.registering(Copy::class) { from(projectDir.resolve("src/main/templates")) { expand("version" to version) } - into(layout.buildDirectory.dir("generated-sources/templates/kotlin/main")) + into(buildDir.resolve("generated-sources/templates/kotlin/main")) } sourceSets { @@ -91,6 +98,9 @@ task("buildRelease") doLast { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index fba027b736..bb376e77b8 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -79,12 +79,14 @@ java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion targetCompatibility = javaVersion - + toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion)) withSourcesJar() } -tasks.withType { - kotlinOptions.jvmTarget = "17" +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "$targetJavaVersion" + } } tasks.test { @@ -146,6 +148,9 @@ task("buildRelease") { // Rename the jar to remove the version and -all val jar = file("build/libs/%s-%s-all.jar".format(project.name, project.version)) + if (!jar.exists()) { + throw IllegalStateException("Jar does not exist: ${jar.absolutePath}") + } jar.renameTo(file("build/libs/%s.jar".format(project.name))) file("build/libs/%s-%s.jar".format(project.name, project.version)).delete() } From 88c42995ae175d7f0562fc17a84a69236373c62c Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 18:14:01 +0100 Subject: [PATCH 10/24] Fix player being close to the camera --- .../typewriter/entries/cinematic/CameraCinematicEntry.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt index 7c80803c5d..d5b235a93f 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt @@ -20,7 +20,7 @@ import me.gabber235.typewriter.utils.GenericPlayerStateProvider.* import me.gabber235.typewriter.utils.ThreadType.SYNC import me.tofaa.entitylib.EntityLib import me.tofaa.entitylib.entity.WrapperEntity -import me.tofaa.entitylib.meta.display.TextDisplayMeta +import me.tofaa.entitylib.meta.types.DisplayMeta import org.bukkit.GameMode import org.bukkit.Location import org.bukkit.Particle @@ -206,7 +206,7 @@ private suspend inline fun Player.teleportIfNeeded( location: Location, ) { if (frame % 10 == 0 || location.distanceSquared(location) > MAX_DISTANCE_SQUARED) SYNC.switchContext { - teleport(location.highUpLocation) + teleport(location) allowFlight = true isFlying = true } @@ -236,7 +236,7 @@ private class DisplayCameraAction( } private fun createEntity(): WrapperEntity { - return EntityLib.createEntity(UUID.randomUUID(), EntityTypes.TEXT_DISPLAY).meta { + return EntityLib.createEntity(UUID.randomUUID(), EntityTypes.ITEM_DISPLAY).meta { positionRotationInterpolationDuration = BASE_INTERPOLATION } } From 91cf6ea690f64469bf1198228524eebe5df9f1fe Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Fri, 16 Feb 2024 19:27:10 +0100 Subject: [PATCH 11/24] Update ZNPCs version --- adapters/ZNPCsPlusAdapter/build.gradle.kts | 2 +- .../me/gabber235/typewriter/entries/cinematic/ZNPCData.kt | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/adapters/ZNPCsPlusAdapter/build.gradle.kts b/adapters/ZNPCsPlusAdapter/build.gradle.kts index 164e9f16f1..2f522b7f72 100644 --- a/adapters/ZNPCsPlusAdapter/build.gradle.kts +++ b/adapters/ZNPCsPlusAdapter/build.gradle.kts @@ -10,7 +10,6 @@ version = file("../../version.txt").readText().trim() repositories { // Required - maven("https://jitpack.io") mavenCentral() maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") maven("https://oss.sonatype.org/content/groups/public/") @@ -20,6 +19,7 @@ repositories { maven("https://repo.opencollab.dev/maven-snapshots/") // ZNPCsPlus Repositories maven("https://repo.pyr.lol/snapshots") + maven("https://jitpack.io") } dependencies { diff --git a/adapters/ZNPCsPlusAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ZNPCData.kt b/adapters/ZNPCsPlusAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ZNPCData.kt index 1463e2d4eb..7904c02261 100644 --- a/adapters/ZNPCsPlusAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ZNPCData.kt +++ b/adapters/ZNPCsPlusAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/ZNPCData.kt @@ -8,7 +8,6 @@ import lol.pyr.znpcsplus.util.NpcLocation import lol.pyr.znpcsplus.util.NpcPose import me.gabber235.typewriter.capture.capturers.ArmSwing import me.gabber235.typewriter.entry.entries.NpcData -import me.gabber235.typewriter.extensions.packetevents.swingArm import me.gabber235.typewriter.logger import org.bukkit.Location import org.bukkit.entity.Player @@ -48,9 +47,7 @@ interface ZNPCData : NpcData { } override fun handlePunching(player: Player, npc: NpcEntry, punching: ArmSwing) { - // TODD: Wait for ZNPCs API to add punching - // For now we directly send the packet - player.swingArm(npc.npc.packetEntityId, punching) + npc.npc.swingHand(punching.swingLeft) } override fun handleInventory(player: Player, npc: NpcEntry, slot: EquipmentSlot, itemStack: ItemStack) { @@ -64,7 +61,7 @@ interface ZNPCData : NpcData { } val property = NpcApiProvider.get().propertyRegistry.getByName(zNpcSlot, ItemStack::class.java) - npc.npc.setProperty(property, itemStack) + npc.npc.setItemProperty(property, itemStack) } override fun teardown(player: Player, npc: NpcEntry) { From 3a096881abde479f531ff20f8c2742570aa42bf0 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 09:21:02 +0100 Subject: [PATCH 12/24] Start with questing --- .../entries/cinematic/CameraCinematicEntry.kt | 1 - .../entries/quest/QuestCompleteEventEntry.kt | 36 ++++ .../entries/quest/QuestStartEventEntry.kt | 36 ++++ .../quest/QuestStatusUpdateEventEntry.kt | 46 +++++ .../entries/quest/SimpleObjectiveEntry.kt | 76 ++++++++ .../entries/quest/SimpleQuestEntry.kt | 54 ++++++ .../entries/quest/TrackedObjectiveAudience.kt | 71 ++++++++ .../entries/quest/TrackedQuestAudience.kt | 61 +++++++ .../entries/audience/RegionAudienceEntry.kt | 15 +- .../me/gabber235/typewriter/Typewriter.kt | 24 +-- .../gabber235/typewriter/TypewriterCommand.kt | 40 ++++- .../typewriter/adapters/EntryAttributes.kt | 2 + .../typewriter/entry/AudienceManager.kt | 61 +++++-- .../me/gabber235/typewriter/entry/Entry.kt | 6 + .../typewriter/entry/EntryReference.kt | 16 ++ .../gabber235/typewriter/entry/FactWatcher.kt | 122 +++++++++++++ .../typewriter/entry/entries/AudienceEntry.kt | 30 ++-- .../typewriter/entry/entries/EntityEntry.kt | 6 +- .../typewriter/entry/entries/FactEntry.kt | 8 +- .../typewriter/entry/entries/QuestEntry.kt | 68 +++++++ .../typewriter/entry/entries/SoundEntry.kt | 6 +- .../typewriter/entry/quest/QuestTracker.kt | 169 ++++++++++++++++++ .../events/AsyncQuestStatusUpdate.kt | 26 +++ .../events/AsyncTrackedQuestUpdate.kt | 25 +++ ...teExpansion.kt => PlaceholderExpansion.kt} | 34 ++-- .../typewriter/facts/FactDatabase.kt | 8 +- .../typewriter/interaction/Interaction.kt | 20 ++- .../interaction/InteractionHandler.kt | 2 +- .../gabber235/typewriter/utils/ThreadType.kt | 8 +- 29 files changed, 999 insertions(+), 78 deletions(-) create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestCompleteEventEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStartEventEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStatusUpdateEventEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleQuestEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedQuestAudience.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/QuestEntry.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncQuestStatusUpdate.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncTrackedQuestUpdate.kt rename plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/{TypewriteExpansion.kt => PlaceholderExpansion.kt} (62%) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt index d5b235a93f..e703e59e9e 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/CameraCinematicEntry.kt @@ -161,7 +161,6 @@ class CameraCinematicAction( this blockPacket PacketType.Play.Client.CLICK_WINDOW_BUTTON this blockPacket PacketType.Play.Client.USE_ITEM this blockPacket PacketType.Play.Client.INTERACT_ENTITY - } private suspend fun Player.teardown() { diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestCompleteEventEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestCompleteEventEntry.kt new file mode 100644 index 0000000000..a8410ee108 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestCompleteEventEntry.kt @@ -0,0 +1,36 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.entries.EventEntry +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.QuestStatus +import me.gabber235.typewriter.events.AsyncQuestStatusUpdate + +@Entry("quest_complete_event", "Triggered when a quest is completed for a player", Colors.YELLOW, "mdi:notebook-check") +/** + * The `Quest Complete Event` entry is triggered when a quest is completed for a player. + * + * When no quest is referenced, it will trigger for all quests. + * + * ## How could this be used? + * This could be used to show a title or notification to a player when a quest is completed. + */ +class QuestCompleteEventEntry( + override val id: String = "", + override val name: String = "", + override val triggers: List> = emptyList(), + @Help("When not set it will trigger for all quests.") + val quest: Ref = emptyRef() +) : EventEntry + +@EntryListener(QuestCompleteEventEntry::class) +fun onQuestComplete(event: AsyncQuestStatusUpdate, query: Query) { + if (event.to != QuestStatus.COMPLETED) return + + query.findWhere { + !it.quest.isSet || it.quest == event.quest + } triggerAllFor event.player +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStartEventEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStartEventEntry.kt new file mode 100644 index 0000000000..907712021d --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStartEventEntry.kt @@ -0,0 +1,36 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.entries.EventEntry +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.QuestStatus +import me.gabber235.typewriter.events.AsyncQuestStatusUpdate + +@Entry("quest_start_event", "Triggered when a quest is started for a player", Colors.YELLOW, "mdi:notebook-plus") +/** + * The `Quest Start Event` entry is triggered when a quest is started for a player. + * + * When no quest is referenced, it will trigger for all quests. + * + * ## How could this be used? + * This could be used to show a title or notification to a player when a quest is started. + */ +class QuestStartEventEntry( + override val id: String = "", + override val name: String = "", + override val triggers: List> = emptyList(), + @Help("When not set it will trigger for all quests.") + val quest: Ref = emptyRef() +) : EventEntry + +@EntryListener(QuestStartEventEntry::class) +fun onQuestStart(event: AsyncQuestStatusUpdate, query: Query) { + if (event.to != QuestStatus.ACTIVE) return + + query.findWhere { + !it.quest.isSet || it.quest == event.quest + } triggerAllFor event.player +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStatusUpdateEventEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStatusUpdateEventEntry.kt new file mode 100644 index 0000000000..1449760b9c --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/QuestStatusUpdateEventEntry.kt @@ -0,0 +1,46 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.entries.EventEntry +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.QuestStatus +import me.gabber235.typewriter.events.AsyncQuestStatusUpdate +import java.util.* + +@Entry( + "quest_status_update_event", + "Triggered when a quest status is updated for a player", + Colors.YELLOW, + "mdi:notebook-edit" +) +/** + * The `Quest Status Update Event` entry is triggered when a quest status is updated for a player. + * + * When no quest is referenced, it will trigger for all quests. + * + * ## How could this be used? + * This could be used to show a title or notification to a player when a quest status is updated. + */ +class QuestStatusUpdateEventEntry( + override val id: String = "", + override val name: String = "", + override val triggers: List> = emptyList(), + @Help("When not set it will trigger for all quests.") + val quest: Ref = emptyRef(), + @Help("When not set it will trigger for all statuses.") + val from: Optional = Optional.empty(), + @Help("The status the quest is updated to.") + val to: QuestStatus +) : EventEntry + +@EntryListener(QuestStatusUpdateEventEntry::class) +fun onQuestStatusUpdate(event: AsyncQuestStatusUpdate, query: Query) { + query.findWhere { + (!it.quest.isSet || it.quest == event.quest) && + (!it.from.isPresent || it.from.get() == event.from) && + it.to == event.to + } triggerAllFor event.player +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt new file mode 100644 index 0000000000..a3cfe48c49 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt @@ -0,0 +1,76 @@ +package me.gabber235.typewriter.entries.quest + +import lirand.api.extensions.server.server +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.entries.* +import org.bukkit.entity.Player +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +@Entry("objective", "An objective definition", Colors.BLUE_VIOLET, "streamline:target-solid") +/** + * The `Objective` entry is a tasks that the player can complete. + * It is mainly for displaying the progress to a player. + * + * It is **not** necessary to use this entry for objectives. + * It is just a visual novelty. + * + * The entry filters the audience based on if the objective is active. + * + * ## How could this be used? + * This could be used to show the players what they need to do. + */ +class SimpleObjectiveEntry( + override val id: String = "", + override val name: String = "", + override val quest: Ref = emptyRef(), + override val children: List> = emptyList(), + override val criteria: List = emptyList(), + override val finishedCriteria: List = emptyList(), + override val displayName: String = "", +) : ObjectiveEntry { + override fun display(): AudienceFilter { + return ObjectiveAudienceFilter( + ref(), + criteria, + ) + } +} + +class ObjectiveAudienceFilter( + objective: Ref, + private val criteria: List, +) : AudienceFilter(objective) { + private val factWatcherSubscriptions = ConcurrentHashMap() + + override fun filter(player: Player): Boolean = criteria.matches(player) + + override fun onPlayerAdd(player: Player) { + super.onPlayerAdd(player) + val subscription = player.listenForFacts( + criteria.map { it.fact }, + ::onFactChange, + ) + + factWatcherSubscriptions[player.uniqueId] = subscription + } + + private fun onFactChange(player: Player, fact: Ref) { + player.refresh() + } + + override fun onPlayerRemove(player: Player) { + super.onPlayerRemove(player) + factWatcherSubscriptions.remove(player.uniqueId)?.cancel(player) + } + + override fun dispose() { + super.dispose() + factWatcherSubscriptions.forEach { (playerId, subscription) -> + val player = server.getPlayer(playerId) ?: return@forEach + subscription.cancel(player) + } + } +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleQuestEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleQuestEntry.kt new file mode 100644 index 0000000000..55007415f2 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleQuestEntry.kt @@ -0,0 +1,54 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.entry.Criteria +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.entries.AudienceEntry +import me.gabber235.typewriter.entry.entries.AudienceFilter +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.QuestStatus +import me.gabber235.typewriter.entry.quest.isQuestActive +import me.gabber235.typewriter.entry.ref +import me.gabber235.typewriter.events.AsyncQuestStatusUpdate +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler + +@Entry("quest", "A quest definition", Colors.MEDIUM_PURPLE, "material-symbols:book-2") +/** + * The `Quest` entry is a collection of tasks that the player can complete. + * It is mainly for displaying the progress to a player. + * + * It is **not** necessary to use this entry for quests. + * It is just a visual novelty. + * + * The entry filters the audience based on if the quest is active. + * + * ## How could this be used? + * This could be used to show the progress of a quest to a player. + * Connect objectives to the quest to show a player what they need to do. + */ +class SimpleQuestEntry( + override val id: String = "", + override val name: String = "", + override val children: List> = emptyList(), + override val displayName: String, + override val activeCriteria: List, + override val completedCriteria: List, +) : QuestEntry { + override fun display(): AudienceFilter = QuestAudienceFilter( + ref() + ) +} + +class QuestAudienceFilter( + private val quest: Ref +) : AudienceFilter(quest) { + override fun filter(player: Player): Boolean = player isQuestActive quest + + @EventHandler + fun onQuestStatusUpdate(event: AsyncQuestStatusUpdate) { + if (event.quest != quest) return + event.player.updateFilter(event.to == QuestStatus.ACTIVE) + } +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt new file mode 100644 index 0000000000..586d005e3c --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt @@ -0,0 +1,71 @@ +package me.gabber235.typewriter.entries.quest + +import com.github.shynixn.mccoroutine.bukkit.launch +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.entries.AudienceEntry +import me.gabber235.typewriter.entry.entries.AudienceFilter +import me.gabber235.typewriter.entry.entries.AudienceFilterEntry +import me.gabber235.typewriter.entry.entries.trackedShowingObjectives +import me.gabber235.typewriter.entry.ref +import me.gabber235.typewriter.events.AsyncTrackedQuestUpdate +import me.gabber235.typewriter.plugin +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import kotlin.time.Duration.Companion.seconds + +@Entry( + "tracked_objective_audience", + "Filters an audience based on if they have a tracked objective", + Colors.MEDIUM_SEA_GREEN, + "mdi:target-account" +) +/** + * The `Tracked Objective Audience` entry filters an audience based on if they have a tracked objective. + * It looks if the player has an objective showing from the quest that is being tracked. + * + * ## How could this be used? + * This could be used to show a boss bar or sidebar based on if a player has an objective showing. + */ +class TrackedObjectiveAudience( + override val id: String, + override val name: String, + override val children: List> +) : AudienceFilterEntry { + override fun display(): AudienceFilter = TrackedObjectiveAudienceFilter( + ref() + ) +} + +class TrackedObjectiveAudienceFilter( + ref: Ref +) : AudienceFilter(ref) { + private var job: Job? = null + + override fun filter(player: Player): Boolean = player.trackedShowingObjectives().isNotEmpty() + + override fun initialize() { + super.initialize() + + job = plugin.launch { + while (true) { + delay(1.seconds) + consideredPlayers.forEach { it.refresh() } + } + } + } + + @EventHandler + private fun onTrackedQuestUpdate(event: AsyncTrackedQuestUpdate) { + event.player.refresh() + } + + override fun dispose() { + super.dispose() + job?.cancel() + job = null + } +} \ No newline at end of file diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedQuestAudience.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedQuestAudience.kt new file mode 100644 index 0000000000..1a499c0e28 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedQuestAudience.kt @@ -0,0 +1,61 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.emptyRef +import me.gabber235.typewriter.entry.entries.AudienceEntry +import me.gabber235.typewriter.entry.entries.AudienceFilter +import me.gabber235.typewriter.entry.entries.AudienceFilterEntry +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.isQuestTracked +import me.gabber235.typewriter.entry.ref +import me.gabber235.typewriter.events.AsyncTrackedQuestUpdate +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler + +@Entry( + "tracked_quest_audience", + "Filters an audience based on if they have a quest tracked", + Colors.MEDIUM_SEA_GREEN, + "mdi:notebook-heart" +) +/** + * The `Tracked Quest Audience` entry filters an audience based on if they have a quest tracked. + * + * If no quest is referenced, it will filter based on if any quest is tracked. + * + * ## How could this be used? + * + * This could be used to show a boss bar or sidebar based on if a player has a quest tracked. + */ +class TrackedQuestAudience( + override val id: String = "", + override val name: String = "", + override val children: List> = emptyList(), + @Help("When not set it will filter based on if any quest is tracked.") + val quest: Ref = emptyRef(), +) : AudienceFilterEntry { + override fun display(): AudienceFilter = TrackedQuestAudienceFilter( + ref(), + quest + ) +} + +class TrackedQuestAudienceFilter( + ref: Ref, + private val quest: Ref +) : AudienceFilter(ref) { + override fun filter(player: Player): Boolean = player.isQuestTracked(quest) + + @EventHandler + private fun onTrackedQuestUpdate(event: AsyncTrackedQuestUpdate) { + if (quest.isSet) { + event.player.updateFilter(event.to == quest) + return + } + + event.player.updateFilter(event.to != null) + } +} \ No newline at end of file diff --git a/adapters/WorldGuardAdapter/src/main/kotlin/com/caleb/typewriter/worldguard/entries/audience/RegionAudienceEntry.kt b/adapters/WorldGuardAdapter/src/main/kotlin/com/caleb/typewriter/worldguard/entries/audience/RegionAudienceEntry.kt index 40dac29a29..e6025286e9 100644 --- a/adapters/WorldGuardAdapter/src/main/kotlin/com/caleb/typewriter/worldguard/entries/audience/RegionAudienceEntry.kt +++ b/adapters/WorldGuardAdapter/src/main/kotlin/com/caleb/typewriter/worldguard/entries/audience/RegionAudienceEntry.kt @@ -9,7 +9,10 @@ import me.gabber235.typewriter.adapters.Colors import me.gabber235.typewriter.adapters.Entry import me.gabber235.typewriter.adapters.modifiers.Help import me.gabber235.typewriter.entry.Ref -import me.gabber235.typewriter.entry.entries.* +import me.gabber235.typewriter.entry.entries.AudienceEntry +import me.gabber235.typewriter.entry.entries.AudienceFilter +import me.gabber235.typewriter.entry.entries.AudienceFilterEntry +import me.gabber235.typewriter.entry.ref import org.bukkit.entity.Player import org.bukkit.event.EventHandler @@ -34,18 +37,18 @@ class RegionAudienceEntry( val region: String = "", ) : AudienceFilterEntry { override fun display(): AudienceFilter { - return RegionAudienceFilter(region, children.into()) + return RegionAudienceFilter(ref(), region) } } class RegionAudienceFilter( + ref: Ref, private val region: String, - children: List, -) : AudienceFilter(children) { +) : AudienceFilter(ref) { @EventHandler fun onRegionEnter(event: RegionsEnterEvent) { - if (event.player.uniqueId !in this) return + if (!canConsider(event.player.uniqueId)) return if (event.regions.none { it.id == region }) return val player = server.getPlayer(event.player.uniqueId) ?: return player.updateFilter(true) @@ -53,7 +56,7 @@ class RegionAudienceFilter( @EventHandler fun onRegionLeave(event: RegionsExitEvent) { - if (event.player.uniqueId !in this) return + if (!canConsider(event.player.uniqueId)) return if (event.regions.none { it.id == region }) return val player = server.getPlayer(event.player.uniqueId) ?: return player.updateFilter(false) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt index 06e14e51a4..63e5f26d3d 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt @@ -12,7 +12,7 @@ import me.gabber235.typewriter.entry.* import me.gabber235.typewriter.entry.dialogue.MessengerFinder import me.gabber235.typewriter.extensions.bstats.BStatsMetrics import me.gabber235.typewriter.extensions.modrinth.Modrinth -import me.gabber235.typewriter.extensions.placeholderapi.TypewriteExpansion +import me.gabber235.typewriter.extensions.placeholderapi.PlaceholderExpansion import me.gabber235.typewriter.facts.FactDatabase import me.gabber235.typewriter.facts.FactStorage import me.gabber235.typewriter.facts.storage.FileFactStorage @@ -58,19 +58,19 @@ class Typewriter : KotlinPlugin(), KoinComponent { singleOf(::EntryDatabaseImpl) singleOf(::StagingManagerImpl) singleOf(::ClientSynchronizerImpl) - singleOf(::InteractionHandler) - singleOf(::MessengerFinder) - singleOf(::CommunicationHandler) - singleOf(::Writers) - singleOf(::PanelHost) + singleOf(::InteractionHandler) + singleOf(::MessengerFinder) + singleOf(::CommunicationHandler) + singleOf(::Writers) + singleOf(::PanelHost) singleOf(::SnippetDatabaseImpl) - singleOf(::FactDatabase) + singleOf(::FactDatabase) singleOf(::FileFactStorage) - singleOf(::EntryListeners) - singleOf(::AudienceManager) - singleOf(::Recorders) + singleOf(::EntryListeners) + singleOf(::AudienceManager) + singleOf(::Recorders) singleOf(::LocalAssetStorage) - singleOf(::AssetManager) + singleOf(::AssetManager) singleOf(::ChatHistoryHandler) singleOf(::ActionBarBlockerHandler) singleOf(::PacketBlocker) @@ -109,7 +109,7 @@ class Typewriter : KotlinPlugin(), KoinComponent { get().initialize() if (server.pluginManager.getPlugin("PlaceholderAPI") != null) { - TypewriteExpansion.register() + PlaceholderExpansion.register() } syncCommands() diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt index 4874d3750f..bea44475cb 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt @@ -13,11 +13,10 @@ import lirand.api.dsl.command.types.exceptions.ChatCommandExceptionType import lirand.api.dsl.command.types.extensions.readUnquoted import me.gabber235.typewriter.entry.* import me.gabber235.typewriter.entry.PageType.CINEMATIC -import me.gabber235.typewriter.entry.entries.CinematicStartTrigger -import me.gabber235.typewriter.entry.entries.EntryTrigger -import me.gabber235.typewriter.entry.entries.ReadableFactEntry +import me.gabber235.typewriter.entry.entries.* import me.gabber235.typewriter.entry.entries.SystemTrigger.CINEMATIC_END -import me.gabber235.typewriter.entry.entries.WritableFactEntry +import me.gabber235.typewriter.entry.quest.trackQuest +import me.gabber235.typewriter.entry.quest.unTrackQuest import me.gabber235.typewriter.events.TypewriterReloadEvent import me.gabber235.typewriter.facts.formattedName import me.gabber235.typewriter.interaction.chatHistory @@ -51,6 +50,8 @@ fun Plugin.typeWriterCommand() = command("typewriter") { triggerCommand() + questCommands() + assetsCommands() } @@ -282,7 +283,38 @@ private fun LiteralDSLBuilder.triggerCommand() = literal("trigger") { EntryTrigger(entry.get()) triggerFor player.get() } } + } +} + +private fun LiteralDSLBuilder.questCommands() = literal("quest") { + literal("track") { + requiresPermissions("typewriter.quest.track") + argument("quest", entryType()) { quest -> + executesPlayer { + source.trackQuest(quest.get().ref()) + } + + argument("player", PlayerType) { player -> + requiresPermissions("typewriter.quest.track.other") + executes { + player.get().trackQuest(quest.get().ref()) + } + } + } + } + + literal("untrack") { + requiresPermissions("typewriter.quest.untrack") + executesPlayer { + source.unTrackQuest() + } + argument("player", PlayerType) { player -> + requiresPermissions("typewriter.quest.untrack.other") + executes { + player.get().unTrackQuest() + } + } } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt index dce936accc..c887cda216 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt @@ -18,6 +18,8 @@ object Colors { const val MYRTLE_GREEN = "#297373" const val YELLOW = "#FBB612" const val PURPLE = "#5843e6" + const val MEDIUM_PURPLE = "#9370DB" + const val BLUE_VIOLET = "#8A2BE2" const val ORANGE = "#F57C00" const val PINK = "#eb4bb8" const val CYAN = "#0abab5" diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt index e0b53be022..2cd34e294a 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt @@ -5,14 +5,17 @@ import me.gabber235.typewriter.entry.entries.AudienceDisplay import me.gabber235.typewriter.entry.entries.AudienceEntry import me.gabber235.typewriter.entry.entries.AudienceFilterEntry import me.gabber235.typewriter.plugin +import org.bukkit.entity.Player import org.bukkit.event.EventHandler import org.bukkit.event.HandlerList import org.bukkit.event.Listener import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerQuitEvent +import org.koin.java.KoinJavaComponent.get class AudienceManager : Listener { - private var displays = emptyList() + private var displays = emptyMap, AudienceDisplay>() + private var roots = emptyList>() fun initialize() { server.pluginManager.registerEvents(this, plugin) @@ -23,31 +26,69 @@ class AudienceManager : Listener { val entries = Query.find() val dependents = entries.filterIsInstance().flatMap { it.children }.map { it.id }.toSet() - val roots = entries.filter { it.id !in dependents } + roots = entries.filter { it.id !in dependents }.map { it.ref() } - displays = roots.map { it.display() } - displays.forEach { it.initialize() } - displays.forEach { server.onlinePlayers.forEach { player -> it.addPlayer(player) } } + displays = entries.associate { it.ref() to it.display() } + + server.onlinePlayers.forEach { player -> + addPlayerForRoots(player) + } + } + + fun addPlayerFor(player: Player, ref: Ref) { + val display = displays[ref] ?: return + display.addPlayer(player) + } + + fun addPlayerForRoots(player: Player) { + roots.forEach { addPlayerFor(player, it) } + } + + fun removePlayerFor(player: Player, ref: Ref) { + val display = displays[ref] ?: return + display.removePlayer(player) + } + + fun removePlayerForRoots(player: Player) { + roots.forEach { removePlayerFor(player, it) } + } + + fun addPlayerToChildren(player: Player, ref: Ref) { + val entry = ref.get() ?: return + entry.children.forEach { addPlayerFor(player, it) } + } + + fun removePlayerFromChildren(player: Player, ref: Ref) { + val entry = ref.get() ?: return + entry.children.forEach { removePlayerFor(player, it) } } + operator fun get(ref: Ref): AudienceDisplay? = displays[ref] + fun unregister() { val displays = displays - this.displays = emptyList() - displays.forEach { it.dispose() } + this.displays = emptyMap() + displays.values.forEach { it.dispose() } } @EventHandler private fun onPlayerJoin(event: PlayerJoinEvent) { - displays.forEach { it.addPlayer(event.player) } + addPlayerForRoots(event.player) } @EventHandler private fun onPlayerQuit(event: PlayerQuitEvent) { - displays.forEach { it.removePlayer(event.player) } + removePlayerForRoots(event.player) } fun shutdown() { unregister() HandlerList.unregisterAll(this) } -} \ No newline at end of file + +} + +fun Player.inAudience(ref: Ref): Boolean { + val manager = get(AudienceManager::class.java) + return manager[ref]?.let { return it.contains(this) } ?: false +} diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/Entry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/Entry.kt index 43bb6eac33..7cf8957df0 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/Entry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/Entry.kt @@ -6,6 +6,7 @@ import me.gabber235.typewriter.adapters.modifiers.Help import me.gabber235.typewriter.entry.entries.ReadableFactEntry import me.gabber235.typewriter.entry.entries.WritableFactEntry import me.gabber235.typewriter.facts.FactData +import org.bukkit.entity.Player interface Entry { val id: String @@ -33,6 +34,11 @@ interface TriggerableEntry : TriggerEntry { val modifiers: List } +@Tags("placeholder") +interface PlaceholderEntry : Entry { + fun display(player: Player?): String? +} + enum class CriteriaOperator { @SerializedName("==") EQUALS, diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryReference.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryReference.kt index bc26615309..dfc18b87ac 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryReference.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryReference.kt @@ -4,6 +4,8 @@ import kotlin.reflect.KClass inline fun emptyRef() = Ref("", E::class) +inline fun E.ref() = Ref(id, E::class) + /** * A reference to an entry. This is used to reference an entry from another entry. * @@ -18,9 +20,23 @@ class Ref( fun get(): E? = Query.findById(klass, id) + override fun toString(): String { return "Ref<${klass.simpleName}>($id)" } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Ref<*> + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } } fun List>.get(): List = mapNotNull { it.get() } \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt new file mode 100644 index 0000000000..007cdcc251 --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt @@ -0,0 +1,122 @@ +package me.gabber235.typewriter.entry + +import me.gabber235.typewriter.entry.entries.EventTrigger +import me.gabber235.typewriter.entry.entries.ReadableFactEntry +import me.gabber235.typewriter.interaction.InteractionHandler +import org.bukkit.entity.Player +import org.koin.java.KoinJavaComponent +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +class FactWatcher( + private val player: Player, +) { + private val factWatch = ConcurrentHashMap, Int>() + private val listeners = mutableListOf() + private var cycle: Int = 0 + + fun tick() { + if (cycle++ % 20 == 0) { + refresh() + } + } + + private fun refresh() { + factWatch.keys.forEach { refreshFact(it) } + } + + fun refreshFact(ref: Ref) { + val old = factWatch[ref] ?: return + val fact = ref.get() ?: return + val new = fact.readForPlayersGroup(player).value + if (old != new) { + factWatch[ref] = new + notifyListeners(ref) + } + } + + private fun notifyListeners(ref: Ref) { + listeners.forEach { listener -> + if (ref in listener) { + listener.listener(player, ref) + } + } + } + + fun addListener( + facts: List>, + listener: (Player, Ref) -> Unit + ): FactListenerSubscription { + val id = UUID.randomUUID() + listeners.add(FactListener(id, facts, listener)) + for (fact in facts) { + factWatch.computeIfAbsent(fact) { fact.get()?.readForPlayersGroup(player)?.value ?: 0 } + } + + return FactListenerSubscription(id) + } + + fun removeListener(subscription: FactListenerSubscription) { + listeners.removeIf { it.id == subscription.id } + + for (fact in factWatch.keys.toList()) { + if (listeners.none { fact in it }) { + factWatch.remove(fact) + } + } + } + + companion object { + @JvmStatic + fun listenForFacts( + player: Player, + facts: List>, + listener: (Player, Ref) -> Unit + ): FactListenerSubscription = player.listenForFacts(facts, listener) + + @JvmStatic + fun stopListening( + player: Player, + subscription: FactListenerSubscription + ) = player.stopListening(subscription) + } +} + +private class FactListener( + val id: UUID, + val facts: List>, + val listener: (Player, Ref) -> Unit, +) { + operator fun contains(ref: Ref) = ref in facts +} + +class FactListenerSubscription( + val id: UUID, +) { + fun cancel(player: Player) = player.stopListening(this) +} + +class RefreshFactTrigger( + val fact: Ref, +) : EventTrigger { + override val id: String + get() = "fact.${fact.id}" +} + +private val Player.factWatcher: FactWatcher? + get() = with(KoinJavaComponent.get(InteractionHandler::class.java)) { + interaction?.factWatcher + } + +fun Player.listenForFacts( + facts: List>, + listener: (Player, Ref) -> Unit +): FactListenerSubscription { + val watcher = factWatcher ?: throw IllegalStateException("Player is not in an interaction") + return watcher.addListener(facts, listener) +} + +fun Player.stopListening(subscription: FactListenerSubscription) { + val watcher = factWatcher ?: return + watcher.removeListener(subscription) +} diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt index e4a3a5cf40..58bf7dd5a2 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt @@ -3,11 +3,13 @@ package me.gabber235.typewriter.entry.entries import lirand.api.extensions.events.unregister import lirand.api.extensions.server.server import me.gabber235.typewriter.adapters.Tags +import me.gabber235.typewriter.entry.AudienceManager import me.gabber235.typewriter.entry.ManifestEntry import me.gabber235.typewriter.entry.Ref import me.gabber235.typewriter.plugin import org.bukkit.entity.Player import org.bukkit.event.Listener +import org.koin.java.KoinJavaComponent.get import java.util.* import java.util.concurrent.ConcurrentSkipListSet @@ -36,6 +38,7 @@ abstract class AudienceDisplay : Listener { } fun addPlayer(player: Player) { + if (playerIds.isEmpty()) initialize() if (!playerIds.add(player.uniqueId)) return onPlayerAdd(player) } @@ -43,43 +46,36 @@ abstract class AudienceDisplay : Listener { fun removePlayer(player: Player) { if (!playerIds.remove(player.uniqueId)) return onPlayerRemove(player) + if (playerIds.isEmpty()) dispose() } abstract fun onPlayerAdd(player: Player) abstract fun onPlayerRemove(player: Player) - operator fun contains(player: Player): Boolean = player.uniqueId in playerIds - operator fun contains(uuid: UUID): Boolean = uuid in playerIds + open operator fun contains(player: Player): Boolean = contains(player.uniqueId) + open operator fun contains(uuid: UUID): Boolean = uuid in playerIds } fun List>.into() = mapNotNull { it.get()?.display() } abstract class AudienceFilter( - private val children: List + private val ref: Ref ) : AudienceDisplay() { private val filteredPlayers: ConcurrentSkipListSet = ConcurrentSkipListSet() override val players: List get() = server.onlinePlayers.filter { it.uniqueId in filteredPlayers } - override fun initialize() { - children.forEach { it.initialize() } - super.initialize() - } - - override fun dispose() { - super.dispose() - children.forEach { it.dispose() } - } + protected val consideredPlayers: List get() = super.players abstract fun filter(player: Player): Boolean fun Player.updateFilter(isFiltered: Boolean) { if (isFiltered) { if (filteredPlayers.add(uniqueId)) { - children.forEach { it.addPlayer(this) } + get(AudienceManager::class.java).addPlayerToChildren(this, ref) } } else { if (filteredPlayers.remove(uniqueId)) { - children.forEach { it.removePlayer(this) } + get(AudienceManager::class.java).removePlayerFromChildren(this, ref) } } } @@ -93,5 +89,11 @@ abstract class AudienceFilter( override fun onPlayerRemove(player: Player) { player.updateFilter(false) } + + fun canConsider(player: Player): Boolean = super.contains(player) + fun canConsider(uuid: UUID): Boolean = super.contains(uuid) + + override fun contains(player: Player): Boolean = contains(player.uniqueId) + override fun contains(uuid: UUID): Boolean = uuid in filteredPlayers } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/EntityEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/EntityEntry.kt index bc1249d240..6fe67a818b 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/EntityEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/EntityEntry.kt @@ -2,19 +2,23 @@ package me.gabber235.typewriter.entry.entries import me.gabber235.typewriter.adapters.Tags import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.entry.PlaceholderEntry import me.gabber235.typewriter.entry.StaticEntry import me.gabber235.typewriter.utils.Sound +import org.bukkit.entity.Player @Tags("entity") interface EntityEntry : StaticEntry @Tags("speaker") -interface SpeakerEntry : EntityEntry { +interface SpeakerEntry : EntityEntry, PlaceholderEntry { @Help("The name of the entity that will be displayed in the chat (e.g. 'Steve' or 'Alex').") val displayName: String @Help("The sound that will be played when the entity speaks.") val sound: Sound + + override fun display(player: Player?): String? = displayName } @Tags("npc") diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt index 238925128e..1f18e3acb2 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/FactEntry.kt @@ -4,6 +4,7 @@ import lirand.api.extensions.server.server import me.gabber235.typewriter.adapters.Tags import me.gabber235.typewriter.adapters.modifiers.Help import me.gabber235.typewriter.adapters.modifiers.MultiLine +import me.gabber235.typewriter.entry.PlaceholderEntry import me.gabber235.typewriter.entry.Ref import me.gabber235.typewriter.entry.StaticEntry import me.gabber235.typewriter.facts.FactData @@ -38,7 +39,7 @@ interface FactEntry : StaticEntry { } @Tags("readable-fact") -interface ReadableFactEntry : FactEntry { +interface ReadableFactEntry : FactEntry, PlaceholderEntry { fun readForPlayersGroup(player: Player): FactData { val factId = identifier(player) ?: return FactData(0) return readForGroup(factId.groupId) @@ -59,6 +60,11 @@ interface ReadableFactEntry : FactEntry { * This should **not** be used directly. Use [readForPlayersGroup] instead. */ fun readSinglePlayer(player: Player): FactData + + override fun display(player: Player?): String? { + if (player == null) return null + return readForPlayersGroup(player).value.toString() + } } @Tags("writable-fact") diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/QuestEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/QuestEntry.kt new file mode 100644 index 0000000000..641da617b0 --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/QuestEntry.kt @@ -0,0 +1,68 @@ +package me.gabber235.typewriter.entry.entries + +import me.gabber235.typewriter.adapters.Tags +import me.gabber235.typewriter.adapters.modifiers.Colored +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.Placeholder +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.quest.trackedQuest +import me.gabber235.typewriter.extensions.placeholderapi.parsePlaceholders +import me.gabber235.typewriter.snippets.snippet +import me.gabber235.typewriter.utils.asMini +import me.gabber235.typewriter.utils.asMiniWithResolvers +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed +import org.bukkit.entity.Player + +@Tags("quest") +interface QuestEntry : AudienceFilterEntry, PlaceholderEntry { + @Help("The name to display to the player.") + @Colored + @Placeholder + val displayName: String + + @Help("When the criteria is met, it considers the quest to be active.") + val activeCriteria: List + + @Help("When the criteria is met, it considers the quest to be completed.") + val completedCriteria: List + + + override fun display(player: Player?): String = displayName.parsePlaceholders(player) +} + +private val inactiveObjectiveDisplay by snippet("quest.objective.inactive", "") +private val showingObjectiveDisplay by snippet("quest.objective.showing", "") +private val finishedObjectiveDisplay by snippet("quest.objective.finished", "✔ ") + +@Tags("objective") +interface ObjectiveEntry : AudienceFilterEntry, PlaceholderEntry { + @Help("The quest that the objective is a part of.") + val quest: Ref + + @Help("The criteria need to be met for the objective to be able to be shown.") + val criteria: List + + @Help("The finished criteria need to be met for the objective to shown as completed. If empty, it will never show as completed.") + val finishedCriteria: List + + @Help("The name to display to the player.") + @Colored + @Placeholder + val displayName: String + + override fun display(player: Player?): String { + val text = when { + player == null -> return displayName.parsePlaceholders(null) + finishedCriteria.isNotEmpty() && finishedCriteria.matches(player) -> finishedObjectiveDisplay + criteria.matches(player) -> showingObjectiveDisplay + else -> inactiveObjectiveDisplay + } + return text.asMiniWithResolvers(parsed("name", displayName)).asMini().parsePlaceholders(player) + } +} + +fun Player.trackedShowingObjectives() = trackedQuest()?.let { questShowingObjectives(it) } ?: emptyList() + +fun Player.questShowingObjectives(quest: Ref) = Query.findWhere { objectiveEntry -> + objectiveEntry.quest == quest && inAudience(objectiveEntry.ref()) +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SoundEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SoundEntry.kt index 4b371fc3d2..c7ae295c8c 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SoundEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SoundEntry.kt @@ -1,12 +1,16 @@ package me.gabber235.typewriter.entry.entries import me.gabber235.typewriter.adapters.Tags +import me.gabber235.typewriter.entry.PlaceholderEntry import me.gabber235.typewriter.entry.StaticEntry import net.kyori.adventure.sound.Sound +import org.bukkit.entity.Player @Tags("sound_id") -interface SoundIdEntry : StaticEntry { +interface SoundIdEntry : StaticEntry, PlaceholderEntry { val soundId: String + + override fun display(player: Player?): String? = soundId } @Tags("sound_source") diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt new file mode 100644 index 0000000000..292b0ebba2 --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt @@ -0,0 +1,169 @@ +package me.gabber235.typewriter.entry.quest + +import com.github.shynixn.mccoroutine.bukkit.launch +import com.github.shynixn.mccoroutine.bukkit.ticks +import kotlinx.coroutines.delay +import lirand.api.extensions.events.SimpleListener +import lirand.api.extensions.events.listen +import lirand.api.extensions.events.unregister +import me.gabber235.typewriter.entry.* +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.events.AsyncQuestStatusUpdate +import me.gabber235.typewriter.events.AsyncTrackedQuestUpdate +import me.gabber235.typewriter.events.PublishedBookEvent +import me.gabber235.typewriter.events.TypewriterReloadEvent +import me.gabber235.typewriter.interaction.InteractionHandler +import me.gabber235.typewriter.plugin +import me.gabber235.typewriter.utils.ThreadType.DISPATCHERS_ASYNC +import org.bukkit.entity.Player +import org.koin.java.KoinJavaComponent.get +import java.util.concurrent.ConcurrentHashMap + +class QuestTracker( + val player: Player, +) { + private val quests = ConcurrentHashMap, QuestStatus>() + private var trackedQuest: Ref? = null + + private val listener = SimpleListener() + private var factWatchSubscription: FactListenerSubscription? = null + + init { + Query.find().forEach { refresh(it.ref()) } + plugin.listen(listener) { refreshWatchedFacts() } + plugin.listen(listener) { refreshWatchedFacts() } + + // At this point, the player is not yet in the interaction handler. + // So we wait until the next tick to register the listener. + plugin.launch { + delay(1.ticks) + refreshWatchedFacts() + } + } + + private fun refreshWatchedFacts() { + factWatchSubscription?.cancel(player) + val facts = Query.find().flatMap { it.activeCriteria + it.completedCriteria }.map { it.fact } + factWatchSubscription = player.listenForFacts( + facts, + listener = { _, ref -> + Query.findWhere { quest -> + quest.activeCriteria.any { it.fact == ref } || quest.completedCriteria.any { it.fact == ref } + }.forEach { + refresh(it.ref()) + } + } + ) + } + + fun end() { + listener.unregister() + factWatchSubscription?.cancel(player) + } + + private fun refresh(ref: Ref) { + val quest = ref.get() ?: return + val status = when { + quest.completedCriteria.matches(player) -> QuestStatus.COMPLETED + quest.activeCriteria.matches(player) -> QuestStatus.ACTIVE + else -> QuestStatus.INACTIVE + } + + val oldStatus = quests[ref] + quests[ref] = status + if (oldStatus == null) return + if (oldStatus == status) return + + if (oldStatus == QuestStatus.INACTIVE && status == QuestStatus.ACTIVE) { + trackQuest(ref) + } else if (trackedQuest == ref && status != QuestStatus.ACTIVE) { + unTrackQuest() + } + + DISPATCHERS_ASYNC.launch { + AsyncQuestStatusUpdate(player, ref, oldStatus, status).callEvent() + } + } + + fun inactiveQuests() = quests.filterValues { it == QuestStatus.INACTIVE }.keys.toList() + fun activeQuests() = quests.filterValues { it == QuestStatus.ACTIVE }.keys.toList() + fun completedQuests() = quests.filterValues { it == QuestStatus.COMPLETED }.keys.toList() + fun isQuestInactive(quest: Ref) = quests[quest] == QuestStatus.INACTIVE + fun isQuestActive(quest: Ref) = quests[quest] == QuestStatus.ACTIVE + fun isQuestCompleted(quest: Ref) = quests[quest] == QuestStatus.COMPLETED + fun trackedQuest() = trackedQuest + + fun trackQuest(quest: Ref) { + val from = trackedQuest + trackedQuest = quest + DISPATCHERS_ASYNC.launch { + AsyncTrackedQuestUpdate(player, from, quest).callEvent() + } + } + + fun unTrackQuest() { + val from = trackedQuest + trackedQuest = null + DISPATCHERS_ASYNC.launch { + AsyncTrackedQuestUpdate(player, from, null).callEvent() + } + } + + companion object { + @JvmStatic + fun inactiveQuests(player: Player) = player.inactiveQuests() + + @JvmStatic + fun activeQuests(player: Player) = player.activeQuests() + + @JvmStatic + fun completedQuests(player: Player) = player.completedQuests() + + @JvmStatic + fun isQuestInactive(player: Player, quest: Ref) = player isQuestInactive quest + + @JvmStatic + fun isQuestActive(player: Player, quest: Ref) = player isQuestActive quest + + @JvmStatic + fun isQuestCompleted(player: Player, quest: Ref) = player isQuestCompleted quest + + @JvmStatic + fun trackedQuest(player: Player) = player.trackedQuest() + + @JvmStatic + fun isQuestTracked(player: Player, quest: Ref) = player isQuestTracked quest + + @JvmStatic + fun trackQuest(player: Player, quest: Ref) = player trackQuest quest + + @JvmStatic + fun unTrackQuest(player: Player) = player.unTrackQuest() + } +} + +private val Player.tracker: QuestTracker? + get() = with(get(InteractionHandler::class.java)) { + interaction?.questTracker + } + +fun Player.inactiveQuests() = tracker?.inactiveQuests() ?: emptyList() +fun Player.activeQuests() = tracker?.activeQuests() ?: emptyList() +fun Player.completedQuests() = tracker?.completedQuests() ?: emptyList() +infix fun Player.isQuestInactive(quest: Ref) = tracker?.isQuestInactive(quest) ?: true +infix fun Player.isQuestActive(quest: Ref) = tracker?.isQuestActive(quest) ?: false +infix fun Player.isQuestCompleted(quest: Ref) = tracker?.isQuestCompleted(quest) ?: false +fun Player.trackedQuest() = tracker?.trackedQuest() +infix fun Player.isQuestTracked(quest: Ref): Boolean { + val trackedQuest = trackedQuest() ?: return false + return trackedQuest == quest +} + +infix fun Player.trackQuest(quest: Ref) = tracker?.trackQuest(quest) +fun Player.unTrackQuest() = tracker?.unTrackQuest() + +enum class QuestStatus { + INACTIVE, + ACTIVE, + COMPLETED +} diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncQuestStatusUpdate.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncQuestStatusUpdate.kt new file mode 100644 index 0000000000..a5b168d18a --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncQuestStatusUpdate.kt @@ -0,0 +1,26 @@ +package me.gabber235.typewriter.events + +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.entries.QuestEntry +import me.gabber235.typewriter.entry.quest.QuestStatus +import org.bukkit.entity.Player +import org.bukkit.event.HandlerList +import org.bukkit.event.player.PlayerEvent + +class AsyncQuestStatusUpdate( + player: Player, + val quest: Ref, + val from: QuestStatus, + val to: QuestStatus, +) : + PlayerEvent(player, true) { + override fun getHandlers(): HandlerList = HANDLER_LIST + + companion object { + @JvmStatic + val HANDLER_LIST = HandlerList() + + @JvmStatic + fun getHandlerList(): HandlerList = HANDLER_LIST + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncTrackedQuestUpdate.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncTrackedQuestUpdate.kt new file mode 100644 index 0000000000..222af871bd --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncTrackedQuestUpdate.kt @@ -0,0 +1,25 @@ +package me.gabber235.typewriter.events + +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.entries.QuestEntry +import org.bukkit.entity.Player +import org.bukkit.event.HandlerList +import org.bukkit.event.player.PlayerEvent + + +class AsyncTrackedQuestUpdate( + player: Player, + val from: Ref?, + val to: Ref?, +) : + PlayerEvent(player, true) { + override fun getHandlers(): HandlerList = HANDLER_LIST + + companion object { + @JvmStatic + val HANDLER_LIST = HandlerList() + + @JvmStatic + fun getHandlerList(): HandlerList = HANDLER_LIST + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/TypewriteExpansion.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/PlaceholderExpansion.kt similarity index 62% rename from plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/TypewriteExpansion.kt rename to plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/PlaceholderExpansion.kt index 47b78a8c3c..78f7ab9d31 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/TypewriteExpansion.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/extensions/placeholderapi/PlaceholderExpansion.kt @@ -3,11 +3,13 @@ package me.gabber235.typewriter.extensions.placeholderapi import lirand.api.extensions.server.server import me.clip.placeholderapi.PlaceholderAPI import me.clip.placeholderapi.expansion.PlaceholderExpansion -import me.gabber235.typewriter.entry.Entry +import me.gabber235.typewriter.entry.PlaceholderEntry import me.gabber235.typewriter.entry.Query -import me.gabber235.typewriter.entry.entries.ReadableFactEntry -import me.gabber235.typewriter.entry.entries.SoundIdEntry import me.gabber235.typewriter.entry.entries.SpeakerEntry +import me.gabber235.typewriter.entry.entries.trackedShowingObjectives +import me.gabber235.typewriter.entry.quest.trackedQuest +import me.gabber235.typewriter.logger +import me.gabber235.typewriter.snippets.snippet import org.bukkit.OfflinePlayer import org.bukkit.entity.Player import org.bukkit.plugin.Plugin @@ -15,7 +17,10 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.util.* -object TypewriteExpansion : PlaceholderExpansion(), KoinComponent { + +private val noneTracked by snippet("quest.tracked.none", "None tracked") + +object PlaceholderExpansion : PlaceholderExpansion(), KoinComponent { private val plugin: Plugin by inject() override fun getIdentifier(): String = "typewriter" @@ -26,27 +31,28 @@ object TypewriteExpansion : PlaceholderExpansion(), KoinComponent { override fun persist(): Boolean = true override fun onPlaceholderRequest(player: Player?, params: String): String? { + // TODO: Replace placeholder if (params.startsWith("speaker_")) { val speakerName = params.substring(8) + logger.warning("Speaker specific placeholder is deprecated, move to the generic entry placeholder: %typewriter_${params}% -> %typewriter_${speakerName}%") val speaker: SpeakerEntry = Query.findById(speakerName) ?: Query.findByName(speakerName) ?: return null + return speaker.displayName } - val entry: Entry = Query.findById(params) ?: Query.findByName(params) ?: return null - if (entry is ReadableFactEntry) { + if (params == "tracked_quest") { if (player == null) return null - return entry.readForPlayersGroup(player).value.toString() + return player.trackedQuest()?.get()?.display(player) ?: noneTracked } - if (entry is SpeakerEntry) { - return entry.displayName - } - - if (entry is SoundIdEntry) { - return entry.soundId + if (params == "tracked_objectives") { + if (player == null) return null + return player.trackedShowingObjectives().joinToString(", ") { it.display(player) } + .ifBlank { noneTracked } } - return null + val entry: PlaceholderEntry = Query.findById(params) ?: Query.findByName(params) ?: return null + return entry.display(player) } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/facts/FactDatabase.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/facts/FactDatabase.kt index 777d7656c5..df6be6434b 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/facts/FactDatabase.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/facts/FactDatabase.kt @@ -2,10 +2,7 @@ package me.gabber235.typewriter.facts import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking -import me.gabber235.typewriter.entry.Modifier -import me.gabber235.typewriter.entry.ModifierOperator -import me.gabber235.typewriter.entry.Query -import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.* import me.gabber235.typewriter.entry.entries.ExpirableFactEntry import me.gabber235.typewriter.entry.entries.PersistableFactEntry import me.gabber235.typewriter.entry.entries.ReadableFactEntry @@ -131,6 +128,9 @@ class FactDatabase : KoinComponent { for ((id, value) in modifications) { val entry = Query.findById(id) ?: continue entry.write(player, value) + if (entry is ReadableFactEntry) { + RefreshFactTrigger(entry.ref()) triggerFor player + } } } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt index 5d28e728cb..82611a8aab 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt @@ -1,12 +1,11 @@ package me.gabber235.typewriter.interaction -import me.gabber235.typewriter.entry.Query +import me.gabber235.typewriter.entry.* import me.gabber235.typewriter.entry.cinematic.CinematicSequence import me.gabber235.typewriter.entry.dialogue.DialogueSequence import me.gabber235.typewriter.entry.entries.* import me.gabber235.typewriter.entry.entries.SystemTrigger.* -import me.gabber235.typewriter.entry.matches -import me.gabber235.typewriter.entry.triggerFor +import me.gabber235.typewriter.entry.quest.QuestTracker import org.bukkit.entity.Player import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -16,6 +15,9 @@ class Interaction(val player: Player) : KoinComponent { private var dialogue: DialogueSequence? = null private var cinematic: CinematicSequence? = null + val questTracker: QuestTracker = QuestTracker(player) + val factWatcher: FactWatcher = FactWatcher(player) + val hasDialogue: Boolean get() = dialogue != null @@ -25,6 +27,7 @@ class Interaction(val player: Player) : KoinComponent { suspend fun tick() { dialogue?.tick() cinematic?.tick() + factWatcher.tick() } /** Handles an event. All [SystemTrigger]'s are handled by the plugin itself. */ @@ -32,6 +35,7 @@ class Interaction(val player: Player) : KoinComponent { triggerActions(event) handleDialogue(event) handleCinematic(event) + handleFactWatcher(event) } /** @@ -154,6 +158,15 @@ class Interaction(val player: Player) : KoinComponent { this.cinematic = CinematicSequence(player, actions, trigger.triggers, trigger.minEndTime) } + private fun handleFactWatcher(event: Event) { + val factRefreshes = event.triggers.filterIsInstance() + if (factRefreshes.isEmpty()) return + + factRefreshes.forEach { + factWatcher.refreshFact(it.fact) + } + } + suspend fun end() { val dialogue = dialogue val cinematic = cinematic @@ -163,5 +176,6 @@ class Interaction(val player: Player) : KoinComponent { dialogue?.end() cinematic?.end(force = true) + questTracker.end() } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/InteractionHandler.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/InteractionHandler.kt index d1c510a337..04467354b7 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/InteractionHandler.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/InteractionHandler.kt @@ -31,7 +31,7 @@ class InteractionHandler : Listener, KoinComponent { private val interactions = ConcurrentHashMap() private var job: Job? = null - private val Player.interaction: Interaction? + internal val Player.interaction: Interaction? get() = interactions[uniqueId] diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/utils/ThreadType.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/utils/ThreadType.kt index bbf15dede2..b97c7e6177 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/utils/ThreadType.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/utils/ThreadType.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import lirand.api.extensions.server.server import me.gabber235.typewriter.plugin enum class ThreadType { @@ -43,18 +44,13 @@ enum class ThreadType { } return Job() } - if (this == REMAIN) { - return launch { - block() - } - } return plugin.launch( when (this) { SYNC -> plugin.minecraftDispatcher ASYNC -> plugin.minecraftDispatcher DISPATCHERS_ASYNC -> Dispatchers.IO - else -> throw IllegalStateException("Unknown thread type: $this") + REMAIN -> if (server.isPrimaryThread) plugin.minecraftDispatcher else plugin.asyncDispatcher } ) { block() From bc784074c599d08476ffa909644c56242193dfdc Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 11:53:38 +0100 Subject: [PATCH 13/24] Start entity from offset --- plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt index 63e5f26d3d..75781eb01d 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/Typewriter.kt @@ -39,11 +39,14 @@ import org.koin.core.module.dsl.withOptions import org.koin.core.qualifier.named import org.koin.dsl.module import org.koin.java.KoinJavaComponent +import java.util.concurrent.atomic.AtomicInteger import java.util.logging.Logger import kotlin.time.Duration.Companion.seconds class Typewriter : KotlinPlugin(), KoinComponent { + private val entityId = AtomicInteger(100000) + override fun onLoad() { super.onLoad() val modules = module { @@ -96,6 +99,7 @@ class Typewriter : KotlinPlugin(), KoinComponent { return } EntityLib.init(PacketEvents.getAPI()) + EntityLib.setEntityIdProvider(entityId::getAndIncrement) get().initialize() get().initialize() From dc217135935f8e3eaa82041756b9b7a1fcc78f44 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 12:22:04 +0100 Subject: [PATCH 14/24] Added priority for pages --- app/lib/models/page.dart | 16 +++ app/lib/models/page.freezed.dart | 36 ++++- app/lib/models/page.g.dart | 136 +++++++++++++++++- app/lib/pages/pages_list.dart | 78 ++++++++++ app/lib/utils/icons.dart | 1 + .../typewriter/entry/EntryDatabase.kt | 26 ++-- .../typewriter/entry/EntryListeners.kt | 3 +- .../typewriter/entry/entries/SidebarEntry.kt | 3 + 8 files changed, 274 insertions(+), 25 deletions(-) create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt diff --git a/app/lib/models/page.dart b/app/lib/models/page.dart index 3af7cbee91..cac6a2ca4e 100644 --- a/app/lib/models/page.dart +++ b/app/lib/models/page.dart @@ -44,6 +44,11 @@ String pageChapter(PageChapterRef ref, String pageName) { return ref.watch(pageProvider(pageName))?.chapter ?? ""; } +@riverpod +int pagePriority(PagePriorityRef ref, String pageName) { + return ref.watch(pageProvider(pageName))?.priority ?? 0; +} + @riverpod String? entryPageId(EntryPageIdRef ref, String entryId) { return ref @@ -135,6 +140,7 @@ class Page with _$Page { required PageType type, @Default([]) List entries, @Default("") String chapter, + @Default(0) int priority, }) = _Page; factory Page.fromJson(Map json) => _$PageFromJson(json); @@ -161,6 +167,16 @@ extension PageExtension on Page { .changePageValue(pageName, "chapter", newChapter); } + Future changePriority(PassingRef ref, int newPriority) async { + updatePage( + ref, + (page) => page.copyWith(priority: newPriority), + ); + await ref + .read(communicatorProvider) + .changePageValue(pageName, "priority", newPriority); + } + Future createEntry(PassingRef ref, Entry entry) async { updatePage( ref, diff --git a/app/lib/models/page.freezed.dart b/app/lib/models/page.freezed.dart index 07c0feab4d..d0cc5c634f 100644 --- a/app/lib/models/page.freezed.dart +++ b/app/lib/models/page.freezed.dart @@ -25,6 +25,7 @@ mixin _$Page { PageType get type => throw _privateConstructorUsedError; List get entries => throw _privateConstructorUsedError; String get chapter => throw _privateConstructorUsedError; + int get priority => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -40,7 +41,8 @@ abstract class $PageCopyWith<$Res> { {@JsonKey(name: "name") String pageName, PageType type, List entries, - String chapter}); + String chapter, + int priority}); } /// @nodoc @@ -60,6 +62,7 @@ class _$PageCopyWithImpl<$Res, $Val extends Page> Object? type = null, Object? entries = null, Object? chapter = null, + Object? priority = null, }) { return _then(_value.copyWith( pageName: null == pageName @@ -78,6 +81,10 @@ class _$PageCopyWithImpl<$Res, $Val extends Page> ? _value.chapter : chapter // ignore: cast_nullable_to_non_nullable as String, + priority: null == priority + ? _value.priority + : priority // ignore: cast_nullable_to_non_nullable + as int, ) as $Val); } } @@ -93,7 +100,8 @@ abstract class _$$PageImplCopyWith<$Res> implements $PageCopyWith<$Res> { {@JsonKey(name: "name") String pageName, PageType type, List entries, - String chapter}); + String chapter, + int priority}); } /// @nodoc @@ -110,6 +118,7 @@ class __$$PageImplCopyWithImpl<$Res> Object? type = null, Object? entries = null, Object? chapter = null, + Object? priority = null, }) { return _then(_$PageImpl( pageName: null == pageName @@ -128,6 +137,10 @@ class __$$PageImplCopyWithImpl<$Res> ? _value.chapter : chapter // ignore: cast_nullable_to_non_nullable as String, + priority: null == priority + ? _value.priority + : priority // ignore: cast_nullable_to_non_nullable + as int, )); } } @@ -139,7 +152,8 @@ class _$PageImpl implements _Page { {@JsonKey(name: "name") required this.pageName, required this.type, final List entries = const [], - this.chapter = ""}) + this.chapter = "", + this.priority = 0}) : _entries = entries; factory _$PageImpl.fromJson(Map json) => @@ -162,10 +176,13 @@ class _$PageImpl implements _Page { @override @JsonKey() final String chapter; + @override + @JsonKey() + final int priority; @override String toString() { - return 'Page(pageName: $pageName, type: $type, entries: $entries, chapter: $chapter)'; + return 'Page(pageName: $pageName, type: $type, entries: $entries, chapter: $chapter, priority: $priority)'; } @override @@ -177,13 +194,15 @@ class _$PageImpl implements _Page { other.pageName == pageName) && (identical(other.type, type) || other.type == type) && const DeepCollectionEquality().equals(other._entries, _entries) && - (identical(other.chapter, chapter) || other.chapter == chapter)); + (identical(other.chapter, chapter) || other.chapter == chapter) && + (identical(other.priority, priority) || + other.priority == priority)); } @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, pageName, type, - const DeepCollectionEquality().hash(_entries), chapter); + const DeepCollectionEquality().hash(_entries), chapter, priority); @JsonKey(ignore: true) @override @@ -204,7 +223,8 @@ abstract class _Page implements Page { {@JsonKey(name: "name") required final String pageName, required final PageType type, final List entries, - final String chapter}) = _$PageImpl; + final String chapter, + final int priority}) = _$PageImpl; factory _Page.fromJson(Map json) = _$PageImpl.fromJson; @@ -218,6 +238,8 @@ abstract class _Page implements Page { @override String get chapter; @override + int get priority; + @override @JsonKey(ignore: true) _$$PageImplCopyWith<_$PageImpl> get copyWith => throw _privateConstructorUsedError; diff --git a/app/lib/models/page.g.dart b/app/lib/models/page.g.dart index 72f13bf42d..40eca7b65f 100644 --- a/app/lib/models/page.g.dart +++ b/app/lib/models/page.g.dart @@ -14,6 +14,7 @@ _$PageImpl _$$PageImplFromJson(Map json) => _$PageImpl( .toList() ?? const [], chapter: json['chapter'] as String? ?? "", + priority: json['priority'] as int? ?? 0, ); Map _$$PageImplToJson(_$PageImpl instance) => @@ -22,6 +23,7 @@ Map _$$PageImplToJson(_$PageImpl instance) => 'type': _$PageTypeEnumMap[instance.type]!, 'entries': instance.entries, 'chapter': instance.chapter, + 'priority': instance.priority, }; const _$PageTypeEnumMap = { @@ -578,6 +580,134 @@ class _PageChapterProviderElement extends AutoDisposeProviderElement String get pageName => (origin as PageChapterProvider).pageName; } +String _$pagePriorityHash() => r'6d67e3c592c0e5656c4c26213da90d64691825b5'; + +/// See also [pagePriority]. +@ProviderFor(pagePriority) +const pagePriorityProvider = PagePriorityFamily(); + +/// See also [pagePriority]. +class PagePriorityFamily extends Family { + /// See also [pagePriority]. + const PagePriorityFamily(); + + /// See also [pagePriority]. + PagePriorityProvider call( + String pageName, + ) { + return PagePriorityProvider( + pageName, + ); + } + + @override + PagePriorityProvider getProviderOverride( + covariant PagePriorityProvider provider, + ) { + return call( + provider.pageName, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'pagePriorityProvider'; +} + +/// See also [pagePriority]. +class PagePriorityProvider extends AutoDisposeProvider { + /// See also [pagePriority]. + PagePriorityProvider( + String pageName, + ) : this._internal( + (ref) => pagePriority( + ref as PagePriorityRef, + pageName, + ), + from: pagePriorityProvider, + name: r'pagePriorityProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$pagePriorityHash, + dependencies: PagePriorityFamily._dependencies, + allTransitiveDependencies: + PagePriorityFamily._allTransitiveDependencies, + pageName: pageName, + ); + + PagePriorityProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.pageName, + }) : super.internal(); + + final String pageName; + + @override + Override overrideWith( + int Function(PagePriorityRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: PagePriorityProvider._internal( + (ref) => create(ref as PagePriorityRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + pageName: pageName, + ), + ); + } + + @override + AutoDisposeProviderElement createElement() { + return _PagePriorityProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is PagePriorityProvider && other.pageName == pageName; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, pageName.hashCode); + + return _SystemHash.finish(hash); + } +} + +mixin PagePriorityRef on AutoDisposeProviderRef { + /// The parameter `pageName` of this provider. + String get pageName; +} + +class _PagePriorityProviderElement extends AutoDisposeProviderElement + with PagePriorityRef { + _PagePriorityProviderElement(super.provider); + + @override + String get pageName => (origin as PagePriorityProvider).pageName; +} + String _$entryPageIdHash() => r'ceb41cef3f8d8b1f0a28e5de7aead8969f03d6ac'; /// See also [entryPageId]. @@ -977,7 +1107,7 @@ class _EntryProviderElement extends AutoDisposeProviderElement String get entryId => (origin as EntryProvider).entryId; } -String _$globalEntryHash() => r'9aa2e0f379f22d9d87dee900fe3122f81698fac3'; +String _$globalEntryHash() => r'df6937f53ba0fe88ed629630eb2b366d2ed3a540'; /// See also [globalEntry]. @ProviderFor(globalEntry) @@ -1106,7 +1236,7 @@ class _GlobalEntryProviderElement extends AutoDisposeProviderElement } String _$globalEntryWithPageHash() => - r'250ed7d1b0146d138e1237d22c12a53e00a7b0b9'; + r'003dec3f932daec4549b62ae8bb744dc256310ec'; /// See also [globalEntryWithPage]. @ProviderFor(globalEntryWithPage) @@ -1237,7 +1367,7 @@ class _GlobalEntryWithPageProviderElement String get entryId => (origin as GlobalEntryWithPageProvider).entryId; } -String _$entryExistsHash() => r'7e7ddbceb9b0efb860e5cd01f53c509eb6009bf9'; +String _$entryExistsHash() => r'5280290c9246fb7003da2717fc9a1639c952a286'; /// See also [entryExists]. @ProviderFor(entryExists) diff --git a/app/lib/pages/pages_list.dart b/app/lib/pages/pages_list.dart index 8c5dba7810..f515396903 100644 --- a/app/lib/pages/pages_list.dart +++ b/app/lib/pages/pages_list.dart @@ -406,6 +406,14 @@ class _PageTile extends HookConsumerWidget { ChangeChapterDialogue(pageId: pageId, chapter: chapter), ), ), + ContextMenuTile.button( + title: "Change Priority", + icon: TWIcons.priority, + onTap: () => showDialog( + context: context, + builder: (_) => ChangePagePriorityDialogue(pageId: pageId), + ), + ), ContextMenuTile.divider(), ContextMenuTile.button( title: "Delete", @@ -944,6 +952,76 @@ class ChangeChapterDialogue extends HookConsumerWidget { } } +class ChangePagePriorityDialogue extends HookConsumerWidget { + const ChangePagePriorityDialogue({ + required this.pageId, + super.key, + }); + + final String pageId; + + Future _changePriority( + BuildContext context, + PassingRef ref, + int newPriority, + ValueNotifier changed, + ) async { + if (changed.value) return; + changed.value = true; + + final navigator = Navigator.of(context); + await ref.read(pageProvider(pageId))?.changePriority(ref, newPriority); + navigator.pop(true); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final priority = ref.watch(pagePriorityProvider(pageId)); + final controller = useTextEditingController(); + final focusNode = useFocusNode(); + final changed = useState(false); + + useDelayedExecution(focusNode.requestFocus); + + return AlertDialog( + title: Text("Change priority of ${pageId.formatted}"), + content: FormattedTextField( + controller: controller, + focus: focusNode, + text: priority.toString(), + hintText: "Priority", + icon: TWIcons.book, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + onSubmitted: (value) async => + _changePriority(context, ref.passing, int.parse(value), changed), + ), + actions: [ + TextButton.icon( + icon: const Iconify(TWIcons.x), + label: const Text("Cancel"), + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).textTheme.bodySmall?.color, + ), + onPressed: () => Navigator.of(context).pop(false), + ), + FilledButton.icon( + onPressed: () async => _changePriority( + context, + ref.passing, + int.parse(controller.text), + changed, + ), + label: const Text("Change"), + icon: const Iconify(TWIcons.pencil), + color: Colors.orange, + ), + ], + ); + } +} + Future showPageDeletionDialogue( BuildContext context, PassingRef ref, diff --git a/app/lib/utils/icons.dart b/app/lib/utils/icons.dart index e31d5ca4aa..ee2bc84292 100644 --- a/app/lib/utils/icons.dart +++ b/app/lib/utils/icons.dart @@ -69,4 +69,5 @@ class TWIcons { static const String help = "fa-solid:question-circle"; static const String treeGraph = "icon-park-solid:chart-graph"; static const String moveEntry = "material-symbols:tab-move"; + static const String priority = "material-symbols:priority-high-rounded"; } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt index e123bde6e0..b46c6d5777 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt @@ -8,8 +8,6 @@ import lirand.api.extensions.events.listen import me.gabber235.typewriter.adapters.AdapterLoader import me.gabber235.typewriter.adapters.customEditors import me.gabber235.typewriter.entry.entries.CustomCommandEntry -import me.gabber235.typewriter.entry.entries.EventEntry -import me.gabber235.typewriter.entry.entries.FactEntry import me.gabber235.typewriter.events.PublishedBookEvent import me.gabber235.typewriter.events.TypewriterReloadEvent import me.gabber235.typewriter.logger @@ -25,8 +23,6 @@ import org.koin.core.qualifier.named import kotlin.reflect.KClass interface EntryDatabase { - val events: List - val facts: List val commandEvents: List fun initialize() @@ -39,9 +35,9 @@ interface EntryDatabase { fun findEntry(klass: KClass, predicate: (E) -> Boolean): E? fun findEntryById(kClass: KClass, id: String): E? - fun getFact(id: String): FactEntry? - fun findFactByName(name: String): FactEntry? fun getPageNames(type: PageType? = null): List + + fun entryPriority(entry: Ref): Int } class EntryDatabaseImpl : EntryDatabase, KoinComponent { @@ -52,10 +48,10 @@ class EntryDatabaseImpl : EntryDatabase, KoinComponent { private var pages: List = emptyList() private var entries: List = emptyList() - override var events = listOf() - override var facts = listOf() override var commandEvents = listOf() + private var entryPriority = emptyMap, Int>() + override fun initialize() { plugin.listen { loadEntries() } plugin.listen { loadEntries() } @@ -64,15 +60,16 @@ class EntryDatabaseImpl : EntryDatabase, KoinComponent { override fun loadEntries() { val pages = readPages(gson) - this.events = pages.flatMap { it.entries.filterIsInstance() } - this.facts = pages.flatMap { it.entries.filterIsInstance() } - val newCommandEvents = pages.flatMap { it.entries.filterIsInstance() } this.commandEvents = CustomCommandEntry.refreshAndRegisterAll(newCommandEvents) this.entries = pages.flatMap { it.entries } + this.entryPriority = pages.flatMap { page -> + page.entries.map { it.ref() to page.priority } + }.toMap() this.pages = pages + entryListeners.register() audienceManager.register() @@ -109,13 +106,15 @@ class EntryDatabaseImpl : EntryDatabase, KoinComponent { } override fun findEntryById(kClass: KClass, id: String): T? = findEntry(kClass) { it.id == id } - override fun getFact(id: String) = facts.firstOrNull { it.id == id } - override fun findFactByName(name: String) = facts.firstOrNull { it.name == name } override fun getPageNames(type: PageType?): List { return if (type == null) pages.map { it.id } else pages.filter { it.type == type }.map { it.id } } + + override fun entryPriority(entry: Ref): Int { + return entryPriority[entry] ?: 0 + } } fun JsonReader.parsePage(id: String, gson: Gson): Result { @@ -181,6 +180,7 @@ data class Page( val id: String = "", val entries: List = emptyList(), val type: PageType = PageType.SEQUENCE, + val priority: Int = 0 ) enum class PageType(val id: String) { diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryListeners.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryListeners.kt index 9b6694e106..ffffdca37f 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryListeners.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryListeners.kt @@ -48,7 +48,6 @@ annotation class EntryListener( */ class EntryListeners : KoinComponent { private val adapterLoader: AdapterLoader by inject() - private val entryDatabase: EntryDatabase by inject() private val listener = object : Listener {} @@ -71,7 +70,7 @@ class EntryListeners : KoinComponent { val entryListeners = adapterLoader.adapters.flatMap { it.eventListeners } - val activeEventEntries = entryDatabase.events.map { it::class }.distinct() + val activeEventEntries = Query.find().map { it::class }.distinct() entryListeners.filter { activeEventEntries.any { activeEventEntry -> it.entry.isSuperclassOf(activeEventEntry) } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt new file mode 100644 index 0000000000..6694e3b45e --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt @@ -0,0 +1,3 @@ +package me.gabber235.typewriter.entry.entries + +interface SidebarEntry : AudienceFilterEntry \ No newline at end of file From 746ae0ffb018766e354f6e31a7095c3d54bf24c8 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 15:30:13 +0100 Subject: [PATCH 15/24] Add sidebar --- .../entries/audience/BossBarEntry.kt | 2 +- .../entries/audience/SidebarEntry.kt | 53 ++++++++ .../quest/ObjectiveSidebarLinesEntry.kt | 51 +++++++ .../general/decorated_text_field.dart | 3 +- app/pubspec.lock | 26 ++-- .../typewriter/adapters/EntryAttributes.kt | 2 + .../typewriter/entry/AudienceManager.kt | 15 ++ .../typewriter/entry/EntryDatabase.kt | 6 +- .../typewriter/entry/SidebarManager.kt | 128 ++++++++++++++++++ .../typewriter/entry/entries/AudienceEntry.kt | 11 +- .../typewriter/entry/entries/SidebarEntry.kt | 29 +++- .../typewriter/entry/quest/QuestTracker.kt | 2 +- .../typewriter/interaction/Interaction.kt | 7 +- 13 files changed, 314 insertions(+), 21 deletions(-) create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/SidebarEntry.kt create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/ObjectiveSidebarLinesEntry.kt create mode 100644 plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/BossBarEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/BossBarEntry.kt index 8cbe1929ce..b7ef907d4b 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/BossBarEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/BossBarEntry.kt @@ -20,7 +20,7 @@ import kotlin.time.Duration.Companion.milliseconds @Entry("boss_bar", "Boss Bar", Colors.GREEN, "carbon:progress-bar") /** - * The `Boss Bar` is a display that shows a bar at the top of the screen. + * The `BossBarEntry` is a display that shows a bar at the top of the screen. * * ## How could this be used? * This could be used to show objectives in a quest, or to show the progress of a task. diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/SidebarEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/SidebarEntry.kt new file mode 100644 index 0000000000..03078e6e0f --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/SidebarEntry.kt @@ -0,0 +1,53 @@ +package me.gabber235.typewriter.entries.audience + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Colored +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.MultiLine +import me.gabber235.typewriter.adapters.modifiers.Placeholder +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.entries.* +import me.gabber235.typewriter.entry.ref +import me.gabber235.typewriter.extensions.placeholderapi.parsePlaceholders +import org.bukkit.entity.Player + +@Entry("sidebar", "Display a sidebar for players", Colors.DARK_ORANGE, "mdi:page-layout-sidebar-right") +/** + * The `SidebarEntry` is a display that shows a sidebar to players. + * + * To display lines on the sidebar, use the `SidebarLinesEntry` as its descendants. + * + * ## How could this be used? + * This could be used to show a list of objectives, or the region a player is in. + */ +class SimpleSidebarEntry( + override val id: String = "", + override val name: String = "", + override val children: List> = emptyList(), + override val title: String = "", +) : SidebarEntry { + override fun display(): AudienceFilter = PassThroughFilter(ref()) +} + +@Entry("simple_sidebar_lines", "Lines for a sidebar", Colors.ORANGE_RED, "bi:layout-text-sidebar") +/** + * The `SimpleSidebarLinesEntry` is a display that shows lines on a sidebar. + * + * Separating lines with a newline character will display them on separate lines. + * + * ## How could this be used? + * This could be used to show a list of objectives, or the region a player is in. + */ +class SimpleSidebarLinesEntry( + override val id: String = "", + override val name: String = "", + @Help("The lines to display on the sidebar. Separate lines with a newline character.") + @Colored + @Placeholder + @MultiLine + val lines: String = "", +) : SidebarLinesEntry { + override fun lines(player: Player): String = lines.parsePlaceholders(player) + override fun display(): AudienceDisplay = PassThroughDisplay() +} diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/ObjectiveSidebarLinesEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/ObjectiveSidebarLinesEntry.kt new file mode 100644 index 0000000000..1aad430909 --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/ObjectiveSidebarLinesEntry.kt @@ -0,0 +1,51 @@ +package me.gabber235.typewriter.entries.quest + +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Colored +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.MultiLine +import me.gabber235.typewriter.adapters.modifiers.Placeholder +import me.gabber235.typewriter.entry.entries.AudienceDisplay +import me.gabber235.typewriter.entry.entries.PassThroughDisplay +import me.gabber235.typewriter.entry.entries.SidebarLinesEntry +import me.gabber235.typewriter.entry.entries.trackedShowingObjectives +import me.gabber235.typewriter.extensions.placeholderapi.parsePlaceholders +import me.gabber235.typewriter.utils.asMini +import me.gabber235.typewriter.utils.asMiniWithResolvers +import org.bukkit.entity.Player + +@Entry( + "objective_sidebar_lines", + "Display all the current objectives in the sidebar", + Colors.ORANGE_RED, + "fluent:clipboard-task-list-ltr-24-filled" +) +/** + * The `ObjectiveSidebarLinesEntry` is a display that shows all the current objectives in the sidebar. + * + * ## How could this be used? + * This could be used to show a list of tracked objectives + */ +class ObjectiveSidebarLinesEntry( + override val id: String = "", + override val name: String = "", + @Help("The format for the line. Use to replace with the objective name.") + @Colored + @Placeholder + @MultiLine + val format: String = "", +) : SidebarLinesEntry { + override fun lines(player: Player): String { + return player.trackedShowingObjectives().joinToString("\n") { + format.parsePlaceholders(player).asMiniWithResolvers( + net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed( + "objective", + it.display(player) + ) + ).asMini() + } + } + + override fun display(): AudienceDisplay = PassThroughDisplay() +} \ No newline at end of file diff --git a/app/lib/widgets/components/general/decorated_text_field.dart b/app/lib/widgets/components/general/decorated_text_field.dart index 0d45a80931..6217df14fa 100644 --- a/app/lib/widgets/components/general/decorated_text_field.dart +++ b/app/lib/widgets/components/general/decorated_text_field.dart @@ -79,7 +79,8 @@ class DecoratedTextField extends HookWidget { onChanged: onChanged, style: style, textCapitalization: TextCapitalization.none, - textInputAction: TextInputAction.done, + textInputAction: + maxLines == 1 ? TextInputAction.done : TextInputAction.newline, textAlign: textAlign, maxLines: maxLines, autofocus: autofocus, diff --git a/app/pubspec.lock b/app/pubspec.lock index ff31eb379f..35e67d6738 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -526,10 +526,10 @@ packages: dependency: transitive description: name: hotreloader - sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" http: dependency: "direct main" description: @@ -619,26 +619,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: cdd14e3836065a1f6302a236ec8b5f700695c803c57ae11a1c84df31e6bcf831 url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.3" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "9b2ef90589911d665277464e0482b209d39882dffaaf4ef69a3561a3354b2ebc" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.2" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: fd3cd66cb2bcd7b50dcd3b413af49d78051f809c8b3f6e047962765c15a0d23d url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" lints: dependency: transitive description: @@ -1048,10 +1048,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" text_scroll: dependency: "direct main" description: @@ -1208,10 +1208,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.0.0" watcher: dependency: transitive description: @@ -1278,4 +1278,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.3.0-279.1.beta <4.0.0" - flutter: ">=3.16.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt index c887cda216..8145d1e251 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/adapters/EntryAttributes.kt @@ -21,6 +21,8 @@ object Colors { const val MEDIUM_PURPLE = "#9370DB" const val BLUE_VIOLET = "#8A2BE2" const val ORANGE = "#F57C00" + const val DARK_ORANGE = "#FF8C00" + const val ORANGE_RED = "#FF4500" const val PINK = "#eb4bb8" const val CYAN = "#0abab5" } \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt index 2cd34e294a..c0d04998d5 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/AudienceManager.kt @@ -12,6 +12,7 @@ import org.bukkit.event.Listener import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerQuitEvent import org.koin.java.KoinJavaComponent.get +import kotlin.reflect.KClass class AudienceManager : Listener { private var displays = emptyMap, AudienceDisplay>() @@ -92,3 +93,17 @@ fun Player.inAudience(ref: Ref): Boolean { val manager = get(AudienceManager::class.java) return manager[ref]?.let { return it.contains(this) } ?: false } + +fun Ref.descendants(klass: KClass): List> { + val entry = get() ?: return emptyList() + return entry.children.flatMap { + val child = it.get() ?: return@flatMap emptyList>() + if (klass.isInstance(child)) { + listOf(it as Ref) + } else if (child is AudienceFilterEntry) { + child.ref().descendants(klass) + } else { + emptyList() + } + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt index b46c6d5777..2b95d2f238 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt @@ -233,4 +233,8 @@ fun createEntryParserGson(adapterLoader: AdapterLoader): Gson { return builder .create() -} \ No newline at end of file +} + +val Entry.priority: Int get() = ref().priority +val Ref.priority: Int + get() = org.koin.java.KoinJavaComponent.get(EntryDatabase::class.java).entryPriority(this) \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt new file mode 100644 index 0000000000..45836c9b43 --- /dev/null +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt @@ -0,0 +1,128 @@ +package me.gabber235.typewriter.entry + +import com.github.retrooper.packetevents.protocol.score.ScoreFormat +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisplayScoreboard +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerResetScore +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective.ObjectiveMode.CREATE +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective.ObjectiveMode.UPDATE +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective.RenderType.INTEGER +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateScore +import lirand.api.extensions.events.listen +import lirand.api.extensions.events.unregister +import me.gabber235.typewriter.entry.entries.SidebarEntry +import me.gabber235.typewriter.entry.entries.SidebarLinesEntry +import me.gabber235.typewriter.events.PublishedBookEvent +import me.gabber235.typewriter.events.TypewriterReloadEvent +import me.gabber235.typewriter.extensions.packetevents.sendPacketTo +import me.gabber235.typewriter.plugin +import me.gabber235.typewriter.utils.asMini +import net.kyori.adventure.text.Component +import org.bukkit.entity.Player +import org.bukkit.event.Listener +import kotlin.math.max + +class SidebarManager( + private val player: Player, +) : Listener { + private var sidebar: Ref? = null + private var lines = emptyList>() + private var lastLines = emptyList() + + init { + plugin.listen(this) { dispose(false) } + plugin.listen(this) { dispose(false) } + } + + fun dispose(force: Boolean) { + if (force) unregister() + disposeSidebar() + sidebar = null + lines = emptyList() + lastLines = emptyList() + } + + fun tick() { + val newSidebar = Query.findWhere { player.inAudience(it.ref()) }.maxByOrNull { it.priority } + val title = newSidebar?.display(player) ?: "" + + if (newSidebar?.ref() != sidebar) { + if (sidebar == null) createSidebar(title) + else disposeSidebar() + + sidebar = newSidebar?.ref() + lines = sidebar?.descendants(SidebarLinesEntry::class) ?: emptyList() + } + + val lines = lines + .filter { player.inAudience(it) } + .mapNotNull { it.get()?.lines(player) } + .flatMap { it.split("\n") } + + if (lines != lastLines) { + sendSidebar(title, lines) + lastLines = lines + } + } + + private fun createSidebar(title: String) { + WrapperPlayServerScoreboardObjective( + "typewriter", + CREATE, + title.asMini(), + INTEGER, + ScoreFormat.blankScore() + ).sendPacketTo(player) + + WrapperPlayServerDisplayScoreboard(1, "typewriter").sendPacketTo(player) + } + + private fun disposeSidebar() { + WrapperPlayServerScoreboardObjective( + "typewriter", + WrapperPlayServerScoreboardObjective.ObjectiveMode.REMOVE, + Component.empty(), + null, + null + ).sendPacketTo(player) + } + + fun sendSidebar(title: String, lines: List) { + val packet = WrapperPlayServerScoreboardObjective( + "typewriter", + UPDATE, + title.asMini(), + INTEGER, + ScoreFormat.blankScore() + ) + packet.sendPacketTo(player) + + val displayPacket = WrapperPlayServerDisplayScoreboard(1, "typewriter") + displayPacket.sendPacketTo(player) + + val lineCount = max(lastLines.size, lines.size).coerceAtMost(15) + + for (i in 0 until lineCount) { + val line = lines.getOrNull(i) + val lastLine = lastLines.getOrNull(i) + if (lastLine == line) continue + + if (line == null) { + WrapperPlayServerResetScore( + "typewriter_line_$i", + "typewriter" + ).sendPacketTo(player) + continue + } + + WrapperPlayServerUpdateScore( + "typewriter_line_$i", + WrapperPlayServerUpdateScore.Action.CREATE_OR_UPDATE_ITEM, + "typewriter", + lineCount - i, + line.asMini(), + ScoreFormat.blankScore(), + ).sendPacketTo(player) + } + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt index 58bf7dd5a2..af03605457 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/AudienceEntry.kt @@ -56,7 +56,10 @@ abstract class AudienceDisplay : Listener { open operator fun contains(uuid: UUID): Boolean = uuid in playerIds } -fun List>.into() = mapNotNull { it.get()?.display() } +class PassThroughDisplay : AudienceDisplay() { + override fun onPlayerAdd(player: Player) {} + override fun onPlayerRemove(player: Player) {} +} abstract class AudienceFilter( private val ref: Ref @@ -97,3 +100,9 @@ abstract class AudienceFilter( override fun contains(uuid: UUID): Boolean = uuid in filteredPlayers } +class PassThroughFilter( + ref: Ref +) : AudienceFilter(ref) { + override fun filter(player: Player): Boolean = true +} + diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt index 6694e3b45e..5d99e8cd45 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/entries/SidebarEntry.kt @@ -1,3 +1,30 @@ package me.gabber235.typewriter.entry.entries -interface SidebarEntry : AudienceFilterEntry \ No newline at end of file +import me.gabber235.typewriter.adapters.Tags +import me.gabber235.typewriter.adapters.modifiers.Colored +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.Placeholder +import me.gabber235.typewriter.entry.PlaceholderEntry +import me.gabber235.typewriter.extensions.placeholderapi.parsePlaceholders +import org.bukkit.entity.Player + +@Tags("sidebar") +interface SidebarEntry : AudienceFilterEntry, PlaceholderEntry { + @Help("The title of the sidebar") + @Colored + @Placeholder + val title: String + + override fun display(player: Player?): String? = title.parsePlaceholders(player) +} + +@Tags("sidebar_lines") +interface SidebarLinesEntry : AudienceEntry, PlaceholderEntry { + /** + * The lines of the sidebar. + * Multiple lines are separated by a newline character. + */ + fun lines(player: Player): String + + override fun display(player: Player?): String? = player?.let { lines(it) } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt index 292b0ebba2..5eaa7ce78e 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/quest/QuestTracker.kt @@ -56,7 +56,7 @@ class QuestTracker( ) } - fun end() { + fun dispose() { listener.unregister() factWatchSubscription?.cancel(player) } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt index 82611a8aab..4c716fe0a8 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt @@ -17,6 +17,7 @@ class Interaction(val player: Player) : KoinComponent { private var cinematic: CinematicSequence? = null val questTracker: QuestTracker = QuestTracker(player) val factWatcher: FactWatcher = FactWatcher(player) + private val sidebarManager: SidebarManager = SidebarManager(player) val hasDialogue: Boolean get() = dialogue != null @@ -28,6 +29,7 @@ class Interaction(val player: Player) : KoinComponent { dialogue?.tick() cinematic?.tick() factWatcher.tick() + sidebarManager.tick() } /** Handles an event. All [SystemTrigger]'s are handled by the plugin itself. */ @@ -176,6 +178,7 @@ class Interaction(val player: Player) : KoinComponent { dialogue?.end() cinematic?.end(force = true) - questTracker.end() + questTracker.dispose() + sidebarManager.dispose(force = true) } -} +} \ No newline at end of file From 85abfca3ade837774ad5db6906bf06f97253b3ae Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 15:53:15 +0100 Subject: [PATCH 16/24] Upgrade gradle --- adapters/BasicAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../CitizensAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../CombatLogXAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../FancyNpcsAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../MythicMobsAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../RPGRegionsAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- adapters/VaultAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../WorldGuardAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- .../ZNPCsPlusAdapter/gradle/wrapper/gradle-wrapper.properties | 2 +- plugin/gradle/wrapper/gradle-wrapper.properties | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/adapters/BasicAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/BasicAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/BasicAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/BasicAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/CitizensAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/CitizensAdapter/gradle/wrapper/gradle-wrapper.properties index a595206642..17655d0ef2 100644 --- a/adapters/CitizensAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/CitizensAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/adapters/CombatLogXAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/CombatLogXAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/CombatLogXAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/CombatLogXAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/FancyNpcsAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/FancyNpcsAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/FancyNpcsAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/FancyNpcsAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/MythicMobsAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/MythicMobsAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/MythicMobsAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/MythicMobsAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/RPGRegionsAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/RPGRegionsAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/RPGRegionsAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/RPGRegionsAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/SuperiorSkyblockAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/SuperiorSkyblockAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/SuperiorSkyblockAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/SuperiorSkyblockAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/VaultAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/VaultAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/VaultAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/VaultAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/WorldGuardAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/WorldGuardAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/WorldGuardAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/WorldGuardAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/adapters/ZNPCsPlusAdapter/gradle/wrapper/gradle-wrapper.properties b/adapters/ZNPCsPlusAdapter/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7d..e846b64f4a 100644 --- a/adapters/ZNPCsPlusAdapter/gradle/wrapper/gradle-wrapper.properties +++ b/adapters/ZNPCsPlusAdapter/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/plugin/gradle/wrapper/gradle-wrapper.properties b/plugin/gradle/wrapper/gradle-wrapper.properties index a595206642..17655d0ef2 100644 --- a/plugin/gradle/wrapper/gradle-wrapper.properties +++ b/plugin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From cc5259a0e705955cd94ab5bdf57ecb5239d84dfa Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 16:51:56 +0100 Subject: [PATCH 17/24] Simple bugs and make changes faster --- .../entries/quest/SimpleObjectiveEntry.kt | 12 ++++--- .../entries/quest/TrackedObjectiveAudience.kt | 9 +++-- .../gabber235/typewriter/entry/FactWatcher.kt | 5 +-- .../typewriter/entry/SidebarManager.kt | 34 +++++++++---------- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt index a3cfe48c49..f8a78c6795 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/SimpleObjectiveEntry.kt @@ -49,12 +49,14 @@ class ObjectiveAudienceFilter( override fun onPlayerAdd(player: Player) { super.onPlayerAdd(player) - val subscription = player.listenForFacts( - criteria.map { it.fact }, - ::onFactChange, - ) - factWatcherSubscriptions[player.uniqueId] = subscription + factWatcherSubscriptions.compute(player.uniqueId) { _, subscription -> + subscription?.cancel(player) + player.listenForFacts( + criteria.map { it.fact }, + ::onFactChange, + ) + } } private fun onFactChange(player: Player, fact: Ref) { diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt index 586d005e3c..4594c130ee 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/quest/TrackedObjectiveAudience.kt @@ -1,6 +1,5 @@ package me.gabber235.typewriter.entries.quest -import com.github.shynixn.mccoroutine.bukkit.launch import kotlinx.coroutines.Job import kotlinx.coroutines.delay import me.gabber235.typewriter.adapters.Colors @@ -12,10 +11,10 @@ import me.gabber235.typewriter.entry.entries.AudienceFilterEntry import me.gabber235.typewriter.entry.entries.trackedShowingObjectives import me.gabber235.typewriter.entry.ref import me.gabber235.typewriter.events.AsyncTrackedQuestUpdate -import me.gabber235.typewriter.plugin +import me.gabber235.typewriter.utils.ThreadType.DISPATCHERS_ASYNC import org.bukkit.entity.Player import org.bukkit.event.EventHandler -import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.milliseconds @Entry( "tracked_objective_audience", @@ -50,9 +49,9 @@ class TrackedObjectiveAudienceFilter( override fun initialize() { super.initialize() - job = plugin.launch { + job = DISPATCHERS_ASYNC.launch { while (true) { - delay(1.seconds) + delay(50.milliseconds) consideredPlayers.forEach { it.refresh() } } } diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt index 007cdcc251..a9602fa55a 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/FactWatcher.kt @@ -13,12 +13,9 @@ class FactWatcher( ) { private val factWatch = ConcurrentHashMap, Int>() private val listeners = mutableListOf() - private var cycle: Int = 0 fun tick() { - if (cycle++ % 20 == 0) { - refresh() - } + refresh() } private fun refresh() { diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt index 45836c9b43..cb9a4063f9 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt @@ -20,7 +20,8 @@ import me.gabber235.typewriter.utils.asMini import net.kyori.adventure.text.Component import org.bukkit.entity.Player import org.bukkit.event.Listener -import kotlin.math.max + +private const val MAX_LINES = 15 class SidebarManager( private val player: Player, @@ -71,7 +72,7 @@ class SidebarManager( CREATE, title.asMini(), INTEGER, - ScoreFormat.blankScore() + ScoreFormat.blankScore(), ).sendPacketTo(player) WrapperPlayServerDisplayScoreboard(1, "typewriter").sendPacketTo(player) @@ -93,36 +94,35 @@ class SidebarManager( UPDATE, title.asMini(), INTEGER, - ScoreFormat.blankScore() + ScoreFormat.blankScore(), ) packet.sendPacketTo(player) val displayPacket = WrapperPlayServerDisplayScoreboard(1, "typewriter") displayPacket.sendPacketTo(player) - val lineCount = max(lastLines.size, lines.size).coerceAtMost(15) - for (i in 0 until lineCount) { - val line = lines.getOrNull(i) - val lastLine = lastLines.getOrNull(i) + for ((index, line) in lines.withIndex().take(MAX_LINES)) { + val lastLine = lastLines.getOrNull(index) if (lastLine == line) continue - if (line == null) { - WrapperPlayServerResetScore( - "typewriter_line_$i", - "typewriter" - ).sendPacketTo(player) - continue - } - WrapperPlayServerUpdateScore( - "typewriter_line_$i", + "typewriter_line_$index", WrapperPlayServerUpdateScore.Action.CREATE_OR_UPDATE_ITEM, "typewriter", - lineCount - i, + MAX_LINES - index, line.asMini(), ScoreFormat.blankScore(), ).sendPacketTo(player) } + + if (lines.size < lastLines.size) { + for (i in lines.size until lastLines.size) { + WrapperPlayServerResetScore( + "typewriter_line_$i", + "typewriter" + ).sendPacketTo(player) + } + } } } \ No newline at end of file From 078d4ba40622d6eed246a6a512c893354776054a Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 17:09:20 +0100 Subject: [PATCH 18/24] Make sure to always buildRelease --- adapters/BasicAdapter/build.gradle.kts | 1 + adapters/CitizensAdapter/build.gradle.kts | 1 + adapters/CombatLogXAdapter/build.gradle.kts | 1 + adapters/FancyNpcsAdapter/build.gradle.kts | 1 + adapters/MythicMobsAdapter/build.gradle.kts | 1 + adapters/RPGRegionsAdapter/build.gradle.kts | 1 + adapters/SuperiorSkyblockAdapter/build.gradle.kts | 1 + adapters/VaultAdapter/build.gradle.kts | 3 +++ adapters/WorldGuardAdapter/build.gradle.kts | 1 + adapters/ZNPCsPlusAdapter/build.gradle.kts | 1 + plugin/build.gradle.kts | 3 +++ 11 files changed, 15 insertions(+) diff --git a/adapters/BasicAdapter/build.gradle.kts b/adapters/BasicAdapter/build.gradle.kts index f7e6da3800..e6058081c4 100644 --- a/adapters/BasicAdapter/build.gradle.kts +++ b/adapters/BasicAdapter/build.gradle.kts @@ -77,6 +77,7 @@ task("buildAndMove") group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/CitizensAdapter/build.gradle.kts b/adapters/CitizensAdapter/build.gradle.kts index 308f2f6522..fd3a20a4c6 100644 --- a/adapters/CitizensAdapter/build.gradle.kts +++ b/adapters/CitizensAdapter/build.gradle.kts @@ -81,6 +81,7 @@ task("buildAndMove") group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/CombatLogXAdapter/build.gradle.kts b/adapters/CombatLogXAdapter/build.gradle.kts index bdec71fda0..e39683fdcd 100644 --- a/adapters/CombatLogXAdapter/build.gradle.kts +++ b/adapters/CombatLogXAdapter/build.gradle.kts @@ -77,6 +77,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/FancyNpcsAdapter/build.gradle.kts b/adapters/FancyNpcsAdapter/build.gradle.kts index 082353f489..b5a13626e0 100644 --- a/adapters/FancyNpcsAdapter/build.gradle.kts +++ b/adapters/FancyNpcsAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/MythicMobsAdapter/build.gradle.kts b/adapters/MythicMobsAdapter/build.gradle.kts index 70c187f352..7e2fc02806 100644 --- a/adapters/MythicMobsAdapter/build.gradle.kts +++ b/adapters/MythicMobsAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/RPGRegionsAdapter/build.gradle.kts b/adapters/RPGRegionsAdapter/build.gradle.kts index 3d1aec248f..1c03a75383 100644 --- a/adapters/RPGRegionsAdapter/build.gradle.kts +++ b/adapters/RPGRegionsAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/SuperiorSkyblockAdapter/build.gradle.kts b/adapters/SuperiorSkyblockAdapter/build.gradle.kts index 41900a8e06..42ea8a231c 100644 --- a/adapters/SuperiorSkyblockAdapter/build.gradle.kts +++ b/adapters/SuperiorSkyblockAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/VaultAdapter/build.gradle.kts b/adapters/VaultAdapter/build.gradle.kts index ba084e0005..772184cdaf 100644 --- a/adapters/VaultAdapter/build.gradle.kts +++ b/adapters/VaultAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { @@ -92,6 +93,8 @@ task("buildRelease") { dependsOn("shadowJar") group = "build" description = "Builds the jar and renames it" + outputs.upToDateWhen { false } + doLast { // Rename the jar to remove the version and -all diff --git a/adapters/WorldGuardAdapter/build.gradle.kts b/adapters/WorldGuardAdapter/build.gradle.kts index 21dfca2e08..c90d1c02bb 100644 --- a/adapters/WorldGuardAdapter/build.gradle.kts +++ b/adapters/WorldGuardAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/adapters/ZNPCsPlusAdapter/build.gradle.kts b/adapters/ZNPCsPlusAdapter/build.gradle.kts index 2f522b7f72..7592dc1c68 100644 --- a/adapters/ZNPCsPlusAdapter/build.gradle.kts +++ b/adapters/ZNPCsPlusAdapter/build.gradle.kts @@ -78,6 +78,7 @@ task("buildAndMove") group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index bb376e77b8..c214a41332 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -116,6 +116,7 @@ task("buildAndMove") { group = "build" description = "Builds the jar and moves it to the server folder" + outputs.upToDateWhen { false } // Move the jar from the build/libs folder to the server/plugins folder doLast { @@ -141,6 +142,8 @@ task("buildRelease") { group = "build" description = "Builds the jar including the flutter web panel" + outputs.upToDateWhen { false } + doLast { // Remove the flutter web folder val flutterWebDest = file("src/main/resources/web") From b4f2851051749ea7e190f134cb0b8137fc3423e7 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 19:10:02 +0100 Subject: [PATCH 19/24] Add message to command --- .../kotlin/me/gabber235/typewriter/TypewriterCommand.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt index bea44475cb..290c8e2b8a 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/TypewriterCommand.kt @@ -292,12 +292,18 @@ private fun LiteralDSLBuilder.questCommands() = literal("quest") { argument("quest", entryType()) { quest -> executesPlayer { source.trackQuest(quest.get().ref()) + source.msg("You are now tracking ${quest.get().display(source)}.") } argument("player", PlayerType) { player -> requiresPermissions("typewriter.quest.track.other") executes { player.get().trackQuest(quest.get().ref()) + source.msg( + "You are now tracking ${ + quest.get().display(player.get()) + } for ${player.get().name}." + ) } } } @@ -307,12 +313,14 @@ private fun LiteralDSLBuilder.questCommands() = literal("quest") { requiresPermissions("typewriter.quest.untrack") executesPlayer { source.unTrackQuest() + source.msg("You are no longer tracking any quests.") } argument("player", PlayerType) { player -> requiresPermissions("typewriter.quest.untrack.other") executes { player.get().unTrackQuest() + source.msg("You are no longer tracking any quests for ${player.get().name}.") } } } From 0cad6247d0f41b864ce154872d99f4b302fc0e78 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 19:34:21 +0100 Subject: [PATCH 20/24] Fix sidebar removed when swithing --- .../typewriter/entry/SidebarManager.kt | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt index cb9a4063f9..7377cd5497 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/SidebarManager.kt @@ -22,12 +22,14 @@ import org.bukkit.entity.Player import org.bukkit.event.Listener private const val MAX_LINES = 15 +private const val SCOREBOARD_OBJECTIVE = "typewriter" class SidebarManager( private val player: Player, ) : Listener { private var sidebar: Ref? = null private var lines = emptyList>() + private var lastTitle = "" private var lastLines = emptyList() init { @@ -40,16 +42,19 @@ class SidebarManager( disposeSidebar() sidebar = null lines = emptyList() + lastTitle = "" lastLines = emptyList() } fun tick() { - val newSidebar = Query.findWhere { player.inAudience(it.ref()) }.maxByOrNull { it.priority } + val newSidebar = Query.findWhere { player.inAudience(it.ref()) }.maxByOrNull { + it.priority + } val title = newSidebar?.display(player) ?: "" if (newSidebar?.ref() != sidebar) { if (sidebar == null) createSidebar(title) - else disposeSidebar() + else if (newSidebar == null) disposeSidebar() sidebar = newSidebar?.ref() lines = sidebar?.descendants(SidebarLinesEntry::class) ?: emptyList() @@ -60,27 +65,28 @@ class SidebarManager( .mapNotNull { it.get()?.lines(player) } .flatMap { it.split("\n") } - if (lines != lastLines) { + if (lines != lastLines || title != lastTitle) { sendSidebar(title, lines) + lastTitle = title lastLines = lines } } private fun createSidebar(title: String) { WrapperPlayServerScoreboardObjective( - "typewriter", + SCOREBOARD_OBJECTIVE, CREATE, title.asMini(), INTEGER, ScoreFormat.blankScore(), ).sendPacketTo(player) - WrapperPlayServerDisplayScoreboard(1, "typewriter").sendPacketTo(player) + WrapperPlayServerDisplayScoreboard(1, SCOREBOARD_OBJECTIVE).sendPacketTo(player) } private fun disposeSidebar() { WrapperPlayServerScoreboardObjective( - "typewriter", + SCOREBOARD_OBJECTIVE, WrapperPlayServerScoreboardObjective.ObjectiveMode.REMOVE, Component.empty(), null, @@ -88,9 +94,9 @@ class SidebarManager( ).sendPacketTo(player) } - fun sendSidebar(title: String, lines: List) { + private fun sendSidebar(title: String, lines: List) { val packet = WrapperPlayServerScoreboardObjective( - "typewriter", + SCOREBOARD_OBJECTIVE, UPDATE, title.asMini(), INTEGER, @@ -98,7 +104,7 @@ class SidebarManager( ) packet.sendPacketTo(player) - val displayPacket = WrapperPlayServerDisplayScoreboard(1, "typewriter") + val displayPacket = WrapperPlayServerDisplayScoreboard(1, SCOREBOARD_OBJECTIVE) displayPacket.sendPacketTo(player) @@ -107,9 +113,9 @@ class SidebarManager( if (lastLine == line) continue WrapperPlayServerUpdateScore( - "typewriter_line_$index", + "${SCOREBOARD_OBJECTIVE}_line_$index", WrapperPlayServerUpdateScore.Action.CREATE_OR_UPDATE_ITEM, - "typewriter", + SCOREBOARD_OBJECTIVE, MAX_LINES - index, line.asMini(), ScoreFormat.blankScore(), @@ -119,8 +125,8 @@ class SidebarManager( if (lines.size < lastLines.size) { for (i in lines.size until lastLines.size) { WrapperPlayServerResetScore( - "typewriter_line_$i", - "typewriter" + "${SCOREBOARD_OBJECTIVE}_line_$i", + SCOREBOARD_OBJECTIVE, ).sendPacketTo(player) } } From fd3d7493bb4b9ee3c2d4ec7262f90a35569b411b Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 19:34:32 +0100 Subject: [PATCH 21/24] Fix loading page priority --- .../main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt index 2b95d2f238..798bfa9c71 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/EntryDatabase.kt @@ -126,6 +126,7 @@ fun JsonReader.parsePage(id: String, gson: Gson): Result { when (nextName()) { "entries" -> page = page.copy(entries = parseEntries(gson)) "type" -> page = page.copy(type = PageType.fromId(nextString()) ?: PageType.SEQUENCE) + "priority" -> page = page.copy(priority = nextInt()) else -> skipValue() } } From 2306864c92ae1e6f3ea30e7d269809a72f81f676 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 19:35:29 +0100 Subject: [PATCH 22/24] Add cinematic audience entry --- .../audience/CinematicAudienceEntry.kt | 74 +++++++++++++++++++ .../entry/cinematic/CinematicSequence.kt | 22 ++++-- .../events/AsyncCinematicEndEvent.kt | 2 +- .../events/AsyncCinematicStartEvent.kt | 2 +- .../typewriter/interaction/Interaction.kt | 10 +-- 5 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/CinematicAudienceEntry.kt diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/CinematicAudienceEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/CinematicAudienceEntry.kt new file mode 100644 index 0000000000..d12240b8fd --- /dev/null +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/audience/CinematicAudienceEntry.kt @@ -0,0 +1,74 @@ +package me.gabber235.typewriter.entries.audience + +import com.google.gson.annotations.SerializedName +import me.gabber235.typewriter.adapters.Colors +import me.gabber235.typewriter.adapters.Entry +import me.gabber235.typewriter.adapters.modifiers.Help +import me.gabber235.typewriter.adapters.modifiers.Page +import me.gabber235.typewriter.entry.PageType +import me.gabber235.typewriter.entry.Ref +import me.gabber235.typewriter.entry.cinematic.isPlayingCinematic +import me.gabber235.typewriter.entry.entries.AudienceEntry +import me.gabber235.typewriter.entry.entries.AudienceFilter +import me.gabber235.typewriter.entry.entries.AudienceFilterEntry +import me.gabber235.typewriter.entry.ref +import me.gabber235.typewriter.events.AsyncCinematicEndEvent +import me.gabber235.typewriter.events.AsyncCinematicStartEvent +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler + +@Entry( + "cinematic_audience", + "Filters an audience based on if they are in a cinematic", + Colors.MEDIUM_SEA_GREEN, + "mdi:movie" +) +/** + * The `Cinematic Audience` entry filters an audience based on if they are in a cinematic. + * + * If no cinematic is referenced, it will filter based on if any cinematic is active. + * + * ## How could this be used? + * This could be used to hide the sidebar or boss bar when a cinematic is playing. + */ +class CinematicAudienceEntry( + override val id: String = "", + override val name: String = "", + override val children: List> = emptyList(), + @Help("When not set it will filter based on if any cinematic is active.") + @Page(PageType.CINEMATIC) + @SerializedName("cinematic") + val pageId: String = "", + @Help("When inverted, the audience will be filtered when not in a cinematic.") + val inverted: Boolean = false +) : AudienceFilterEntry { + override fun display(): AudienceFilter = CinematicAudienceFilter( + ref(), + pageId, + inverted, + ) +} + +class CinematicAudienceFilter( + ref: Ref, + private val pageId: String, + private val inverted: Boolean, +) : AudienceFilter(ref) { + override fun filter(player: Player): Boolean { + val inCinematic = if (pageId.isNotBlank()) player.isPlayingCinematic(pageId) else player.isPlayingCinematic() + return if (inverted) !inCinematic else inCinematic + } + + @EventHandler + fun onCinematicStart(event: AsyncCinematicStartEvent) { + if (pageId.isNotBlank() && event.pageId != pageId) return + event.player.updateFilter(!inverted) + } + + @EventHandler + fun onCinematicEnd(event: AsyncCinematicEndEvent) { + if (pageId.isNotBlank() && event.pageId != pageId) return + event.player.updateFilter(inverted) + } +} + diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/CinematicSequence.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/CinematicSequence.kt index 975ac3781f..43c8a476d0 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/CinematicSequence.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/entry/cinematic/CinematicSequence.kt @@ -9,18 +9,17 @@ import me.gabber235.typewriter.entry.triggerFor import me.gabber235.typewriter.events.AsyncCinematicEndEvent import me.gabber235.typewriter.events.AsyncCinematicStartEvent import me.gabber235.typewriter.events.AsyncCinematicTickEvent -import me.gabber235.typewriter.interaction.startBlockingActionBar -import me.gabber235.typewriter.interaction.startBlockingMessages -import me.gabber235.typewriter.interaction.stopBlockingActionBar -import me.gabber235.typewriter.interaction.stopBlockingMessages +import me.gabber235.typewriter.interaction.* import me.gabber235.typewriter.utils.ThreadType.DISPATCHERS_ASYNC import org.bukkit.entity.Player +import org.koin.java.KoinJavaComponent import java.util.* private const val STARTING_FRAME = -1 private const val ENDED_FRAME = -2 class CinematicSequence( + val pageId: String, private val player: Player, private val actions: List, private val triggers: List>, @@ -43,7 +42,7 @@ class CinematicSequence( } DISPATCHERS_ASYNC.switchContext { - AsyncCinematicStartEvent(player).callEvent() + AsyncCinematicStartEvent(player, pageId).callEvent() } } @@ -89,7 +88,16 @@ class CinematicSequence( triggers triggerEntriesFor player DISPATCHERS_ASYNC.switchContext { - AsyncCinematicEndEvent(player, originalFrame).callEvent() + AsyncCinematicEndEvent(player, originalFrame, pageId).callEvent() } } -} \ No newline at end of file +} + +private val Player.cinematicSequence: CinematicSequence? + get() = with(KoinJavaComponent.get(InteractionHandler::class.java)) { + interaction?.cinematic + } + +fun Player.isPlayingCinematic(pageId: String): Boolean = cinematicSequence?.pageId == pageId + +fun Player.isPlayingCinematic(): Boolean = cinematicSequence != null \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicEndEvent.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicEndEvent.kt index 802f49915f..157e470829 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicEndEvent.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicEndEvent.kt @@ -4,7 +4,7 @@ import org.bukkit.entity.Player import org.bukkit.event.HandlerList import org.bukkit.event.player.PlayerEvent -class AsyncCinematicEndEvent(player: Player, val frame: Int) : PlayerEvent(player, true) { +class AsyncCinematicEndEvent(player: Player, val frame: Int, val pageId: String) : PlayerEvent(player, true) { override fun getHandlers(): HandlerList = HANDLER_LIST companion object { diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicStartEvent.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicStartEvent.kt index 41b503c267..2e26efcbdb 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicStartEvent.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/events/AsyncCinematicStartEvent.kt @@ -4,7 +4,7 @@ import org.bukkit.entity.Player import org.bukkit.event.HandlerList import org.bukkit.event.player.PlayerEvent -class AsyncCinematicStartEvent(player: Player) : PlayerEvent(player, true) { +class AsyncCinematicStartEvent(player: Player, val pageId: String) : PlayerEvent(player, true) { override fun getHandlers(): HandlerList = HANDLER_LIST companion object { diff --git a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt index 4c716fe0a8..befc669b3f 100644 --- a/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt +++ b/plugin/src/main/kotlin/me/gabber235/typewriter/interaction/Interaction.kt @@ -13,10 +13,10 @@ import org.koin.core.component.inject class Interaction(val player: Player) : KoinComponent { private val interactionHandler: InteractionHandler by inject() - private var dialogue: DialogueSequence? = null - private var cinematic: CinematicSequence? = null - val questTracker: QuestTracker = QuestTracker(player) - val factWatcher: FactWatcher = FactWatcher(player) + internal var dialogue: DialogueSequence? = null + internal var cinematic: CinematicSequence? = null + internal val questTracker: QuestTracker = QuestTracker(player) + internal val factWatcher: FactWatcher = FactWatcher(player) private val sidebarManager: SidebarManager = SidebarManager(player) val hasDialogue: Boolean @@ -157,7 +157,7 @@ class Interaction(val player: Player) : KoinComponent { } } - this.cinematic = CinematicSequence(player, actions, trigger.triggers, trigger.minEndTime) + this.cinematic = CinematicSequence(trigger.pageId, player, actions, trigger.triggers, trigger.minEndTime) } private fun handleFactWatcher(event: Event) { From 3783f3dc9e99ca80688a52242b02a0be15d9cd99 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 19:48:12 +0100 Subject: [PATCH 23/24] Typo for almost year? --- .../entries/cinematic/SubtitleDialgueCinematicEntry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SubtitleDialgueCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SubtitleDialgueCinematicEntry.kt index 0195e2d0e8..82c56a8cb6 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SubtitleDialgueCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SubtitleDialgueCinematicEntry.kt @@ -23,7 +23,7 @@ import net.kyori.adventure.title.Title.Times import org.bukkit.entity.Player import java.time.Duration -@Entry("subtitle_dialogue_cinematic", "Show an action bar message", Colors.CYAN, "fa6-solid:diagram-next") +@Entry("subtitle_dialogue_cinematic", "Show an subtitle message", Colors.CYAN, "fa6-solid:diagram-next") /** * The `Subtitle Dialogue Cinematic Entry` is a cinematic entry that displays an animated subtitle message. * The speaker is displayed in the action bar, and the dialogue is displayed in the subtitle. From 97d2086964f6638ed97f6a57c69a925de55247d7 Mon Sep 17 00:00:00 2001 From: Gabber235 Date: Sat, 17 Feb 2024 20:40:12 +0100 Subject: [PATCH 24/24] Fix coloring --- .../typewriter/entries/cinematic/SoundCinematicEntry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SoundCinematicEntry.kt b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SoundCinematicEntry.kt index eb9a858576..c5b2c8652c 100644 --- a/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SoundCinematicEntry.kt +++ b/adapters/BasicAdapter/src/main/kotlin/me/gabber235/typewriter/entries/cinematic/SoundCinematicEntry.kt @@ -30,7 +30,7 @@ class SoundCinematicEntry( override val id: String, override val name: String, override val criteria: List, - @Segments(icon = "fa6-solid:music") + @Segments(icon = "fa6-solid:music", color = Colors.YELLOW) val segments: List, ) : CinematicEntry { override fun create(player: Player): CinematicAction {