From 7cdeb582f878537418aae55ac411354324ac892a Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Sun, 5 Dec 2021 11:42:27 +0900 Subject: [PATCH 01/43] [update] emit warning on unused members --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index 19e1d82018..79f5cc7a59 100644 --- a/build.sbt +++ b/build.sbt @@ -141,6 +141,7 @@ lazy val root = (project in file(".")) "-deprecation", "-Ypatmat-exhaust-depth", "320", "-Ymacro-annotations", + "-Ywarn-unused", ), javacOptions ++= Seq("-encoding", "utf8") ) From f2ab1aae495ff1b0bc35520440a812e985d14c2d Mon Sep 17 00:00:00 2001 From: Lucky3028 Date: Sat, 15 Jan 2022 21:44:59 +0900 Subject: [PATCH 02/43] =?UTF-8?q?revert:=20GB=E3=81=A7=E3=81=AE=E6=A3=98?= =?UTF-8?q?=E3=81=AE=E9=8E=A7=E5=88=A4=E5=AE=9A=E3=82=92=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unchama/seichiassist/listener/EntityListener.scala | 2 +- .../unchama/seichiassist/task/GiganticBerserkTask.scala | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) 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..88afb609e3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala @@ -199,6 +199,6 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, //プレイヤーが整地ワールドに居ない場合終了 if (!player.getWorld.isSeichi) return val GBTR = new GiganticBerserkTask - GBTR.PlayerKillEnemy(player, entity) + GBTR.PlayerKillEnemy(player) } } \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala b/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala index 2dfe40790e..dab4b76f82 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala @@ -10,7 +10,7 @@ import com.github.unchama.seichiassist.util.Util import com.github.unchama.seichiassist.{LevelThresholds, SeichiAssist} import org.bukkit.ChatColor._ import org.bukkit.Sound -import org.bukkit.entity.{LivingEntity, Player} +import org.bukkit.entity.Player import scala.util.Random @@ -20,7 +20,7 @@ class GiganticBerserkTask { : ConcurrentEffect : NonServerThreadContextShift : DiscordNotificationAPI - ](p: Player, e: LivingEntity)(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { + ](p: Player)(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { val player = p val uuid = p.getUniqueId val playerdata = SeichiAssist.playermap(uuid) @@ -54,9 +54,6 @@ class GiganticBerserkTask { //進化待機状態の場合終了 if (playerdata.giganticBerserk.canEvolve) return - // 棘の鎧で倒した場合終了 - if (Util.isEntityKilledByThornsEnchant(e)) return - // stage * level val level = playerdata.giganticBerserk.level val n = (playerdata.giganticBerserk.stage * 10) + level From a19fe1d436d4f56024d3130c73965f21eb74eae7 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Wed, 26 Jan 2022 05:22:24 +0900 Subject: [PATCH 03/43] [update] migrate VoteCommand to command builder --- .../unchama/seichiassist/SeichiAssist.scala | 4 +- .../seichiassist/commands/VoteCommand.scala | 59 +++++++++++++++++++ .../commands/legacy/VoteCommand.scala | 48 --------------- 3 files changed, 61 insertions(+), 50 deletions(-) create mode 100644 src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala delete mode 100644 src/main/scala/com/github/unchama/seichiassist/commands/legacy/VoteCommand.scala diff --git a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala index 35b63ae59f..db1a98113f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala @@ -28,7 +28,7 @@ import com.github.unchama.seichiassist.MaterialSets.BlockBreakableBySkill import com.github.unchama.seichiassist.SeichiAssist.seichiAssistConfig import com.github.unchama.seichiassist.bungee.BungeeReceiver import com.github.unchama.seichiassist.commands._ -import com.github.unchama.seichiassist.commands.legacy.{DonationCommand, GachaCommand, VoteCommand} +import com.github.unchama.seichiassist.commands.legacy.{DonationCommand, GachaCommand} import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.asyncShift import com.github.unchama.seichiassist.data.player.PlayerData @@ -508,7 +508,7 @@ class SeichiAssist extends JavaPlugin() { // コマンドの登録 Map( "gacha" -> new GachaCommand(), - "vote" -> new VoteCommand, + "vote" -> VoteCommand.executor, "donation" -> new DonationCommand, "map" -> MapCommand.executor, "ef" -> new EffectCommand(fastDiggingEffectSystem.settingsApi).executor, diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala new file mode 100644 index 0000000000..8af83a4e56 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala @@ -0,0 +1,59 @@ +package com.github.unchama.seichiassist.commands + +import cats.data.Kleisli +import cats.effect.IO +import com.github.unchama.contextualexecutor.builder.Parsers +import com.github.unchama.seichiassist.SeichiAssist +import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder +import com.github.unchama.seichiassist.util.Util +import com.github.unchama.targetedeffect.{TargetedEffect, UnfocusedEffect} +import com.github.unchama.targetedeffect.commandsender.MessageEffect +import org.bukkit.ChatColor.{GREEN, RED, YELLOW} +import org.bukkit.command.{CommandSender, TabExecutor} + +object VoteCommand { + sealed trait Operation + case object Record extends Operation + + val usageEchoEcexutor: TargetedEffect[CommandSender] = MessageEffect(List( + s"$RED/vote record <プレイヤー名>", + "投票特典配布用コマンドです(マルチ鯖対応済)" + )) + val executor: TabExecutor = playerCommandBuilder + .argumentsParsers( + List( + Parsers.fromOptionParser({ + case "record" => Some(Record) + case _ => None + }, usageEchoEcexutor), + Parsers.identity[String] + ) + ) + .execution(context => { + val args = context.args.parsed + val command: Operation = args.head.asInstanceOf + val name: String = args(1).asInstanceOf + command match { + case Record => { + //引数が2つの時の処理 + val lowerCasePlayerName = Util.getName(name) + //プレイヤーオンライン、オフラインにかかわらずsqlに送信(マルチ鯖におけるコンフリクト防止の為) + IO { + for { + _ <- MessageEffect(s"$YELLOW${lowerCasePlayerName}の投票特典配布処理開始…") + _ <- UnfocusedEffect { + SeichiAssist.databaseGateway.playerDataManipulator.incrementVotePoint(lowerCasePlayerName) + } + k = if (SeichiAssist.databaseGateway.playerDataManipulator.addChainVote(lowerCasePlayerName)) { + MessageEffect(s"${GREEN}連続投票数の記録に成功") + } else { + MessageEffect(s"${RED}連続投票数の記録に失敗") + } + } yield k + } + } + } + }) + .build() + .asNonBlockingTabExecutor() +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/legacy/VoteCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/legacy/VoteCommand.scala deleted file mode 100644 index fd48b61d46..0000000000 --- a/src/main/scala/com/github/unchama/seichiassist/commands/legacy/VoteCommand.scala +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.unchama.seichiassist.commands.legacy - -import com.github.unchama.seichiassist.SeichiAssist -import com.github.unchama.seichiassist.database.DatabaseGateway -import com.github.unchama.seichiassist.util.Util -import org.bukkit.ChatColor._ -import org.bukkit.command.{Command, CommandExecutor, CommandSender} - -class VoteCommand extends CommandExecutor { - override def onCommand(sender: CommandSender, command: Command, label: String, args: Array[String]): Boolean = { - val databaseGateway: DatabaseGateway = SeichiAssist.databaseGateway - - def notifyRecordCommandUsage(): Unit = { - sender.sendMessage(Array( - s"$RED/vote record <プレイヤー名>", - "投票特典配布用コマンドです(マルチ鯖対応済)" - )) - } - - def printHelp(): Unit = { - notifyRecordCommandUsage() - } - - if (args.length == 0) { - printHelp() - } else if (args(0).equalsIgnoreCase("record")) { - if (args.length != 2) { //引数が2つでない時の処理 - notifyRecordCommandUsage() - } else { - //引数が2つの時の処理 - val lowerCasePlayerName = Util.getName(args(1)) - //プレイヤーオンライン、オフラインにかかわらずsqlに送信(マルチ鯖におけるコンフリクト防止の為) - sender.sendMessage(s"$YELLOW${lowerCasePlayerName}の投票特典配布処理開始…") - //mysqlにも書き込んどく - databaseGateway.playerDataManipulator.incrementVotePoint(lowerCasePlayerName) - - if (databaseGateway.playerDataManipulator.addChainVote(lowerCasePlayerName)) - sender.sendMessage(s"${GREEN}連続投票数の記録に成功") - else - sender.sendMessage(s"${RED}連続投票数の記録に失敗") - } - } else { - printHelp() - } - - true - } -} From 683f766dbf7383ea36a7ee80abe6b1537a8402f4 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Wed, 26 Jan 2022 05:30:11 +0900 Subject: [PATCH 04/43] fix: compile error --- .../com/github/unchama/seichiassist/commands/VoteCommand.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala index 8af83a4e56..41587dd014 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala @@ -26,7 +26,7 @@ object VoteCommand { case "record" => Some(Record) case _ => None }, usageEchoEcexutor), - Parsers.identity[String] + Parsers.identity ) ) .execution(context => { From 80a35a52fd819be86ec28d7753146264e9756128 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Fri, 11 Feb 2022 10:20:32 +0900 Subject: [PATCH 05/43] [add] persistence mode for SinglePhasedRepositoryInitialization --- ...V1.7.5__Create_player_rate_limit_table.sql | 6 +++++ .../RepositoryInitializationExt.scala | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql create mode 100644 src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala diff --git a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql new file mode 100644 index 0000000000..743666f46d --- /dev/null +++ b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS player_rate_limit( + uuid CHAR(36) NOT NULL, + rate_limit_name CHAR(32) NOT NULL, + current_value INT NOT NULL, + PRIMARY KEY(uuid, rate_limit_name) +); diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala new file mode 100644 index 0000000000..71da03519c --- /dev/null +++ b/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala @@ -0,0 +1,24 @@ +package com.github.unchama.datarepository.template.initialization + +import cats.effect.Sync +import scalikejdbc._ + +object RepositoryInitializationExt { + implicit class ForSinglePhased[F[_]: Sync, R](self: SinglePhasedRepositoryInitialization[F, R]) { + def overwriteWithDatabaseValue(key: String, default: => R) + (extractor: WrappedResultSet => R): SinglePhasedRepositoryInitialization[F, R] = { + // TODO: ゲームサーバが終了したときには一時的に格納された抜ける前のpermit countを全部破棄しても良い + self.extendPreparation { case (uuid, _ ) => + (_: R) => Sync[F].delay { + DB.readOnly { implicit session => + sql"""SELECT * FROM player_rate_limit where uuid = $uuid and rate_limit_name = $key""" + .map(extractor) + .single() + .apply() + }.getOrElse(default) + } + } + } + } +} + From f1ec43314830cd54e61cec1f29fb8b9874f20cef Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:40:41 +0900 Subject: [PATCH 06/43] Update src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala Co-authored-by: Lucky --- .../com/github/unchama/seichiassist/commands/VoteCommand.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala index 41587dd014..80cd1ef976 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala @@ -17,7 +17,7 @@ object VoteCommand { val usageEchoEcexutor: TargetedEffect[CommandSender] = MessageEffect(List( s"$RED/vote record <プレイヤー名>", - "投票特典配布用コマンドです(マルチ鯖対応済)" + "投票特典配布用コマンドです" )) val executor: TabExecutor = playerCommandBuilder .argumentsParsers( From af8104872687e3bbd3d765b77e3347f70b08abb3 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 12 Feb 2022 21:16:47 +0900 Subject: [PATCH 07/43] [add] read/write from db --- ...V1.7.5__Create_player_rate_limit_table.sql | 2 +- .../RepositoryInitializationExt.scala | 42 ++++++++++++------- .../RateLimiterRepositoryDefinitions.scala | 20 ++++++++- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql index 743666f46d..fb5df0056d 100644 --- a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql +++ b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS player_rate_limit( uuid CHAR(36) NOT NULL, rate_limit_name CHAR(32) NOT NULL, - current_value INT NOT NULL, + current_value CHAR(32) NOT NULL, PRIMARY KEY(uuid, rate_limit_name) ); diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala index 71da03519c..7cfe4405a1 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala @@ -1,22 +1,36 @@ package com.github.unchama.datarepository.template.initialization -import cats.effect.Sync +import cats.effect.{ConcurrentEffect, Sync, Timer} +import cats.implicits._ +import com.github.unchama.generic.ContextCoercion +import com.github.unchama.generic.ContextCoercion.coercibleComputation +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus +import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import scalikejdbc._ +import scala.concurrent.duration.FiniteDuration + object RepositoryInitializationExt { - implicit class ForSinglePhased[F[_]: Sync, R](self: SinglePhasedRepositoryInitialization[F, R]) { - def overwriteWithDatabaseValue(key: String, default: => R) - (extractor: WrappedResultSet => R): SinglePhasedRepositoryInitialization[F, R] = { - // TODO: ゲームサーバが終了したときには一時的に格納された抜ける前のpermit countを全部破棄しても良い - self.extendPreparation { case (uuid, _ ) => - (_: R) => Sync[F].delay { - DB.readOnly { implicit session => - sql"""SELECT * FROM player_rate_limit where uuid = $uuid and rate_limit_name = $key""" - .map(extractor) - .single() - .apply() - }.getOrElse(default) - } + implicit class ForSinglePhased[ + F[_]: ContextCoercion[*[_], G]: Sync, + G[_]: ConcurrentEffect: Timer, + R: OrderedMonus + ](self: SinglePhasedRepositoryInitialization[F, RateLimiter[F, R]]) { + def overwriteWithDatabaseValue(key: String, upperLimit: R, reset: FiniteDuration) + (extractor: WrappedResultSet => R): SinglePhasedRepositoryInitialization[F, RateLimiter[F, R]] = { + self.extendPreparation { case (uuid, _) => + _ => for { + newRateLimiter <- FixedWindowRateLimiter.in[G, F, R](upperLimit, reset) + usedPermission <- Sync[F].delay { + DB.readOnly { implicit session => + sql"""SELECT * FROM player_rate_limit where uuid = $uuid and rate_limit_name = $key""" + .map(extractor) + .single() + .apply() + }.getOrElse(OrderedMonus[R].empty) + } + _ <- newRateLimiter.requestPermission(usedPermission) + } yield newRateLimiter } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 3638290638..d3c2cb1c55 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -1,12 +1,16 @@ package com.github.unchama.seichiassist.subsystems.buildcount.application.application import cats.effect.{ConcurrentEffect, Sync, Timer} +import cats.implicits._ import com.github.unchama.datarepository.template.finalization.RepositoryFinalization +import com.github.unchama.datarepository.template.initialization.RepositoryInitializationExt.ForSinglePhased import com.github.unchama.datarepository.template.initialization.SinglePhasedRepositoryInitialization import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} +import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount +import scalikejdbc._ object RateLimiterRepositoryDefinitions { @@ -21,9 +25,21 @@ object RateLimiterRepositoryDefinitions { config.oneMinuteBuildExpLimit, 1.minute ) + }.overwriteWithDatabaseValue("buildcount", config.oneMinuteBuildExpLimit, 1.minute) { + wrs => BuildExpAmount(wrs.bigDecimal("buildcount")) } - def finalization[F[_] : Sync, Player]: RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = - RepositoryFinalization.trivial + def finalization[F[_] : Sync, Player: HasUuid](implicit config: Configuration): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = + RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => + // NOTE: 1分間のタイムスライスで、レートリミッターの残量の最大値はコンフィグで規定された値なので、 + // これをすることによってどのぐらい残量があるか確認できる。 + rateLimiter.requestPermission(BuildExpAmount.ofNonNegative(config.oneMinuteBuildExpLimit.amount)).map { bea => + DB.localTx { implicit session => + sql"""INSERT INTO player_rate_limit VALUES(${HasUuid[Player].of(p)}, "buildcount", ${bea.toPlainString})""" + .execute() + .apply() + } + } + } } From e7690e37dff3e28db25a7af24fd6b943fcc0a0db Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 12 Feb 2022 21:20:59 +0900 Subject: [PATCH 08/43] [add] trivial implementation of HasUuid --- .../scala/com/github/unchama/minecraft/algebra/HasUuid.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/com/github/unchama/minecraft/algebra/HasUuid.scala b/src/main/scala/com/github/unchama/minecraft/algebra/HasUuid.scala index 3db36d331f..f6270f744d 100644 --- a/src/main/scala/com/github/unchama/minecraft/algebra/HasUuid.scala +++ b/src/main/scala/com/github/unchama/minecraft/algebra/HasUuid.scala @@ -14,4 +14,6 @@ object HasUuid { def apply[T](implicit ev: HasUuid[T]): HasUuid[T] = ev + implicit val trivial: HasUuid[UUID] = x => x + } From 432952603091b49d8d11d80e1ff123ea02b7a4f4 Mon Sep 17 00:00:00 2001 From: Lucky3028 Date: Fri, 18 Feb 2022 14:15:15 +0900 Subject: [PATCH 09/43] =?UTF-8?q?feat:=20=E9=85=8D=E5=B8=83=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E5=8E=9F=E5=9B=A0=E4=B8=8D=E6=98=8E?= =?UTF-8?q?=E3=81=AE=E3=81=9F=E3=82=81=E3=80=81=E9=85=8D=E5=B8=83=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=81=AF=E3=81=9A=E3=81=AE=E3=83=81=E3=83=A7?= =?UTF-8?q?=E3=82=B3=E3=83=81=E3=83=83=E3=83=97=E3=82=AF=E3=83=83=E3=82=AD?= =?UTF-8?q?=E3=83=BC=E3=82=92=E6=B1=BA=E3=82=81=E6=89=93=E3=81=A1=E3=81=A7?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=81=97=E4=BB=98=E4=B8=8E=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subsystems/seasonalevents/commands/EventCommand.scala | 7 +++++++ .../seasonalevents/valentine/ValentineItemData.scala | 6 ++---- .../seasonalevents/valentine/ValentineListener.scala | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala index 73b4409b20..6bdfc81bec 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala @@ -7,11 +7,14 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Ann import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.HalloweenItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYearItemData._ +import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineItemData.cookieOf import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.TargetedEffect._ import org.bukkit.command.TabExecutor import org.bukkit.entity.Player +import java.util.UUID + class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { import com.github.unchama.targetedeffect._ @@ -46,6 +49,9 @@ class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { anniversaryShovel ) + def valentineGrantEffect: TargetedEffect[Player] = + Util.grantItemStacksEffect(cookieOf("kinton", UUID.fromString("85dd5867-db09-4a2f-bae7-8d38d5a9c547"))) + val executor: TabExecutor = playerCommandBuilder .execution { context => val effect = context.args.yetToBeParsed match { @@ -53,6 +59,7 @@ class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { case "christmas" :: _ => christsmasGrantEffect case "newyear" :: _ => newYearGrantEffect case "halloween" :: _ => halloweenGrantEffect + case "valentine" :: _ => valentineGrantEffect case _ => emptyEffect } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala index 8d64a249ea..ca63d6d97e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.valentine import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valentine.{END_DATE, EVENT_YEAR} import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor._ -import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.SkullMeta import org.bukkit.{Bukkit, Material} @@ -66,8 +65,7 @@ object ValentineItemData { //region GiftedCookie -> 棒メニューでもらえるやつ - def cookieOf(player: Player): ItemStack = { - val playerName = player.getName + def cookieOf(playerName: String, playerUuid: UUID): ItemStack = { val loreList = { val header = List( "", @@ -90,7 +88,7 @@ object ValentineItemData { import item._ setByte(NBTTagConstants.typeIdTag, 2.toByte) setObject(NBTTagConstants.expiryDateTag, END_DATE) - setObject(NBTTagConstants.producerUuidTag, player.getUniqueId) + setObject(NBTTagConstants.producerUuidTag, playerUuid) setString(NBTTagConstants.producerNameTag, playerName) } .pipe(_.getItem) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala index 95f22ef3a6..ad366bbff5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala @@ -102,7 +102,7 @@ class ValentineListener[ val effects = if (hasNotJoinedBeforeYet) SequentialEffect( - grantItemStacksEffect(cookieOf(player)), + grantItemStacksEffect(cookieOf(player.getName, playerUuid)), MessageEffect(s"${AQUA}チョコチップクッキーを付与しました。"), FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f)) else TargetedEffect.emptyEffect From ad60a3747d8597754b1de09435b8fe2d30fd867c Mon Sep 17 00:00:00 2001 From: yuuki1293 Date: Fri, 18 Feb 2022 16:04:39 +0900 Subject: [PATCH 10/43] =?UTF-8?q?fix:=20=E6=AD=BB=E4=BA=A1=E3=83=A1?= =?UTF-8?q?=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E3=83=81=E3=83=A7?= =?UTF-8?q?=E3=82=B3=E3=82=92=E3=83=81=E3=83=A7=E3=82=B3=E3=83=81=E3=83=83?= =?UTF-8?q?=E3=83=97=E3=82=AF=E3=83=83=E3=82=AD=E3=83=BC=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seasonalevents/valentine/ValentineItemData.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala index 8d64a249ea..42cdb6a243 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala @@ -105,19 +105,19 @@ object ValentineItemData { Option(new NBTItem(item).getObject(NBTTagConstants.producerUuidTag, classOf[UUID])) def deathMessages(playerName: String, cookieProducerName: String): Seq[String] = Seq( - s"${playerName}は${cookieProducerName}のチョコレートを食べた!猟奇的な味だった。", - s"$playerName!${cookieProducerName}からのチョコだと思ったかい?ざぁんねんっ!", + s"${playerName}は${cookieProducerName}のチョコチップクッキーを食べた!猟奇的な味だった。", + s"$playerName!${cookieProducerName}からのチョコチップクッキーだと思ったかい?ざぁんねんっ!", s"${playerName}は${cookieProducerName}のプレゼントで鼻血が止まらない!(計画通り)", - s"${playerName}は${cookieProducerName}のチョコレートを頬張ったまま息絶えた!", - s"${playerName}は${cookieProducerName}のチョコにアレが入っているとはを知らずに食べた…", - s"${playerName}は${cookieProducerName}のチョコなんか食ってないであくしろはたらけ", + s"${playerName}は${cookieProducerName}のチョコチップクッキーを頬張ったまま息絶えた!", + s"${playerName}は${cookieProducerName}のチョコチップクッキーにアレが入っているとはを知らずに食べた…", + s"${playerName}は${cookieProducerName}のチョコチップクッキーなんか食ってないであくしろはたらけ", s"${cookieProducerName}は${playerName}に日頃の恨みを晴らした!スッキリ!", s"${cookieProducerName}による${playerName}への痛恨の一撃!ハッピーバレンタインッ!", s"${cookieProducerName}は${playerName}が食べる姿を、満面の笑みで見つめている!", s"${cookieProducerName}は悪くない!${playerName}が悪いんだっ!", s"${cookieProducerName}は${playerName}を討伐した!", s"こうして${cookieProducerName}のイタズラでまた1人${playerName}が社畜となった。", - s"おい聞いたか!${cookieProducerName}が${playerName}にチョコ送ったらしいぞー!" + s"おい聞いたか!${cookieProducerName}が${playerName}にチョコチップクッキー送ったらしいぞー!" ) //endregion From 4fdd44e1578498c8bce02cf137ccc5e5cf7bf8b0 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 18:07:48 +0900 Subject: [PATCH 11/43] =?UTF-8?q?[update]=20RefDict=E3=82=92=E7=94=A8?= =?UTF-8?q?=E3=81=84=E3=81=9F=E5=AE=9F=E8=A3=85=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryInitializationExt.scala | 38 ---------------- .../generic/ratelimiting/RateLimiter.scala | 8 ++++ .../subsystems/buildcount/System.scala | 7 ++- .../IncrementBuildExpWhenBuiltByHand.scala | 7 +-- .../RateLimiterRepositoryDefinitions.scala | 41 +++++++++--------- .../domain/BuildAmountPermission.scala | 24 +++++++++++ .../BuildAmountRateLimitPersistence.scala | 8 ++++ .../JdbcBuildAmountRateLimitPersistence.scala | 43 +++++++++++++++++++ 8 files changed, 112 insertions(+), 64 deletions(-) delete mode 100644 src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala deleted file mode 100644 index 7cfe4405a1..0000000000 --- a/src/main/scala/com/github/unchama/datarepository/template/initialization/RepositoryInitializationExt.scala +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.unchama.datarepository.template.initialization - -import cats.effect.{ConcurrentEffect, Sync, Timer} -import cats.implicits._ -import com.github.unchama.generic.ContextCoercion -import com.github.unchama.generic.ContextCoercion.coercibleComputation -import com.github.unchama.generic.algebra.typeclasses.OrderedMonus -import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} -import scalikejdbc._ - -import scala.concurrent.duration.FiniteDuration - -object RepositoryInitializationExt { - implicit class ForSinglePhased[ - F[_]: ContextCoercion[*[_], G]: Sync, - G[_]: ConcurrentEffect: Timer, - R: OrderedMonus - ](self: SinglePhasedRepositoryInitialization[F, RateLimiter[F, R]]) { - def overwriteWithDatabaseValue(key: String, upperLimit: R, reset: FiniteDuration) - (extractor: WrappedResultSet => R): SinglePhasedRepositoryInitialization[F, RateLimiter[F, R]] = { - self.extendPreparation { case (uuid, _) => - _ => for { - newRateLimiter <- FixedWindowRateLimiter.in[G, F, R](upperLimit, reset) - usedPermission <- Sync[F].delay { - DB.readOnly { implicit session => - sql"""SELECT * FROM player_rate_limit where uuid = $uuid and rate_limit_name = $key""" - .map(extractor) - .single() - .apply() - }.getOrElse(OrderedMonus[R].empty) - } - _ <- newRateLimiter.requestPermission(usedPermission) - } yield newRateLimiter - } - } - } -} - diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index eb82adf850..73ad331d72 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -34,6 +34,12 @@ trait RateLimiter[F[_], A] { */ def requestPermission(a: A): F[A] + /** + * 次にリセットされるまで送っても良いリクエスト量を取得する作用を返す。 + * この作用が発火したあとでも、送って良いリクエスト量は変化しない。 + * @return [[A]] によって記述される、次にリセットされるまで送っても良いリクエスト量を取得する作用 + */ + def peekAvailablePermission: F[A] } object RateLimiter { @@ -53,6 +59,8 @@ object RateLimiter { val newCount = (count |+| a) min maxCount (newCount, newCount |-| count) } + + override def peekAvailablePermission: F[A] = countRef.get } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala index 0a9c595add..a5e9b30255 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala @@ -7,14 +7,16 @@ import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.concurrent.ReadOnlyRef +import com.github.unchama.generic.ratelimiting.RateLimiter import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{ClassifyPlayerWorld, IncrementBuildExpWhenBuiltByHand, IncrementBuildExpWhenBuiltWithSkill} import com.github.unchama.seichiassist.subsystems.buildcount.application.application.{BuildAmountDataRepositoryDefinition, RateLimiterRepositoryDefinitions} import com.github.unchama.seichiassist.subsystems.buildcount.application.{BuildExpMultiplier, Configuration} import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.actions.ClassifyBukkitPlayerWorld import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.listeners.BuildExpIncrementer +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData -import com.github.unchama.seichiassist.subsystems.buildcount.infrastructure.JdbcBuildAmountDataPersistence +import com.github.unchama.seichiassist.subsystems.buildcount.infrastructure.{JdbcBuildAmountDataPersistence, JdbcBuildAmountRateLimitPersistence} import com.github.unchama.util.logging.log4cats.PrefixedLogger import io.chrisdavenport.log4cats.Logger import org.bukkit.entity.Player @@ -41,12 +43,13 @@ object System { implicit val expMultiplier: BuildExpMultiplier = configuration.multipliers implicit val persistence: JdbcBuildAmountDataPersistence[G] = new JdbcBuildAmountDataPersistence[G]() + implicit val rateLimitPersistence: JdbcBuildAmountRateLimitPersistence[G, F] = new JdbcBuildAmountRateLimitPersistence[G, F]() implicit val logger: Logger[F] = PrefixedLogger[F]("BuildAssist-BuildAmount")(rootLogger) for { rateLimiterRepositoryControls <- BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction( + RepositoryDefinition.Phased.SinglePhased.withoutTappingAction[G, Player, RateLimiter[G, BuildAmountPermission]]( RateLimiterRepositoryDefinitions.initialization[F, G], RateLimiterRepositoryDefinitions.finalization[G, UUID] ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala index ff2aea5b5d..f4c497862d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala @@ -7,6 +7,7 @@ import com.github.unchama.generic.ratelimiting.RateLimiter import com.github.unchama.generic.{Diff, RefExtra} import com.github.unchama.minecraft.actions.SendMinecraftMessage import com.github.unchama.seichiassist.subsystems.buildcount.application.BuildExpMultiplier +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData @@ -35,7 +36,7 @@ object IncrementBuildExpWhenBuiltByHand { : ClassifyPlayerWorld[*[_], Player] : SendMinecraftMessage[*[_], Player], Player - ](rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildExpAmount]], + ](rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildAmountPermission]], dataRepository: KeyedDataRepository[Player, Ref[F, BuildAmountData]]) (implicit multiplier: BuildExpMultiplier): IncrementBuildExpWhenBuiltByHand[F, Player] = (player: Player, by: BuildExpAmount) => { @@ -51,8 +52,8 @@ object IncrementBuildExpWhenBuiltByHand { F.pure(BuildExpAmount(0)) ) amountToIncrement <- - rateLimiterRepository(player).requestPermission(amountToRequestIncrement) - dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))(_.modifyExpAmount(_.add(amountToIncrement))) + rateLimiterRepository(player).requestPermission(BuildAmountPermission(amountToRequestIncrement)) + dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))(_.modifyExpAmount(_.add(amountToIncrement.raw))) _ <- Diff .ofPairBy(dataPair)(_.levelCorrespondingToExp) .traverse(LevelUpNotifier[F, Player].notifyTo(player)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index d3c2cb1c55..6cb0462566 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -2,14 +2,16 @@ package com.github.unchama.seichiassist.subsystems.buildcount.application.applic import cats.effect.{ConcurrentEffect, Sync, Timer} import cats.implicits._ +import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefinition import com.github.unchama.datarepository.template.finalization.RepositoryFinalization -import com.github.unchama.datarepository.template.initialization.RepositoryInitializationExt.ForSinglePhased import com.github.unchama.datarepository.template.initialization.SinglePhasedRepositoryInitialization import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount +import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence import scalikejdbc._ object RateLimiterRepositoryDefinitions { @@ -19,27 +21,24 @@ object RateLimiterRepositoryDefinitions { def initialization[ F[_] : ConcurrentEffect : Timer, G[_] : Sync : ContextCoercion[*[_], F] - ](implicit config: Configuration): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = - SinglePhasedRepositoryInitialization.withSupplier { - FixedWindowRateLimiter.in[F, G, BuildExpAmount]( - config.oneMinuteBuildExpLimit, - 1.minute - ) - }.overwriteWithDatabaseValue("buildcount", config.oneMinuteBuildExpLimit, 1.minute) { - wrs => BuildExpAmount(wrs.bigDecimal("buildcount")) - } + ]( + implicit config: Configuration, + persistence: BuildAmountRateLimitPersistence[G] + ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildAmountPermission]] = { + val rateLimiter = FixedWindowRateLimiter.in[F, G, BuildAmountPermission]( + BuildAmountPermission(config.oneMinuteBuildExpLimit), + 1.minute + ) - def finalization[F[_] : Sync, Player: HasUuid](implicit config: Configuration): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = - RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => - // NOTE: 1分間のタイムスライスで、レートリミッターの残量の最大値はコンフィグで規定された値なので、 - // これをすることによってどのぐらい残量があるか確認できる。 - rateLimiter.requestPermission(BuildExpAmount.ofNonNegative(config.oneMinuteBuildExpLimit.amount)).map { bea => - DB.localTx { implicit session => - sql"""INSERT INTO player_rate_limit VALUES(${HasUuid[Player].of(p)}, "buildcount", ${bea.toPlainString})""" - .execute() - .apply() - } + RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(BuildAmountPermission.orderedMonus.empty) + .initialization + .extendPreparation { (_, _) => usedPermission => + rateLimiter.flatTap(rateLimiter => rateLimiter.requestPermission(usedPermission)) } - } + } + def finalization[F[_] : Sync, Player: HasUuid](implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[F]): RepositoryFinalization[F, Player, RateLimiter[F, BuildAmountPermission]] = + RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => + rateLimiter.peekAvailablePermission.flatMap(a => persistence.write(HasUuid[Player].of(p), a)) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala new file mode 100644 index 0000000000..00977d94d4 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala @@ -0,0 +1,24 @@ +package com.github.unchama.seichiassist.subsystems.buildcount.domain + +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount + +case class BuildAmountPermission(raw: BuildExpAmount) + +object BuildAmountPermission { + implicit val orderedMonus: OrderedMonus[BuildAmountPermission] = new OrderedMonus[BuildAmountPermission] { + private val expIsOrderedMonus = OrderedMonus[BuildExpAmount] + /** + * 切り捨て減算。 + * `x: A, y: A` について、 `z: A` = `|-|(x, y)` は + * `x <= y + z` となるような最小の `z` として定義される。 + */ + override def |-|(x: BuildAmountPermission, y: BuildAmountPermission): BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.|-|(x.raw, y.raw)) + + override def empty: BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.empty) + + override def combine(x: BuildAmountPermission, y: BuildAmountPermission): BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.combine(x.raw, y.raw)) + + override def compare(x: BuildAmountPermission, y: BuildAmountPermission): Int = expIsOrderedMonus.compare(x.raw, y.raw) + } +} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala new file mode 100644 index 0000000000..6e32643b84 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala @@ -0,0 +1,8 @@ +package com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata + +import com.github.unchama.generic.RefDict +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission + +import java.util.UUID + +trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountPermission] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala new file mode 100644 index 0000000000..3d2169ccbf --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -0,0 +1,43 @@ +package com.github.unchama.seichiassist.subsystems.buildcount.infrastructure + +import cats.effect.{ConcurrentEffect, Sync, Timer} +import cats.implicits._ +import com.github.unchama.generic.ContextCoercion +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus +import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} +import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount +import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{BuildAmountData, BuildAmountDataPersistence, BuildAmountRateLimitPersistence} +import scalikejdbc._ + +import java.util.UUID + +class JdbcBuildAmountRateLimitPersistence[ + SyncContext[_]: ContextCoercion[*[_], ConcurrentContext], + ConcurrentContext[_]: ConcurrentEffect: Timer +](implicit F: Sync[SyncContext], config: Configuration) + extends BuildAmountRateLimitPersistence[SyncContext] { + + override def read(key: UUID): SyncContext[Option[BuildAmountPermission]] = + F.delay { + DB.localTx { implicit session => + sql"select build_count from rate_limit where uuid = ${key.toString} and rate_limit_name = 'build'" + .stripMargin + .map { rs => + val exp = BuildExpAmount(BigDecimal(rs.string("current_value"))) + + BuildAmountPermission(exp) + } + .first().apply() + } + } + + override def write(key: UUID, value: BuildAmountPermission): SyncContext[Unit] = F.delay { + DB.localTx { implicit session => + sql"update playerdata set build_count = $value where uuid = ${key.toString} and rate_limit_name = 'build'" + .stripMargin + .update().apply() + } + } +} From d27efa8be8b19adc5beb2a84d9474905b69d7138 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 18:43:53 +0900 Subject: [PATCH 12/43] =?UTF-8?q?[add]=20RateLimiter#peekAvailablePermissi?= =?UTF-8?q?on=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterPermissionPeekSpec.scala | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala new file mode 100644 index 0000000000..2134692939 --- /dev/null +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -0,0 +1,60 @@ +package com.github.unchama.generic.ratelimiting + +import cats.effect.{SyncIO, Timer} +import cats.implicits._ +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus +import com.github.unchama.generic.ContextCoercion._ +import com.github.unchama.testutil.concurrent.tests.{ConcurrentEffectTest, TaskDiscreteEventually} +import com.github.unchama.testutil.execution.MonixTestSchedulerTests +import eu.timepit.refined.api.Refined +import eu.timepit.refined.numeric.NonNegative +import eu.timepit.refined.refineV +import eu.timepit.refined.auto._ +import monix.catnap.SchedulerEffect +import monix.eval.Task +import monix.execution.ExecutionModel +import monix.execution.schedulers.TestScheduler +import org.scalatest.matchers.should.Matchers +import org.scalatest.time.SpanSugar.convertIntToGrainOfTime +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +class RateLimiterPermissionPeekSpec extends AnyWordSpec + with ScalaCheckPropertyChecks + with Matchers + with TaskDiscreteEventually + with ConcurrentEffectTest + with MonixTestSchedulerTests { + implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig(10000) + + implicit val monixScheduler: TestScheduler = TestScheduler(ExecutionModel.SynchronousExecution) + implicit val monixTimer: Timer[Task] = SchedulerEffect.timer(monixScheduler) + + type Natural = Int Refined NonNegative + + implicit val intOrderedMonus: OrderedMonus[Natural] = new OrderedMonus[Natural] { + override def |-|(x: Natural, y: Natural): Natural = + if (x >= y) refineV[NonNegative](x - y).getOrElse(throw new RuntimeException) + else 0 + + override def empty: Natural = 0 + + override def combine(x: Natural, y: Natural): Natural = + refineV[NonNegative](x + y).getOrElse(throw new RuntimeException) + + override def compare(x: Natural, y: Natural): Int = x.value.compare(y.value) + } + + "RateLimiter" should { + "not modify available permission when peaked" in { + val program = for { + rateLimiter <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](100, 10.seconds).coerceTo[Task] + peek1 <- rateLimiter.peekAvailablePermission.coerceTo[Task] + peek2 <- rateLimiter.peekAvailablePermission.coerceTo[Task] + } yield peek1 == peek2 + + assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) + } + } +} From 63b1dabd7b98f46063125eaf5f79639ec38f6046 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Sat, 19 Feb 2022 19:21:04 +0900 Subject: [PATCH 13/43] Update src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala Co-authored-by: Kory | Ryosuke Kondo <6561358+kory33@users.noreply.github.com> --- .../com/github/unchama/generic/ratelimiting/RateLimiter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index 73ad331d72..c170e6d8e7 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -36,7 +36,7 @@ trait RateLimiter[F[_], A] { /** * 次にリセットされるまで送っても良いリクエスト量を取得する作用を返す。 - * この作用が発火したあとでも、送って良いリクエスト量は変化しない。 + * この作用の発火が、送って良いリクエスト量に影響することはない。 * @return [[A]] によって記述される、次にリセットされるまで送っても良いリクエスト量を取得する作用 */ def peekAvailablePermission: F[A] From 85bb1d89726ecfb82efc688576d497da2f8a4aea Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 20:27:16 +0900 Subject: [PATCH 14/43] =?UTF-8?q?[fix]=20=E6=AD=A3=E3=81=97=E3=81=8F?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=80=A4=E3=81=A7=E6=B0=B8=E7=B6=9A=E5=8C=96?= =?UTF-8?q?=E3=81=8C=E3=81=AA=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=9F?= =?UTF-8?q?=E3=81=93=E3=81=A8=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterRepositoryDefinitions.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 6cb0462566..1469d2c2dc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -6,13 +6,12 @@ import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefi import com.github.unchama.datarepository.template.finalization.RepositoryFinalization import com.github.unchama.datarepository.template.initialization.SinglePhasedRepositoryInitialization import com.github.unchama.generic.ContextCoercion +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission -import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence -import scalikejdbc._ object RateLimiterRepositoryDefinitions { @@ -25,15 +24,17 @@ object RateLimiterRepositoryDefinitions { implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[G] ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildAmountPermission]] = { + val max = config.oneMinuteBuildExpLimit val rateLimiter = FixedWindowRateLimiter.in[F, G, BuildAmountPermission]( - BuildAmountPermission(config.oneMinuteBuildExpLimit), + BuildAmountPermission(max), 1.minute ) RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(BuildAmountPermission.orderedMonus.empty) .initialization - .extendPreparation { (_, _) => usedPermission => - rateLimiter.flatTap(rateLimiter => rateLimiter.requestPermission(usedPermission)) + .extendPreparation { (_, _) => availablePermission => + val consumedPermission = OrderedMonus[BuildAmountPermission].|-|(BuildAmountPermission(max), availablePermission) + rateLimiter.flatTap(rateLimiter => rateLimiter.requestPermission(consumedPermission).void) } } From 78c058e897803d81c7e63c232ff35ec8ca3bf112 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 20:28:35 +0900 Subject: [PATCH 15/43] =?UTF-8?q?[update]=20SQL=E3=81=AE=E3=82=AB=E3=83=A9?= =?UTF-8?q?=E3=83=A0=E3=82=92=E6=95=B0=E5=80=A4=E5=9E=8B=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=80=81=E3=81=8A=E3=82=88=E3=81=B3=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=82=92=E7=8B=AC=E7=AB=8B=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=83=86=E3=83=BC=E3=83=96=E3=83=AB=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V1.7.5__Create_player_rate_limit_table.sql | 9 ++++----- .../JdbcBuildAmountRateLimitPersistence.scala | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql index fb5df0056d..b00cd80a62 100644 --- a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql +++ b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql @@ -1,6 +1,5 @@ -CREATE TABLE IF NOT EXISTS player_rate_limit( - uuid CHAR(36) NOT NULL, - rate_limit_name CHAR(32) NOT NULL, - current_value CHAR(32) NOT NULL, - PRIMARY KEY(uuid, rate_limit_name) +CREATE TABLE IF NOT EXISTS build_count_rate_limit( + uuid CHAR(36) PRIMARY KEY NOT NULL, + -- 注: DECIMALは(全体の桁数, 小数点以下の桁数)というフォーマットである + available_permission DECIMAL(17, 5) UNSIGNED NOT NULL ); diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala index 3d2169ccbf..c7758c812b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -22,10 +22,10 @@ class JdbcBuildAmountRateLimitPersistence[ override def read(key: UUID): SyncContext[Option[BuildAmountPermission]] = F.delay { DB.localTx { implicit session => - sql"select build_count from rate_limit where uuid = ${key.toString} and rate_limit_name = 'build'" + sql"select available_permission from build_count_rate_limit where uuid = ${key.toString}" .stripMargin .map { rs => - val exp = BuildExpAmount(BigDecimal(rs.string("current_value"))) + val exp = BuildExpAmount(rs.bigDecimal("available_permission")) BuildAmountPermission(exp) } @@ -35,7 +35,7 @@ class JdbcBuildAmountRateLimitPersistence[ override def write(key: UUID, value: BuildAmountPermission): SyncContext[Unit] = F.delay { DB.localTx { implicit session => - sql"update playerdata set build_count = $value where uuid = ${key.toString} and rate_limit_name = 'build'" + sql"update build_count_rate_limit set available_permission = ${value.raw.toPlainString} where uuid = ${key.toString}" .stripMargin .update().apply() } From c6361c4849dc791ee461c112e7e39b97294e67d3 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 22:37:36 +0900 Subject: [PATCH 16/43] =?UTF-8?q?[update]=20=E4=BF=9D=E5=AD=98=E6=99=82?= =?UTF-8?q?=E5=88=BB=E3=82=82=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...V1.7.5__Create_player_rate_limit_table.sql | 3 +- .../unchama/seichiassist/SeichiAssist.scala | 2 + .../subsystems/buildcount/System.scala | 10 +-- .../IncrementBuildExpWhenBuiltByHand.scala | 7 +-- .../RateLimiterRepositoryDefinitions.scala | 63 +++++++++++++++---- .../domain/BuildAmountPermission.scala | 24 ------- .../domain/BuildAmountPersistenceRecord.scala | 16 +++++ .../BuildAmountRateLimitPersistence.scala | 4 +- .../JdbcBuildAmountRateLimitPersistence.scala | 18 +++--- 9 files changed, 90 insertions(+), 57 deletions(-) delete mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala diff --git a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql index b00cd80a62..5771dac93a 100644 --- a/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql +++ b/src/main/resources/db/migration/V1.7.5__Create_player_rate_limit_table.sql @@ -1,5 +1,6 @@ CREATE TABLE IF NOT EXISTS build_count_rate_limit( uuid CHAR(36) PRIMARY KEY NOT NULL, -- 注: DECIMALは(全体の桁数, 小数点以下の桁数)というフォーマットである - available_permission DECIMAL(17, 5) UNSIGNED NOT NULL + available_permission DECIMAL(17, 5) UNSIGNED NOT NULL, + updated_date DATETIME NOT NULL ); diff --git a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala index 35b63ae59f..220c16af4d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala @@ -208,6 +208,8 @@ class SeichiAssist extends JavaPlugin() { implicit val configuration: subsystems.buildcount.application.Configuration = seichiAssistConfig.buildCountConfiguration + implicit val syncIoClock: Clock[SyncIO] = Clock.create + subsystems.buildcount.System.wired[IO, SyncIO](loggerF).unsafeRunSync() } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala index a5e9b30255..2b3a223e51 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.buildcount -import cats.effect.{ConcurrentEffect, SyncEffect, Timer} +import cats.effect.{Clock, ConcurrentEffect, SyncEffect, Timer} import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls @@ -14,10 +14,11 @@ import com.github.unchama.seichiassist.subsystems.buildcount.application.applica import com.github.unchama.seichiassist.subsystems.buildcount.application.{BuildExpMultiplier, Configuration} import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.actions.ClassifyBukkitPlayerWorld import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.listeners.BuildExpIncrementer -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.buildcount.infrastructure.{JdbcBuildAmountDataPersistence, JdbcBuildAmountRateLimitPersistence} import com.github.unchama.util.logging.log4cats.PrefixedLogger +import io.chrisdavenport.cats.effect.time.JavaTime import io.chrisdavenport.log4cats.Logger import org.bukkit.entity.Player import org.bukkit.event.Listener @@ -36,7 +37,7 @@ object System { def wired[ F[_] : ConcurrentEffect : NonServerThreadContextShift : Timer, - G[_] : SyncEffect : ContextCoercion[*[_], F] + G[_] : SyncEffect : ContextCoercion[*[_], F] : Clock ](rootLogger: Logger[F]) (implicit configuration: Configuration): G[System[F, G]] = { import com.github.unchama.minecraft.bukkit.actions.SendBukkitMessage._ @@ -45,11 +46,12 @@ object System { implicit val persistence: JdbcBuildAmountDataPersistence[G] = new JdbcBuildAmountDataPersistence[G]() implicit val rateLimitPersistence: JdbcBuildAmountRateLimitPersistence[G, F] = new JdbcBuildAmountRateLimitPersistence[G, F]() implicit val logger: Logger[F] = PrefixedLogger[F]("BuildAssist-BuildAmount")(rootLogger) + implicit val javaTimeG: JavaTime[G] = JavaTime.fromClock for { rateLimiterRepositoryControls <- BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction[G, Player, RateLimiter[G, BuildAmountPermission]]( + RepositoryDefinition.Phased.SinglePhased.withoutTappingAction[G, Player, RateLimiter[G, BuildExpAmount]]( RateLimiterRepositoryDefinitions.initialization[F, G], RateLimiterRepositoryDefinitions.finalization[G, UUID] ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala index f4c497862d..ff2aea5b5d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala @@ -7,7 +7,6 @@ import com.github.unchama.generic.ratelimiting.RateLimiter import com.github.unchama.generic.{Diff, RefExtra} import com.github.unchama.minecraft.actions.SendMinecraftMessage import com.github.unchama.seichiassist.subsystems.buildcount.application.BuildExpMultiplier -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData @@ -36,7 +35,7 @@ object IncrementBuildExpWhenBuiltByHand { : ClassifyPlayerWorld[*[_], Player] : SendMinecraftMessage[*[_], Player], Player - ](rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildAmountPermission]], + ](rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildExpAmount]], dataRepository: KeyedDataRepository[Player, Ref[F, BuildAmountData]]) (implicit multiplier: BuildExpMultiplier): IncrementBuildExpWhenBuiltByHand[F, Player] = (player: Player, by: BuildExpAmount) => { @@ -52,8 +51,8 @@ object IncrementBuildExpWhenBuiltByHand { F.pure(BuildExpAmount(0)) ) amountToIncrement <- - rateLimiterRepository(player).requestPermission(BuildAmountPermission(amountToRequestIncrement)) - dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))(_.modifyExpAmount(_.add(amountToIncrement.raw))) + rateLimiterRepository(player).requestPermission(amountToRequestIncrement) + dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))(_.modifyExpAmount(_.add(amountToIncrement))) _ <- Diff .ofPairBy(dataPair)(_.levelCorrespondingToExp) .traverse(LevelUpNotifier[F, Player].notifyTo(player)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 1469d2c2dc..48bee133b3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -1,17 +1,23 @@ package com.github.unchama.seichiassist.subsystems.buildcount.application.application +import cats.Monad import cats.effect.{ConcurrentEffect, Sync, Timer} import cats.implicits._ import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefinition import com.github.unchama.datarepository.template.finalization.RepositoryFinalization import com.github.unchama.datarepository.template.initialization.SinglePhasedRepositoryInitialization import com.github.unchama.generic.ContextCoercion +import com.github.unchama.generic.ContextCoercion._ import com.github.unchama.generic.algebra.typeclasses.OrderedMonus import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence +import io.chrisdavenport.cats.effect.time.JavaTime + +import java.util.concurrent.TimeUnit object RateLimiterRepositoryDefinitions { @@ -19,27 +25,58 @@ object RateLimiterRepositoryDefinitions { def initialization[ F[_] : ConcurrentEffect : Timer, - G[_] : Sync : ContextCoercion[*[_], F] + G[_] : Sync: ContextCoercion[*[_], F] : JavaTime ]( implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[G] - ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildAmountPermission]] = { + ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = { val max = config.oneMinuteBuildExpLimit - val rateLimiter = FixedWindowRateLimiter.in[F, G, BuildAmountPermission]( - BuildAmountPermission(max), - 1.minute - ) + val span = 1.minute + val rateLimiter = BuildAmountPersistenceRecord.now(max).flatMap { bapr => + FixedWindowRateLimiter.in[F, G, BuildExpAmount]( + bapr.raw, + span + ) + } - RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(BuildAmountPermission.orderedMonus.empty) + import cats.effect.implicits._ + // QUESTION: Gを露出させたくないのでこうしたがこれはセマンティクス的に合っているのか? + val maxValueWithCurrentTime = BuildAmountPersistenceRecord.now[G](max).coerceTo[F].toIO.unsafeRunSync() + RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(maxValueWithCurrentTime) .initialization - .extendPreparation { (_, _) => availablePermission => - val consumedPermission = OrderedMonus[BuildAmountPermission].|-|(BuildAmountPermission(max), availablePermission) - rateLimiter.flatTap(rateLimiter => rateLimiter.requestPermission(consumedPermission).void) + .extendPreparation { (_, _) => loadedRecord => + // NOTE: これはファイナライゼーションされたときのレートリミッターと + // イニシャライゼーションで作成されるレートリミッターの 時刻起点が + // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 + // 趣旨を達成するためにとりあえずこの実装を使う。 + // 必要であれば再度編集して同期を取るようにすること。 + val duration = java.time.Duration.between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) + val scalaDuration = FiniteDuration.apply(duration.toNanos, TimeUnit.NANOSECONDS) + // 記録した日時が十分に新しければ更新 + val postInitialization: RateLimiter[G, BuildExpAmount] => G[Unit] = rateLimiter => { + if (scalaDuration >= span) { + // it's expired, do nothing. + Monad[G].pure(()) + } else { + // it's still active + val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.raw) + rateLimiter.requestPermission(consumedPermission).void + } + } + + rateLimiter.flatTap(postInitialization) } } - def finalization[F[_] : Sync, Player: HasUuid](implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[F]): RepositoryFinalization[F, Player, RateLimiter[F, BuildAmountPermission]] = + def finalization[ + F[_] : Sync : JavaTime, + Player: HasUuid + ](implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[F]): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => - rateLimiter.peekAvailablePermission.flatMap(a => persistence.write(HasUuid[Player].of(p), a)) + for { + currentRecord <- rateLimiter.peekAvailablePermission + persistenceRecord <- BuildAmountPersistenceRecord.now(currentRecord) + _ <- persistence.write(HasUuid[Player].of(p), persistenceRecord) + } yield () } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala deleted file mode 100644 index 00977d94d4..0000000000 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPermission.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.unchama.seichiassist.subsystems.buildcount.domain - -import com.github.unchama.generic.algebra.typeclasses.OrderedMonus -import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount - -case class BuildAmountPermission(raw: BuildExpAmount) - -object BuildAmountPermission { - implicit val orderedMonus: OrderedMonus[BuildAmountPermission] = new OrderedMonus[BuildAmountPermission] { - private val expIsOrderedMonus = OrderedMonus[BuildExpAmount] - /** - * 切り捨て減算。 - * `x: A, y: A` について、 `z: A` = `|-|(x, y)` は - * `x <= y + z` となるような最小の `z` として定義される。 - */ - override def |-|(x: BuildAmountPermission, y: BuildAmountPermission): BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.|-|(x.raw, y.raw)) - - override def empty: BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.empty) - - override def combine(x: BuildAmountPermission, y: BuildAmountPermission): BuildAmountPermission = BuildAmountPermission(expIsOrderedMonus.combine(x.raw, y.raw)) - - override def compare(x: BuildAmountPermission, y: BuildAmountPermission): Int = expIsOrderedMonus.compare(x.raw, y.raw) - } -} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala new file mode 100644 index 0000000000..b473e215d4 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala @@ -0,0 +1,16 @@ +package com.github.unchama.seichiassist.subsystems.buildcount.domain + +import cats.Functor +import cats.implicits._ +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount +import io.chrisdavenport.cats.effect.time.JavaTime + +import java.time.{LocalDateTime, ZoneId} + +case class BuildAmountPersistenceRecord(raw: BuildExpAmount, recordTime: LocalDateTime) + +object BuildAmountPersistenceRecord { + def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountPersistenceRecord] = { + JavaTime[F].getLocalDateTime(ZoneId.systemDefault()).map(ldt => BuildAmountPersistenceRecord(buildExpAmount, ldt)) + } +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala index 6e32643b84..4da4a84823 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala @@ -1,8 +1,8 @@ package com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata import com.github.unchama.generic.RefDict -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord import java.util.UUID -trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountPermission] +trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountPersistenceRecord] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala index c7758c812b..8ecedd3b1b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -1,14 +1,11 @@ package com.github.unchama.seichiassist.subsystems.buildcount.infrastructure import cats.effect.{ConcurrentEffect, Sync, Timer} -import cats.implicits._ import com.github.unchama.generic.ContextCoercion -import com.github.unchama.generic.algebra.typeclasses.OrderedMonus -import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPermission +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount -import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{BuildAmountData, BuildAmountDataPersistence, BuildAmountRateLimitPersistence} +import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence import scalikejdbc._ import java.util.UUID @@ -19,23 +16,26 @@ class JdbcBuildAmountRateLimitPersistence[ ](implicit F: Sync[SyncContext], config: Configuration) extends BuildAmountRateLimitPersistence[SyncContext] { - override def read(key: UUID): SyncContext[Option[BuildAmountPermission]] = + override def read(key: UUID): SyncContext[Option[BuildAmountPersistenceRecord]] = F.delay { DB.localTx { implicit session => sql"select available_permission from build_count_rate_limit where uuid = ${key.toString}" .stripMargin .map { rs => val exp = BuildExpAmount(rs.bigDecimal("available_permission")) + val ldt = rs.localDateTime("record_date") - BuildAmountPermission(exp) + BuildAmountPersistenceRecord(exp, ldt) } .first().apply() } } - override def write(key: UUID, value: BuildAmountPermission): SyncContext[Unit] = F.delay { + override def write(key: UUID, value: BuildAmountPersistenceRecord): SyncContext[Unit] = F.delay { DB.localTx { implicit session => - sql"update build_count_rate_limit set available_permission = ${value.raw.toPlainString} where uuid = ${key.toString}" + sql""" + |update build_count_rate_limit set available_permission = ${value.raw.toPlainString}, record_date = ${value.recordTime} + |where uuid = ${key.toString}""" .stripMargin .update().apply() } From 627b5bd3fb566ccd88c3198c87aec0d54cf27fea Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Sat, 19 Feb 2022 23:11:55 +0900 Subject: [PATCH 17/43] Update src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala Co-authored-by: Kory | Ryosuke Kondo <6561358+kory33@users.noreply.github.com> --- .../application/RateLimiterRepositoryDefinitions.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 48bee133b3..e65d79ce9c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -50,8 +50,12 @@ object RateLimiterRepositoryDefinitions { // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 // 趣旨を達成するためにとりあえずこの実装を使う。 // 必要であれば再度編集して同期を取るようにすること。 - val duration = java.time.Duration.between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) - val scalaDuration = FiniteDuration.apply(duration.toNanos, TimeUnit.NANOSECONDS) + val duration = FiniteDuration( + java.time.Duration + .between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) + .toNanos, + TimeUnit.NANOSECONDS + ) // 記録した日時が十分に新しければ更新 val postInitialization: RateLimiter[G, BuildExpAmount] => G[Unit] = rateLimiter => { if (scalaDuration >= span) { From 32900d2e2a6fbdadff37481f85735736190210c2 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 23:21:07 +0900 Subject: [PATCH 18/43] =?UTF-8?q?[update]=20now=E3=82=92extendPreparation?= =?UTF-8?q?=E3=81=A7=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterRepositoryDefinitions.scala | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index e65d79ce9c..cad0473a77 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -39,36 +39,37 @@ object RateLimiterRepositoryDefinitions { ) } - import cats.effect.implicits._ - // QUESTION: Gを露出させたくないのでこうしたがこれはセマンティクス的に合っているのか? - val maxValueWithCurrentTime = BuildAmountPersistenceRecord.now[G](max).coerceTo[F].toIO.unsafeRunSync() - RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(maxValueWithCurrentTime) + val maxValueWithCurrentTimeG = BuildAmountPersistenceRecord.now[G](max) + RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence)(maxValueWithCurrentTimeG) .initialization - .extendPreparation { (_, _) => loadedRecord => - // NOTE: これはファイナライゼーションされたときのレートリミッターと - // イニシャライゼーションで作成されるレートリミッターの 時刻起点が - // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 - // 趣旨を達成するためにとりあえずこの実装を使う。 - // 必要であれば再度編集して同期を取るようにすること。 - val duration = FiniteDuration( - java.time.Duration - .between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) - .toNanos, - TimeUnit.NANOSECONDS - ) - // 記録した日時が十分に新しければ更新 - val postInitialization: RateLimiter[G, BuildExpAmount] => G[Unit] = rateLimiter => { - if (scalaDuration >= span) { - // it's expired, do nothing. - Monad[G].pure(()) - } else { - // it's still active - val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.raw) - rateLimiter.requestPermission(consumedPermission).void - } - } - - rateLimiter.flatTap(postInitialization) + .extendPreparation { (_, _) => + loadedRecord => + // NOTE: これはファイナライゼーションされたときのレートリミッターと + // イニシャライゼーションで作成されるレートリミッターの 時刻起点が + // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 + // 趣旨を達成するためにとりあえずこの実装を使う。 + // 必要であれば再度編集して同期を取るようにすること。 + for { + maxValueWithCurrentTime <- maxValueWithCurrentTimeG + duration = FiniteDuration( + java.time.Duration + .between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) + .toNanos, + TimeUnit.NANOSECONDS + ) + // 記録した日時が十分に新しければ更新 + postInitialization = (rateLimiter: RateLimiter[G, BuildExpAmount]) => { + if (duration >= span) { + // it's expired, do nothing. + Monad[G].pure(()) + } else { + // it's still active + val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.raw) + rateLimiter.requestPermission(consumedPermission).void + } + } + rateLimiter <- rateLimiter.flatTap(postInitialization) + } yield rateLimiter } } From c87237ef8eb893b62f61aa6abd7325962bc9e854 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 23:23:08 +0900 Subject: [PATCH 19/43] [rename] BuildAmountPersistenceRecord => BuildAmountRateLimiterSnapshot --- .../application/RateLimiterRepositoryDefinitions.scala | 8 ++++---- ...eRecord.scala => BuildAmountRateLimiterSnapshot.scala} | 8 ++++---- .../playerdata/BuildAmountRateLimitPersistence.scala | 4 ++-- .../JdbcBuildAmountRateLimitPersistence.scala | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) rename src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/{BuildAmountPersistenceRecord.scala => BuildAmountRateLimiterSnapshot.scala} (65%) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index cad0473a77..b0e7f79671 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -12,7 +12,7 @@ import com.github.unchama.generic.algebra.typeclasses.OrderedMonus import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountRateLimiterSnapshot import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence import io.chrisdavenport.cats.effect.time.JavaTime @@ -32,14 +32,14 @@ object RateLimiterRepositoryDefinitions { ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = { val max = config.oneMinuteBuildExpLimit val span = 1.minute - val rateLimiter = BuildAmountPersistenceRecord.now(max).flatMap { bapr => + val rateLimiter = BuildAmountRateLimiterSnapshot.now(max).flatMap { bapr => FixedWindowRateLimiter.in[F, G, BuildExpAmount]( bapr.raw, span ) } - val maxValueWithCurrentTimeG = BuildAmountPersistenceRecord.now[G](max) + val maxValueWithCurrentTimeG = BuildAmountRateLimiterSnapshot.now[G](max) RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence)(maxValueWithCurrentTimeG) .initialization .extendPreparation { (_, _) => @@ -80,7 +80,7 @@ object RateLimiterRepositoryDefinitions { RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => for { currentRecord <- rateLimiter.peekAvailablePermission - persistenceRecord <- BuildAmountPersistenceRecord.now(currentRecord) + persistenceRecord <- BuildAmountRateLimiterSnapshot.now(currentRecord) _ <- persistence.write(HasUuid[Player].of(p), persistenceRecord) } yield () } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala similarity index 65% rename from src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala rename to src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala index b473e215d4..9c09ac1f5f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountPersistenceRecord.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala @@ -7,10 +7,10 @@ import io.chrisdavenport.cats.effect.time.JavaTime import java.time.{LocalDateTime, ZoneId} -case class BuildAmountPersistenceRecord(raw: BuildExpAmount, recordTime: LocalDateTime) +case class BuildAmountRateLimiterSnapshot(raw: BuildExpAmount, recordTime: LocalDateTime) -object BuildAmountPersistenceRecord { - def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountPersistenceRecord] = { - JavaTime[F].getLocalDateTime(ZoneId.systemDefault()).map(ldt => BuildAmountPersistenceRecord(buildExpAmount, ldt)) +object BuildAmountRateLimiterSnapshot { + def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountRateLimiterSnapshot] = { + JavaTime[F].getLocalDateTime(ZoneId.systemDefault()).map(ldt => BuildAmountRateLimiterSnapshot(buildExpAmount, ldt)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala index 4da4a84823..87a2a1c57b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala @@ -1,8 +1,8 @@ package com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata import com.github.unchama.generic.RefDict -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountRateLimiterSnapshot import java.util.UUID -trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountPersistenceRecord] +trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountRateLimiterSnapshot] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala index 8ecedd3b1b..d33f594c6d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -3,7 +3,7 @@ package com.github.unchama.seichiassist.subsystems.buildcount.infrastructure import cats.effect.{ConcurrentEffect, Sync, Timer} import com.github.unchama.generic.ContextCoercion import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration -import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountPersistenceRecord +import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountRateLimiterSnapshot import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence import scalikejdbc._ @@ -16,7 +16,7 @@ class JdbcBuildAmountRateLimitPersistence[ ](implicit F: Sync[SyncContext], config: Configuration) extends BuildAmountRateLimitPersistence[SyncContext] { - override def read(key: UUID): SyncContext[Option[BuildAmountPersistenceRecord]] = + override def read(key: UUID): SyncContext[Option[BuildAmountRateLimiterSnapshot]] = F.delay { DB.localTx { implicit session => sql"select available_permission from build_count_rate_limit where uuid = ${key.toString}" @@ -25,13 +25,13 @@ class JdbcBuildAmountRateLimitPersistence[ val exp = BuildExpAmount(rs.bigDecimal("available_permission")) val ldt = rs.localDateTime("record_date") - BuildAmountPersistenceRecord(exp, ldt) + BuildAmountRateLimiterSnapshot(exp, ldt) } .first().apply() } } - override def write(key: UUID, value: BuildAmountPersistenceRecord): SyncContext[Unit] = F.delay { + override def write(key: UUID, value: BuildAmountRateLimiterSnapshot): SyncContext[Unit] = F.delay { DB.localTx { implicit session => sql""" |update build_count_rate_limit set available_permission = ${value.raw.toPlainString}, record_date = ${value.recordTime} From d9a637900d8c7d1ac306bc8783d31439fcd72413 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 23:31:22 +0900 Subject: [PATCH 20/43] =?UTF-8?q?[docs]=20BuildAmountRateLimiterSnapshot?= =?UTF-8?q?=E3=81=AE=E8=AA=AC=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/BuildAmountRateLimiterSnapshot.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala index 9c09ac1f5f..8d0184e5ba 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala @@ -7,10 +7,16 @@ import io.chrisdavenport.cats.effect.time.JavaTime import java.time.{LocalDateTime, ZoneId} +/** + * `RateLimiter[F, BuildExpAmount]`がどの程度のリクエストをタイムスライスの中で受け付けたかについて日時付きで保存するクラス + * @param raw タイムスライスの中で受け付けられた建築量 + * @param recordTime 取得した時間 + */ case class BuildAmountRateLimiterSnapshot(raw: BuildExpAmount, recordTime: LocalDateTime) object BuildAmountRateLimiterSnapshot { def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountRateLimiterSnapshot] = { - JavaTime[F].getLocalDateTime(ZoneId.systemDefault()).map(ldt => BuildAmountRateLimiterSnapshot(buildExpAmount, ldt)) + JavaTime[F].getLocalDateTime(ZoneId.systemDefault()) + .map(ldt => BuildAmountRateLimiterSnapshot(buildExpAmount, ldt)) } } From 6e8adb96dc2b5895a12c89ea59eaf0d522347a98 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sat, 19 Feb 2022 23:32:12 +0900 Subject: [PATCH 21/43] [rename] BuildAmountRateLimiterSnapshot#raw => #amount --- .../application/RateLimiterRepositoryDefinitions.scala | 4 ++-- .../buildcount/domain/BuildAmountRateLimiterSnapshot.scala | 4 ++-- .../infrastructure/JdbcBuildAmountRateLimitPersistence.scala | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index b0e7f79671..a49a7c6f11 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -34,7 +34,7 @@ object RateLimiterRepositoryDefinitions { val span = 1.minute val rateLimiter = BuildAmountRateLimiterSnapshot.now(max).flatMap { bapr => FixedWindowRateLimiter.in[F, G, BuildExpAmount]( - bapr.raw, + bapr.amount, span ) } @@ -64,7 +64,7 @@ object RateLimiterRepositoryDefinitions { Monad[G].pure(()) } else { // it's still active - val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.raw) + val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.amount) rateLimiter.requestPermission(consumedPermission).void } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala index 8d0184e5ba..0368a1cca9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala @@ -9,10 +9,10 @@ import java.time.{LocalDateTime, ZoneId} /** * `RateLimiter[F, BuildExpAmount]`がどの程度のリクエストをタイムスライスの中で受け付けたかについて日時付きで保存するクラス - * @param raw タイムスライスの中で受け付けられた建築量 + * @param amount タイムスライスの中で受け付けられた建築量 * @param recordTime 取得した時間 */ -case class BuildAmountRateLimiterSnapshot(raw: BuildExpAmount, recordTime: LocalDateTime) +case class BuildAmountRateLimiterSnapshot(amount: BuildExpAmount, recordTime: LocalDateTime) object BuildAmountRateLimiterSnapshot { def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountRateLimiterSnapshot] = { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala index d33f594c6d..9f716094b4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -34,7 +34,7 @@ class JdbcBuildAmountRateLimitPersistence[ override def write(key: UUID, value: BuildAmountRateLimiterSnapshot): SyncContext[Unit] = F.delay { DB.localTx { implicit session => sql""" - |update build_count_rate_limit set available_permission = ${value.raw.toPlainString}, record_date = ${value.recordTime} + |update build_count_rate_limit set available_permission = ${value.amount.toPlainString}, record_date = ${value.recordTime} |where uuid = ${key.toString}""" .stripMargin .update().apply() From 46c89485259a98cb8e8a1068937702bef26fba37 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 16:35:33 +0900 Subject: [PATCH 22/43] =?UTF-8?q?[docs]=20=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E8=AA=AC=E6=98=8E=E3=81=8C=E6=AD=A3=E7=A2=BA=E3=81=A7?= =?UTF-8?q?=E3=81=AF=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=A8=E3=81=93?= =?UTF-8?q?=E3=82=8D=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildcount/domain/BuildAmountRateLimiterSnapshot.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala index 0368a1cca9..3d68683f75 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala @@ -8,8 +8,8 @@ import io.chrisdavenport.cats.effect.time.JavaTime import java.time.{LocalDateTime, ZoneId} /** - * `RateLimiter[F, BuildExpAmount]`がどの程度のリクエストをタイムスライスの中で受け付けたかについて日時付きで保存するクラス - * @param amount タイムスライスの中で受け付けられた建築量 + * `RateLimiter[F, BuildExpAmount]`によって保持された残量について日時付きで保存するクラス + * @param amount そのタイムスライスにおけるリクエスト量の上限 * @param recordTime 取得した時間 */ case class BuildAmountRateLimiterSnapshot(amount: BuildExpAmount, recordTime: LocalDateTime) From 6807192b5118a6629924aa49bd1d53fa5766cf77 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 16:37:12 +0900 Subject: [PATCH 23/43] =?UTF-8?q?[update]=20RateLimiter=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90=E3=81=99=E3=82=8B=E6=99=82=E3=80=81BARL=E3=82=92?= =?UTF-8?q?=E7=B5=8C=E7=94=B1=E3=81=9B=E3=81=9A=E3=81=AB=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=80=A4=E3=82=92=E6=B8=A1=E3=81=99=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/RateLimiterRepositoryDefinitions.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index a49a7c6f11..60fc344849 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -32,12 +32,7 @@ object RateLimiterRepositoryDefinitions { ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = { val max = config.oneMinuteBuildExpLimit val span = 1.minute - val rateLimiter = BuildAmountRateLimiterSnapshot.now(max).flatMap { bapr => - FixedWindowRateLimiter.in[F, G, BuildExpAmount]( - bapr.amount, - span - ) - } + val rateLimiter = FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) val maxValueWithCurrentTimeG = BuildAmountRateLimiterSnapshot.now[G](max) RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence)(maxValueWithCurrentTimeG) From 3022f666fee6ac2e9ccb7f967bfc0f8830244d6c Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 17:07:53 +0900 Subject: [PATCH 24/43] =?UTF-8?q?[fix]=20=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E6=99=82=E3=81=AB=E3=82=B9=E3=83=8A=E3=83=83=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=83=E3=83=88=E3=81=8C=E5=AD=98=E5=9C=A8=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=81=8B=E3=82=82=E3=81=97=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6?= =?UTF-8?q?=E6=AD=A3=E3=81=97=E3=81=8F=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RefDictBackedRepositoryDefinition.scala | 17 +++++++ .../RateLimiterRepositoryDefinitions.scala | 51 ++++++++++--------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala index 3cf688e81b..6c4d22036e 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala @@ -30,6 +30,23 @@ object RefDictBackedRepositoryDefinition { RepositoryDefinition.Phased.SinglePhased.withoutTappingAction(initialization, finalization) } + def usingUuidRefDictWithoutDefault[ + F[_] : Monad, Player, R + ](refDict: RefDict[F, UUID, R]): RepositoryDefinition.Phased.SinglePhased[F, Player, Option[R]] = { + val initialization: SinglePhasedRepositoryInitialization[F, Option[R]] = + (uuid, _) => refDict + .read(uuid) + .map(PrefetchResult.Success.apply) + + val finalization: RepositoryFinalization[F, UUID, Option[R]] = + RepositoryFinalization.withoutAnyFinalization( + (uuid, optR) => + optR.fold(Monad[F].pure(()))(r => refDict.write(uuid, r)) + ) + + RepositoryDefinition.Phased.SinglePhased.withoutTappingAction(initialization, finalization) + } + def usingUuidRefDict[F[_] : Monad, Player, R](refDict: RefDict[F, UUID, R]) (defaultValue: R): RepositoryDefinition.Phased.SinglePhased[F, Player, R] = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 60fc344849..6660f29b88 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -17,6 +17,7 @@ import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.Bui import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence import io.chrisdavenport.cats.effect.time.JavaTime +import java.time.ZoneId import java.util.concurrent.TimeUnit object RateLimiterRepositoryDefinitions { @@ -35,34 +36,36 @@ object RateLimiterRepositoryDefinitions { val rateLimiter = FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) val maxValueWithCurrentTimeG = BuildAmountRateLimiterSnapshot.now[G](max) - RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence)(maxValueWithCurrentTimeG) + RefDictBackedRepositoryDefinition + .usingUuidRefDictWithoutDefault(persistence) .initialization .extendPreparation { (_, _) => - loadedRecord => - // NOTE: これはファイナライゼーションされたときのレートリミッターと - // イニシャライゼーションで作成されるレートリミッターの 時刻起点が - // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 - // 趣旨を達成するためにとりあえずこの実装を使う。 - // 必要であれば再度編集して同期を取るようにすること。 + loadedRecordOpt => for { - maxValueWithCurrentTime <- maxValueWithCurrentTimeG - duration = FiniteDuration( - java.time.Duration - .between(loadedRecord.recordTime, maxValueWithCurrentTime.recordTime) - .toNanos, - TimeUnit.NANOSECONDS - ) - // 記録した日時が十分に新しければ更新 - postInitialization = (rateLimiter: RateLimiter[G, BuildExpAmount]) => { - if (duration >= span) { - // it's expired, do nothing. - Monad[G].pure(()) - } else { - // it's still active - val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.amount) - rateLimiter.requestPermission(consumedPermission).void + currentLocalTime <- JavaTime[G].getLocalDateTime(ZoneId.systemDefault()) + // NOTE: これはファイナライゼーションされたときのレートリミッターと + // イニシャライゼーションで作成されるレートリミッターの 時刻起点が + // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 + // 趣旨を達成するためにとりあえずこの実装を使う。 + // 必要であれば再度編集して同期を取るようにすること。 + postInitialization = (rateLimiter: RateLimiter[G, BuildExpAmount]) => + loadedRecordOpt.fold(Monad[G].pure(())) { loadedRecord => + val duration = FiniteDuration( + java.time.Duration + .between(loadedRecord.recordTime, currentLocalTime) + .toNanos, + TimeUnit.NANOSECONDS + ) + + if (duration >= span) { + // it's expired, do nothing. + Monad[G].pure(()) + } else { + // it's still active + val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.amount) + rateLimiter.requestPermission(consumedPermission).void + } } - } rateLimiter <- rateLimiter.flatTap(postInitialization) } yield rateLimiter } From 3eb8c6594586c795e2665d6b050c5926c112ed65 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 17:09:05 +0900 Subject: [PATCH 25/43] =?UTF-8?q?[docs]=20=E3=82=88=E3=82=8A=E6=AD=A3?= =?UTF-8?q?=E7=A2=BA=E3=81=AA=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/RateLimiterRepositoryDefinitions.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 6660f29b88..9032bb666d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -44,9 +44,9 @@ object RateLimiterRepositoryDefinitions { for { currentLocalTime <- JavaTime[G].getLocalDateTime(ZoneId.systemDefault()) // NOTE: これはファイナライゼーションされたときのレートリミッターと - // イニシャライゼーションで作成されるレートリミッターの 時刻起点が - // スパンの倍数になっているとは限らないので多少の誤差を発生させるが、 - // 趣旨を達成するためにとりあえずこの実装を使う。 + // イニシャライゼーションで作成されるレートリミッターが起動した時刻の差が + // 規定時間の整数倍になっているとは限らないので多少の誤差を発生させることがある。 + // しかし、とりあえず趣旨を達成するためにこの実装を使う。 // 必要であれば再度編集して同期を取るようにすること。 postInitialization = (rateLimiter: RateLimiter[G, BuildExpAmount]) => loadedRecordOpt.fold(Monad[G].pure(())) { loadedRecord => From 51734f21c68a14729c25e04c2f4b540f4013fc2a Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 17:16:10 +0900 Subject: [PATCH 26/43] =?UTF-8?q?[test]=20=E3=82=88=E3=82=8A=E4=BB=95?= =?UTF-8?q?=E6=A7=98=E3=82=92=E5=8F=8D=E6=98=A0=E3=81=97=E3=81=9F=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterPermissionPeekSpec.scala | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index 2134692939..a31e583700 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -47,14 +47,32 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec } "RateLimiter" should { - "not modify available permission when peaked" in { + "not decrease permits after peek" in { + val maxPermits: Natural = 100 + val period = 10.seconds val program = for { - rateLimiter <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](100, 10.seconds).coerceTo[Task] - peek1 <- rateLimiter.peekAvailablePermission.coerceTo[Task] - peek2 <- rateLimiter.peekAvailablePermission.coerceTo[Task] + rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] + peek1 <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + _ <- monixTimer.sleep(5.seconds) + peek2 <- rateLimiterA.peekAvailablePermission.coerceTo[Task] } yield peek1 == peek2 assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) } + + "keep equality of permits with another RateLimiter which has not been peeked" in { + val maxPermits: Natural = 100 + val period = 10.seconds + val program = for { + rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] + rateLimiterB <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] + _ <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + _ <- monixTimer.sleep(5.seconds) + peekA <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + peekB <- rateLimiterB.peekAvailablePermission.coerceTo[Task] + } yield peekA == peekB + + assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) + } } } From 0e8c0b502f4084f26b07e1324a2c786878b05fa5 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 17:16:37 +0900 Subject: [PATCH 27/43] =?UTF-8?q?[rename]=20=E3=83=A1=E3=82=BD=E3=83=83?= =?UTF-8?q?=E3=83=89=E5=90=8D=E3=82=92=E8=A4=87=E6=95=B0=E5=BD=A2=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unchama/generic/ratelimiting/RateLimiter.scala | 4 ++-- .../application/RateLimiterRepositoryDefinitions.scala | 2 +- .../ratelimiting/RateLimiterPermissionPeekSpec.scala | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index c170e6d8e7..22bbf64cfd 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -39,7 +39,7 @@ trait RateLimiter[F[_], A] { * この作用の発火が、送って良いリクエスト量に影響することはない。 * @return [[A]] によって記述される、次にリセットされるまで送っても良いリクエスト量を取得する作用 */ - def peekAvailablePermission: F[A] + def peekAvailablePermissions: F[A] } object RateLimiter { @@ -60,7 +60,7 @@ object RateLimiter { (newCount, newCount |-| count) } - override def peekAvailablePermission: F[A] = countRef.get + override def peekAvailablePermissions: F[A] = countRef.get } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 9032bb666d..33c2ccdeef 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -77,7 +77,7 @@ object RateLimiterRepositoryDefinitions { ](implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[F]): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => for { - currentRecord <- rateLimiter.peekAvailablePermission + currentRecord <- rateLimiter.peekAvailablePermissions persistenceRecord <- BuildAmountRateLimiterSnapshot.now(currentRecord) _ <- persistence.write(HasUuid[Player].of(p), persistenceRecord) } yield () diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index a31e583700..7ae05575a9 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -52,9 +52,9 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val period = 10.seconds val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] - peek1 <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + peek1 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] _ <- monixTimer.sleep(5.seconds) - peek2 <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + peek2 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] } yield peek1 == peek2 assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) @@ -66,10 +66,10 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] rateLimiterB <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] - _ <- rateLimiterA.peekAvailablePermission.coerceTo[Task] + _ <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] _ <- monixTimer.sleep(5.seconds) - peekA <- rateLimiterA.peekAvailablePermission.coerceTo[Task] - peekB <- rateLimiterB.peekAvailablePermission.coerceTo[Task] + peekA <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] + peekB <- rateLimiterB.peekAvailablePermissions.coerceTo[Task] } yield peekA == peekB assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) From 708cea2036df1c74325d07b2cbe75a563c9b7bff Mon Sep 17 00:00:00 2001 From: Lucky Date: Sun, 20 Feb 2022 19:47:09 +0900 Subject: [PATCH 28/43] fix: remove unnecessary function --- .../com/github/unchama/seichiassist/util/Util.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala index 3253ae7b7e..01c42be490 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala @@ -568,15 +568,4 @@ object Util { case object WEST extends Direction } - - /** - * 死亡したエンティティの死因が棘の鎧かどうか - */ - def isEntityKilledByThornsEnchant(entity: LivingEntity): Boolean = { - if (entity == null) return false - val event = entity.getLastDamageCause - if (event == null) return false - - event.getCause == DamageCause.THORNS - } } From 8f97a36a860dfd3fec9fe83a9526fd073ce6b44d Mon Sep 17 00:00:00 2001 From: Lucky3028 Date: Sun, 20 Feb 2022 19:50:59 +0900 Subject: [PATCH 29/43] Revert "fix: remove unnecessary function" This reverts commit 708cea2036df1c74325d07b2cbe75a563c9b7bff. --- .../com/github/unchama/seichiassist/util/Util.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala index 01c42be490..3253ae7b7e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala @@ -568,4 +568,15 @@ object Util { case object WEST extends Direction } + + /** + * 死亡したエンティティの死因が棘の鎧かどうか + */ + def isEntityKilledByThornsEnchant(entity: LivingEntity): Boolean = { + if (entity == null) return false + val event = entity.getLastDamageCause + if (event == null) return false + + event.getCause == DamageCause.THORNS + } } From e54b0274251e336aeb3867dad307fc0ddbe9ed37 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 20:38:35 +0900 Subject: [PATCH 30/43] =?UTF-8?q?[test]=20awaitForProgram=E3=81=AB?= =?UTF-8?q?=E6=B8=A1=E3=81=99=E7=A7=92=E6=95=B0=E3=82=92sleep=E3=81=A8?= =?UTF-8?q?=E5=90=8C=E7=A8=8B=E5=BA=A6=E3=81=AB=E9=95=B7=E3=81=8F=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterPermissionPeekSpec.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index 7ae05575a9..2a2eea8f9b 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -53,11 +53,14 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] peek1 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - _ <- monixTimer.sleep(5.seconds) + _ <- monixTimer.sleep(2.seconds) peek2 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - } yield peek1 == peek2 + } yield { + assert(peek1 == peek2) + () + } - assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) + awaitForProgram(runConcurrent(program)(100), 2.seconds) } "keep equality of permits with another RateLimiter which has not been peeked" in { @@ -70,9 +73,12 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec _ <- monixTimer.sleep(5.seconds) peekA <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] peekB <- rateLimiterB.peekAvailablePermissions.coerceTo[Task] - } yield peekA == peekB + } yield { + assert(peekA == peekB) + () + } - assert(awaitForProgram(runConcurrent(program)(100), 1.second).forall(a => a)) + awaitForProgram(runConcurrent(program)(100), 5.seconds) } } } From 62266cae80ce6db6979c89f8bb3ecf43cf1da460 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 21:24:10 +0900 Subject: [PATCH 31/43] =?UTF-8?q?[test]=20=E5=86=97=E9=95=B7=E3=81=AAcoerc?= =?UTF-8?q?eTo=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ratelimiting/RateLimiterPermissionPeekSpec.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index 2a2eea8f9b..208391ec67 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -51,7 +51,7 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val maxPermits: Natural = 100 val period = 10.seconds val program = for { - rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] + rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) peek1 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] _ <- monixTimer.sleep(2.seconds) peek2 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] @@ -67,8 +67,8 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val maxPermits: Natural = 100 val period = 10.seconds val program = for { - rateLimiterA <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] - rateLimiterB <- FixedWindowRateLimiter.in[Task, SyncIO, Natural](maxPermits, period).coerceTo[Task] + rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) + rateLimiterB <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) _ <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] _ <- monixTimer.sleep(5.seconds) peekA <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] From 1e7cd98b479f021d1e89c69718a41e7026bc97fd Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Sun, 20 Feb 2022 21:54:29 +0900 Subject: [PATCH 32/43] =?UTF-8?q?[test]=20=E6=9C=9F=E9=99=90=E3=82=92?= =?UTF-8?q?=E9=81=A9=E5=88=87=E3=81=AB=E3=82=BB=E3=83=83=E3=83=88=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterPermissionPeekSpec.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index 208391ec67..288c631288 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -15,6 +15,7 @@ import monix.eval.Task import monix.execution.ExecutionModel import monix.execution.schedulers.TestScheduler import org.scalatest.matchers.should.Matchers +import org.scalatest.time.Span import org.scalatest.time.SpanSugar.convertIntToGrainOfTime import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks @@ -49,28 +50,33 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec "RateLimiter" should { "not decrease permits after peek" in { val maxPermits: Natural = 100 - val period = 10.seconds + val sleepPeriod = 2.seconds + // any2stringadd!!! :rage: + val period: Span = sleepPeriod plus 5.seconds + val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) peek1 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - _ <- monixTimer.sleep(2.seconds) + _ <- monixTimer.sleep(sleepPeriod) peek2 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] } yield { assert(peek1 == peek2) () } - awaitForProgram(runConcurrent(program)(100), 2.seconds) + awaitForProgram(runConcurrent(program)(100), sleepPeriod) } "keep equality of permits with another RateLimiter which has not been peeked" in { val maxPermits: Natural = 100 - val period = 10.seconds + val sleepPeriod = 5.seconds + // any2stringadd!!! :rage: + val period = sleepPeriod plus 5.seconds val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) rateLimiterB <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) _ <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - _ <- monixTimer.sleep(5.seconds) + _ <- monixTimer.sleep(sleepPeriod) peekA <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] peekB <- rateLimiterB.peekAvailablePermissions.coerceTo[Task] } yield { @@ -78,7 +84,7 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec () } - awaitForProgram(runConcurrent(program)(100), 5.seconds) + awaitForProgram(runConcurrent(program)(100), sleepPeriod) } } } From 755e7fcf2a06a1cd94615c493a8b96cf26320f6b Mon Sep 17 00:00:00 2001 From: Tea <72652320+LargoGreenTea@users.noreply.github.com> Date: Mon, 21 Feb 2022 19:49:27 +0900 Subject: [PATCH 33/43] [add] url to monocraft --- .../unchama/seichiassist/listener/PlayerInventoryListener.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala index 5d2799636d..7fb7dac5db 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala @@ -473,6 +473,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } else if (itemstackcurrent.getType == Material.BOOK_AND_QUILL) { // 投票リンク表示 player.sendMessage(RED.toString + "" + UNDERLINE + "https://minecraft.jp/servers/54d3529e4ddda180780041a7/vote") + player.sendMessage(RED.toString + "" + UNDERLINE + "https://monocraft.net/servers/Cf3BffNIRMERDNbAfWQm") player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.closeInventory() } else if (isSkull && itemstackcurrent.getItemMeta.asInstanceOf[SkullMeta].getOwner == "MHF_ArrowLeft") { From 3da9620666ecda56474e6eb41b025d4e88c24095 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:58:04 +0900 Subject: [PATCH 34/43] Update src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala --- .../unchama/seichiassist/listener/PlayerInventoryListener.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala index 7fb7dac5db..37f8c8cb33 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala @@ -473,7 +473,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } else if (itemstackcurrent.getType == Material.BOOK_AND_QUILL) { // 投票リンク表示 player.sendMessage(RED.toString + "" + UNDERLINE + "https://minecraft.jp/servers/54d3529e4ddda180780041a7/vote") - player.sendMessage(RED.toString + "" + UNDERLINE + "https://monocraft.net/servers/Cf3BffNIRMERDNbAfWQm") + player.sendMessage(RED.toString + "" + UNDERLINE + "https://monocraft.net/servers/Cf3BffNIRMERDNbAfWQm") player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.closeInventory() } else if (isSkull && itemstackcurrent.getItemMeta.asInstanceOf[SkullMeta].getOwner == "MHF_ArrowLeft") { From beac55af12e58ddf376134c77148b60447762908 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Wed, 23 Feb 2022 23:12:14 +0900 Subject: [PATCH 35/43] =?UTF-8?q?[update]=20FixedWindowRateLimiter#in?= =?UTF-8?q?=E3=81=AB=E6=9C=80=E5=A4=A7=E5=80=A4=E3=81=A8=E7=95=B0=E3=81=AA?= =?UTF-8?q?=E3=82=8B=E5=88=9D=E6=9C=9F=E5=80=A4=E3=82=92=E6=8C=81=E3=81=9F?= =?UTF-8?q?=E3=81=9B=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../generic/ratelimiting/FixedWindowRateLimiter.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala index c49caff113..d0c19189b2 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala @@ -19,13 +19,16 @@ object FixedWindowRateLimiter { F[_] : ConcurrentEffect : Timer, G[_] : Sync : ContextCoercion[*[_], F], A: OrderedMonus - ](maxPermits: A, resetDuration: FiniteDuration): G[RateLimiter[G, A]] = { + ](maxPermits: A, resetDuration: FiniteDuration, firstPermits: Option[A] = None): G[RateLimiter[G, A]] = { val zero = OrderedMonus[A].empty for { countRef <- Ref.of[G, A](zero) rateLimiter = RateLimiter.fromCountRef(countRef)(maxPermits) + _ <- firstPermits.fold(Monad[G].pure(())) { first => + rateLimiter.requestPermission(first).void + } refreshPermits = countRef.set(zero).coerceTo[F] rateLimiterRef = new WeakReference(rateLimiter) From ff5f45f25dd5ea12d366c509ab80941148188aca Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Wed, 23 Feb 2022 23:25:39 +0900 Subject: [PATCH 36/43] update: apply suggestions from code review Co-authored-by: Kory | Ryosuke Kondo <6561358+kory33@users.noreply.github.com> --- .../generic/ratelimiting/RateLimiter.scala | 2 +- .../RateLimiterPermissionPeekSpec.scala | 25 +++---------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index 22bbf64cfd..cd220ae48d 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -60,7 +60,7 @@ object RateLimiter { (newCount, newCount |-| count) } - override def peekAvailablePermissions: F[A] = countRef.get + override def peekAvailablePermissions: F[A] = countRef.get.map(maxCount |-| _) } } diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala index 288c631288..91e476bd3b 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/RateLimiterPermissionPeekSpec.scala @@ -48,25 +48,6 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec } "RateLimiter" should { - "not decrease permits after peek" in { - val maxPermits: Natural = 100 - val sleepPeriod = 2.seconds - // any2stringadd!!! :rage: - val period: Span = sleepPeriod plus 5.seconds - - val program = for { - rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) - peek1 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - _ <- monixTimer.sleep(sleepPeriod) - peek2 <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - } yield { - assert(peek1 == peek2) - () - } - - awaitForProgram(runConcurrent(program)(100), sleepPeriod) - } - "keep equality of permits with another RateLimiter which has not been peeked" in { val maxPermits: Natural = 100 val sleepPeriod = 5.seconds @@ -75,10 +56,10 @@ class RateLimiterPermissionPeekSpec extends AnyWordSpec val program = for { rateLimiterA <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) rateLimiterB <- FixedWindowRateLimiter.in[Task, Task, Natural](maxPermits, period) - _ <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] + _ <- rateLimiterA.peekAvailablePermissions _ <- monixTimer.sleep(sleepPeriod) - peekA <- rateLimiterA.peekAvailablePermissions.coerceTo[Task] - peekB <- rateLimiterB.peekAvailablePermissions.coerceTo[Task] + peekA <- rateLimiterA.peekAvailablePermissions + peekB <- rateLimiterB.peekAvailablePermissions } yield { assert(peekA == peekB) () From dc414bf6a499ff4d254b90b34c46f85dfe06b527 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Wed, 23 Feb 2022 23:36:53 +0900 Subject: [PATCH 37/43] [fix] compile error --- .../com/github/unchama/generic/ratelimiting/RateLimiter.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index cd220ae48d..673ee08c59 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -1,5 +1,6 @@ package com.github.unchama.generic.ratelimiting +import cats.Functor import cats.effect.concurrent.Ref import com.github.unchama.generic.algebra.typeclasses.OrderedMonus @@ -50,7 +51,7 @@ object RateLimiter { /** * 送信したリクエスト数を保持する参照セルの情報を見る [[RateLimiter]] を作成する。 */ - def fromCountRef[F[_], A: OrderedMonus](countRef: Ref[F, A])(maxCount: A): RateLimiter[F, A] = + def fromCountRef[F[_]: Functor, A: OrderedMonus](countRef: Ref[F, A])(maxCount: A): RateLimiter[F, A] = new RateLimiter[F, A] { override protected val A: OrderedMonus[A] = implicitly From 3fa4f5750938581f953d4f5f583a4d8282c9105f Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Wed, 23 Feb 2022 23:37:26 +0900 Subject: [PATCH 38/43] Update src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala Co-authored-by: Kory | Ryosuke Kondo <6561358+kory33@users.noreply.github.com> --- .../generic/ratelimiting/FixedWindowRateLimiter.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala index d0c19189b2..caee4dce9e 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala @@ -21,14 +21,12 @@ object FixedWindowRateLimiter { A: OrderedMonus ](maxPermits: A, resetDuration: FiniteDuration, firstPermits: Option[A] = None): G[RateLimiter[G, A]] = { val zero = OrderedMonus[A].empty + val initialCount = maxPermits |-| firstPermits.getOrElse(maxPermits) for { - countRef <- Ref.of[G, A](zero) + countRef <- Ref.of[G, A](initialCount) rateLimiter = RateLimiter.fromCountRef(countRef)(maxPermits) - _ <- firstPermits.fold(Monad[G].pure(())) { first => - rateLimiter.requestPermission(first).void - } refreshPermits = countRef.set(zero).coerceTo[F] rateLimiterRef = new WeakReference(rateLimiter) From 41746d30867fe5e4c80487262c6519676b7b798b Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Wed, 23 Feb 2022 23:40:30 +0900 Subject: [PATCH 39/43] [fix] compile error --- .../unchama/generic/ratelimiting/FixedWindowRateLimiter.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala index caee4dce9e..1fc35c6ae0 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala @@ -5,6 +5,7 @@ import cats.effect.concurrent.Ref import cats.effect.{Concurrent, ConcurrentEffect, IO, Sync, Timer} import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.algebra.typeclasses.OrderedMonus +import com.github.unchama.generic.algebra.typeclasses.OrderedMonus._ import scala.concurrent.duration.FiniteDuration import scala.ref.WeakReference From 9635f22526ca4be5eb0fe4a798a9a3dd8882e4ca Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Wed, 23 Feb 2022 23:58:36 +0900 Subject: [PATCH 40/43] =?UTF-8?q?[update]=20in=E3=81=AB=E5=88=9D=E6=9C=9F?= =?UTF-8?q?=E5=80=A4=E3=82=92=E4=B8=8E=E3=81=88=E3=81=A6flatTap=E3=82=92?= =?UTF-8?q?=E6=B6=88=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RateLimiterRepositoryDefinitions.scala | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 33c2ccdeef..b85fe62c0e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -40,7 +40,7 @@ object RateLimiterRepositoryDefinitions { .usingUuidRefDictWithoutDefault(persistence) .initialization .extendPreparation { (_, _) => - loadedRecordOpt => + loadedRecordOpt => { for { currentLocalTime <- JavaTime[G].getLocalDateTime(ZoneId.systemDefault()) // NOTE: これはファイナライゼーションされたときのレートリミッターと @@ -48,26 +48,24 @@ object RateLimiterRepositoryDefinitions { // 規定時間の整数倍になっているとは限らないので多少の誤差を発生させることがある。 // しかし、とりあえず趣旨を達成するためにこの実装を使う。 // 必要であれば再度編集して同期を取るようにすること。 - postInitialization = (rateLimiter: RateLimiter[G, BuildExpAmount]) => - loadedRecordOpt.fold(Monad[G].pure(())) { loadedRecord => - val duration = FiniteDuration( - java.time.Duration - .between(loadedRecord.recordTime, currentLocalTime) - .toNanos, - TimeUnit.NANOSECONDS - ) - - if (duration >= span) { - // it's expired, do nothing. - Monad[G].pure(()) - } else { - // it's still active - val consumedPermission = OrderedMonus[BuildExpAmount].|-|(max, loadedRecord.amount) - rateLimiter.requestPermission(consumedPermission).void - } + rateLimiter <- loadedRecordOpt.fold( + FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) + ) { loadedRecord => + val duration = FiniteDuration( + java.time.Duration + .between(loadedRecord.recordTime, currentLocalTime) + .toNanos, + TimeUnit.NANOSECONDS + ) + if (duration >= span) { + // expired + FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) + } else { + FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span, Some(loadedRecord.amount)) } - rateLimiter <- rateLimiter.flatTap(postInitialization) + } } yield rateLimiter + } } } From f97a0d9d0ed70b1c6fa856c8a56a3c490405fcb3 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Thu, 24 Feb 2022 00:07:43 +0900 Subject: [PATCH 41/43] Update src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala Co-authored-by: Kory | Ryosuke Kondo <6561358+kory33@users.noreply.github.com> --- .../application/RateLimiterRepositoryDefinitions.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index b85fe62c0e..d26e340470 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -48,9 +48,7 @@ object RateLimiterRepositoryDefinitions { // 規定時間の整数倍になっているとは限らないので多少の誤差を発生させることがある。 // しかし、とりあえず趣旨を達成するためにこの実装を使う。 // 必要であれば再度編集して同期を取るようにすること。 - rateLimiter <- loadedRecordOpt.fold( - FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) - ) { loadedRecord => + initialPermitCount = loadedRecordOpt.fold(max) { loadedRecord => val duration = FiniteDuration( java.time.Duration .between(loadedRecord.recordTime, currentLocalTime) @@ -59,11 +57,12 @@ object RateLimiterRepositoryDefinitions { ) if (duration >= span) { // expired - FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span) + max } else { - FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span, Some(loadedRecord.amount)) + loadedRecord.amount } } + rateLimiter <- FixedWindowRateLimiter.in[F, G, BuildExpAmount](max, span, Some(initialPermitCount)) } yield rateLimiter } } From 9396e556eb2150940db0fc1ebe01e4e1d65c7f69 Mon Sep 17 00:00:00 2001 From: Kisaragi Date: Thu, 24 Feb 2022 00:08:40 +0900 Subject: [PATCH 42/43] [docs] move comment to more appropriate place --- .../application/RateLimiterRepositoryDefinitions.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index d26e340470..1001912fbb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -43,11 +43,6 @@ object RateLimiterRepositoryDefinitions { loadedRecordOpt => { for { currentLocalTime <- JavaTime[G].getLocalDateTime(ZoneId.systemDefault()) - // NOTE: これはファイナライゼーションされたときのレートリミッターと - // イニシャライゼーションで作成されるレートリミッターが起動した時刻の差が - // 規定時間の整数倍になっているとは限らないので多少の誤差を発生させることがある。 - // しかし、とりあえず趣旨を達成するためにこの実装を使う。 - // 必要であれば再度編集して同期を取るようにすること。 initialPermitCount = loadedRecordOpt.fold(max) { loadedRecord => val duration = FiniteDuration( java.time.Duration @@ -55,6 +50,11 @@ object RateLimiterRepositoryDefinitions { .toNanos, TimeUnit.NANOSECONDS ) + // NOTE: これはファイナライゼーションされたときのレートリミッターと + // イニシャライゼーションで作成されるレートリミッターが起動した時刻の差が + // 規定時間の整数倍になっているとは限らないので多少の誤差を発生させることがある。 + // しかし、とりあえず趣旨を達成するためにこの実装を使う。 + // 必要であれば再度編集して同期を取るようにすること。 if (duration >= span) { // expired max From d4fe11923968051ded55a8d0754454fb5bf209bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Feb 2022 22:06:49 +0000 Subject: [PATCH 43/43] [bump] 31 -> 32 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 0618acc1f7..9a69519b19 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 := "31" +ThisBuild / version := "32" ThisBuild / organization := "click.seichi" ThisBuild / description := "ギガンティック☆整地鯖の独自要素を司るプラグイン"