├── .gitignore
├── .idea
└── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── LICENSE
├── README.md
├── build.gradle.kts
├── devtools
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── gg
│ └── tropic
│ └── practice
│ ├── PracticeDevTools.kt
│ ├── command
│ └── MapManageCommands.kt
│ ├── feature
│ └── CloudSyncFeature.kt
│ └── map
│ └── MapManageServices.kt
├── docs
├── infrastructure.md
└── setup.md
├── game
├── build.gradle.kts
└── src
│ └── main
│ ├── java
│ └── gg
│ │ └── tropic
│ │ └── practice
│ │ ├── command
│ │ └── ChunkDumpCommand.java
│ │ ├── games
│ │ └── ranked
│ │ │ └── PotPvPEloCalculator.java
│ │ └── utilities
│ │ └── PlayerRespawnUtilities.java
│ └── kotlin
│ └── gg
│ └── tropic
│ └── practice
│ ├── Extensions.kt
│ ├── PracticeGame.kt
│ ├── autoscale
│ └── ReplicationAutoScaleTask.kt
│ ├── command
│ ├── NoSpeedCommand.kt
│ └── ReplicationHealthCommand.kt
│ ├── cooldown
│ └── EnderPearlCooldown.kt
│ ├── expectation
│ └── ExpectationService.kt
│ ├── feature
│ ├── ApolloFeature.kt
│ ├── CloudSyncFeature.kt
│ └── GameReportFeature.kt
│ ├── games
│ ├── GameExt.kt
│ ├── GameImpl.kt
│ ├── GameService.kt
│ ├── event
│ │ └── GameStartEvent.kt
│ ├── loadout
│ │ ├── CustomLoadout.kt
│ │ ├── DefaultLoadout.kt
│ │ └── SelectedLoadout.kt
│ ├── ranked
│ │ ├── CalculationResult.kt
│ │ └── EloCalculator.kt
│ └── tasks
│ │ ├── GameStartTask.kt
│ │ └── GameStopTask.kt
│ ├── integration
│ └── StaffReportsDataProvider.kt
│ ├── map
│ ├── BuiltMapReplication.kt
│ ├── MapReplicationService.kt
│ └── ReadyMapTemplate.kt
│ ├── metrics
│ ├── GameServerCollection.kt
│ ├── MetricsService.kt
│ └── collectors
│ │ ├── PlayerDistributionCollector.kt
│ │ └── ReplicationCountCollector.kt
│ ├── resources
│ ├── DuelsNametagImpl.kt
│ ├── DuelsVisibilityImpl.kt
│ └── GameScoreboardAdapter.kt
│ └── services
│ └── SpectateRequestService.kt
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lobby
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── gg
│ └── tropic
│ └── practice
│ ├── PracticeLobby.kt
│ ├── category
│ ├── SettingExt.kt
│ └── visibility
│ │ └── SpawnPlayerVisibility.kt
│ ├── commands
│ ├── DuelCommands.kt
│ ├── DuelGamesCommand.kt
│ ├── DuelRequestsCommand.kt
│ ├── KitEditorCommand.kt
│ ├── LeaderboardsCommand.kt
│ ├── SpawnCommand.kt
│ ├── SpectateCommand.kt
│ ├── StatResetTokenCommand.kt
│ ├── StatisticsCommand.kt
│ ├── SwitchLobbyServerCommand.kt
│ ├── TogglePlayerVisibilityCommand.kt
│ ├── ToggleSpectatorsCommand.kt
│ ├── TournamentCommand.kt
│ └── admin
│ │ ├── LobbyAdminCommands.kt
│ │ └── SpawnHologramCommand.kt
│ ├── duel
│ └── DuelRequestUtilities.kt
│ ├── feature
│ └── CloudSyncFeature.kt
│ ├── hologram
│ ├── AbstractScrollingLeaderboard.kt
│ ├── ScrollingKitLeaderboardHologram.kt
│ ├── ScrollingLeaderboardService.kt
│ └── ScrollingTypeLeaderboardHologram.kt
│ ├── menu
│ ├── CasualQueueSelectSizeMenu.kt
│ ├── DuelGamesMenu.kt
│ ├── JoinQueueMenu.kt
│ ├── LeaderboardsMenu.kt
│ ├── PlayerMainMenu.kt
│ ├── StatisticsMenu.kt
│ ├── editor
│ │ ├── AllowRemoveItemsWithinInventory.kt
│ │ ├── EditLoadoutContentsMenu.kt
│ │ ├── EditorKitSelectionMenu.kt
│ │ └── ExtraContentSelectionMenu.kt
│ ├── party
│ │ ├── PartyPlayGameSelectMenu.kt
│ │ └── PartyPlayTVTFights.kt
│ ├── pipeline
│ │ └── DuelRequestPipeline.kt
│ ├── template
│ │ ├── TemplateKitMenu.kt
│ │ └── TemplateMapMenu.kt
│ └── tournaments
│ │ └── TournamentCreationPipeline.kt
│ ├── player
│ ├── IntRangeUtils.kt
│ ├── LobbyPlayer.kt
│ ├── LobbyPlayerExt.kt
│ ├── LobbyPlayerService.kt
│ ├── PlayerState.kt
│ ├── hotbar
│ │ └── LobbyHotbarService.kt
│ └── prevention
│ │ └── PreventionListeners.kt
│ ├── provider
│ ├── SettingProvider.kt
│ └── impl
│ │ ├── BasicsSettingProvider.kt
│ │ └── LemonSettingProvider.kt
│ ├── queue
│ └── QueueService.kt
│ ├── scoreboard
│ ├── LobbyScoreboardAdapter.kt
│ └── ScoreboardInfoService.kt
│ └── statresets
│ └── StatResetTokens.kt
├── renovate.json
├── services
├── application
│ ├── api
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── gg
│ │ │ └── tropic
│ │ │ └── practice
│ │ │ └── application
│ │ │ └── api
│ │ │ ├── DPSDataSync.kt
│ │ │ ├── DPSDataSyncKeys.kt
│ │ │ ├── DPSDataSyncSource.kt
│ │ │ ├── DPSRedisService.kt
│ │ │ ├── DPSRedisShared.kt
│ │ │ └── defaults
│ │ │ ├── game
│ │ │ ├── ImmutableAbstractGame.kt
│ │ │ └── ImmutableDuelExpectation.kt
│ │ │ ├── kit
│ │ │ ├── ImmutableKit.kt
│ │ │ ├── ImmutableKitContainer.kt
│ │ │ ├── KitDataSync.kt
│ │ │ └── group
│ │ │ │ ├── ImmutableKitContainer.kt
│ │ │ │ ├── ImmutableKitGroup.kt
│ │ │ │ └── KitGroupDataSync.kt
│ │ │ └── map
│ │ │ ├── ImmutableMap.kt
│ │ │ ├── ImmutableMapContainer.kt
│ │ │ └── MapDataSync.kt
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── gg
│ │ └── tropic
│ │ └── practice
│ │ └── application
│ │ └── ApplicationServer.kt
├── games
│ └── game-manager
│ │ ├── build.gradle.kts
│ │ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── gg
│ │ └── tropic
│ │ └── practice
│ │ └── games
│ │ └── manager
│ │ └── GameManager.kt
├── queue
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── gg
│ │ └── tropic
│ │ └── practice
│ │ └── queue
│ │ ├── GameQueue.kt
│ │ └── GameQueueManager.kt
├── replications
│ ├── replication-manager
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── gg
│ │ │ └── tropic
│ │ │ └── practice
│ │ │ └── replications
│ │ │ └── manager
│ │ │ ├── ReplicationManager.kt
│ │ │ └── ReplicationModels.kt
│ └── replication-models
│ │ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── gg
│ │ └── tropic
│ │ └── practice
│ │ └── replications
│ │ └── models
│ │ ├── Replication.kt
│ │ └── ReplicationStatus.kt
├── statistics
│ ├── build.gradle.kts
│ ├── leaderboards
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── gg
│ │ │ └── tropic
│ │ │ └── practice
│ │ │ └── statistics
│ │ │ └── leaderboards
│ │ │ ├── DateFloorUtils.kt
│ │ │ ├── ImmutablePracticeProfile.kt
│ │ │ ├── Leaderboard.kt
│ │ │ ├── LeaderboardManager.kt
│ │ │ └── LeaderboardType.kt
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── gg
│ │ └── tropic
│ │ └── practice
│ │ └── statistics
│ │ ├── ApplyUpdates.kt
│ │ ├── ApplyUpdatesStateless.kt
│ │ ├── Counter.kt
│ │ ├── GlobalStatistics.kt
│ │ ├── KitStatistics.kt
│ │ ├── Volatile.kt
│ │ ├── VolatileImpls.kt
│ │ └── ranked
│ │ └── RankedKitStatistics.kt
└── tournaments
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── gg
│ └── tropic
│ └── practice
│ └── tournaments
│ ├── StateMachine.kt
│ ├── Tournament.kt
│ └── TournamentManager.kt
├── settings.gradle.kts
└── shared
├── build.gradle.kts
└── src
└── main
└── kotlin
└── gg
└── tropic
└── practice
├── Environment.kt
├── PracticeShared.kt
├── commands
├── CommandExt.kt
├── KitCommands.kt
├── KitGroupCommands.kt
├── MapCommands.kt
├── MatchInventoryCommand.kt
├── RegionCommand.kt
├── admin
│ ├── MatchInfoCommand.kt
│ ├── RankedBanCommand.kt
│ ├── ResetStatsCommand.kt
│ ├── TerminateMatchCommand.kt
│ └── matchlist
│ │ ├── MatchListCommand.kt
│ │ └── MatchListMenu.kt
├── customizers
│ ├── KitCommandCustomizers.kt
│ ├── KitGroupCommandCustomizers.kt
│ └── MapCommandCustomizers.kt
└── menu
│ └── MapRatingOverviewMenu.kt
├── configuration
├── PracticeConfiguration.kt
├── PracticeConfigurationService.kt
└── subconfig
│ └── DataSampleThresholds.kt
├── expectation
└── GameExpectation.kt
├── friendship
├── FriendshipRequirement.kt
├── FriendshipStateSetting.kt
├── Friendships.kt
├── NoOpFriendshipRequirement.kt
└── PluginFriendshipRequirement.kt
├── games
├── AbstractGame.kt
├── GameReference.kt
├── GameReport.kt
├── GameReportSnapshot.kt
├── GameReportStatus.kt
├── GameState.kt
├── GameStatus.kt
├── GameStatusIndexes.kt
├── duels
│ └── DuelRequest.kt
├── spectate
│ └── SpectateRequest.kt
└── team
│ ├── GameTeam.kt
│ └── GameTeamSide.kt
├── guilds
├── GuildPluginGuildProvider.kt
├── GuildProvider.kt
├── Guilds.kt
└── NoOpGuildProvider.kt
├── integration
└── StaffReportsService.kt
├── kit
├── Kit.kt
├── KitContainer.kt
├── KitService.kt
├── feature
│ └── FeatureFlag.kt
└── group
│ ├── KitGroup.kt
│ ├── KitGroupContainer.kt
│ └── KitGroupService.kt
├── leaderboards
└── Leaderboards.kt
├── libraries
└── SharedLibraries.kt
├── map
├── Map.kt
├── MapContainer.kt
├── MapService.kt
├── metadata
│ ├── AbstractMapMetadata.kt
│ ├── anonymous
│ │ ├── Bounds.kt
│ │ ├── BukkitExt.kt
│ │ └── Position.kt
│ ├── impl
│ │ ├── MapLevelMetadata.kt
│ │ ├── MapSpawnMetadata.kt
│ │ └── MapZoneMetadata.kt
│ ├── scanner
│ │ ├── AbstractMapMetadataScanner.kt
│ │ ├── MetadataScannerUtilities.kt
│ │ └── impl
│ │ │ ├── MapLevelMetadataScanner.kt
│ │ │ ├── MapSpawnMetadataScanner.kt
│ │ │ └── MapZoneMetadataScanner.kt
│ └── sign
│ │ ├── MapSignMetadataModel.kt
│ │ └── SignParserUtilities.kt
├── rating
│ ├── MapRating.kt
│ └── MapRatingService.kt
└── utilities
│ ├── MapBlockFaceUtilities.kt
│ ├── MapMetadata.kt
│ └── MapMetadataScanUtilities.kt
├── party
└── WParty.kt
├── profile
├── PracticeProfile.kt
├── PracticeProfileService.kt
├── loadout
│ └── Loadout.kt
└── ranked
│ └── RankedBan.kt
├── queue
├── MinMaxRangedNumber.kt
├── QueueEntry.kt
├── QueueState.kt
└── QueueType.kt
├── region
├── PlayerRegionFromRedisProxy.kt
└── Region.kt
├── reports
├── GameReportService.kt
└── menu
│ ├── PlayerViewMenu.kt
│ ├── SelectPlayerMenu.kt
│ └── utility
│ └── RomanNumerals.kt
├── serializable
└── Message.kt
├── services
├── GameManagerService.kt
├── LeaderboardManagerService.kt
├── MIPPlayerCache.kt
├── ReplicationManagerService.kt
├── ScoreboardTitleService.kt
└── TournamentManagerService.kt
├── settings
├── ChatVisibility.kt
├── DuelsSettingCategory.kt
├── SettingsExt.kt
├── particles
│ └── FlightEffectSetting.kt
├── restriction
│ └── RangeRestriction.kt
└── scoreboard
│ ├── LobbyScoreboardView.kt
│ └── ScoreboardStyle.kt
├── tournaments
├── ScheduledMatchList.kt
├── TournamentConfig.kt
├── TournamentMember.kt
└── TournamentMemberList.kt
└── utilities
├── Items.kt
└── Ping.kt
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scala Practice
2 | A highly-scalable practice plugin used on the `potpvp.com` and `solara.gg` servers.
3 |
4 | ## Docs
5 | - [Infrastructure Overview](docs/infrastructure.md)
6 | - [Setup](docs/setup.md)
7 |
8 | -----
9 | 
10 |
11 | YourKit supports open source projects with innovative and intelligent tools
12 | for monitoring and profiling Java and .NET applications.
13 | YourKit is the creator of YourKit Java Profiler,
14 | YourKit .NET Profiler,
15 | and YourKit YouMonitor.
16 | for monitoring and profiling Java and .NET applications.
17 |
18 | -----
19 | ## Credits
20 | - GrowlyX
21 | - DripW
22 | - Elb1to
23 |
--------------------------------------------------------------------------------
/devtools/build.gradle.kts:
--------------------------------------------------------------------------------
1 | repositories {
2 | maven("https://repo.glaremasters.me/repository/concuncan/")
3 | maven("https://repo.dmulloy2.net/repository/public/")
4 | }
5 |
6 | dependencies {
7 | api(project(":shared"))
8 | compileOnly("com.grinderwolf:slimeworldmanager-plugin:2.2.1")
9 | }
10 |
--------------------------------------------------------------------------------
/devtools/src/main/kotlin/gg/tropic/practice/PracticeDevTools.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice
2 |
3 | import gg.scala.commons.ExtendedScalaPlugin
4 | import gg.scala.commons.core.plugin.*
5 |
6 | /**
7 | * @author GrowlyX
8 | * @since 9/22/2023
9 | */
10 | @Plugin(
11 | name = "TropicPractice-DevTools",
12 | version = "%remote%/%branch%/%id%"
13 | )
14 | @PluginAuthor("Tropic")
15 | @PluginWebsite("https://tropic.gg")
16 | @PluginDependencyComposite(
17 | PluginDependency("scala-commons"),
18 | PluginDependency("Lemon"),
19 | PluginDependency("ScBasics", soft = true)
20 | )
21 | class PracticeDevTools : ExtendedScalaPlugin()
22 | {
23 | init
24 | {
25 | PracticeShared
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/devtools/src/main/kotlin/gg/tropic/practice/feature/CloudSyncFeature.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice.feature
2 |
3 | import gg.scala.cloudsync.shared.discovery.CloudSyncDiscoveryService
4 | import gg.scala.commons.agnostic.sync.ServerSync
5 | import gg.scala.commons.annotations.plugin.SoftDependency
6 | import gg.scala.flavor.service.Configure
7 | import gg.scala.flavor.service.Service
8 | import gg.scala.flavor.service.ignore.IgnoreAutoScan
9 |
10 | /**
11 | * @author GrowlyX
12 | * @since 8/5/2022
13 | */
14 | @Service
15 | @IgnoreAutoScan
16 | @SoftDependency("cloudsync")
17 | object CloudSyncFeature
18 | {
19 | @Configure
20 | fun configure()
21 | {
22 | CloudSyncDiscoveryService
23 | .discoverable.assets.add(
24 | "gg.tropic.practice:devtools:TropicPractice-devtools${
25 | if ("dev" in ServerSync.getLocalGameServer().groups) ":gradle-dev" else ""
26 | }"
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/devtools/src/main/kotlin/gg/tropic/practice/map/MapManageServices.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice.map
2 |
3 | import com.grinderwolf.swm.api.SlimePlugin
4 | import com.grinderwolf.swm.api.loaders.SlimeLoader
5 | import gg.scala.flavor.inject.Inject
6 | import gg.scala.flavor.service.Configure
7 | import gg.scala.flavor.service.Service
8 | import gg.tropic.practice.PracticeDevTools
9 |
10 | /**
11 | * @author GrowlyX
12 | * @since 9/22/2023
13 | */
14 | @Service
15 | object MapManageServices
16 | {
17 | @Inject
18 | lateinit var plugin: PracticeDevTools
19 |
20 | lateinit var slimePlugin: SlimePlugin
21 | lateinit var loader: SlimeLoader
22 |
23 | @Configure
24 | fun configure()
25 | {
26 | slimePlugin = plugin.server.pluginManager
27 | .getPlugin("SlimeWorldManager") as SlimePlugin
28 |
29 | loader = slimePlugin.getLoader("mongodb")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/infrastructure.md:
--------------------------------------------------------------------------------
1 | # Infrastructure
2 |
3 | **Components:**
4 | - **Game Server:** Hosts a certain amount of games each. Runs the plugin built from the game module.
5 | - **Lobby Server:** Frontend for all other components listed here.
6 | - **Application:** Standalone application computing data shared on all server instances.
7 | - **Queue:** List of players for each GameType, TeamSize & Kit tuple which is iterated through and matched based on the amount of players in the queue entry, and other constraints (matchmaking settings like ping/ELO restrictions). Each queue is iterated through independently.
8 | - **Leaderboards:** Runs and caches leaderboards for all player profiles. Caches leaderboard entries in a sorted set in Redis.
9 | - **GameManager:** Keeps statuses of ongoing games for each game server. Similar to ReplicationManager's behavior, games are invalidated if no status update is received by its server in under 2 seconds of its last update.
10 | - **ReplicationManager:** Keeps statuses of available map replications for each game server.
11 | - If it does not receive another status update within 2 seconds, the game server is marked as unhealthy and replications are not used for any new matches.
12 |
13 | **Game Servers:**
14 | - Contains replications of Maps. Maps and replications are decoupled. Replications exist within a game server's lifetime, and more specifically, a single game's lifetime. The model for a single replication in code is a `BuiltMapReplication`. The replications are built from the map template. Map world templates and the data model itself is decoupled, meaning the template can be updated without the need for Map synchronization.
15 | - Map world templates are stored in GridFS buckets in MongoDB through SlimeWorldManager. The model for an in-memory map template that can be used to generation replications in code is a `ReadyMapTemplate`.
16 | - 16 replications for each available map is generated on startup to be used for any incoming maps.
17 | - Each game server pushes its local replication status to the ReplicationManager every half-second.
18 | - **Replications:**
19 | - Have globally unique IDs.
20 | - Are broadcast to the ReplicationManager.
21 | - Are bound to (when in use) to a game instance.
22 | - Are invalidated when the game instance bound to it is disposed of.
23 | - **Generation:**
24 | - 8 of each map is generated on startup. Replications are generated on-demand if a replication request is received and there is no available replication. The generation process is fairly quick, and can be run asynchronously.
25 |
--------------------------------------------------------------------------------
/docs/setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | **Required at least:**
4 | - *(optional)* A lobby server
5 | - A DevTools server
6 | - Ensure SWM `async-world-generation` is `false` on your DevTools server.
7 | - Ensure MongoDB details are configured properly for SWM on **all game and devtools** server instances.
8 | - Ensure the `TropicPractice-DevTools` plugin is in your DevTools server.
9 |
10 | **Setting up a map:**
11 | - Create a new SWM world in your Mongo database using `/swm create mongo`.
12 | - Paste in your map. **DO NOT ADD YOUR METADATA SIGNS YET!**
13 | - Use the command `/mapmanage create ` using the same `templateName` you used to create you SWM template world.
14 | - You will be then teleported to a locally-generated copy of your SWM template world. **SET UP YOUR METADATA SIGNS NOW.** A prompt will be started in chat asking you to go to the lowest/highest corners and type something in chat. Once this is done, your map should be saved.
15 | - **Metadata:**
16 | - Metadata comes in the form of signs. You can place these signs in any orientation except against blocks (they must be standalone signs). The first line should be the metadata type (i.e. `[spawn]`), and the second line can be the metadata ID (i.e. `a` or `b`). These signs are scanned, stored, and then removed from the world on map creation.
17 |
18 | **Managing maps:**
19 | - To edit the map data model itself, you can join any of your practice instances. You can then use the `/map` command to edit anything map related.
20 |
21 | **Setting up a kit:**
22 | - Use the `/kit create ` command to create a new kit.
23 | - **Feature Flags:**
24 | - Control the behavior of kits when used in a game and when shown to players in frontend components of the plugin. Feature flags can be added with or without additional context (metadata) which is given in the form of a pair of Strings.
25 | - **Example:**
26 | - `/kit features add nodebuff PlaceBlocks`: Adds the `PlaceBlocks` feature flag with no metadata.
27 | - `/kit features add nodebuff ExpirePlacedBlocksAfterNSeconds`: Adds the `ExpirePlacedBlocksAfterNSeconds` with default metadata.
28 | - `/kit features metadata add nodebuff ExpirePlacedBlocksAfterNSeconds time 10`: Adds the `time=10` metadata to the `ExpirePlacedBlocksAfterNSeconds` feature flag.
29 |
--------------------------------------------------------------------------------
/game/build.gradle.kts:
--------------------------------------------------------------------------------
1 | repositories {
2 | maven("https://repo.glaremasters.me/repository/concuncan/")
3 | maven("https://repo.dmulloy2.net/repository/public/")
4 | maven("https://repo.lunarclient.dev")
5 | }
6 |
7 | dependencies {
8 | api(project(":shared"))
9 | api(project(":services:statistics"))
10 | api(project(":services:replications:replication-models"))
11 |
12 | compileOnly("dev.cubxity.plugins:unifiedmetrics-api:0.3.8")
13 | compileOnly("com.grinderwolf:slimeworldmanager-plugin:2.2.1")
14 | compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
15 |
16 | compileOnly("gg.tropic.game.extensions:tropic-core-game-extensions:1.2.8")
17 |
18 | compileOnly("com.lunarclient:apollo-api:1.0.6")
19 | compileOnly("com.lunarclient:apollo-extra-adventure4:1.0.6")
20 | }
21 |
--------------------------------------------------------------------------------
/game/src/main/java/gg/tropic/practice/games/ranked/PotPvPEloCalculator.java:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice.games.ranked;
2 |
3 | import gg.scala.commons.annotations.inject.AutoBind;
4 | import org.jetbrains.annotations.Contract;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | /**
8 | * @author Colin McDonald
9 | * @since 2/18/2017
10 | */
11 | @AutoBind
12 | public final class PotPvPEloCalculator implements EloCalculator {
13 |
14 | public static final PotPvPEloCalculator INSTANCE =
15 | new PotPvPEloCalculator(35.0, 7, 25, 7, 25);
16 |
17 | private final double kPower;
18 | private final int minEloGain;
19 | private final int maxEloGain;
20 | private final int minEloLoss;
21 | private final int maxEloLoss;
22 |
23 | public PotPvPEloCalculator(double kPower, int minEloGain, int maxEloGain, int minEloLoss, int maxEloLoss) {
24 | this.kPower = kPower;
25 | this.minEloGain = minEloGain;
26 | this.maxEloGain = maxEloGain;
27 | this.minEloLoss = minEloLoss;
28 | this.maxEloLoss = maxEloLoss;
29 | }
30 |
31 | @Contract("_, _ -> new")
32 | public @NotNull CalculationResult calculate(int winnerElo, int loserElo) {
33 | double winnerQ = Math.pow(10, ((double) winnerElo) / 300D);
34 | double loserQ = Math.pow(10, ((double) loserElo) / 300D);
35 |
36 | double winnerE = winnerQ / (winnerQ + loserQ);
37 | double loserE = loserQ / (winnerQ + loserQ);
38 |
39 | int winnerGain = (int) (kPower * (1 - winnerE));
40 | int loserGain = (int) (kPower * (0 - loserE));
41 |
42 | winnerGain = Math.min(winnerGain, maxEloGain);
43 | winnerGain = Math.max(winnerGain, minEloGain);
44 |
45 | // loserGain will be negative so pay close attention here
46 | loserGain = Math.min(loserGain, -minEloLoss);
47 | loserGain = Math.max(loserGain, -maxEloLoss);
48 |
49 | return new CalculationResult(winnerElo, winnerGain, loserElo, loserGain);
50 | }
51 |
52 | @NotNull
53 | @Override
54 | public CalculationResult getNewRating(int winnerElo, int loserElo) {
55 | double winnerQ = Math.pow(10, ((double) winnerElo) / 300D);
56 | double loserQ = Math.pow(10, ((double) loserElo) / 300D);
57 |
58 | double winnerE = winnerQ / (winnerQ + loserQ);
59 | double loserE = loserQ / (winnerQ + loserQ);
60 |
61 | int winnerGain = (int) (kPower * (1 - winnerE));
62 | int loserGain = (int) (kPower * (0 - loserE));
63 |
64 | winnerGain = Math.min(winnerGain, maxEloGain);
65 | winnerGain = Math.max(winnerGain, minEloGain);
66 |
67 | // loserGain will be negative so pay close attention here
68 | loserGain = Math.min(loserGain, -minEloLoss);
69 | loserGain = Math.max(loserGain, -maxEloLoss);
70 |
71 | return new CalculationResult(winnerElo, winnerGain, loserElo, loserGain);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/game/src/main/java/gg/tropic/practice/utilities/PlayerRespawnUtilities.java:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice.utilities;
2 |
3 | import net.minecraft.server.v1_8_R3.EnumDifficulty;
4 | import net.minecraft.server.v1_8_R3.PacketPlayOutRespawn;
5 | import net.minecraft.server.v1_8_R3.WorldSettings;
6 | import net.minecraft.server.v1_8_R3.WorldType;
7 | import org.bukkit.Location;
8 | import org.bukkit.entity.Player;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import static xyz.xenondevs.particle.utils.ReflectionUtils.sendPacket;
12 |
13 | /**
14 | * @author GrowlyX
15 | * @since 12/24/2023
16 | */
17 | public class PlayerRespawnUtilities {
18 |
19 | public static void mockPlayerRespawn(@NotNull final Player player) {
20 | final byte actualDimension = (byte) player.getWorld().getEnvironment().getId();
21 | final EnumDifficulty difficulty = switch (player.getWorld().getDifficulty()) {
22 | case PEACEFUL -> EnumDifficulty.PEACEFUL;
23 | case EASY -> EnumDifficulty.EASY;
24 | case HARD -> EnumDifficulty.HARD;
25 | default -> EnumDifficulty.NORMAL;
26 | };
27 | final WorldType worldType = switch (player.getWorld().getWorldType()) {
28 | case NORMAL -> WorldType.NORMAL;
29 | case VERSION_1_1 -> WorldType.NORMAL_1_1;
30 | case LARGE_BIOMES -> WorldType.LARGE_BIOMES;
31 | case AMPLIFIED -> WorldType.AMPLIFIED;
32 | default -> WorldType.FLAT;
33 | };
34 | final WorldSettings.EnumGamemode gameMode = switch (player.getGameMode()) {
35 | case CREATIVE -> WorldSettings.EnumGamemode.CREATIVE;
36 | case ADVENTURE -> WorldSettings.EnumGamemode.ADVENTURE;
37 | default -> WorldSettings.EnumGamemode.SURVIVAL;
38 | };
39 |
40 | final Location location = player.getLocation();
41 | sendPacket(
42 | player,
43 | new PacketPlayOutRespawn(
44 | actualDimension, difficulty,
45 | worldType, gameMode
46 | )
47 | );
48 | player.teleport(location);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/game/src/main/kotlin/gg/tropic/practice/Extensions.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice
2 |
3 | import org.bukkit.GameMode
4 | import org.bukkit.entity.Player
5 |
6 | /**
7 | * @author GrowlyX
8 | * @since 8/5/2022
9 | */
10 | fun Player.resetAttributes()
11 | {
12 | this.health = this.maxHealth
13 |
14 | this.foodLevel = 20
15 | this.saturation = 12.8f
16 | this.maximumNoDamageTicks = 20
17 | this.fireTicks = 0
18 | this.fallDistance = 0.0f
19 | this.level = 0
20 | this.exp = 0.0f
21 | this.walkSpeed = 0.2f
22 | this.inventory.heldItemSlot = 0
23 | this.allowFlight = false
24 |
25 | this.inventory.clear()
26 | this.inventory.armorContents = null
27 |
28 | this.closeInventory()
29 |
30 | this.gameMode = GameMode.SURVIVAL
31 | this.fireTicks = 0
32 |
33 | for (potionEffect in this.activePotionEffects)
34 | {
35 | this.removePotionEffect(potionEffect.type)
36 | }
37 |
38 | this.updateInventory()
39 | }
40 |
--------------------------------------------------------------------------------
/game/src/main/kotlin/gg/tropic/practice/PracticeGame.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice
2 |
3 | import gg.scala.basics.plugin.profile.BasicsProfileService
4 | import gg.scala.commons.ExtendedScalaPlugin
5 | import gg.scala.commons.agnostic.sync.ServerSync
6 | import gg.scala.commons.annotations.container.ContainerEnable
7 | import gg.scala.commons.core.plugin.*
8 | import gg.scala.lemon.channel.ChatChannelService
9 | import gg.scala.lemon.redirection.aggregate.ServerAggregateHandler
10 | import gg.scala.lemon.redirection.aggregate.impl.LeastTrafficServerAggregateHandler
11 | import gg.tropic.practice.settings.ChatVisibility
12 | import gg.tropic.practice.settings.DuelsSettingCategory
13 | import org.bukkit.Bukkit
14 |
15 | /**
16 | * @author GrowlyX
17 | * @since 8/4/2022
18 | */
19 | @Plugin(
20 | name = "TropicPractice",
21 | version = "%remote%/%branch%/%id%"
22 | )
23 | @PluginAuthor("Tropic")
24 | @PluginWebsite("https://tropic.gg")
25 | @PluginDependencyComposite(
26 | PluginDependency("scala-commons"),
27 | PluginDependency("Lemon"),
28 | PluginDependency("SlimeWorldManager"),
29 | PluginDependency("CoreGameExtensions"),
30 | PluginDependency("ScBasics"),
31 | PluginDependency("Parties"),
32 | PluginDependency("UnifiedMetrics", soft = true),
33 | PluginDependency("cloudsync", soft = true),
34 | PluginDependency("Friends", soft = true),
35 | PluginDependency("ScStaff", soft = true),
36 | PluginDependency("Apollo-Bukkit", soft = true)
37 | )
38 | class PracticeGame : ExtendedScalaPlugin()
39 | {
40 | init
41 | {
42 | PracticeShared
43 | }
44 |
45 | @ContainerEnable
46 | fun containerEnable()
47 | {
48 | devProvider = {
49 | "mipgameDEV" in ServerSync.getLocalGameServer().groups
50 | }
51 |
52 | ChatChannelService.default
53 | .displayToPlayer { player, other ->
54 | val chatVisibility = BasicsProfileService.find(other)
55 | ?.setting(
56 | "${DuelsSettingCategory.DUEL_SETTING_PREFIX}:chat-visibility",
57 | ChatVisibility.Global
58 | )
59 | ?: ChatVisibility.Global
60 |
61 | val bukkitPlayer = Bukkit.getPlayer(player)
62 | when (chatVisibility)
63 | {
64 | ChatVisibility.Global -> true
65 | ChatVisibility.Match -> bukkitPlayer != null && bukkitPlayer.world.name == other.world.name
66 | }
67 | }
68 |
69 | val lobbyRedirector = LeastTrafficServerAggregateHandler(
70 | lobbyGroup().suffixWhenDev()
71 | )
72 | lobbyRedirector.subscribe()
73 |
74 | flavor {
75 | bind() to lobbyRedirector
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/game/src/main/kotlin/gg/tropic/practice/autoscale/ReplicationAutoScaleTask.kt:
--------------------------------------------------------------------------------
1 | package gg.tropic.practice.autoscale
2 |
3 | import gg.scala.commons.ExtendedScalaPlugin
4 | import gg.scala.flavor.inject.Inject
5 | import gg.scala.flavor.service.Close
6 | import gg.scala.flavor.service.Configure
7 | import gg.scala.flavor.service.Service
8 | import gg.tropic.practice.map.Map
9 | import gg.tropic.practice.map.MapReplicationService
10 | import gg.tropic.practice.map.MapService
11 | import me.lucko.helper.Events
12 | import me.lucko.helper.Schedulers
13 | import java.util.logging.Level
14 |
15 | /**
16 | * @author GrowlyX
17 | * @since 1/1/2024
18 | */
19 | @Service(priority = 30)
20 | object ReplicationAutoScaleTask : Thread("replication-auto-scale")
21 | {
22 | @Inject
23 | lateinit var plugin: ExtendedScalaPlugin
24 |
25 | private const val TARGET_REPLICATIONS = 32
26 | private const val FLOOR_REQUIRED_FOR_AUTO_SCALE = 0.85
27 |
28 | var lastAutoScaleCheck = 0L
29 |
30 | var lastAutoScaleEvent = 0L
31 | var lastAutoScaleEventCount = 0
32 |
33 | private fun runSilently()
34 | {
35 | if (!plugin.isEnabled)
36 | {
37 | return
38 | }
39 |
40 | val mappings = mutableMapOf