Skip to content

Commit

Permalink
Implement new difficulty settings system for normal and league games
Browse files Browse the repository at this point in the history
  • Loading branch information
timoschwarzer committed Nov 25, 2024
1 parent 50fb73c commit cf8b009
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 29 deletions.
25 changes: 22 additions & 3 deletions src/main/kotlin/wotw/io/messages/protobuf/Messages.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import kotlin.math.pow

enum class GameDifficulty {
Easy,
Normal,
Hard,
}

@Serializable
data class UserInfo(
@ProtoNumber(1) val id: String,
Expand Down Expand Up @@ -156,8 +162,21 @@ data class SetSaveGuidRestrictionsMessage(
)

@Serializable
data class SetEnforceSeedDifficultyMessage(
@ProtoNumber(1) val shouldEnforceSeedDifficulty: Boolean,
data class GameDifficultySettingsOverrides(
@ProtoNumber(1) val easy: Setting,
@ProtoNumber(2) val normal: Setting,
@ProtoNumber(3) val hard: Setting,
) {
enum class Setting {
Allow,
Warn,
Deny,
}
}

@Serializable
data class SetGameDifficultySettingsOverridesMessage(
@ProtoNumber(1) @Required val overrides: GameDifficultySettingsOverrides? = null,
)

@Serializable
Expand All @@ -166,7 +185,7 @@ data class InitGameSyncMessage(
@ProtoNumber(2) val blockStartingNewGame: Boolean = false,
@ProtoNumber(3) val saveGuidRestrictions: SetSaveGuidRestrictionsMessage,
@ProtoNumber(4) val preventCheats: Boolean = false,
@ProtoNumber(5) val enforceSeedDifficulty: SetEnforceSeedDifficultyMessage,
@ProtoNumber(5) val difficultySettingsOverrides: SetGameDifficultySettingsOverridesMessage,
)

@Serializable
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/wotw/io/messages/protobuf/Packet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ data class Packet(
29 to typeOf<SetSaveGuidRestrictionsMessage>(),
30 to typeOf<OverrideInGameTimeMessage>(),
31 to typeOf<ShowUINotificationMessage>(),
32 to typeOf<SetEnforceSeedDifficultyMessage>(),
32 to typeOf<SetGameDifficultySettingsOverridesMessage>(),

100 to typeOf<TrackerUpdate>(),
101 to typeOf<ResetTracker>(),
Expand Down
24 changes: 13 additions & 11 deletions src/main/kotlin/wotw/server/bingo/GoalDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ fun Random.nextTriangular(min: Double, max: Double, mode: Double): Double {
min + sqrt((1 - rand) * (max - min) * (max - mode))
}

data class GeneratorConfig(val random: Random, val difficulty: Difficulty = Difficulty.NORMAL, val repeats: Int = 0)
enum class Difficulty {
EASY, NORMAL, HARD
data class GeneratorConfig(val random: Random, val difficulty: BingoDifficulty = BingoDifficulty.Normal, val repeats: Int = 0)
enum class BingoDifficulty {
Easy,
Normal,
Hard,
}

fun aggr(aggr: AggregationExpression.Aggregation, group: NumberStateGeneratorGroup)= NumberStateGenerator {
Expand Down Expand Up @@ -115,7 +117,7 @@ fun uber(group: Int, state: Int) = NumberStateGenerator {
UberStateExpression(group, state)
}

fun difficulty(difficulties: Map<Difficulty, NumberStateGenerator>) = NumberStateGenerator { config ->
fun difficulty(difficulties: Map<BingoDifficulty, NumberStateGenerator>) = NumberStateGenerator { config ->
difficulties[config.difficulty]?.invoke(config)
}

Expand Down Expand Up @@ -215,16 +217,16 @@ fun group(
}
subsetGoal -> {
val maxAmount = when (difficulty) {
Difficulty.EASY -> 2
Difficulty.NORMAL -> 3
Difficulty.HARD -> 4
BingoDifficulty.Easy -> 2
BingoDifficulty.Normal -> 3
BingoDifficulty.Hard -> 4
}

val goalAmount = random.nextInt(1, maxAmount + 1)
val requiredAmount = when (difficulty) {
Difficulty.EASY -> 1
Difficulty.NORMAL -> if (random.nextDouble() > orChance) goalAmount else 1
Difficulty.HARD -> goalAmount
BingoDifficulty.Easy -> 1
BingoDifficulty.Normal -> if (random.nextDouble() > orChance) goalAmount else 1
BingoDifficulty.Hard -> goalAmount
}
val generatedGoals = mutableListOf<BingoGoal>()

Expand All @@ -246,4 +248,4 @@ fun group(
}

})
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/wotw/server/constants/Compatibility.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package wotw.server.constants

import io.github.z4kn4fein.semver.constraints.toConstraint

val SUPPORTED_CLIENT_VERSIONS = "^4.25.0".toConstraint()
val SUPPORTED_CLIENT_VERSIONS = "^4.26.0-beta.1".toConstraint()
4 changes: 2 additions & 2 deletions src/main/kotlin/wotw/server/database/EntityCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,15 @@ open class EntityCache<KEY : Any, VALUE : Any>(
}
}

@Serializable
data class WorldMembershipEnvironmentCacheEntry(
val worldMembershipId: Long,
val playerId: String,
val worldId: Long?,
val worldSeedId: Long?,
val universeWorldMembershipIds: Set<Long>,
val worldWorldMembershipIds: Set<Long>,
)

@Serializable
data class MultiverseMemberCacheEntry(
val multiverseId: Long,
val playerAndSpectatorPlayerIds: Set<String>,
Expand All @@ -107,6 +106,7 @@ class WorldMembershipEnvironmentCache : EntityCache<Long, WorldMembershipEnviron
worldMembershipId,
worldMembership?.user?.id?.value ?: "<invalid cache>",
worldMembership?.world?.id?.value,
worldMembership?.world?.seed?.id?.value,
worldMembership?.world?.universe?.memberships?.map { it.id.value }?.toSet() ?: emptySet(),
worldMembership?.world?.memberships?.map { it.id.value }?.toSet() ?: emptySet(),
)
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/wotw/server/database/model/LeagueSeason.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.jetbrains.exposed.sql.json.jsonb
import wotw.io.messages.UniversePreset
import wotw.io.messages.WorldPreset
import wotw.io.messages.json
import wotw.io.messages.protobuf.GameDifficulty
import wotw.server.game.handlers.GameHandlerType
import wotw.server.seedgen.SeedGeneratorService
import wotw.server.util.assertTransaction
Expand Down Expand Up @@ -121,6 +122,8 @@ object LeagueSeasons : LongIdTable("league_seasons") {
val announcementSent = bool("announcement_sent").default(false)

val minimumInGameTimeToAllowBreaks = float("minimum_in_game_time_to_allow_breaks").default(60f * 60f * 2f)

val gameDifficulty = enumeration("game_difficulty", GameDifficulty::class).default(GameDifficulty.Normal)
}

/**
Expand Down Expand Up @@ -178,6 +181,7 @@ class LeagueSeason(id: EntityID<Long>) : LongEntity(id) {
var backgroundImageUrl by LeagueSeasons.backgroundImageUrl
var announcementSent by LeagueSeasons.announcementSent
var minimumInGameTimeToAllowBreaks by LeagueSeasons.minimumInGameTimeToAllowBreaks
var gameDifficulty by LeagueSeasons.gameDifficulty

val nextContinuationAt: Instant get() = this.nextContinuationAtCache ?: this.updateNextContinuationAtTimestamp()

Expand Down
20 changes: 20 additions & 0 deletions src/main/kotlin/wotw/server/database/model/Seed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.jetbrains.exposed.sql.javatime.datetime
import org.jetbrains.exposed.sql.json.jsonb
import wotw.io.messages.UniversePreset
import wotw.io.messages.json
import wotw.io.messages.protobuf.GameDifficultySettingsOverrides

object Seeds : LongIdTable("seeds") {
// TODO: Make seedgen return the used UniverseSettings and save them here instead of the preset
Expand Down Expand Up @@ -46,4 +47,23 @@ class WorldSeed(id: EntityID<Long>): LongEntity(id){
var seed by Seed referencedOn WorldSeeds.seed
var worldIndex by WorldSeeds.worldIndex
var content by WorldSeeds.content

fun inferGameDifficultySettingsOverrides(): GameDifficultySettingsOverrides {
// TODO: Temporary.
// Replace this with something that doesn't rely on the seedgen config at some point

if (seed.seedgenConfig.worldSettings[worldIndex].hard) {
return GameDifficultySettingsOverrides(
GameDifficultySettingsOverrides.Setting.Deny,
GameDifficultySettingsOverrides.Setting.Deny,
GameDifficultySettingsOverrides.Setting.Allow,
)
}

return GameDifficultySettingsOverrides(
GameDifficultySettingsOverrides.Setting.Deny,
GameDifficultySettingsOverrides.Setting.Allow,
GameDifficultySettingsOverrides.Setting.Deny,
)
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/wotw/server/game/GameConnectionHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package wotw.server.game
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import wotw.io.messages.protobuf.InitGameSyncMessage
import wotw.io.messages.protobuf.SetEnforceSeedDifficultyMessage
import wotw.io.messages.protobuf.SetGameDifficultySettingsOverridesMessage
import wotw.io.messages.protobuf.SetSaveGuidRestrictionsMessage
import wotw.server.database.model.User
import wotw.server.database.model.WorldMembership
Expand Down Expand Up @@ -71,7 +71,7 @@ class GameConnectionHandler(
true,
),
handler.shouldPreventCheats(worldMembership),
SetEnforceSeedDifficultyMessage(handler.shouldEnforceSeedDifficulty())
SetGameDifficultySettingsOverridesMessage(handler.getDifficultySettingsOverrides(worldMembership.id.value))
)
)

Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/wotw/server/game/handlers/GameHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.serializer
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import wotw.io.messages.protoBuf
import wotw.io.messages.protobuf.GameDifficultySettingsOverrides
import wotw.io.messages.protobuf.MoodGuid
import wotw.io.messages.protobuf.SetBlockStartingNewGameMessage
import wotw.server.api.AggregationStrategyRegistry
Expand Down Expand Up @@ -185,5 +186,5 @@ abstract class GameHandler<CLIENT_INFO_TYPE : Any>(

open fun canDuplicateMultiverse(): Boolean = true

open fun shouldEnforceSeedDifficulty(): Boolean = false
open suspend fun getDifficultySettingsOverrides(worldMembershipId: WorldMembershipId): GameDifficultySettingsOverrides? = null
}
34 changes: 28 additions & 6 deletions src/main/kotlin/wotw/server/game/handlers/NormalGameHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,19 @@ class NormalGameHandler(multiverseId: Long, server: WotwBackendServer) : GameHan
"enableRaceMode" -> {
state.raceModeEnabled = true

server.multiverseMemberCache.getOrNull(multiverseId)?.worldMembershipIds?.let { worldMembershipIds ->
server.connections.toPlayers(
worldMembershipIds,
SetEnforceSeedDifficultyMessage(true),
)
newSuspendedTransaction {
val worlds = getMultiverse().worlds

worlds.forEach { world ->
world.seed?.let { worldSeed ->
server.connections.toPlayers(
world.memberships.map { it.id.value },
SetGameDifficultySettingsOverridesMessage(
worldSeed.inferGameDifficultySettingsOverrides()
),
)
}
}
}

notifyMultiverseOrClientInfoChanged()
Expand Down Expand Up @@ -629,5 +637,19 @@ class NormalGameHandler(multiverseId: Long, server: WotwBackendServer) : GameHan
}
}

override fun shouldEnforceSeedDifficulty(): Boolean = state.raceModeEnabled
override suspend fun getDifficultySettingsOverrides(worldMembershipId: WorldMembershipId): GameDifficultySettingsOverrides? {
if (!state.raceModeEnabled) {
return null
}

return newSuspendedTransaction {
server.worldMembershipEnvironmentCache.get(worldMembershipId).worldSeedId?.let { worldSeedId ->
WorldSeed.findById(worldSeedId)?.let { worldSeed ->
return@newSuspendedTransaction worldSeed.inferGameDifficultySettingsOverrides()
}
}

return@newSuspendedTransaction null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class LeagueGameHandler(multiverseId: Long, server: WotwBackendServer) :

private var seasonMinimumInGameTimeToAllowBreaksCache: Float? = null

private var gameDifficultyCache: GameDifficulty? = null

private suspend fun getGameDifficulty(): GameDifficulty = gameDifficultyCache ?: newSuspendedTransaction {
getLeagueGame().season.gameDifficulty
}

private suspend fun getSeasonMinimumInGameTimeToAllowBreaks(): Float {
return seasonMinimumInGameTimeToAllowBreaksCache ?: newSuspendedTransaction {
val season = getLeagueGame().season
Expand Down Expand Up @@ -269,9 +275,17 @@ class LeagueGameHandler(multiverseId: Long, server: WotwBackendServer) :
}
}

override fun shouldEnforceSeedDifficulty(): Boolean = true

fun getPlayerDisconnectedTime(worldMembership: WorldMembership): Float {
return state.playerDisconnectedTimes[worldMembership.id.value] ?: 0f
}

override suspend fun getDifficultySettingsOverrides(worldMembershipId: WorldMembershipId): GameDifficultySettingsOverrides {
val difficulty = getGameDifficulty()

return GameDifficultySettingsOverrides(
if (difficulty == GameDifficulty.Easy) GameDifficultySettingsOverrides.Setting.Allow else GameDifficultySettingsOverrides.Setting.Deny,
if (difficulty == GameDifficulty.Normal) GameDifficultySettingsOverrides.Setting.Allow else GameDifficultySettingsOverrides.Setting.Deny,
if (difficulty == GameDifficulty.Hard) GameDifficultySettingsOverrides.Setting.Allow else GameDifficultySettingsOverrides.Setting.Deny,
)
}
}

0 comments on commit cf8b009

Please sign in to comment.