diff --git a/build.sbt b/build.sbt index bb08daf145..25be4612db 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ import java.io._ ThisBuild / scalaVersion := "2.13.1" // ThisBuild / version はGitHub Actionsによって自動更新される。 // 次の行は ThisBuild / version := "(\d*)" の形式でなければならない。 -ThisBuild / version := "24" +ThisBuild / version := "25" ThisBuild / organization := "click.seichi" ThisBuild / description := "ギガンティック☆整地鯖の独自要素を司るプラグイン" diff --git a/src/main/resources/db/migration/V1.7.4__Create_Slow_Query_Index_ranking.sql b/src/main/resources/db/migration/V1.7.4__Create_Slow_Query_Index_ranking.sql new file mode 100644 index 0000000000..7ba6a9430c --- /dev/null +++ b/src/main/resources/db/migration/V1.7.4__Create_Slow_Query_Index_ranking.sql @@ -0,0 +1,4 @@ +use seichiassist; + +-- MySQLのスロークエリを引いた結果遅かったSELECTのクエリに対してINDEXを貼るようにする。 +ALTER TABLE playerdata ADD INDEX index_playerdata_playtick(playtick); diff --git a/src/main/scala/com/github/unchama/generic/MapExtra.scala b/src/main/scala/com/github/unchama/generic/MapExtra.scala index ba975492fb..502a3c6300 100644 --- a/src/main/scala/com/github/unchama/generic/MapExtra.scala +++ b/src/main/scala/com/github/unchama/generic/MapExtra.scala @@ -29,4 +29,19 @@ object MapExtra { } } .toMap + + /** + * + * @param map 元となるMap + * @param base キーの集合 + * @param default 埋める値 + * @tparam K キー + * @tparam V 値 + * @return + */ + def fillOnBaseSet[K, V](map: Map[K, V], base: Set[K], default: V): Map[K, V] = { + val keys = map.keys.toSet + require(keys.subsetOf(base)) + map ++ (base -- keys).map(a => a -> default) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala index ec51253d8a..01b6797a15 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala @@ -98,7 +98,7 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, import ManagedWorld._ level >= SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreaklevel && - (player.getWorld.isSeichiSkillAllowed || playerData.settings.multipleidbreakflag) + (player.getWorld.isSeichiSkillAllowed && playerData.settings.multipleidbreakflag) } import com.github.unchama.seichiassist.data.syntax._ diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala index 2a9531c6d7..bceb4bf6e4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala @@ -116,7 +116,7 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, val isMultiTypeBreakingSkillEnabled = { import com.github.unchama.seichiassist.ManagedWorld._ playerLevel >= SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreaklevel && - (player.getWorld.isSeichiSkillAllowed || playerData.settings.multipleidbreakflag) + (player.getWorld.isSeichiSkillAllowed && playerData.settings.multipleidbreakflag) } val totalBreakRangeVolume = { diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala index 5260c9de76..1f125e9f34 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala @@ -565,7 +565,7 @@ object FirstPage extends Menu { val buttonLore = List( s"$GRAY・整地ワールド間を移動するとき", s"$GRAY・拠点を建築するとき", - s"に使います", + s"$GRAY に使います", s"$DARK_RED${UNDERLINE}クリックするとワープします", s"${DARK_GRAY}command=>[/spawn]" ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala index f51060f101..06f9ddcf0e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala @@ -1,17 +1,18 @@ package com.github.unchama.seichiassist.subsystems.discordnotification -import cats.effect.{ContextShift, Sync} -import com.github.unchama.seichiassist.SeichiAssist +import cats.effect.{ContextShift, LiftIO, Sync} import com.github.unchama.seichiassist.meta.subsystem.Subsystem -import com.github.unchama.seichiassist.subsystems.discordnotification.infrastructure.WebhookDiscordNotificationSender +import com.github.unchama.seichiassist.subsystems.discordnotification.infrastructure.{DefaultDiscordNotificationSender, WebhookDiscordNotificationSender} +import io.chrisdavenport.log4cats.Logger trait System[F[_]] extends Subsystem[F] { implicit val globalNotification: DiscordNotificationAPI[F] } object System { - def wired[F[_] : Sync : ContextShift](configuration: SystemConfiguration): System[F] = new System[F] { - implicit override val globalNotification: DiscordNotificationAPI[F] = - new WebhookDiscordNotificationSender[F](configuration.webhookUrl) + def wired[F[_] : Sync : ContextShift : Logger : LiftIO](configuration: SystemConfiguration): System[F] = new System[F] { + implicit override val globalNotification: DiscordNotificationAPI[F] = { + WebhookDiscordNotificationSender.tryCreate(configuration.webhookUrl).getOrElse(new DefaultDiscordNotificationSender) + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala new file mode 100644 index 0000000000..56f9fa0ccc --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala @@ -0,0 +1,15 @@ +package com.github.unchama.seichiassist.subsystems.discordnotification.infrastructure + +import cats.effect.LiftIO +import com.github.unchama.seichiassist.SeichiAssist +import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI +import io.chrisdavenport.log4cats.Logger + +/** + * この実装は[[send]]が呼ばれるたびに警告をロガーに流す以外は何もしない。 + */ +final class DefaultDiscordNotificationSender[F[_]: Logger: LiftIO] extends DiscordNotificationAPI[F] { + override def send(message: String): F[Unit] = { + SeichiAssist.instance.loggerF.warn("Discordへの送信が試みられましたが、URLが無効、もしくは与えられていません。コンフィグを確認してください。").to + } +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala index d94e2f25ff..3cc95cf50f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala @@ -2,15 +2,16 @@ package com.github.unchama.seichiassist.subsystems.discordnotification.infrastru import cats.effect.{ContextShift, Sync} import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI +import io.chrisdavenport.log4cats.Logger import org.bukkit.Bukkit import java.io.IOException -import java.net.{HttpURLConnection, URL} +import java.net.{HttpURLConnection, MalformedURLException, URL} import java.nio.charset.StandardCharsets import scala.util.Using import scala.util.chaining.scalaUtilChainingOps -class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift](webhookURL: String) extends DiscordNotificationAPI[F] { +class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift] private(webhookURL: String) extends DiscordNotificationAPI[F] { assert( webhookURL.nonEmpty, "GlobalNotificationSenderのURLに空文字列が指定されました。コンフィグを確認してください。" @@ -49,3 +50,20 @@ class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift](webhookURL: Str } } yield () } + +object WebhookDiscordNotificationSender { + /** + * [[WebhookDiscordNotificationSender]] を作成することを試みる。 + * @param webhookURL Discordに送信されるwebhookのURL + * @tparam F 文脈 + * @return 初期化に成功した場合はSome、初期化中に特定の例外が送出された場合はNone。マスクされない例外が送出されたときは、再送出する。 + */ + def tryCreate[F[_]: Sync: ContextShift: Logger](webhookURL: String): Option[WebhookDiscordNotificationSender[F]] = { + try { + Some(new WebhookDiscordNotificationSender[F](webhookURL)) + } catch { + case _: MalformedURLException => None + case _: AssertionError => None + } + } +} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 23887427aa..059b87aaca 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -2,6 +2,7 @@ package com.github.unchama.seichiassist.subsystems.present.infrastructure import cats.Applicative import cats.effect.Sync +import com.github.unchama.generic.MapExtra import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence, RevokeWarning} import eu.timepit.refined.api.Refined @@ -167,7 +168,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - Right(filledEntries(associatedEntries, idSliceWithPagination).toList) + Right(MapExtra.fillOnBaseSet(associatedEntries.toMap, idSliceWithPagination, PresentClaimingState.Unavailable).toList) } } } @@ -185,7 +186,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - filledEntries(associatedEntries, validPresentIDs).toMap + MapExtra.fillOnBaseSet(associatedEntries.toMap, validPresentIDs.toSet, PresentClaimingState.Unavailable) } } @@ -223,11 +224,6 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It ItemStackBlobProxy.blobToItemStack(rs.string("itemstack")) } - private def filledEntries(knownState: List[(PresentID, PresentClaimingState)], validGlobalId: Iterable[PresentID]): Iterable[(PresentID, PresentClaimingState)] = { - val globalEntries = validGlobalId.map(id => (id, PresentClaimingState.Unavailable)) - (globalEntries ++ knownState) - } - private def computeValidPresentCount: F[Long] = Sync[F].delay { DB.readOnly { implicit session => sql"""SELECT COUNT(*) AS c FROM present""" diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala index ff610ac118..3917ff20a2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala @@ -4,11 +4,11 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRange import java.time.LocalDate object Valentine { - val EVENT_YEAR: Int = 2018 + val EVENT_YEAR: Int = 2022 val START_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 2, 13) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 2, 27) val itemDropRate: Double = validateItemDropRate(0.3) val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/valentine$EVENT_YEAR") def isInEvent: Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(LocalDate.now()) -} \ No newline at end of file +} diff --git a/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala b/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala new file mode 100644 index 0000000000..d50097bb76 --- /dev/null +++ b/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala @@ -0,0 +1,39 @@ +package com.github.unchama.generic + +import org.scalatest.wordspec.AnyWordSpec + +class MapExtraSpec extends AnyWordSpec { + "fillOnBaseSet" should { + "return empty Map" in { + assert(MapExtra.fillOnBaseSet[String, Int](Map(), Set(), 1).isEmpty) + } + + "fail if map's key is not sub-set of set" in { + assertThrows[IllegalArgumentException] { + MapExtra.fillOnBaseSet[String, Int](Map("a" -> 2), Set(), 1) + } + } + + "return filled Map" in { + assert( + MapExtra.fillOnBaseSet( + Map("a" -> 1, "b" -> 2, "c" -> 3), + Set("a", "b", "c", "d"), + 4 + ) == Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 4) + ) + } + + "return filled Map when given Map is empty" in { + assert( + MapExtra.fillOnBaseSet( + Map(), + Set("A", "B", "C", "D"), + 42 + ) == Map("A" -> 42, "B" -> 42, "C" -> 42, "D" -> 42) + ) + } + + + } +}