├── .docs ├── api │ ├── custom-build-preparation.md │ ├── import-world.md │ ├── load-world.md │ ├── migrate-world.md │ ├── properties.md │ ├── setup-a-devserver.md │ └── use-data-source.md ├── config │ ├── configure-world.md │ ├── convert-world-to-srf.md │ └── setup-data-sources.md ├── img │ └── build-channel.png ├── other │ └── faq.md └── usage │ ├── commands-and-permissions.md │ ├── install.md │ └── using.md ├── .github └── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── SLIME_FORMAT ├── api ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── api │ │ ├── AdvancedSlimePaperAPI.java │ │ ├── SlimeDataConverter.java │ │ ├── SlimeNMSBridge.java │ │ ├── events │ │ └── LoadSlimeWorldEvent.java │ │ ├── exceptions │ │ ├── CorruptedWorldException.java │ │ ├── InvalidWorldException.java │ │ ├── NewerFormatException.java │ │ ├── SlimeException.java │ │ ├── UnknownWorldException.java │ │ ├── WorldAlreadyExistsException.java │ │ ├── WorldLoadedException.java │ │ └── WorldTooBigException.java │ │ ├── loaders │ │ ├── SlimeLoader.java │ │ ├── SlimeSerializationAdapter.java │ │ └── UpdatableLoader.java │ │ ├── utils │ │ ├── NibbleArray.java │ │ └── SlimeFormat.java │ │ └── world │ │ ├── SlimeChunk.java │ │ ├── SlimeChunkSection.java │ │ ├── SlimeWorld.java │ │ ├── SlimeWorldInstance.java │ │ └── properties │ │ ├── SlimeProperties.java │ │ ├── SlimeProperty.java │ │ ├── SlimePropertyMap.java │ │ └── type │ │ ├── SlimePropertyBoolean.java │ │ ├── SlimePropertyByte.java │ │ ├── SlimePropertyByteArray.java │ │ ├── SlimePropertyDouble.java │ │ ├── SlimePropertyFloat.java │ │ ├── SlimePropertyInt.java │ │ ├── SlimePropertyIntArray.java │ │ ├── SlimePropertyList.java │ │ ├── SlimePropertyLong.java │ │ ├── SlimePropertyLongArray.java │ │ ├── SlimePropertyShort.java │ │ └── SlimePropertyString.java │ └── resources │ ├── main.yml │ ├── plugin.yml │ ├── sources.yml │ └── worlds.yml ├── aspaper-api ├── build.gradle.kts.patch └── paper-patches │ └── features │ └── 0001-API-Branding.patch ├── aspaper-server ├── build.gradle.kts.patch ├── minecraft-patches │ ├── features │ │ ├── 0001-Disable-dragon-battle.patch │ │ ├── 0002-World-overrides.patch │ │ ├── 0003-Avoid-IO-call-for-UUID.patch │ │ ├── 0004-Prevent-config-disk-io-on-world-load.patch │ │ └── 0005-Read-only-dimension-data-store.patch │ └── sources │ │ ├── ca │ │ └── spottedleaf │ │ │ └── moonrise │ │ │ └── patches │ │ │ └── chunk_system │ │ │ └── scheduling │ │ │ ├── ChunkHolderManager.java.patch │ │ │ ├── NewChunkHolder.java.patch │ │ │ └── task │ │ │ └── ChunkLoadTask.java.patch │ │ └── net │ │ └── minecraft │ │ ├── server │ │ └── level │ │ │ └── ServerLevel.java.patch │ │ └── world │ │ └── level │ │ └── chunk │ │ └── storage │ │ └── SerializableChunkData.java.patch ├── paper-patches │ ├── features │ │ ├── 0001-Warning-if-people-use-old-swm-api.patch │ │ ├── 0002-Warning-if-people-use-old-swm-plugin.patch │ │ ├── 0003-Branding.patch │ │ ├── 0004-Delete-temp-folder-after-world-is-unloaded.patch │ │ └── 0005-Prevent-config-disk-io-on-world-load.patch │ └── files │ │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── bukkit │ │ └── craftbukkit │ │ └── CraftServer.java.patch └── src │ └── main │ ├── java │ └── com │ │ └── infernalsuite │ │ └── asp │ │ ├── AdvancedSlimePaper.java │ │ ├── Converter.java │ │ ├── InternalPlugin.java │ │ ├── SimpleDataFixerConverter.java │ │ ├── SlimeNMSBridgeImpl.java │ │ ├── config │ │ └── SlimePaperWorldConfig.java │ │ ├── level │ │ ├── ChunkDataLoadTask.java │ │ ├── CommonLoadTask.java │ │ ├── FastChunkPruner.java │ │ ├── NMSSlimeChunk.java │ │ ├── ReadOnlyDimensionDataStorage.java │ │ ├── SafeNmsChunkWrapper.java │ │ ├── SlimeBootstrap.java │ │ ├── SlimeChunkConverter.java │ │ ├── SlimeChunkLevel.java │ │ ├── SlimeInMemoryWorld.java │ │ ├── SlimeLevelGenerator.java │ │ └── SlimeLevelInstance.java │ │ └── util │ │ └── NmsUtil.java │ └── resources │ └── META-INF │ └── services │ ├── com.infernalsuite.asp.api.AdvancedSlimePaperAPI │ └── com.infernalsuite.asp.api.SlimeNMSBridge ├── build-data ├── aspaper.at └── dev-imports.txt ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── asp.base-conventions.gradle.kts │ ├── asp.internal-conventions.gradle.kts │ ├── asp.publishing-conventions.gradle.kts │ ├── com │ └── infernalsuite │ │ └── asp │ │ └── conventions │ │ └── PublishConfiguration.kt │ ├── constants.kt │ └── extensions.kt ├── core ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── infernalsuite │ └── asp │ ├── SlimeLogger.java │ ├── Util.java │ ├── pdc │ ├── AdventureDataTypeRegistry.java │ └── AdventurePersistentDataContainer.java │ ├── serialization │ ├── SlimeSerializationAdapterImpl.java │ ├── SlimeWorldReader.java │ ├── anvil │ │ ├── AnvilImportData.java │ │ └── AnvilWorldReader.java │ └── slime │ │ ├── ChunkPruner.java │ │ ├── SlimeSerializer.java │ │ └── reader │ │ ├── SlimeWorldReaderRegistry.java │ │ ├── VersionedByteSlimeWorldReader.java │ │ └── impl │ │ ├── SimpleWorldFormat.java │ │ ├── v10 │ │ ├── v10SlimeWorldDeSerializer.java │ │ └── v10WorldFormat.java │ │ ├── v11 │ │ ├── v11SlimeWorldDeSerializer.java │ │ └── v11WorldFormat.java │ │ ├── v12 │ │ ├── v12SlimeWorldDeSerializer.java │ │ └── v12WorldFormat.java │ │ └── v1_9 │ │ ├── Upgrade.java │ │ ├── upgrade │ │ ├── v1_13WorldUpgrade.java │ │ ├── v1_16WorldUpgrade.java │ │ └── v1_18WorldUpgrade.java │ │ ├── v1_9SlimeChunk.java │ │ ├── v1_9SlimeChunkSection.java │ │ ├── v1_9SlimeWorld.java │ │ ├── v1_9SlimeWorldDeserializer.java │ │ ├── v1_9WorldFormat.java │ │ └── v1_v9SlimeConverter.java │ └── skeleton │ ├── SkeletonCloning.java │ ├── SkeletonSlimeWorld.java │ ├── SlimeChunkSectionSkeleton.java │ └── SlimeChunkSkeleton.java ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── importer ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── infernalsuite │ └── asp │ └── importer │ └── SWMImporter.java ├── loaders ├── api-loader │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── loaders │ │ └── api │ │ ├── APILoader.java │ │ └── MapStructure.java ├── build.gradle.kts ├── file-loader │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── loaders │ │ └── file │ │ └── FileLoader.java ├── mongo-loader │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── loaders │ │ └── mongo │ │ └── MongoLoader.java ├── mysql-loader │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── loaders │ │ └── mysql │ │ └── MysqlLoader.java └── redis-loader │ ├── build.gradle.kts │ └── src │ └── main │ └── java │ └── com │ └── infernalsuite │ └── asp │ └── loaders │ └── redis │ ├── RedisLoader.java │ └── util │ └── StringByteCodec.java ├── plugin ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── infernalsuite │ │ └── asp │ │ └── plugin │ │ ├── SWPlugin.java │ │ ├── commands │ │ ├── CommandManager.java │ │ ├── SlimeCommand.java │ │ ├── exception │ │ │ └── MessageCommandException.java │ │ ├── parser │ │ │ ├── BukkitWorldParser.java │ │ │ ├── NamedSlimeLoader.java │ │ │ ├── NamedSlimeLoaderParser.java │ │ │ ├── NamedWorldData.java │ │ │ ├── NamedWorldDataParser.java │ │ │ ├── SlimeWorldParser.java │ │ │ └── suggestion │ │ │ │ └── KnownSlimeWorldSuggestionProvider.java │ │ └── sub │ │ │ ├── CloneWorldCmd.java │ │ │ ├── CreateWorldCmd.java │ │ │ ├── DSListCmd.java │ │ │ ├── DeleteWorldCmd.java │ │ │ ├── GotoCmd.java │ │ │ ├── HelpCmd.java │ │ │ ├── ImportWorldCmd.java │ │ │ ├── LoadTemplateWorldCmd.java │ │ │ ├── LoadWorldCmd.java │ │ │ ├── MigrateWorldCmd.java │ │ │ ├── ReloadConfigCmd.java │ │ │ ├── SaveWorldCmd.java │ │ │ ├── SetSpawnCmd.java │ │ │ ├── UnloadWorldCmd.java │ │ │ ├── VersionCmd.java │ │ │ └── WorldListCmd.java │ │ ├── config │ │ ├── ConfigManager.java │ │ ├── DatasourcesConfig.java │ │ ├── WorldData.java │ │ └── WorldsConfig.java │ │ ├── loader │ │ └── LoaderManager.java │ │ └── util │ │ └── ExecutorUtil.java │ ├── resource-templates │ └── paper-plugin.yml │ └── resources │ ├── main.yml │ ├── sources.yml │ └── worlds.yml ├── scripts ├── apatch.sh └── upstreamCommit.sh └── settings.gradle.kts /.docs/api/custom-build-preparation.md: -------------------------------------------------------------------------------- 1 | ## How can I build a custom build? 2 | 3 | ASWM uses modern paperweight tooling inorder to interact with the server source. 4 | 5 | To compile ASWM, you need JDK 17 and an internet connection. 6 | 7 | Clone this repo, run `./gradlew applyPatches`, then `./gradlew createReobfBundlerJar` from your terminal. You can find the compiled jar in the project root's build/libs directory. 8 | To get a full list of tasks, run `./gradlew tasks`. 9 | 10 | ## Building the Plugin 11 | 12 | To build the ASWM plugin, execute the following command in the plugin module: 13 | 14 | ``` 15 | gradle clean shadowJar 16 | ``` 17 | -------------------------------------------------------------------------------- /.docs/api/import-world.md: -------------------------------------------------------------------------------- 1 | ## Importing Worlds 2 | 3 | You need three things to import a world: a world folder, a world name and a data source. Here's an example of how to import a world: 4 | ```java 5 | SlimePlugin plugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); 6 | 7 | File worldDir = new File("my_world_foler"); 8 | String worldName = "my_world"; 9 | SlimeLoader loader = plugin.getLoader("mysql"); 10 | 11 | try { 12 | // note that this method should be called asynchronously 13 | plugin.importWorld(worldDir, worldName, loader); 14 | } catch (WorldAlreadyExistsException | InvalidWorldException | WorldLoadedException | WorldTooBigException | IOException exception) { 15 | // exception handling 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /.docs/api/load-world.md: -------------------------------------------------------------------------------- 1 | ## Loading Worlds 2 | 3 | First, retrieve the SlimeWorldManager plugin API: 4 | ```java 5 | SlimePlugin plugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); 6 | ``` 7 | Now, you need a loader. A SlimeLoader is a class that reads and stores worlds from a data source. In this case, we'll be using the MySQL loader: 8 | ```java 9 | SlimeLoader sqlLoader = plugin.getLoader("mysql"); 10 | ``` 11 | 12 | Before actually loading the world, you need a SlimePropertyMap Object. Check the [property API documentation](properties.md) for further details. 13 | 14 | That's it, you've got everything you need! Now, let's load the world from the data source and generate it: 15 | ```java 16 | try { 17 | // note that this method should be called asynchronously 18 | SlimeWorld world = plugin.loadWorld(sqlLoader, "my-world", props); 19 | 20 | // note that this method must be called synchronously 21 | plugin.loadWorld(world); 22 | } catch (UnknownWorldException | IOException | CorruptedWorldException | NewerFormatException | WorldInUseException | UnsupportedWorldException exception) { 23 | // exception handling 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /.docs/api/migrate-world.md: -------------------------------------------------------------------------------- 1 | ## Migrating Worlds 2 | 3 | To migrate a world you need three things: a world name, the data source where the world is currently stored in and another data source to store the world. Here's an example of a world migration: 4 | ```java 5 | SlimePlugin plugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); 6 | 7 | String worldName = "my_world"; 8 | SlimeLoader currentLoader = plugin.getLoader("mysql"); 9 | SlimeLoader newLoader = plugin.getLoader("mongodb"); 10 | 11 | try { 12 | // note that this method should be called asynchronously 13 | plugin.migrateWorld(worldName, currentLoader, newLoader); 14 | } catch (IOException | WorldInUseException | WorldAlreadyExistsException | UnknownWorldException exception) { 15 | // exception handling 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /.docs/api/properties.md: -------------------------------------------------------------------------------- 1 | # Properties API 2 | 3 | Property "types" are handled by [SlimeProperty][1] instances. Whilst creating [SlimeProperty][1] objects is not allowed, there is a [list of all available properties][2]. Properties and their values are stored in [SlimePropertyMaps][3]. 4 | 5 | 6 | **Example Usage:** 7 | ```java 8 | // create a new and empty property map 9 | SlimePropertyMap properties = new SlimePropertyMap(); 10 | 11 | properties.setValue(SlimeProperties.DIFFICULTY, "normal"); 12 | properties.setValue(SlimeProperties.SPAWN_X, 123); 13 | properties.setValue(SlimeProperties.SPAWN_Y, 112); 14 | properties.setValue(SlimeProperties.SPAWN_Z, 170); 15 | properties.setValue(SlimeProperties.ALLOW_ANIMALS, false); 16 | properties.setValue(SlimeProperties.ALLOW_MONSTERS, false); 17 | properties.setValue(SlimeProperties.DRAGON_BATTLE, false); 18 | properties.setValue(SlimeProperties.PVP, false); 19 | properties.setValue(SlimeProperties.ENVIRONMENT, "normal"); 20 | properties.setValue(SlimeProperties.WORLD_TYPE, "DEFAULT"); 21 | properties.setValue(SlimeProperties.DEFAULT_BIOME, "minecraft:plains"); 22 | // add as many as you would like 23 | ``` 24 | 25 | [1]: ../../api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperty.java 26 | [2]: ../../api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperties.java 27 | [3]: ../../api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimePropertyMap.java 28 | -------------------------------------------------------------------------------- /.docs/api/setup-a-devserver.md: -------------------------------------------------------------------------------- 1 | # Setup a devserver 2 | It's possible to configure your plugins gradle setup to allow running a server with the plugin right out of your IDE via gradle. This can lead to an improved developer experience. 3 | 4 | 5 | This guide assumes: 6 | - you are using `shadowJar` (`com.github.johnrengelman.shadow`) 7 | - the Kotlin DSL (`gradle.build.kts`) 8 | 9 | To the top of your gradle file add the following two imports. It's fine for them to be marked as errors by your IDE for now. 10 | ```kt 11 | import dev.s7a.gradle.minecraft.server.tasks.LaunchMinecraftServerTask 12 | import java.io.FileOutputStream 13 | ``` 14 | 15 | Then you will need to add the server plugin to your plugins. ([Latest Version](https://github.com/sya-ri/minecraft-server-gradle-plugin)) 16 | 17 | ```kt 18 | 19 | plugins { 20 | // ...your other gradle plugins 21 | id("dev.s7a.gradle.minecraft.server") version "2.0.0" // always choose the latest version from the link above 22 | } 23 | ``` 24 | 25 | At last add the following code to the file. It configures a minecraft server and automatically downloads the specified version of ASWM and AdvancedSlimePaper. 26 | ```kt 27 | task("runServer") { 28 | dependsOn("shadowJar") 29 | val slimeBuild = "INSERT BUILD VERSION" 30 | val slimeVersion = "1.19.3-R0.1" 31 | doFirst { 32 | copy { 33 | val file = tasks.named("shadowJar").flatMap { shadow -> shadow.archiveFile }.get().asFile; 34 | from(file) 35 | into(buildDir.resolve("MinecraftServer/plugins")) 36 | } 37 | 38 | val slimeJarFileOutput = buildDir.resolve("MinecraftServer/plugins/AdvancedSlimeManager.jar") 39 | uri(ASWM.plugin(slimeBuild, slimeVersion)) 40 | .toURL().openStream().use { it.copyTo(FileOutputStream(slimeJarFileOutput)) } 41 | } 42 | 43 | jarUrl.set(ASWM.server(slimeBuild, slimeVersion)) 44 | agreeEula.set(true) 45 | } 46 | 47 | class ASWM { 48 | companion object { 49 | fun server(build: String, version: String) : String { 50 | return "https://dl.rapture.pw/IS/ASP/main/${build}/slimeworldmanager-paperclip-${version}-SNAPSHOT-reobf.jar"; 51 | } 52 | fun plugin(build: String, version: String) : String { 53 | return "https://dl.rapture.pw/IS/ASP/main/${build}/plugin-${version}-SNAPSHOT.jar"; 54 | } 55 | } 56 | } 57 | 58 | ``` 59 | **IMPORTANT** Don't forget to replace `INSERT BUILD VERSION` with a valid commit ID **from #new-build on our discord server** or github (looks like this `42175f090baf00494c0fb25588f1e22ad4d9558f` <-- don't use this version as its outdated) 60 | 61 | Just click the highlighted link and copy the ID part. 62 | ![](../img/build-channel.png) 63 | -------------------------------------------------------------------------------- /.docs/api/use-data-source.md: -------------------------------------------------------------------------------- 1 | ## Custom Data Sources 2 | 3 | Advanced Slime World Manager supports three data sources out of the box: the file system, MySQL and MongoDB. However, there are situations where you might want to use other data sources. To do so, you can create your own implementation of the SlimeLoader interface. 4 | 5 | SlimeLoaders are classes used to load worlds from specific data sources. Remember to check out the javadocs for the SlimeLoader interface prior to creating your own implementation, as it contains information on what every method should exactly do. You can also take a look at the [FileLoader class](../../plugin/src/main/java/com/grinderwolf/swm/plugin/loaders/file/FileLoader.java) for an example of a SlimeLoader. 6 | 7 | Once you've got your own SlimeLoader, remember to register it so you can use it later: 8 | ```java 9 | SlimePlugin plugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); 10 | 11 | plugin.registerLoader("my_data_source", new MyCustomSlimeLoader()); 12 | ``` 13 | -------------------------------------------------------------------------------- /.docs/config/configure-world.md: -------------------------------------------------------------------------------- 1 | # World Configuration 2 | 3 | To configure a world, open the 'worlds.yml' file inside the ASWM config folder. Here is an example of a worlds.yml file: 4 | ```yaml 5 | worlds: 6 | my_great_world: 7 | source: mongodb 8 | loadOnStartup: false 9 | readOnly: true 10 | spawn: 940, 2, -370 11 | allowMonsters: false 12 | allowAnimals: false 13 | difficulty: peaceful 14 | pvp: false 15 | environment: NORMAL 16 | worldType: default 17 | ``` 18 | 19 | Then, save it and reload it by using the /swm reload command. You're good to go! 20 | 21 | ## Config options 22 | #### `source` 23 | Description: the name of the data source where the world is stored.
24 | Available options: `file`, `mysql`, `mongodb`. Any other datasources provided by third-party plugins can also be used.
25 | Defaults to: `file`. 26 | 27 | #### `loadOnStartup` 28 | Description: whether or not the world should be loaded when the server starts up.
29 | Available options: `true` and `false`. 30 | 31 | #### `readOnly` 32 | Description: if true, changes to the world will never be stored. If false, the world will be locked, so no other server can access it without being on read-only mode.
33 | Available options: `true` and `false`.
34 | Defaults to: `false`. 35 | 36 | #### `spawn` 37 | Description: spawn coordinates for the world.
38 | Available options: `, , `.
39 | Defaults to: `0, 255, 0`. 40 | 41 | #### `allowMonsters` 42 | Description: whether or not monsters can spawn on this world.
43 | Available options: `true` and `false`.
44 | Defaults to: `true`. 45 | 46 | #### `allowAnimals` 47 | Description: whether or not animals can spawn on this world.
48 | Available options: `true` and `false`.
49 | Defaults to: `true`. 50 | 51 | #### `difficulty` 52 | Description: the difficulty of the world.
53 | Available options: `peaceful`, `easy`, `normal` and `hard`.
54 | Defaults to: `peaceful`. 55 | 56 | #### `pvp` 57 | Description: if true, PvP will be allowed on this world.
58 | Available options: `true` and `false`.
59 | Defaults to: `true`. 60 | 61 | #### `environment` 62 | Description: sets the world environment.
63 | Available options: `normal`, `nether`, `the_end`.
64 | Defaults to: `normal`. 65 | 66 | #### `worldType` 67 | Description: sets the level type.
68 | Available options: `default`, `flat`, `large_biomes`, `amplified`, `customized`, `debug_all_block_states`, `default_1_1`.
69 | Defaults to: `default`. 70 | -------------------------------------------------------------------------------- /.docs/config/convert-world-to-srf.md: -------------------------------------------------------------------------------- 1 | # Converting Worlds 2 | 3 | To be able to load a world with ASWM, you have to convert it to the SRF. There are two ways of doing this: 4 | 5 | ## Using the in-game command 6 | 7 | 1. Place your world inside your server's root directory. 8 | 2. Make sure the world is unloaded. Loaded worlds cannot be converted. 9 | 3. Run the command `/aswm import [new-world-name]`. If you want the world to have the same name as the world folder, simply ignore the _[new-world-name]_ argument. 10 | 4. Done! The world is now inside the data source you've provided. 11 | 12 | ### Usage as API 13 | 14 | The importer tool may be used as a dependency in your projects to import worlds programmatically. 15 | 16 | The basic usage of the API is as follows: 17 | ```java 18 | File theOutputFile = SWMImporter.getDestinationFile(theWorldDir); 19 | 20 | try { 21 | SWMImporter.importWorld(theWorldDir, theOutputFile, true); 22 | } catch (IOException | InvalidWorldException exception) { 23 | // exception handling 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /.docs/config/setup-data-sources.md: -------------------------------------------------------------------------------- 1 | ## Data Source Configuration 2 | 3 | Before using MySQL or MongoDB to store your worlds, you've got to configure them. To do so, navigate to the SWM config folder located inside your plugins directory, and open the 'sources.yml' file. Inside there are all the parameters you need to set. Here's an example of how your sources.yml should look like: 4 | 5 | ```yaml 6 | file: 7 | # The path to the directory where slime worlds are stored 8 | path: slime_worlds 9 | mysql: 10 | enabled: true 11 | host: 127.0.0.1 12 | port: 3306 13 | username: my_mysql_username 14 | password: my_mysql_password 15 | database: slimeworldmanager 16 | mongodb: 17 | enabled: true 18 | host: 127.0.0.1 19 | port: 27017 20 | username: my_mongo_username 21 | password: my_mongo_password 22 | auth: admin 23 | database: slimeworldmanager 24 | collection: worlds 25 | ``` 26 | 27 | **Remember to enable MySQL and/or MongoDB if you are going to use them!** 28 | -------------------------------------------------------------------------------- /.docs/img/build-channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfernalSuite/AdvancedSlimePaper/2b60acc0578293049d8837cd716ffd256f2e1090/.docs/img/build-channel.png -------------------------------------------------------------------------------- /.docs/other/faq.md: -------------------------------------------------------------------------------- 1 | ## FAQ 2 | 3 | ### Which Spigot versions is this compatible with? 4 | 5 | ASWM is not compatible with spigot, as it is its own fork. 6 | 7 | ### Can I override the default world? 8 | 9 | Yes, you can! 10 | 11 | ### My server stops when booting up with a 'Failed to find ClassModifier classes' error. 12 | 13 | Go to [Installing Slime World Manager](../usage/install.md) and follow the steps described there. 14 | 15 | ### Is ASWM compatible with Multiverse-Core? 16 | 17 | Multiverse-Core detects ASWM worlds as unloaded, as it cannot find the world directory, and then just ignores them. There should not be any issues; however, Multiverse-Core commands will not work with ASWM worlds. 18 | 19 | ### What's the world size limit? 20 | 21 | The Slime Region Format can handle up a 46340x4630 chunk area. That's the maximum size that SWM can _theoretically_ handle, given enough memory. However, having a world so big is not recommended at all. 22 | 23 | There's not a specific value that you shouldn't exceed _- except for the theoretical limit, of course_. ASWM keeps a copy of all the chunks loaded in memory until the world is unloaded, so the more chunks you have, the bigger the ram usage is. How far you want to go depends on how much ram you are willing to let ASWM use. Moreover, the ram usage per chunk isn't a constant value, as it depends on the actual data stored in the chunk. 24 | -------------------------------------------------------------------------------- /.docs/usage/install.md: -------------------------------------------------------------------------------- 1 | ## Installing Advanced Slime World Manager 2 | 3 | ### Releases 4 | 5 | ASWM releases can be found [here](https://github.com/InfernalSuite/AdvancedSlimePaper/releases). More recent 6 | releases can be found in the [Discord](https://discord.gg/YevvsMa) under the #new-builds channel. 7 | 8 | ### How to install ASWM 9 | 10 | Installing ASWM is an easy task. First, download the latest version of the plugin and server jar. Then, follow this step: 11 | 1. Place the downloaded `slimeworldmanager-plugin-.jar` file inside your server's plugin folder. 12 | 2. Place the `slimeworldmanager-server-.jar` file inside your server's main directory. 13 | 3. Modify your server startup command to instead run that same server jar. 14 | ``` 15 | java -jar "slimeworldmanager-server-.jar" 16 | ``` 17 | -------------------------------------------------------------------------------- /.docs/usage/using.md: -------------------------------------------------------------------------------- 1 | # Using Advanced Slime World Manager 2 | 3 | ## How does ASWM work? 4 | 5 | In short, ASWM (a fork of [SWM](https://www.spigotmc.org/resources/slimeworldmanager.69974/)) is a Bukkit world manager plugin, like Multiverse or MultiWorld. However, these plugins are limited to loading traditional Minecraft worlds - and they're great at it, too. ASWM uses the Slime Region Format, which has some advantages over the traditional MC format - you can read more about this [here](https://www.spigotmc.org/resources/slimeworldmanager.69974/). 6 | 7 | ## How can I use ASWM? 8 | 9 | ASWM needs to have worlds to load, otherwise it's useless. First of all, you have to import your world to the SRF, so ASWM can load it. To do so, check out [this page](../config/convert-world-to-srf.md). 10 | 11 | Once you have your world imported and stored inside a data source, you've got to configure it, so the plugin knows where the world is stored, its spawn location, etc... Take a look at [this page](../config/configure-world.md) for more information on how to achieve this. 12 | 13 | Now that you've saved and configured your world, it's time to load it. Type `/swm load `. That's it! Now you can teleport yourself to the world by using the `/swm goto ` command. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the issue** 11 | 12 | **Spigot version** 13 | 14 | **Plugin version** 15 | 16 | **How to reproduce** 17 | 18 | **Crash reports (if available)** 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for SWM 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | 4 | **/.gradle/ 5 | .DS_Store 6 | build/ 7 | **/run/ 8 | 9 | **/*.iml 10 | 11 | aspaper-server/build.gradle.kts 12 | aspaper-server/src/minecraft/ 13 | paper-server/ 14 | aspaper-api/build.gradle.kts 15 | paper-api/ 16 | paper-api-generator/ 17 | aspaper-server/.gradle/ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced Slime Paper (ASWM) [![Build Status](https://ci.infernalsuite.com/app/rest/builds/buildType:(id:AdvancedSlimePaper_Build)/statusIcon)](https://ci.infernalsuite.com/viewType.html?buildTypeId=AdvancedSlimePaper_Build&guest=1) 2 | 3 | [](https://discord.gg/YevvsMa) 4 | 5 | Advanced Slime Paper is a fork of Paper implementing the Slime Region Format developed by Hypixel. Originally a plugin, this project has been converted to a 6 | fork to maximize our ability to provide fixes and performance improvements. 7 | Its goal is to provide server administrators with an easy-to-use tool to load worlds faster and save space. 8 | 9 | ## Releases 10 | 11 | ASP builds can be found [here](https://infernalsuite.com/download/asp). Older 12 | builds can be found in the [Discord](https://discord.gg/YevvsMa) under the #aspaper-builds channel. 13 | 14 | ## Wiki 15 | For API and usage info check out the docs at https://infernalsuite.com/ 16 | 17 | ## Javadocs 18 | The Javadocs can be found [here](https://docs.infernalsuite.com/). 19 | 20 | ## Credits 21 | 22 | Thanks to: 23 | * All the contributors that actively maintain ASWM and added features to SWM. 24 | * [Paul19988](https://github.com/Paul19988) - ASWM Creator. 25 | * [ComputerNerd100](https://github.com/ComputerNerd100) - Large Contributor & Maintainer. 26 | * [b0ykoe](https://github.com/b0ykoe) - Provider of build services & repositories. 27 | * [Owen1212055](https://github.com/Owen1212055) - Large Contributor & Maintainer. 28 | * [Gerolmed](https://github.com/Gerolmed) - Contributor & Maintainer. 29 | * [Grinderwolf](https://github.com/Grinderwolf) - The original creator. 30 | * [Glare](https://glaremasters.me) - Providing the original Maven repository. 31 | * [Minikloon](https://twitter.com/Minikloon) and all the [Hypixel](https://twitter.com/HypixelNetwork) team for developing the SRF. 32 | -------------------------------------------------------------------------------- /SLIME_FORMAT: -------------------------------------------------------------------------------- 1 | ------------------------------------- 2 | 3 | “Slime” file format 4 | 2 bytes - magic = 0xB10B 5 | 1 byte (ubyte) - version, current = 0x0C 6 | 4 bytes (int) - world version 7 | 4 bytes (int) - compressed chunks size 8 | 4 bytes (int) - uncompressed chunks size 9 | (size determined from bitmask) 10 | compressed using zstd 11 | 12 | 4 bytes (int) - compressed “extra” size 13 | 4 bytes (int) - uncompressed “extra” size 14 | [depends] - extra compound tag compressed using zstd (used for PDC, and/or custom data) 15 | 16 | ------------------------------------- 17 | 18 | Custom chunk format 19 | 4 byte (int) - chunk x 20 | 4 byte (int) - chunk z 21 | 4 bytes (int) section count 22 | [for each section] 23 | 1 byte (boolean) - has sky light 24 | [if has sky light] 25 | 2048 bytes - sky light 26 | 1 byte (boolean) - has block light 27 | [if has block light] 28 | 2048 bytes - block light 29 | 4 bytes (int) - block states byte size 30 | 31 | 4 bytes (int) - biomes byte size 32 | 33 | 4 bytes (int) - heightmaps size 34 | 35 | same format as mc, uncompressed 36 | 4 bytes (int) - tile entities size 37 | 38 | Same format as mc 39 | inside an nbt list named “tileEntities”, in a global compound 40 | uncompressed 41 | 4 bytes (int) entities size 42 | 43 | Same format as mc EXCEPT optional “CustomId” 44 | inside an nbt list named “entities”, in a global compound 45 | uncompressed 46 | [depends] - compound tag uncompressed (used for PDC, and/or custom data 47 | 48 | ------------------------------------- 49 | 50 | Version history: 51 | - v1: Initial release. 52 | - v2: Added "extra" nbt tag for per-world custom data. 53 | - v3: Added entities storage. 54 | - v4: Added support for 1.13 worlds and removed HypixelBlocks3. 55 | - v5: Skylight and blocklight might not always be present. 56 | - v6: Added world versioning 57 | - v7: Added world maps 58 | - v8: Variable biomes size 59 | - v9: Fix issue with biomes size, causing old worlds to be corrupted 60 | - v10: Use minecraft version id, remove legacy version artifacts 61 | - v11: Move entities and tile entities into the chunk structure 62 | - v12: Add support for chunk-based PDC 63 | -------------------------------------------------------------------------------- /api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | api(libs.annotations) 8 | api(libs.adventure.nbt) 9 | 10 | compileOnly(paperApi()) 11 | } 12 | 13 | publishConfiguration { 14 | name = "Advanced Slime Paper API" 15 | description = "API for Advanced Slime Paper" 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/SlimeDataConverter.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeWorld; 4 | import net.kyori.adventure.nbt.CompoundBinaryTag; 5 | import net.kyori.adventure.nbt.ListBinaryTag; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | import java.util.List; 9 | 10 | @ApiStatus.Internal 11 | public interface SlimeDataConverter { 12 | 13 | // Will return new (fixed) instance 14 | SlimeWorld applyDataFixers(SlimeWorld world); 15 | 16 | CompoundBinaryTag convertChunkTo1_13(CompoundBinaryTag globalTag); 17 | 18 | List convertEntities(List input, int from, int to); 19 | List convertTileEntities(List input, int from, int to); 20 | ListBinaryTag convertBlockPalette(ListBinaryTag input, int from, int to); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/SlimeNMSBridge.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeWorld; 4 | import com.infernalsuite.asp.api.world.SlimeWorldInstance; 5 | import net.kyori.adventure.nbt.CompoundBinaryTag; 6 | import net.kyori.adventure.nbt.ListBinaryTag; 7 | import net.kyori.adventure.util.Services; 8 | import org.bukkit.World; 9 | import org.bukkit.persistence.PersistentDataContainer; 10 | import org.jetbrains.annotations.ApiStatus; 11 | 12 | import java.io.IOException; 13 | import java.util.List; 14 | 15 | @ApiStatus.Internal 16 | public interface SlimeNMSBridge { 17 | 18 | // Overriding 19 | boolean loadOverworldOverride(); 20 | 21 | boolean loadNetherOverride(); 22 | 23 | boolean loadEndOverride(); 24 | 25 | void setDefaultWorlds(SlimeWorld normalWorld, SlimeWorld netherWorld, SlimeWorld endWorld) throws IOException; 26 | 27 | SlimeWorldInstance loadInstance(SlimeWorld slimeWorld); 28 | 29 | SlimeWorldInstance getInstance(World world); 30 | 31 | int getCurrentVersion(); 32 | 33 | static SlimeNMSBridge instance() { 34 | return Holder.INSTANCE; 35 | } 36 | 37 | void extractCraftPDC(PersistentDataContainer source, CompoundBinaryTag.Builder builder); 38 | 39 | SlimeDataConverter getSlimeDataConverter(); 40 | 41 | @ApiStatus.Internal 42 | class Holder { 43 | private static final SlimeNMSBridge INSTANCE = Services.service(SlimeNMSBridge.class).orElseThrow(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/events/LoadSlimeWorldEvent.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.events; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeWorldInstance; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.HandlerList; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Objects; 9 | 10 | public class LoadSlimeWorldEvent extends Event { 11 | 12 | private static final HandlerList handlers = new HandlerList(); 13 | private final SlimeWorldInstance slimeWorld; 14 | 15 | public LoadSlimeWorldEvent(SlimeWorldInstance slimeWorld) { 16 | super(false); 17 | this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); 18 | } 19 | 20 | public static HandlerList getHandlerList() { 21 | return handlers; 22 | } 23 | 24 | @Override 25 | public @NotNull HandlerList getHandlers() { 26 | return handlers; 27 | } 28 | 29 | public SlimeWorldInstance getSlimeWorld() { 30 | return slimeWorld; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/CorruptedWorldException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a world could not 5 | * be read from its data file. 6 | */ 7 | public class CorruptedWorldException extends SlimeException { 8 | 9 | public CorruptedWorldException(String world) { 10 | this(world, null); 11 | } 12 | 13 | public CorruptedWorldException(String world, Exception ex) { 14 | super("World " + world + " seems to be corrupted", ex); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/InvalidWorldException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | import java.io.File; 4 | import java.nio.file.Path; 5 | 6 | /** 7 | * Exception thrown when a folder does 8 | * not contain a valid Minecraft world. 9 | */ 10 | public class InvalidWorldException extends SlimeException { 11 | 12 | public InvalidWorldException(Path worldDir, String reason) { 13 | super("Directory " + worldDir.toString() + " does not contain a valid MC world! " + reason); 14 | } 15 | 16 | public InvalidWorldException(Path worldDir) { 17 | super("Directory " + worldDir.toString() + " does not contain a valid MC world!"); 18 | } 19 | 20 | public static InvalidWorldException legacy(File worldDir, String reason) { 21 | return new InvalidWorldException(worldDir.toPath(), reason); 22 | } 23 | 24 | public static InvalidWorldException legacy(File worldDir) { 25 | return new InvalidWorldException(worldDir.toPath()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/NewerFormatException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a world is encoded 5 | * using a newer SRF format than the one that 6 | * SWM supports. 7 | */ 8 | public class NewerFormatException extends SlimeException { 9 | 10 | public NewerFormatException(byte version) { 11 | super("v" + version); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/SlimeException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Generic SWM exception. 5 | */ 6 | public class SlimeException extends Exception { 7 | 8 | public SlimeException(String message) { 9 | super(message); 10 | } 11 | 12 | public SlimeException(String message, Exception ex) { 13 | super(message, ex); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/UnknownWorldException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a 5 | * world could not be found. 6 | */ 7 | public class UnknownWorldException extends SlimeException { 8 | 9 | public UnknownWorldException(String world) { 10 | super("Unknown world " + world); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a world 5 | * already exists inside a data source. 6 | */ 7 | public class WorldAlreadyExistsException extends SlimeException { 8 | 9 | public WorldAlreadyExistsException(String world) { 10 | super("World " + world + " already exists!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldLoadedException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a world is loaded 5 | * when trying to import it. 6 | */ 7 | public class WorldLoadedException extends SlimeException { 8 | 9 | public WorldLoadedException(String worldName) { 10 | super("World " + worldName + " is loaded! Unload it before importing."); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldTooBigException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.exceptions; 2 | 3 | /** 4 | * Exception thrown when a MC world is 5 | * too big to be converted into the SRF. 6 | */ 7 | public class WorldTooBigException extends SlimeException { 8 | 9 | public WorldTooBigException(String worldName) { 10 | super("World " + worldName + " is too big to be converted into the SRF!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeLoader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.loaders; 2 | 3 | import com.infernalsuite.asp.api.exceptions.UnknownWorldException; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | /** 9 | * SlimeLoaders are in charge of loading worlds 10 | * from a data source, and also locking and 11 | * deleting them. 12 | */ 13 | public interface SlimeLoader { 14 | 15 | /** 16 | * Read a world's data file. 17 | * 18 | * @param worldName The name of the world. 19 | * @return The world's data file, contained inside a byte array. 20 | * @throws UnknownWorldException if the world cannot be found. 21 | * @throws IOException if the world could not be obtained. 22 | */ 23 | byte[] readWorld(String worldName) throws UnknownWorldException, IOException; 24 | 25 | /** 26 | * Checks whether a world exists 27 | * inside the data source. 28 | * 29 | * @param worldName The name of the world. 30 | * @return true if the world exists inside the data source, false otherwhise. 31 | * @throws IOException if the world could not be obtained. 32 | */ 33 | boolean worldExists(String worldName) throws IOException; 34 | 35 | /** 36 | * Returns the current saved world names. 37 | * 38 | * @return a list containing all the world names 39 | * @throws IOException if the list could not be obtained 40 | */ 41 | List listWorlds() throws IOException; 42 | 43 | /** 44 | * Saves the world's data file. This method will also 45 | * lock the world, in case it's not locked already. 46 | * 47 | * @param worldName The name of the world. 48 | * @param serializedWorld The world's data file, contained inside a byte array. 49 | * @throws IOException if the world could not be saved. 50 | */ 51 | void saveWorld(String worldName, byte[] serializedWorld) throws IOException; 52 | 53 | /** 54 | * Deletes a world from the data source. 55 | * 56 | * @param worldName name of the world 57 | * @throws UnknownWorldException if the world could not be found. 58 | * @throws IOException if the world could not be deleted. 59 | */ 60 | void deleteWorld(String worldName) throws UnknownWorldException, IOException; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeSerializationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.loaders; 2 | 3 | import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; 4 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 5 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 6 | import com.infernalsuite.asp.api.world.SlimeWorld; 7 | import com.infernalsuite.asp.api.world.SlimeWorldInstance; 8 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 9 | import org.jetbrains.annotations.ApiStatus; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.io.IOException; 14 | 15 | @ApiStatus.Experimental 16 | public interface SlimeSerializationAdapter { 17 | 18 | /** 19 | * Serializes a {@link SlimeWorld} with the current format {@link SlimeSerializationAdapter#getSlimeFormat()}. 20 | *

21 | * If the world is loaded, make sure to use a serializable copy that can be obtained with {@link SlimeWorldInstance#getSerializableCopy()}. 22 | * 23 | * @param slimeWorld World to serialize. 24 | * @return Serialized world data in bytes. 25 | * @throws IllegalArgumentException If the world is a {@link SlimeWorldInstance} 26 | */ 27 | byte[] serializeWorld(@NotNull SlimeWorld slimeWorld); 28 | 29 | /** 30 | * Deserializes a world from the given byte array and applies data fixers to the world. 31 | *

32 | * Unlike {@link AdvancedSlimePaperAPI#readWorld(SlimeLoader, String, boolean, SlimePropertyMap)} this does not 33 | * save data fixed worlds to the provided {@link SlimeLoader} automatically. 34 | * 35 | * @param worldName Name of the world. 36 | * @param serializedWorld Serialized world data in bytes. 37 | * @param loader {@link SlimeLoader} used when saving the world. 38 | * @param propertyMap A {@link SlimePropertyMap} object containing all the properties of the world. 39 | * @param readOnly Whether read-only mode is enabled. 40 | * @return A {@link SlimeWorld}, which is the in-memory representation of the world. 41 | * @throws IOException if there was a generic problem reading the world. 42 | * @throws CorruptedWorldException if the world retrieved cannot be properly parsed into a {@link SlimeWorld} object. 43 | * @throws NewerFormatException if the world uses a newer version of the SRF. 44 | */ 45 | @NotNull SlimeWorld deserializeWorld(@NotNull String worldName, byte[] serializedWorld, @Nullable SlimeLoader loader, 46 | @NotNull SlimePropertyMap propertyMap, boolean readOnly) 47 | throws CorruptedWorldException, NewerFormatException, IOException; 48 | 49 | /** 50 | * The current slime format version used by AdvancedSlimePaper. 51 | * 52 | * @return The slime format version 53 | */ 54 | int getSlimeFormat(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/loaders/UpdatableLoader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.loaders; 2 | 3 | import java.io.IOException; 4 | 5 | public abstract class UpdatableLoader implements SlimeLoader { 6 | 7 | public abstract void update() throws NewerStorageException, IOException; 8 | 9 | public static class NewerStorageException extends Exception { 10 | 11 | private final int implementationVersion; 12 | private final int storageVersion; 13 | 14 | 15 | public NewerStorageException(int implementationVersion, int storageVersion) { 16 | this.implementationVersion = implementationVersion; 17 | this.storageVersion = storageVersion; 18 | } 19 | 20 | public int getImplementationVersion() { 21 | return implementationVersion; 22 | } 23 | 24 | public int getStorageVersion() { 25 | return storageVersion; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/utils/NibbleArray.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.utils; 2 | 3 | /** 4 | * Credits to Minikloon for this class. 5 | * 6 | * Source: https://github.com/Minikloon/CraftyWorld/blob/master/crafty-common/src/main/kotlin/world/crafty/common/utils/NibbleArray.kt 7 | */ 8 | public class NibbleArray { 9 | 10 | private final byte[] backing; 11 | 12 | public NibbleArray(int size) { 13 | this(new byte[size / 2]); 14 | } 15 | 16 | public NibbleArray(byte[] backing) { 17 | this.backing = backing; 18 | } 19 | 20 | public int get(int index) { 21 | int value = this.backing[index / 2]; 22 | 23 | return index % 2 == 0 ? value & 0xF : (value & 0xF0) >> 4; 24 | } 25 | 26 | public void set(int index, int value) { 27 | int nibble = value & 0xF; 28 | int halfIndex = index / 2; 29 | int previous = this.backing[halfIndex]; 30 | 31 | if (index % 2 == 0) { 32 | this.backing[halfIndex] = (byte) (previous & 0xF0 | nibble); 33 | } else { 34 | this.backing[halfIndex] = (byte) (previous & 0xF | nibble << 4); 35 | } 36 | } 37 | 38 | public byte[] getBacking() { 39 | return backing; 40 | } 41 | 42 | public NibbleArray clone() { 43 | return new NibbleArray(this.backing.clone()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.utils; 2 | 3 | /** 4 | * Class containing some standards of the SRF. 5 | */ 6 | public class SlimeFormat { 7 | 8 | /** First bytes of every SRF file **/ 9 | public static final byte[] SLIME_HEADER = new byte[] { -79, 11 }; 10 | 11 | /** Latest version of the SRF that SWM supports **/ 12 | public static final byte SLIME_VERSION = 12; 13 | } 14 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world; 2 | 3 | import net.kyori.adventure.nbt.BinaryTag; 4 | import net.kyori.adventure.nbt.CompoundBinaryTag; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import javax.annotation.Nullable; 9 | 10 | /** 11 | * In-memory representation of a SRF chunk. 12 | */ 13 | public interface SlimeChunk { 14 | 15 | /** 16 | * Returns the X coordinate of the chunk. 17 | * 18 | * @return X coordinate of the chunk. 19 | */ 20 | int getX(); 21 | 22 | /** 23 | * Returns the Z coordinate of the chunk. 24 | * 25 | * @return Z coordinate of the chunk. 26 | */ 27 | int getZ(); 28 | 29 | /** 30 | * Returns all the sections of the chunk. 31 | * 32 | * @return A {@link SlimeChunkSection} array. 33 | */ 34 | SlimeChunkSection[] getSections(); 35 | 36 | /** 37 | * Returns the height maps of the chunk. 38 | * 39 | * @return A {@link CompoundBinaryTag} containing all the height maps of the chunk. 40 | */ 41 | CompoundBinaryTag getHeightMaps(); 42 | 43 | /** 44 | * Returns all the tile entities of the chunk. 45 | * 46 | * @return A {@link CompoundBinaryTag} containing all the tile entities of the chunk. 47 | */ 48 | List getTileEntities(); 49 | 50 | /** 51 | * Returns all the entities of the chunk. 52 | * 53 | * @return A {@link CompoundBinaryTag} containing all the entities 54 | */ 55 | List getEntities(); 56 | 57 | /** 58 | * Returns the extra data of the chunk. 59 | * Any information can be stored in this {@link Map} 60 | * and later retrieved, as it's saved alongside the chunk data. 61 | *
62 | * Beware, a compound tag under the key "ChunkBukkitValues" will be stored here. 63 | * It is used for storing chunk-based Bukkit PDC. Do not overwrite it. 64 | * 65 | * @return A {@link Map} containing the extra data of the chunk as NBT tags, 66 | */ 67 | Map getExtraData(); 68 | 69 | /** 70 | * Upgrade data used to fix the chunks. 71 | * Not intended to be serialized. 72 | * @return A {@link CompoundBinaryTag} containing the upgrade data of the chunk, 73 | */ 74 | @Nullable 75 | CompoundBinaryTag getUpgradeData(); 76 | } 77 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunkSection.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world; 2 | 3 | import com.infernalsuite.asp.api.utils.NibbleArray; 4 | import net.kyori.adventure.nbt.CompoundBinaryTag; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | /** 8 | * In-memory representation of a SRF chunk section. 9 | */ 10 | public interface SlimeChunkSection { 11 | 12 | CompoundBinaryTag getBlockStatesTag(); 13 | 14 | CompoundBinaryTag getBiomeTag(); 15 | 16 | /** 17 | * Returns the block light data. 18 | * 19 | * @return A {@link NibbleArray} with the block light data. 20 | */ 21 | @Nullable 22 | NibbleArray getBlockLight(); 23 | 24 | /** 25 | * Returns the sky light data. 26 | * 27 | * @return A {@link NibbleArray} containing the sky light data. 28 | */ 29 | @Nullable 30 | NibbleArray getSkyLight(); 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorldInstance.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world; 2 | 3 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 4 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import org.bukkit.World; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.concurrent.ConcurrentMap; 10 | 11 | /* 12 | * Represents a loaded SlimeWorld. This world is synchronized with the state of the bukkit world. 13 | */ 14 | public interface SlimeWorldInstance extends SlimeWorld { 15 | 16 | /** 17 | * Returns the bukkit instance of the loaded world. 18 | * 19 | * @return The bukkit world. 20 | */ 21 | @NotNull World getBukkitWorld(); 22 | 23 | SlimeWorld getSerializableCopy(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperty.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties; 2 | 3 | import net.kyori.adventure.nbt.BinaryTag; 4 | 5 | import java.util.function.Function; 6 | 7 | /** 8 | * A property describing behavior of a slime world. 9 | */ 10 | public abstract class SlimeProperty { 11 | 12 | private final String key; 13 | private final T defaultValue; 14 | private final Function validator; 15 | 16 | protected SlimeProperty(String key, T defaultValue) { 17 | this(key, defaultValue, null); 18 | } 19 | 20 | protected SlimeProperty(String key, T defaultValue, Function validator) { 21 | this.key = key; 22 | 23 | if (defaultValue != null && validator != null && !validator.apply(defaultValue)) { 24 | throw new IllegalArgumentException("Invalid default value for property " + key + "! " + defaultValue); 25 | } 26 | 27 | this.defaultValue = defaultValue; 28 | this.validator = validator; 29 | } 30 | 31 | protected abstract Z createTag(T value); 32 | 33 | protected abstract T readValue(Z tag); 34 | 35 | protected abstract Z cast(BinaryTag rawTag); 36 | 37 | public final boolean applyValidator(T value) { 38 | return this.validator == null || this.validator.apply(value); 39 | } 40 | 41 | public final String getKey() { 42 | return this.key; 43 | } 44 | 45 | public final T getDefaultValue() { 46 | return this.defaultValue; 47 | } 48 | 49 | public final Function getValidator() { 50 | return this.validator; 51 | } 52 | 53 | @Override 54 | public final String toString() { 55 | return "SlimeProperty{" + 56 | "key='" + key + '\'' + 57 | ", defaultValue=" + defaultValue + 58 | '}'; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties; 2 | 3 | import net.kyori.adventure.nbt.BinaryTag; 4 | import net.kyori.adventure.nbt.CompoundBinaryTag; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * A Property Map object. 11 | */ 12 | public class SlimePropertyMap { 13 | 14 | private final Map properties; 15 | 16 | public SlimePropertyMap() { 17 | this(new HashMap<>()); 18 | } 19 | 20 | public SlimePropertyMap(final Map properties) { 21 | this.properties = properties; 22 | } 23 | 24 | /** 25 | * Return the current value of the given property 26 | * 27 | * @param property The slime property 28 | * @return The current value 29 | */ 30 | public T getValue(final SlimeProperty property) { 31 | if (this.properties.containsKey(property.getKey())) { 32 | return property.readValue(property.cast(this.properties.get(property.getKey()))); 33 | } else { 34 | return property.getDefaultValue(); 35 | } 36 | } 37 | 38 | /** 39 | * Return the properties (CompoundMap) 40 | * 41 | * @return The properties 42 | */ 43 | public Map getProperties() { 44 | return this.properties; 45 | } 46 | 47 | /** 48 | * Update the value of the given property 49 | * 50 | * @param property The slime property 51 | * @param value The new value 52 | * @throws IllegalArgumentException if the value fails validation. 53 | */ 54 | public void setValue(final SlimeProperty property, final T value) { 55 | if (!property.applyValidator(value)) throw new IllegalArgumentException("'%s' is not a valid property value.".formatted(value)); 56 | this.properties.put(property.getKey(), property.createTag(value)); 57 | } 58 | 59 | /** 60 | * Copies all values from the specified {@link SlimePropertyMap}. 61 | * If the same property has different values on both maps, the one 62 | * on the provided map will be used. 63 | * 64 | * @param other A {@link SlimePropertyMap}. 65 | */ 66 | public void merge(final SlimePropertyMap other) { 67 | this.properties.putAll(other.properties); 68 | } 69 | 70 | /** 71 | * Returns a {@link CompoundBinaryTag} containing every property set in this map. 72 | * 73 | * @return A {@link CompoundBinaryTag} with all the properties stored in this map. 74 | */ 75 | public CompoundBinaryTag toCompound() { 76 | return CompoundBinaryTag.builder().put(this.properties).build(); 77 | } 78 | 79 | public static SlimePropertyMap fromCompound(final CompoundBinaryTag tag) { 80 | final Map tags = new HashMap<>(tag.size()); 81 | tag.forEach(entry -> tags.put(entry.getKey(), entry.getValue())); 82 | return new SlimePropertyMap(tags); 83 | } 84 | 85 | @SuppressWarnings("MethodDoesntCallSuperMethod") 86 | public SlimePropertyMap clone() { 87 | return new SlimePropertyMap(new HashMap<>(this.properties)); 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return "SlimePropertyMap{" + properties + '}'; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyBoolean.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.ByteBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * A slime property of type boolean 13 | */ 14 | public class SlimePropertyBoolean extends SlimeProperty { 15 | 16 | public static SlimePropertyBoolean create(final @NotNull String key, final boolean defaultValue) { 17 | Preconditions.checkNotNull(key, "Key cannot be null"); 18 | return new SlimePropertyBoolean(key, defaultValue); 19 | } 20 | 21 | public static SlimePropertyBoolean create(final @NotNull String key, final boolean defaultValue, final @NotNull Function validator) { 22 | Preconditions.checkNotNull(key, "Key cannot be null"); 23 | Preconditions.checkNotNull(validator, "Use SlimePropertyBoolean#create(String, boolean) instead"); 24 | return new SlimePropertyBoolean(key, defaultValue, validator); 25 | } 26 | 27 | /** 28 | * @deprecated Use {@link #create(String, boolean)} instead 29 | */ 30 | @Deprecated(forRemoval = true) 31 | public SlimePropertyBoolean(String key, Boolean defaultValue) { 32 | super(key, defaultValue); 33 | } 34 | 35 | /** 36 | * @deprecated Use {@link #create(String, boolean, Function)} instead 37 | */ 38 | @Deprecated(forRemoval = true) 39 | public SlimePropertyBoolean(String key, Boolean defaultValue, Function validator) { 40 | super(key, defaultValue, validator); 41 | } 42 | 43 | @Override 44 | protected ByteBinaryTag createTag(final Boolean value) { 45 | return value ? ByteBinaryTag.ONE : ByteBinaryTag.ZERO; 46 | } 47 | 48 | @Override 49 | protected Boolean readValue(final ByteBinaryTag tag) { 50 | return tag.value() == 1; 51 | } 52 | 53 | @Override 54 | protected ByteBinaryTag cast(BinaryTag rawTag) { 55 | return (ByteBinaryTag) rawTag; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByte.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.ByteBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyByte extends SlimeProperty { 12 | 13 | public static SlimePropertyByte create(final @NotNull String key, final byte defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyByte(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyByte create(final @NotNull String key, final byte defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyByte#create(String, byte) instead"); 21 | return new SlimePropertyByte(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyByte(String key, Byte defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyByte(String key, Byte defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected ByteBinaryTag createTag(final Byte value) { 34 | return ByteBinaryTag.byteBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected Byte readValue(final ByteBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected ByteBinaryTag cast(BinaryTag rawTag) { 44 | return (ByteBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByteArray.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.ByteArrayBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyByteArray extends SlimeProperty { 12 | 13 | public static SlimePropertyByteArray create(final @NotNull String key, final byte[] defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyByteArray(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyByteArray create(final @NotNull String key, final byte[] defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyByteArray#create(String, byte[]) instead"); 21 | return new SlimePropertyByteArray(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyByteArray(String key, byte[] defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyByteArray(String key, byte[] defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected ByteArrayBinaryTag createTag(final byte[] value) { 34 | return ByteArrayBinaryTag.byteArrayBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected byte[] readValue(final ByteArrayBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected ByteArrayBinaryTag cast(BinaryTag rawTag) { 44 | return (ByteArrayBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyDouble.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.DoubleBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyDouble extends SlimeProperty { 12 | 13 | public static SlimePropertyDouble create(final @NotNull String key, final double defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyDouble(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyDouble create(final @NotNull String key, final double defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyDouble#create(String, double) instead"); 21 | return new SlimePropertyDouble(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyDouble(String key, Double defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyDouble(String key, Double defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected DoubleBinaryTag createTag(final Double value) { 34 | return DoubleBinaryTag.doubleBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected Double readValue(final DoubleBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected DoubleBinaryTag cast(BinaryTag rawTag) { 44 | return (DoubleBinaryTag) rawTag; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyFloat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.FloatBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * A slime property of type float 13 | */ 14 | public class SlimePropertyFloat extends SlimeProperty { 15 | 16 | public static SlimePropertyFloat create(final @NotNull String key, final float defaultValue) { 17 | Preconditions.checkNotNull(key, "Key cannot be null"); 18 | return new SlimePropertyFloat(key, defaultValue); 19 | } 20 | 21 | public static SlimePropertyFloat create(final @NotNull String key, final float defaultValue, final @NotNull Function validator) { 22 | Preconditions.checkNotNull(key, "Key cannot be null"); 23 | Preconditions.checkNotNull(validator, "Use SlimePropertyFloat#create(String, float) instead"); 24 | return new SlimePropertyFloat(key, defaultValue, validator); 25 | } 26 | 27 | /** 28 | * @deprecated use {@link #create(String, float)} instead 29 | */ 30 | @Deprecated(forRemoval = true) 31 | public SlimePropertyFloat(String key, Float defaultValue) { 32 | super(key, defaultValue); 33 | } 34 | 35 | /** 36 | * @deprecated use {@link #create(String, float, Function)} instead 37 | */ 38 | @Deprecated(forRemoval = true) 39 | public SlimePropertyFloat(String key, Float defaultValue, Function validator) { 40 | super(key, defaultValue, validator); 41 | } 42 | 43 | @Override 44 | protected FloatBinaryTag createTag(final Float value) { 45 | return FloatBinaryTag.floatBinaryTag(value); 46 | } 47 | 48 | @Override 49 | protected Float readValue(final FloatBinaryTag tag) { 50 | return tag.value(); 51 | } 52 | 53 | @Override 54 | protected FloatBinaryTag cast(BinaryTag rawTag) { 55 | return (FloatBinaryTag) rawTag; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyInt.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.IntBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * A slime property of type integer 13 | */ 14 | public class SlimePropertyInt extends SlimeProperty { 15 | 16 | public static SlimePropertyInt create(final @NotNull String key, final int defaultValue) { 17 | Preconditions.checkNotNull(key, "Key cannot be null"); 18 | return new SlimePropertyInt(key, defaultValue); 19 | } 20 | 21 | public static SlimePropertyInt create(final @NotNull String key, final int defaultValue, final @NotNull Function validator) { 22 | Preconditions.checkNotNull(key, "Key cannot be null"); 23 | Preconditions.checkNotNull(validator, "Use SlimePropertyInt#create(String, int) instead"); 24 | return new SlimePropertyInt(key, defaultValue, validator); 25 | } 26 | 27 | /** 28 | * @deprecated Use {@link #create(String, int)} instead 29 | */ 30 | @Deprecated(forRemoval = true) 31 | public SlimePropertyInt(String key, Integer defaultValue) { 32 | super(key, defaultValue); 33 | } 34 | 35 | /** 36 | * @deprecated Use {@link #create(String, int, Function)} instead 37 | */ 38 | @Deprecated(forRemoval = true) 39 | public SlimePropertyInt(String key, Integer defaultValue, Function validator) { 40 | super(key, defaultValue, validator); 41 | } 42 | 43 | @Override 44 | protected IntBinaryTag createTag(final Integer value) { 45 | return IntBinaryTag.intBinaryTag(value); 46 | } 47 | 48 | @Override 49 | protected Integer readValue(final IntBinaryTag tag) { 50 | return tag.value(); 51 | } 52 | 53 | @Override 54 | protected IntBinaryTag cast(BinaryTag rawTag) { 55 | return (IntBinaryTag) rawTag; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyIntArray.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.IntArrayBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyIntArray extends SlimeProperty { 12 | 13 | public static SlimePropertyIntArray create(final @NotNull String key, final int[] defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyIntArray(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyIntArray create(final @NotNull String key, final int[] defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyIntArray#create(String, int[]) instead"); 21 | return new SlimePropertyIntArray(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyIntArray(String key, int[] defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyIntArray(String key, int[] defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected IntArrayBinaryTag createTag(final int[] value) { 34 | return IntArrayBinaryTag.intArrayBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected int[] readValue(final IntArrayBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected IntArrayBinaryTag cast(BinaryTag rawTag) { 44 | return (IntArrayBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLong.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.LongBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyLong extends SlimeProperty { 12 | 13 | public static SlimePropertyLong create(final @NotNull String key, final long defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyLong(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyLong create(final @NotNull String key, final long defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyLong#create(String, long) instead"); 21 | return new SlimePropertyLong(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyLong(String key, Long defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyLong(String key, Long defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected LongBinaryTag createTag(final Long value) { 34 | return LongBinaryTag.longBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected Long readValue(final LongBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected LongBinaryTag cast(BinaryTag rawTag) { 44 | return (LongBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLongArray.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.LongArrayBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyLongArray extends SlimeProperty { 12 | 13 | public static SlimePropertyLongArray create(final @NotNull String key, final long[] defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyLongArray(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyLongArray create(final @NotNull String key, final long[] defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyLongArray#create(String, long[]) instead"); 21 | return new SlimePropertyLongArray(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyLongArray(String key, long[] defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyLongArray(String key, long[] defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected LongArrayBinaryTag createTag(final long[] value) { 34 | return LongArrayBinaryTag.longArrayBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected long[] readValue(final LongArrayBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected LongArrayBinaryTag cast(BinaryTag rawTag) { 44 | return (LongArrayBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyShort.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.ShortBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | public class SlimePropertyShort extends SlimeProperty { 12 | 13 | public static SlimePropertyShort create(final @NotNull String key, final short defaultValue) { 14 | Preconditions.checkNotNull(key, "Key cannot be null"); 15 | return new SlimePropertyShort(key, defaultValue); 16 | } 17 | 18 | public static SlimePropertyShort create(final @NotNull String key, final short defaultValue, final @NotNull Function validator) { 19 | Preconditions.checkNotNull(key, "Key cannot be null"); 20 | Preconditions.checkNotNull(validator, "Use SlimePropertyShort#create(String, short) instead"); 21 | return new SlimePropertyShort(key, defaultValue, validator); 22 | } 23 | 24 | private SlimePropertyShort(String key, Short defaultValue) { 25 | super(key, defaultValue); 26 | } 27 | 28 | private SlimePropertyShort(String key, Short defaultValue, Function validator) { 29 | super(key, defaultValue, validator); 30 | } 31 | 32 | @Override 33 | protected ShortBinaryTag createTag(final Short value) { 34 | return ShortBinaryTag.shortBinaryTag(value); 35 | } 36 | 37 | @Override 38 | protected Short readValue(final ShortBinaryTag tag) { 39 | return tag.value(); 40 | } 41 | 42 | @Override 43 | protected ShortBinaryTag cast(BinaryTag rawTag) { 44 | return (ShortBinaryTag) rawTag; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyString.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.api.world.properties.type; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.infernalsuite.asp.api.world.properties.SlimeProperty; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.StringBinaryTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * A slime property of type integer 13 | */ 14 | public class SlimePropertyString extends SlimeProperty { 15 | 16 | public static SlimePropertyString create(final @NotNull String key, final String defaultValue) { 17 | Preconditions.checkNotNull(key, "Key cannot be null"); 18 | return new SlimePropertyString(key, defaultValue); 19 | } 20 | 21 | public static SlimePropertyString create(final @NotNull String key, final String defaultValue, final @NotNull Function validator) { 22 | Preconditions.checkNotNull(key, "Key cannot be null"); 23 | Preconditions.checkNotNull(validator, "Use SlimePropertyString#create(String, String) instead"); 24 | return new SlimePropertyString(key, defaultValue, validator); 25 | } 26 | 27 | /** 28 | * @deprecated Use {@link #create(String, String)} instead 29 | */ 30 | @Deprecated(forRemoval = true) 31 | public SlimePropertyString(String key, String defaultValue) { 32 | super(key, defaultValue); 33 | } 34 | 35 | /** 36 | * @deprecated Use {@link #create(String, String, Function)} instead 37 | */ 38 | @Deprecated(forRemoval = true) 39 | public SlimePropertyString(String key, String defaultValue, Function validator) { 40 | super(key, defaultValue, validator); 41 | } 42 | 43 | @Override 44 | protected StringBinaryTag createTag(final String value) { 45 | return StringBinaryTag.stringBinaryTag(value); 46 | } 47 | 48 | @Override 49 | protected String readValue(final StringBinaryTag tag) { 50 | return tag.value(); 51 | } 52 | 53 | @Override 54 | protected StringBinaryTag cast(BinaryTag rawTag) { 55 | return (StringBinaryTag) rawTag; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /api/src/main/resources/main.yml: -------------------------------------------------------------------------------- 1 | # TO PREVENT NULL. 2 | updater: 3 | enabled: false 4 | onjoinmessage: true -------------------------------------------------------------------------------- /api/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: SlimeWorldManager 2 | version: 2.10.0 3 | main: com.grinderwolf.swm.plugin.SWMPlugin 4 | authors: 5 | - Grinderwolf 6 | - Paul19988 7 | - Jools 8 | - SoapTurtle 9 | - Gerolmed 10 | - b0ykoe 11 | - Owen 12 | - ComputerNerd100 13 | description: Advanced Slime World Manager is a resource that implements the Slime Region Format, designed by the Hypixel Team to load and save worlds more efficiently. 14 | api-version: 1.18 15 | commands: 16 | swm: 17 | aliases: aswm 18 | description: Main SWM command. 19 | usage: /swm 20 | -------------------------------------------------------------------------------- /api/src/main/resources/sources.yml: -------------------------------------------------------------------------------- 1 | # Inside this file is the configuration options 2 | # for the data sources that SWM supports 3 | mysql: 4 | enabled: false 5 | host: 127.0.0.1 6 | port: 3306 7 | username: slimeworldmanager 8 | password: '' 9 | database: slimeworldmanager 10 | usessl: false 11 | mongodb: 12 | enabled: false 13 | host: 127.0.0.1 14 | port: 27017 15 | auth: admin 16 | username: slimeworldmanager 17 | password: '' 18 | database: slimeworldmanager 19 | collection: worlds 20 | uri: '' 21 | file: 22 | path: slime_worlds -------------------------------------------------------------------------------- /api/src/main/resources/worlds.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration file for all the slime worlds 2 | # 3 | # Example configuration: 4 | # worlds: 5 | # world1: 6 | # source: file 7 | # difficulty: peaceful 8 | # spawn: 32, 40, 97 9 | # allowMonsters: false 10 | # allowAnimals: false 11 | # loadOnStartup: true 12 | # readOnly: true 13 | # world2: 14 | # source: mysql 15 | # difficulty: hard 16 | # spawn: -140, 38, 159 17 | # allowMonsters: false 18 | # allowAnimals: false 19 | # loadOnStartup: false 20 | # readOnly: true 21 | # world3: 22 | # source: seaweed 23 | # difficulty: easy 24 | # spawn: -59, 67, -2 25 | # allowMonsters: false 26 | # allowAnimals: true 27 | # loadOnStartup: true 28 | # readOnly: false 29 | worlds: -------------------------------------------------------------------------------- /aspaper-api/build.gradle.kts.patch: -------------------------------------------------------------------------------- 1 | --- a/paper-api/build.gradle.kts 2 | +++ b/paper-api/build.gradle.kts 3 | @@ -39,7 +_,7 @@ 4 | } 5 | 6 | dependencies { 7 | - 8 | + api(project(":api")) //ASP 9 | // api dependencies are listed transitively to API consumers 10 | api("com.google.guava:guava:33.3.1-jre") 11 | api("com.google.code.gson:gson:2.11.0") 12 | @@ -93,7 +_,7 @@ 13 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 14 | } 15 | 16 | -val generatedApiPath: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() 17 | +val generatedApiPath: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() 18 | idea { 19 | module { 20 | generatedSourceDirs.add(generatedApiPath.toFile()) 21 | @@ -103,6 +_,18 @@ 22 | main { 23 | java { 24 | srcDir(generatedApiPath) 25 | + srcDir(file("../paper-api/src/main/java")) 26 | + } 27 | + resources { 28 | + srcDir(file("../paper-api/src/main/resources")) 29 | + } 30 | + } 31 | + test { 32 | + java { 33 | + srcDir(file("../paper-api/src/test/java")) 34 | + } 35 | + resources { 36 | + srcDir(file("../paper-api/src/test/resources")) 37 | } 38 | } 39 | } 40 | @@ -169,7 +_,7 @@ 41 | 42 | tasks.withType { 43 | val options = options as StandardJavadocDocletOptions 44 | - options.overview = "src/main/javadoc/overview.html" 45 | + options.overview = "../paper-api/src/main/javadoc/overview.html" 46 | options.use() 47 | options.isDocFilesSubDirs = true 48 | options.links( 49 | @@ -202,11 +_,11 @@ 50 | } 51 | 52 | // workaround for https://github.com/gradle/gradle/issues/4046 53 | - inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset") 54 | + inputs.dir("../paper-api/src/main/javadoc").withPropertyName("javadoc-sourceset") 55 | val fsOps = services.fileSystemOperations 56 | doLast { 57 | fsOps.copy { 58 | - from("src/main/javadoc") { 59 | + from("../paper-api/src/main/javadoc") { 60 | include("**/doc-files/**") 61 | } 62 | into("build/docs/javadoc") 63 | -------------------------------------------------------------------------------- /aspaper-api/paper-patches/features/0001-API-Branding.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: David Mayr 3 | Date: Sun, 9 Mar 2025 20:50:45 +0100 4 | Subject: [PATCH] API Branding 5 | 6 | 7 | diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java 8 | index 652ff54e7c50412503725d628bfe72ed03059790..d26bbab5b67b6456a2713e67c2bcfa17e7478992 100644 9 | --- a/src/main/java/io/papermc/paper/ServerBuildInfo.java 10 | +++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java 11 | @@ -19,6 +19,12 @@ public interface ServerBuildInfo { 12 | */ 13 | Key BRAND_PAPER_ID = Key.key("papermc", "paper"); 14 | 15 | + 16 | + /** 17 | + * The brand id for AdvancedSlimePaper. 18 | + */ 19 | + Key BRAND_ADVANCED_SLIME_PAPER_ID = Key.key("infernalsuite", "advancedslimepaper"); 20 | + 21 | /** 22 | * Gets the {@code ServerBuildInfo}. 23 | * 24 | -------------------------------------------------------------------------------- /aspaper-server/build.gradle.kts.patch: -------------------------------------------------------------------------------- 1 | --- a/paper-server/build.gradle.kts 2 | +++ b/paper-server/build.gradle.kts 3 | @@ -21,6 +_,17 @@ 4 | // macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java") 5 | // gitFilePatches = true 6 | 7 | + val aspaper = forks.register("aspaper") { 8 | + upstream.patchDir("paperServer") { 9 | + upstreamPath = "paper-server" 10 | + excludes = setOf("src/minecraft", "patches", "build.gradle.kts") 11 | + patchesDir = rootDirectory.dir("aspaper-server/paper-patches") 12 | + outputDir = rootDirectory.dir("paper-server") 13 | + } 14 | + } 15 | + 16 | + activeFork = aspaper 17 | + 18 | spigot { 19 | buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42" 20 | packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment 21 | @@ -101,7 +_,20 @@ 22 | } 23 | } 24 | 25 | -val log4jPlugins = sourceSets.create("log4jPlugins") 26 | +sourceSets { 27 | + main { 28 | + java { srcDir("../paper-server/src/main/java") } 29 | + resources { srcDir("../paper-server/src/main/resources") } 30 | + } 31 | + test { 32 | + java { srcDir("../paper-server/src/test/java") } 33 | + resources { srcDir("../paper-server/src/test/resources") } 34 | + } 35 | +} 36 | + 37 | +val log4jPlugins = sourceSets.create("log4jPlugins") { 38 | + java { srcDir("../paper-server/src/log4jPlugins/java") } 39 | +} 40 | configurations.named(log4jPlugins.compileClasspathConfigurationName) { 41 | extendsFrom(configurations.compileClasspath.get()) 42 | } 43 | @@ -119,7 +_,9 @@ 44 | } 45 | 46 | dependencies { 47 | - implementation(project(":paper-api")) 48 | + implementation(project(":aspaper-api")) //ASP 49 | + implementation(project(":core")) //ASP 50 | + implementation("commons-io:commons-io:2.11.0") 51 | implementation("ca.spottedleaf:concurrentutil:0.0.3") 52 | implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ 53 | implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 54 | @@ -189,14 +_,14 @@ 55 | val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() 56 | attributes( 57 | "Main-Class" to "org.bukkit.craftbukkit.Main", 58 | - "Implementation-Title" to "Paper", 59 | + "Implementation-Title" to "AdvancedSlimePaper", //ASP 60 | "Implementation-Version" to implementationVersion, 61 | "Implementation-Vendor" to date, 62 | - "Specification-Title" to "Paper", 63 | + "Specification-Title" to "AdvancedSlimePaper", //ASP 64 | "Specification-Version" to project.version, 65 | - "Specification-Vendor" to "Paper Team", 66 | - "Brand-Id" to "papermc:paper", 67 | - "Brand-Name" to "Paper", 68 | + "Specification-Vendor" to "InfernalSuite Team", //ASP 69 | + "Brand-Id" to "infernalsuite:advancedslimepaper", //ASP 70 | + "Brand-Name" to "AdvancedSlimePaper", //ASP 71 | "Build-Number" to (build ?: ""), 72 | "Build-Time" to buildTime.toString(), 73 | "Git-Branch" to gitBranch, 74 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: evlad 3 | Date: Mon, 9 Sep 2024 21:33:11 +0300 4 | Subject: [PATCH] Disable dragon battle 5 | 6 | 7 | diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java 8 | index c5ddf6c0f0ff795da2f7aec8915a081b334423ec..a373f5b8e03f4179a4d9f63d79abc19a38f952b6 100644 9 | --- a/net/minecraft/server/level/ServerLevel.java 10 | +++ b/net/minecraft/server/level/ServerLevel.java 11 | @@ -689,7 +689,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe 12 | ); 13 | this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), this.structureCheck); // CraftBukkit 14 | if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END 15 | - this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit 16 | + // ASP START 17 | + if (bootstrap == null || bootstrap.initial().getPropertyMap().getValue(com.infernalsuite.asp.api.world.properties.SlimeProperties.DRAGON_BATTLE)) { 18 | + this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit 19 | + } else { 20 | + this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), new EndDragonFight.Data(false, true, true, false,Optional.empty(),Optional.empty(),Optional.empty())); // ASP - disable dragon 21 | + } 22 | + // ASP END 23 | } else { 24 | this.dragonFight = null; 25 | } 26 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/features/0002-World-overrides.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> 3 | Date: Mon, 26 Dec 2022 11:25:35 -0500 4 | Subject: [PATCH] World overrides 5 | 6 | 7 | diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java 8 | index 1509c96ae9247d1d5ddd0fc50210f88da10bf3de..bbc4be3c3811ee19bd791cb6626474038d35a298 100644 9 | --- a/net/minecraft/server/MinecraftServer.java 10 | +++ b/net/minecraft/server/MinecraftServer.java 11 | @@ -516,18 +516,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 3 | Date: Mon, 10 Mar 2025 11:41:14 +0100 4 | Subject: [PATCH] Avoid IO call for UUID 5 | 6 | 7 | diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java 8 | index 8429a97047f4321df96fd11559867bce74d0a49c..60608cea4e564c1ce47f8a4de2c1a48986bbdecb 100644 9 | --- a/net/minecraft/server/level/ServerLevel.java 10 | +++ b/net/minecraft/server/level/ServerLevel.java 11 | @@ -617,7 +617,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe 12 | super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor 13 | this.pvpMode = server.isPvpAllowed(); 14 | this.levelStorageAccess = levelStorageAccess; 15 | - this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()); 16 | + this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()) : UUID.randomUUID(); //ASP - avoid IO calls 17 | // CraftBukkit end 18 | this.tickTime = tickTime; 19 | this.server = server; 20 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: David Mayr 3 | Date: Wed, 12 Mar 2025 21:14:56 +0100 4 | Subject: [PATCH] Prevent config disk io on world load 5 | 6 | 7 | diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java 8 | index 60608cea4e564c1ce47f8a4de2c1a48986bbdecb..1b5d65136421b63353b1c6cd8ae5d413ec070b92 100644 9 | --- a/net/minecraft/server/level/ServerLevel.java 10 | +++ b/net/minecraft/server/level/ServerLevel.java 11 | @@ -614,7 +614,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe 12 | ) { 13 | //ASP end 14 | // CraftBukkit start 15 | - super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor 16 | + super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> bootstrap != null ? com.infernalsuite.asp.config.SlimePaperWorldConfig.initializeOrGet() : server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor //ASP - Optimize world config 17 | this.pvpMode = server.isPvpAllowed(); 18 | this.levelStorageAccess = levelStorageAccess; 19 | this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()) : UUID.randomUUID(); //ASP - avoid IO calls 20 | diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java 21 | index 1dbe7c7c1051c3972105534a07ce50d4cf98fc85..e1d3c292b9efccb032245f4f1618f2650f0bc619 100644 22 | --- a/net/minecraft/world/level/Level.java 23 | +++ b/net/minecraft/world/level/Level.java 24 | @@ -851,7 +851,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl 25 | this.maxSectionY = this.maxY >> 4; 26 | this.sectionsCount = this.maxSectionY - this.minSectionY + 1; 27 | // Paper end - getblock optimisations - cache world height/sections 28 | - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot 29 | + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), !(this instanceof com.infernalsuite.asp.level.SlimeLevelInstance)); // Spigot //ASP - Improve Slime IO 30 | this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config 31 | this.generator = gen; 32 | this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); 33 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: David Mayr 3 | Date: Thu, 13 Mar 2025 00:09:20 +0100 4 | Subject: [PATCH] Read only dimension data store 5 | 6 | 7 | diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java 8 | index 6540b2d6a1062d883811ce240c49d30d1925b291..f89eb4e909c2eeb22732dcb368b3758637f036f7 100644 9 | --- a/net/minecraft/server/level/ServerChunkCache.java 10 | +++ b/net/minecraft/server/level/ServerChunkCache.java 11 | @@ -207,7 +207,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon 12 | LOGGER.error("Failed to create dimension data storage directory", (Throwable)var15); 13 | } 14 | 15 | - this.dataStorage = new DimensionDataStorage(path, fixerUpper, level.registryAccess()); 16 | + //ASP start - No dimension data storage 17 | + if(level instanceof com.infernalsuite.asp.level.SlimeLevelInstance) { 18 | + this.dataStorage = new com.infernalsuite.asp.level.ReadOnlyDimensionDataStorage(path, fixerUpper, level.registryAccess()); 19 | + } else { 20 | + this.dataStorage = new DimensionDataStorage(path, fixerUpper, level.registryAccess()); 21 | + } 22 | + //ASP end - No dimension data storage 23 | this.chunkMap = new ChunkMap( 24 | level, 25 | levelStorageAccess, 26 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch: -------------------------------------------------------------------------------- 1 | --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java 2 | +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java 3 | @@ -184,7 +_,9 @@ 4 | }; 5 | } 6 | 7 | - public void close(final boolean save, final boolean halt) { 8 | + public void close(boolean save, boolean halt) { // ASP 9 | + if (this.world instanceof com.infernalsuite.asp.level.SlimeLevelInstance) save = false; // ASP 10 | + 11 | TickThread.ensureTickThread("Closing world off-main"); 12 | if (halt) { 13 | LOGGER.info("Waiting 60s for chunk system to halt for world '" + WorldUtil.getWorldName(this.world) + "'"); 14 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch: -------------------------------------------------------------------------------- 1 | --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java 2 | +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java 3 | @@ -116,7 +_,7 @@ 4 | } 5 | 6 | if (!transientChunk) { 7 | - if (entityChunk != null) { 8 | + if (!(this.world instanceof com.infernalsuite.asp.level.SlimeLevelInstance) && entityChunk != null) { 9 | final List entities = ChunkEntitySlices.readEntities(this.world, entityChunk); 10 | 11 | ((ChunkSystemServerLevel)this.world).moonrise$getEntityLookup().addEntityChunkEntities(entities, new ChunkPos(this.chunkX, this.chunkZ)); 12 | @@ -894,7 +_,7 @@ 13 | final ChunkEntitySlices entityChunk = state.entityChunk(); 14 | final PoiChunk poiChunk = state.poiChunk(); 15 | 16 | - final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk); 17 | + final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk) || world instanceof com.infernalsuite.asp.level.SlimeLevelInstance; //ASP - prevent saving 18 | 19 | // unload chunk data 20 | if (chunk != null) { 21 | @@ -910,13 +_,24 @@ 22 | } 23 | 24 | if (chunk instanceof LevelChunk levelChunk) { 25 | + //ASP start - unloadStage1 sets the entitySlices to null, so we can't get them otherwise unfortunately. 26 | + //This needs to be above world.unload since world.unload removes block entities 27 | + if(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance instance) { 28 | + instance.unload(levelChunk, entityChunk); 29 | + } 30 | + //ASP end 31 | this.world.unload(levelChunk); 32 | } 33 | + 34 | } 35 | 36 | // unload entity data 37 | if (entityChunk != null) { 38 | - this.saveEntities(entityChunk, true); 39 | + //ASP start - prevent saving 40 | + if(!(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance)) { 41 | + this.saveEntities(entityChunk, true); 42 | + } 43 | + //ASP end - prevent saving 44 | // yes this is a hack to pass the compound tag through... 45 | final CompoundTag lastEntityUnload = this.lastEntityUnload; 46 | this.lastEntityUnload = null; 47 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java.patch: -------------------------------------------------------------------------------- 1 | --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java 2 | +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java 3 | @@ -33,7 +_,7 @@ 4 | private static final Logger LOGGER = LoggerFactory.getLogger(ChunkLoadTask.class); 5 | 6 | public final NewChunkHolder chunkHolder; 7 | - private final ChunkDataLoadTask loadTask; 8 | + private final com.infernalsuite.asp.level.CommonLoadTask loadTask; // ASP 9 | 10 | private volatile boolean cancelled; 11 | private NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask; 12 | @@ -45,11 +_,20 @@ 13 | final NewChunkHolder chunkHolder, final Priority priority) { 14 | super(scheduler, world, chunkX, chunkZ); 15 | this.chunkHolder = chunkHolder; 16 | - this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); 17 | - this.loadTask.addCallback((final GenericDataLoadTask.TaskResult result) -> { 18 | - ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement 19 | - ChunkLoadTask.this.tryCompleteLoad(); 20 | - }); 21 | + // ASWM start 22 | + if (world instanceof com.infernalsuite.asp.level.SlimeLevelInstance levelInstance) { 23 | + this.loadTask = levelInstance.getLoadTask(this, scheduler, world, chunkX, chunkZ, priority, result -> { 24 | + ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement 25 | + ChunkLoadTask.this.tryCompleteLoad(); 26 | + }); 27 | + } else { 28 | + this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); 29 | + ((ChunkDataLoadTask) this.loadTask).addCallback((final GenericDataLoadTask.TaskResult result) -> { 30 | + ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement 31 | + ChunkLoadTask.this.tryCompleteLoad(); 32 | + }); 33 | + } 34 | + // ASWM end 35 | } 36 | 37 | private void tryCompleteLoad() { 38 | @@ -276,7 +_,7 @@ 39 | 40 | private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {} 41 | 42 | - private static final class ChunkDataLoadTask extends CallbackDataLoadTask { 43 | + private static final class ChunkDataLoadTask extends CallbackDataLoadTask implements com.infernalsuite.asp.level.CommonLoadTask { // ASP 44 | private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, 45 | final int chunkZ, final Priority priority) { 46 | super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority); 47 | -------------------------------------------------------------------------------- /aspaper-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch: -------------------------------------------------------------------------------- 1 | --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java 2 | +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java 3 | @@ -387,6 +_,12 @@ 4 | if (flag2) { 5 | lightEngine.queueSectionData(LightLayer.SKY, sectionPos, sectionData.skyLight); 6 | } 7 | + 8 | + //ASP start 9 | + if (level instanceof com.infernalsuite.asp.level.SlimeLevelInstance) { 10 | + poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection); 11 | + } 12 | + //ASP end 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /aspaper-server/paper-patches/features/0001-Warning-if-people-use-old-swm-api.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: kyngs 3 | Date: Sat, 1 Jun 2024 18:57:39 +0200 4 | Subject: [PATCH] Warning if people use old swm api 5 | 6 | 7 | diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java 8 | index 3e82ea07ca4194844c5528446e2c4a46ff4acee5..1f8bff31ce60f9a1b143e749916fa51cf115f5d7 100644 9 | --- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java 10 | +++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java 11 | @@ -64,6 +64,15 @@ class PaperPluginInstanceManager { 12 | } 13 | 14 | public @Nullable Plugin getPlugin(@NotNull String name) { 15 | + // ASP start - Warn if someone tries to get the old API instance 16 | + if (name.equals("SlimeWorldManager")) { 17 | + server.getLogger().warning(""" 18 | + Hey! It seems like you're trying to access the old SlimeWorldManager API. 19 | + Since 1.21.0 the API is now provided by the server directly. 20 | + See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. 21 | + """); 22 | + } 23 | + // ASP end 24 | return this.lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Paper 25 | } 26 | 27 | -------------------------------------------------------------------------------- /aspaper-server/paper-patches/features/0002-Warning-if-people-use-old-swm-plugin.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: kyngs 3 | Date: Sat, 1 Jun 2024 18:57:39 +0200 4 | Subject: [PATCH] Warning if people use old swm plugin 5 | 6 | 7 | diff --git a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java 8 | index 26422904751647a061397ce978bba752149003cd..4940083475948eac4fc06446f7ee7e1e8e04d676 100644 9 | --- a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java 10 | +++ b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java 11 | @@ -26,6 +26,15 @@ public abstract class SimpleProviderStorage implements ProviderStorage { 12 | 13 | @Override 14 | public void register(PluginProvider provider) { 15 | + // ASP start - sanity check for old SlimeWorldManager 16 | + if (provider.getMeta().getName().equals("SlimeWorldManager")) { 17 | + LOGGER.warn(""" 18 | + Hey! It looks like you're trying to load the old SlimeWorldManager plugin. 19 | + ASP no longer works like that, and you should remove the plugin from your server. 20 | + See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. 21 | + """); 22 | + return; 23 | + } // ASP end 24 | this.providers.add(provider); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: David Mayr 3 | Date: Mon, 10 Mar 2025 12:09:41 +0100 4 | Subject: [PATCH] Delete temp folder after world is unloaded 5 | 6 | 7 | diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java 8 | index 090b295ccd7f61c2f1cc5131ffb0371c22e48f04..0bbc51dcf0ba7773b818994890d14fd0300608f3 100644 9 | --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java 10 | +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java 11 | @@ -1518,6 +1518,12 @@ public final class CraftServer implements Server { 12 | handle.getChunkSource().close(save); 13 | io.papermc.paper.FeatureHooks.closeEntityManager(handle, save); // SPIGOT-6722: close entityManager // Paper - chunk system 14 | handle.levelStorageAccess.close(); 15 | + 16 | + //ASP start - avoid temp storage leak during runtime 17 | + if(handle instanceof com.infernalsuite.asp.level.SlimeLevelInstance asp) { 18 | + asp.deleteTempFiles(); 19 | + } 20 | + //ASP end 21 | } catch (Exception ex) { 22 | this.getLogger().log(Level.SEVERE, null, ex); 23 | } 24 | -------------------------------------------------------------------------------- /aspaper-server/paper-patches/features/0005-Prevent-config-disk-io-on-world-load.patch: -------------------------------------------------------------------------------- 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2 | From: David Mayr 3 | Date: Wed, 12 Mar 2025 21:14:56 +0100 4 | Subject: [PATCH] Prevent config disk io on world load 5 | 6 | 7 | diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java 8 | index e0d4222a99f22d7130d95cf29b034a98f2f3b76e..776440ec20c7bf0acac8678773b0a5d29548c270 100644 9 | --- a/src/main/java/org/spigotmc/SpigotConfig.java 10 | +++ b/src/main/java/org/spigotmc/SpigotConfig.java 11 | @@ -78,7 +78,12 @@ public class SpigotConfig { 12 | } 13 | } 14 | 15 | + // ASP start - Improve Slime IO 16 | public static void readConfig(Class clazz, Object instance) { // Paper - package-private -> public 17 | + readConfig(clazz, instance, true); 18 | + } 19 | + public static void readConfig(Class clazz, Object instance, boolean save) { 20 | + // ASP end - Improve Slime IO 21 | for (Method method : clazz.getDeclaredMethods()) { 22 | if (Modifier.isPrivate(method.getModifiers())) { 23 | if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { 24 | @@ -95,7 +100,11 @@ public class SpigotConfig { 25 | } 26 | 27 | try { 28 | - SpigotConfig.config.save(SpigotConfig.CONFIG_FILE); 29 | + // ASP start - Improve Slime IO 30 | + if(save) { 31 | + SpigotConfig.config.save(SpigotConfig.CONFIG_FILE); 32 | + } 33 | + // ASP end - Improve Slime IO 34 | } catch (IOException ex) { 35 | Bukkit.getLogger().log(Level.SEVERE, "Could not save " + SpigotConfig.CONFIG_FILE, ex); 36 | } 37 | diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java 38 | index 89e2adbc1e1a0709d03e151e3ffcdbff10a44098..6b2241efbc248324f178a547aea9f398ed624247 100644 39 | --- a/src/main/java/org/spigotmc/SpigotWorldConfig.java 40 | +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java 41 | @@ -10,17 +10,28 @@ public class SpigotWorldConfig { 42 | private final YamlConfiguration config; 43 | private boolean verbose; 44 | 45 | + // ASP start - Improve Slime IO 46 | public SpigotWorldConfig(String worldName) { 47 | + this(worldName, true); 48 | + } 49 | + // ASP end - Improve Slime IO 50 | + 51 | + public SpigotWorldConfig(String worldName, boolean saveOnLoad) { // ASP - Improve Slime IO 52 | this.worldName = worldName; 53 | this.config = SpigotConfig.config; 54 | - this.init(); 55 | + this.init(saveOnLoad); // ASP - Improve Slime IO 56 | } 57 | 58 | + // ASP start - Improve Slime IO 59 | public void init() { 60 | + init(true); 61 | + } 62 | + public void init(boolean saveOnLoad) { 63 | + // ASP end - Improve Slime IO 64 | this.verbose = this.getBoolean("verbose", false); // Paper 65 | 66 | this.log("-------- World Settings For [" + this.worldName + "] --------"); 67 | - SpigotConfig.readConfig(SpigotWorldConfig.class, this); 68 | + SpigotConfig.readConfig(SpigotWorldConfig.class, this, saveOnLoad); // ASP - Improve Slime IO 69 | } 70 | 71 | private void log(String s) { 72 | -------------------------------------------------------------------------------- /aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch: -------------------------------------------------------------------------------- 1 | --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java 2 | +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java 3 | @@ -1508,6 +_,8 @@ 4 | return false; 5 | } 6 | 7 | + com.infernalsuite.asp.AdvancedSlimePaper.instance().onWorldUnload(world.getName()); // ASP - Remove unloaded world from map 8 | + 9 | try { 10 | if (save) { 11 | handle.save(null, true, false); // Paper - Fix saving in unloadWorld 12 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.config; 2 | 3 | import io.papermc.paper.configuration.Configurations; 4 | import io.papermc.paper.configuration.PaperConfigurations; 5 | import io.papermc.paper.configuration.WorldConfiguration; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.world.level.GameRules; 9 | import org.spigotmc.SpigotWorldConfig; 10 | 11 | import java.nio.file.Path; 12 | 13 | public class SlimePaperWorldConfig { 14 | 15 | private static final ResourceLocation FAKE_WORLD_KEY = ResourceLocation.fromNamespaceAndPath("infernalsuite", "asp-slimeworld"); 16 | public static WorldConfiguration cachedSlimeWorldConfig; 17 | 18 | private SlimePaperWorldConfig() {} 19 | 20 | public static WorldConfiguration initializeOrGet() { 21 | if(cachedSlimeWorldConfig != null) 22 | return cachedSlimeWorldConfig; 23 | 24 | 25 | initialize(MinecraftServer.getServer().paperConfigurations, MinecraftServer.getServer()); 26 | return cachedSlimeWorldConfig; 27 | } 28 | 29 | private static void initialize( 30 | PaperConfigurations paperConfigurations, 31 | MinecraftServer server 32 | ) { 33 | SpigotWorldConfig spigotWorldConfig = new SpigotWorldConfig("asp-slimeworld"); 34 | 35 | GameRules gameRules = new GameRules(server.worldLoader.dataConfiguration().enabledFeatures()); 36 | 37 | Configurations.ContextMap contextMap = PaperConfigurations.createWorldContextMap( 38 | /* 39 | * This might break if Paper team decides to extend/edit the functionality of this the world path property. 40 | * But the goal is for paper to treat the supplied folder as a world folder and create a paper-world.yml file there. 41 | * 42 | * Users can edit this file to change the config for all slime worlds. 43 | */ 44 | Path.of("config", "advancedslimepaper"), 45 | 46 | "asp-slimeworld", 47 | FAKE_WORLD_KEY, 48 | spigotWorldConfig, 49 | server.registryAccess(), 50 | gameRules 51 | ); 52 | cachedSlimeWorldConfig = paperConfigurations.createWorldConfig(contextMap); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/CommonLoadTask.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; 4 | import ca.spottedleaf.concurrentutil.util.Priority; 5 | 6 | public interface CommonLoadTask { 7 | 8 | boolean schedule(boolean schedule); 9 | 10 | Priority getPriority(); 11 | 12 | boolean cancel(); 13 | 14 | void lowerPriority(Priority priority); 15 | 16 | void raisePriority(Priority priority); 17 | 18 | void setPriority(Priority priority); 19 | } 20 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/FastChunkPruner.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; 4 | import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; 5 | import com.infernalsuite.asp.api.world.SlimeWorld; 6 | import com.infernalsuite.asp.api.world.properties.SlimeProperties; 7 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 8 | import com.infernalsuite.asp.util.NmsUtil; 9 | import net.minecraft.world.level.chunk.LevelChunk; 10 | import net.minecraft.world.level.chunk.LevelChunkSection; 11 | 12 | public class FastChunkPruner { 13 | 14 | public static boolean canBePruned(SlimeWorld world, LevelChunk chunk) { 15 | return canBePruned(world, chunk, null); 16 | } 17 | 18 | public static boolean canBePruned(SlimeWorld world, LevelChunk chunk, ChunkEntitySlices slices) { 19 | NewChunkHolder chunkHolder = NmsUtil.getChunkHolder(chunk); 20 | 21 | // Kenox 22 | // It's not safe to assume that the chunk can be pruned 23 | // if there isn't a loaded chunk there 24 | if (chunkHolder == null) { 25 | return false; 26 | } 27 | 28 | SlimePropertyMap propertyMap = world.getPropertyMap(); 29 | if (propertyMap.getValue(SlimeProperties.SHOULD_LIMIT_SAVE)) { 30 | int minX = propertyMap.getValue(SlimeProperties.SAVE_MIN_X); 31 | int maxX = propertyMap.getValue(SlimeProperties.SAVE_MAX_X); 32 | 33 | int minZ = propertyMap.getValue(SlimeProperties.SAVE_MIN_Z); 34 | int maxZ = propertyMap.getValue(SlimeProperties.SAVE_MAX_Z); 35 | 36 | int chunkX = chunk.locX; 37 | int chunkZ = chunk.locZ; 38 | 39 | if (chunkX < minX || chunkX > maxX) { 40 | return true; 41 | } 42 | 43 | if (chunkZ < minZ || chunkZ > maxZ) { 44 | return true; 45 | } 46 | } 47 | 48 | String pruningSetting = world.getPropertyMap().getValue(SlimeProperties.CHUNK_PRUNING); 49 | if (pruningSetting.equals("aggressive")) { 50 | if(slices == null) { 51 | //in case no slices were provided, try getting them from the chunk holder 52 | slices = chunkHolder.getEntityChunk(); 53 | } 54 | 55 | return chunk.blockEntities.isEmpty() && (slices == null || slices.isEmpty()) && areSectionsEmpty(chunk); 56 | } 57 | 58 | return false; 59 | } 60 | 61 | private static boolean areSectionsEmpty(LevelChunk chunk) { 62 | for (LevelChunkSection section : chunk.getSections()) { 63 | if (!section.hasOnlyAir()) { 64 | return false; 65 | } 66 | } 67 | 68 | return true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import com.mojang.datafixers.DataFixer; 4 | import net.minecraft.core.HolderLookup; 5 | import net.minecraft.world.level.saveddata.SavedData; 6 | import net.minecraft.world.level.storage.DimensionDataStorage; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.nio.file.Path; 11 | import java.util.Optional; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | /* 15 | * This dimension data storage does not serialize and/or load from disk. 16 | */ 17 | public class ReadOnlyDimensionDataStorage extends DimensionDataStorage { 18 | 19 | public ReadOnlyDimensionDataStorage(Path dataFolder, DataFixer fixerUpper, HolderLookup.Provider registries) { 20 | super(dataFolder, fixerUpper, registries); 21 | } 22 | 23 | @SuppressWarnings("unchecked") 24 | @Override 25 | public @Nullable T get(SavedData.@NotNull Factory factory, @NotNull String name) { 26 | Optional optional = this.cache.get(name); 27 | if(optional == null) { 28 | return null; 29 | } 30 | return (T) optional.orElse(null); 31 | } 32 | 33 | @Override 34 | public @NotNull CompletableFuture scheduleSave() { 35 | return CompletableFuture.completedFuture(null); 36 | } 37 | 38 | @Override 39 | public void saveAndJoin() {} 40 | 41 | @Override 42 | public void close() {} 43 | 44 | } 45 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeChunk; 4 | import com.infernalsuite.asp.api.world.SlimeChunkSection; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.CompoundBinaryTag; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class SafeNmsChunkWrapper implements SlimeChunk { 12 | 13 | private final NMSSlimeChunk wrapper; 14 | private final SlimeChunk safety; 15 | 16 | public SafeNmsChunkWrapper(NMSSlimeChunk wrapper, SlimeChunk safety) { 17 | this.wrapper = wrapper; 18 | this.safety = safety; 19 | } 20 | 21 | @Override 22 | public int getX() { 23 | return this.wrapper.getX(); 24 | } 25 | 26 | @Override 27 | public int getZ() { 28 | return this.wrapper.getZ(); 29 | } 30 | 31 | @Override 32 | public SlimeChunkSection[] getSections() { 33 | if (shouldDefaultBackToSlimeChunk()) { 34 | return this.safety.getSections(); 35 | } 36 | 37 | return this.wrapper.getSections(); 38 | } 39 | 40 | @Override 41 | public CompoundBinaryTag getHeightMaps() { 42 | if (shouldDefaultBackToSlimeChunk()) { 43 | return this.safety.getHeightMaps(); 44 | } 45 | 46 | return this.wrapper.getHeightMaps(); 47 | } 48 | 49 | @Override 50 | public List getTileEntities() { 51 | if (shouldDefaultBackToSlimeChunk()) { 52 | return this.safety.getTileEntities(); 53 | } 54 | 55 | return this.wrapper.getTileEntities(); 56 | } 57 | 58 | @Override 59 | public List getEntities() { 60 | if (shouldDefaultBackToSlimeChunk()) { 61 | return this.safety.getEntities(); 62 | } 63 | 64 | return this.wrapper.getEntities(); 65 | } 66 | 67 | @Override 68 | public Map getExtraData() { 69 | if (shouldDefaultBackToSlimeChunk()) { 70 | return this.safety.getExtraData(); 71 | } 72 | 73 | return this.wrapper.getExtraData(); 74 | } 75 | 76 | @Override 77 | public CompoundBinaryTag getUpgradeData() { 78 | if (shouldDefaultBackToSlimeChunk()) { 79 | return this.safety.getUpgradeData(); 80 | } 81 | 82 | return this.wrapper.getUpgradeData(); 83 | } 84 | 85 | /* 86 | Slime chunks can still be requested but not actually loaded, this caused 87 | some things to not properly save because they are not "loaded" into the chunk. 88 | See ChunkMap#protoChunkToFullChunk 89 | anything in the if statement will not be loaded and is stuck inside the runnable. 90 | Inorder to possibly not corrupt the state, simply refer back to the slime saved object. 91 | */ 92 | public boolean shouldDefaultBackToSlimeChunk() { 93 | return this.safety != null && !this.wrapper.getChunk().loaded; 94 | } 95 | 96 | public NMSSlimeChunk getWrapper() { 97 | return wrapper; 98 | } 99 | 100 | public SlimeChunk getSafety() { 101 | return safety; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeWorld; 4 | 5 | public record SlimeBootstrap( 6 | SlimeWorld initial 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkLevel.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeChunk; 4 | import net.minecraft.world.level.ChunkPos; 5 | import net.minecraft.world.level.block.Block; 6 | import net.minecraft.world.level.chunk.LevelChunk; 7 | import net.minecraft.world.level.chunk.LevelChunkSection; 8 | import net.minecraft.world.level.chunk.UpgradeData; 9 | import net.minecraft.world.level.levelgen.blending.BlendingData; 10 | import net.minecraft.world.level.material.Fluid; 11 | import net.minecraft.world.ticks.LevelChunkTicks; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | public class SlimeChunkLevel extends LevelChunk { 15 | 16 | private final SlimeInMemoryWorld inMemoryWorld; 17 | private final NMSSlimeChunk nmsSlimeChunk; 18 | private final @Nullable SlimeChunk slimeReference; 19 | 20 | public SlimeChunkLevel( 21 | SlimeLevelInstance world, 22 | @Nullable SlimeChunk reference, 23 | ChunkPos pos, 24 | UpgradeData upgradeData, 25 | LevelChunkTicks blockTickScheduler, 26 | LevelChunkTicks fluidTickScheduler, 27 | long inhabitedTime, 28 | @Nullable LevelChunkSection[] sectionArrayInitializer, 29 | @Nullable LevelChunk.PostLoadProcessor entityLoader, 30 | @Nullable BlendingData blendingData 31 | ) { 32 | super(world, pos, upgradeData, blockTickScheduler, fluidTickScheduler, inhabitedTime, sectionArrayInitializer, entityLoader, blendingData); 33 | this.inMemoryWorld = world.slimeInstance; 34 | this.nmsSlimeChunk = new NMSSlimeChunk(this, reference); 35 | this.slimeReference = reference; 36 | } 37 | 38 | @Override 39 | public void loadCallback() { 40 | //Not the earliest point where we can do promote the chunk in storage, but it's the easiest without any further patches, 41 | //and without causing a potential memory leak, and It's where bukkit calls its chunk load event so we should be fine. 42 | this.inMemoryWorld.promoteInChunkStorage(this); 43 | 44 | super.loadCallback(); 45 | } 46 | 47 | public SlimeChunk getSafeSlimeReference() { 48 | if(this.slimeReference == null) return this.nmsSlimeChunk; 49 | return new SafeNmsChunkWrapper(this.nmsSlimeChunk, this.slimeReference); 50 | } 51 | 52 | public NMSSlimeChunk getNmsSlimeChunk() { 53 | return nmsSlimeChunk; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.level; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import net.minecraft.core.Holder; 5 | import net.minecraft.world.level.biome.Biome; 6 | import net.minecraft.world.level.biome.BiomeSource; 7 | import net.minecraft.world.level.biome.Climate; 8 | import net.minecraft.world.level.levelgen.FlatLevelSource; 9 | import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | import java.util.stream.Stream; 14 | 15 | public class SlimeLevelGenerator extends FlatLevelSource { 16 | 17 | public SlimeLevelGenerator(Holder biome) { 18 | super(new FlatLevelGeneratorSettings(Optional.empty(), biome, List.of()), getSource(biome)); 19 | } 20 | 21 | private static BiomeSource getSource(Holder biome) { 22 | return new BiomeSource() { 23 | @Override 24 | protected MapCodec codec() { 25 | return null; 26 | } 27 | 28 | @Override 29 | protected Stream> collectPossibleBiomes() { 30 | return Stream.of(biome); 31 | } 32 | 33 | @Override 34 | public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler noise) { 35 | return biome; 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /aspaper-server/src/main/java/com/infernalsuite/asp/util/NmsUtil.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.util; 2 | 3 | import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; 4 | import com.infernalsuite.asp.InternalPlugin; 5 | import net.minecraft.world.level.chunk.LevelChunk; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.craftbukkit.scheduler.CraftScheduler; 8 | import org.bukkit.plugin.Plugin; 9 | 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | public class NmsUtil { 13 | 14 | public static long asLong(int chunkX, int chunkZ) { 15 | return (((long) chunkZ) * Integer.MAX_VALUE + ((long) chunkX)); 16 | //return (long)chunkX & 4294967295L | ((long)chunkZ & 4294967295L) << 32; 17 | } 18 | 19 | public static void runSyncAndWait(Runnable runnable) { 20 | if (Bukkit.isPrimaryThread()) { 21 | runnable.run(); 22 | return; 23 | } 24 | 25 | CountDownLatch latch = new CountDownLatch(1); 26 | RuntimeException[] runtimeException = new RuntimeException[1]; 27 | 28 | Bukkit.getScheduler().runTask(new InternalPlugin(), () -> { 29 | try { 30 | runnable.run(); 31 | } catch (RuntimeException e) { 32 | runtimeException[0] = e; 33 | } finally { 34 | latch.countDown(); 35 | } 36 | }); 37 | 38 | try { 39 | latch.await(); 40 | } catch (InterruptedException e) { 41 | throw new RuntimeException(e); // Rather propagate the interrupt (and thus prevent further execution) than continue 42 | } 43 | 44 | if (runtimeException[0] != null) { 45 | throw runtimeException[0]; 46 | } 47 | } 48 | 49 | public static NewChunkHolder getChunkHolder(LevelChunk chunk) { 50 | return chunk.level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunk.getPos().x, chunk.getPos().z); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.AdvancedSlimePaperAPI: -------------------------------------------------------------------------------- 1 | com.infernalsuite.asp.AdvancedSlimePaper 2 | -------------------------------------------------------------------------------- /aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.SlimeNMSBridge: -------------------------------------------------------------------------------- 1 | com.infernalsuite.asp.SlimeNMSBridgeImpl -------------------------------------------------------------------------------- /build-data/aspaper.at: -------------------------------------------------------------------------------- 1 | # This file is auto generated, any changes may be overridden! 2 | # See CONTRIBUTING.md on how to add access transformers. 3 | public ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices entities 4 | public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkLoadTask chunkHolder 5 | public net.minecraft.server.MinecraftServer commandStorage 6 | public net.minecraft.world.level.chunk.storage.SerializableChunkData makeBiomeCodec(Lnet/minecraft/core/Registry;)Lcom/mojang/serialization/Codec; 7 | public net.minecraft.world.level.chunk.storage.SerializableChunkData postLoadChunk(Lnet/minecraft/server/level/ServerLevel;Ljava/util/List;Ljava/util/List;)Lnet/minecraft/world/level/chunk/LevelChunk$PostLoadProcessor; 8 | -------------------------------------------------------------------------------- /build-data/dev-imports.txt: -------------------------------------------------------------------------------- 1 | # You can use this file to import files from minecraft libraries into the project 2 | # format: 3 | # 4 | # both fully qualified and a file based syntax are accepted for : 5 | # authlib com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java 6 | # datafixerupper com.mojang.datafixers.DataFixerBuilder 7 | # datafixerupper com/mojang/datafixers/util/Either.java 8 | # To import classes from the vanilla Minecraft jar use `minecraft` as the artifactId: 9 | # minecraft net.minecraft.world.level.entity.LevelEntityGetterAdapter 10 | # minecraft net/minecraft/world/level/entity/LevelEntityGetter.java -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.testing.logging.TestExceptionFormat 2 | import org.gradle.api.tasks.testing.logging.TestLogEvent 3 | 4 | plugins { 5 | java 6 | id("io.papermc.paperweight.patcher") 7 | } 8 | 9 | paperweight { 10 | upstreams.paper { 11 | ref = providers.gradleProperty("paperRef") 12 | 13 | // Setup file patches for build scripts 14 | patchFile { 15 | path = "paper-api/build.gradle.kts" 16 | outputFile = file("aspaper-api/build.gradle.kts") 17 | patchFile = file("aspaper-api/build.gradle.kts.patch") 18 | } 19 | patchFile { 20 | path = "paper-server/build.gradle.kts" 21 | outputFile = file("aspaper-server/build.gradle.kts") 22 | patchFile = file("aspaper-server/build.gradle.kts.patch") 23 | } 24 | 25 | patchDir("paperApi") { 26 | upstreamPath = "paper-api" 27 | excludes = setOf("build.gradle.kts") 28 | patchesDir = file("aspaper-api/paper-patches") 29 | outputDir = file("paper-api") 30 | } 31 | } 32 | } 33 | 34 | subprojects { 35 | apply(plugin = "java-library") 36 | apply(plugin = "maven-publish") 37 | 38 | extensions.configure { 39 | toolchain { 40 | languageVersion = JavaLanguageVersion.of(JAVA_VERSION) 41 | } 42 | } 43 | 44 | repositories { 45 | mavenCentral() 46 | maven(PAPER_MAVEN_PUBLIC_URL) 47 | } 48 | 49 | tasks.withType().configureEach { 50 | isPreserveFileTimestamps = false 51 | isReproducibleFileOrder = true 52 | } 53 | tasks.withType { 54 | options.encoding = Charsets.UTF_8.name() 55 | options.release = JAVA_VERSION 56 | options.isFork = true 57 | } 58 | tasks.withType { 59 | options.encoding = Charsets.UTF_8.name() 60 | } 61 | tasks.withType { 62 | filteringCharset = Charsets.UTF_8.name() 63 | } 64 | tasks.withType { 65 | testLogging { 66 | showStackTraces = true 67 | exceptionFormat = TestExceptionFormat.FULL 68 | events(TestLogEvent.STANDARD_OUT) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | group = "com.infernalsuite" 6 | 7 | repositories { 8 | mavenLocal() 9 | mavenCentral() 10 | gradlePluginPortal() 11 | } 12 | 13 | fun convertPlugin(plugin: Provider): String { 14 | val id = plugin.get().pluginId 15 | return "$id:$id.gradle.plugin:${plugin.get().version}" 16 | } 17 | 18 | dependencies { 19 | implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) 20 | implementation(convertPlugin(libs.plugins.blossom)) 21 | implementation(convertPlugin(libs.plugins.indragit)) 22 | implementation(convertPlugin(libs.plugins.profiles)) 23 | implementation(convertPlugin(libs.plugins.kotlin.jvm)) 24 | implementation(convertPlugin(libs.plugins.lombok)) 25 | implementation(convertPlugin(libs.plugins.paperweight.patcher)) 26 | implementation(convertPlugin(libs.plugins.plugin.yml.paper)) 27 | implementation(convertPlugin(libs.plugins.shadow)) 28 | } 29 | -------------------------------------------------------------------------------- /buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "buildSrc" 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | mavenCentral() 7 | gradlePluginPortal() 8 | } 9 | } 10 | 11 | dependencyResolutionManagement { 12 | versionCatalogs { 13 | create("libs") { 14 | from(files("../gradle/libs.versions.toml")) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/asp.base-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import net.kyori.indra.git.IndraGitExtension 2 | 3 | plugins { 4 | `java-library` 5 | id("net.kyori.indra.git") 6 | } 7 | 8 | group = rootProject.providers.gradleProperty("group").get() 9 | version = rootProject.providers.gradleProperty("apiVersion").get() 10 | 11 | java { 12 | toolchain { 13 | languageVersion.set(JavaLanguageVersion.of(JAVA_VERSION)) 14 | } 15 | withJavadocJar() 16 | withSourcesJar() 17 | } 18 | 19 | repositories { 20 | mavenLocal() 21 | mavenCentral() 22 | 23 | maven(PAPER_MAVEN_PUBLIC_URL) 24 | 25 | maven("https://repo.codemc.io/repository/nms/") 26 | maven("https://repo.rapture.pw/repository/maven-releases/") 27 | maven("https://repo.glaremasters.me/repository/concuncan/") 28 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") 29 | maven("https://oss.sonatype.org/content/repositories/snapshots/") 30 | } 31 | 32 | dependencies { 33 | // api(platform(project(":gradle:platform"))) 34 | } 35 | 36 | tasks { 37 | compileJava { 38 | options.encoding = Charsets.UTF_8.name() 39 | options.release.set(JAVA_VERSION) 40 | } 41 | 42 | javadoc { 43 | options.encoding = Charsets.UTF_8.name() 44 | (options as StandardJavadocDocletOptions) 45 | .tags("apiNote:a:API Note", "implSpec:a:Implementation Requirements", "implNote:a:Implementation Note") 46 | } 47 | 48 | processResources { 49 | filteringCharset = Charsets.UTF_8.name() 50 | filesMatching(listOf("paper-plugin.yml", "version.txt")) { 51 | expand("gitCommitId" to (project.the().commit()?.name ?: "unknown")) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/asp.internal-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import net.kyori.indra.git.IndraGitExtension 2 | import org.gradle.kotlin.dsl.the 3 | 4 | plugins { 5 | id("net.kyori.indra.git") 6 | } 7 | 8 | version = "${rootProject.providers.gradleProperty("apiVersion").get()}-${the().commit()?.name ?: "SNAPSHOT"}" 9 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/infernalsuite/asp/conventions/PublishConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.conventions 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.model.ObjectFactory 5 | import org.gradle.api.provider.Property 6 | import javax.inject.Inject 7 | 8 | open class PublishConfiguration @Inject constructor(objects: ObjectFactory) { 9 | 10 | companion object { 11 | internal fun Project.publishConfiguration(): PublishConfiguration { 12 | return extensions.create("publishConfiguration", PublishConfiguration::class.java) 13 | } 14 | } 15 | 16 | val name: Property = objects.property(String::class.java) 17 | val description: Property = objects.property(String::class.java) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/constants.kt: -------------------------------------------------------------------------------- 1 | const val JAVA_VERSION = 21 2 | const val PAPER_MAVEN_PUBLIC_URL = "https://repo.papermc.io/repository/maven-public/" -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/extensions.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.accessors.dm.LibrariesForLibs 2 | import org.gradle.api.Project 3 | import org.gradle.api.artifacts.Dependency 4 | import org.gradle.api.file.FileSystemLocation 5 | import org.gradle.api.provider.Provider 6 | import org.gradle.kotlin.dsl.the 7 | import java.nio.file.Path 8 | 9 | val Project.libs: LibrariesForLibs 10 | get() = the() 11 | 12 | // Utils for working with java.nio.file.Path from a FileSystemLocation 13 | // Courtesy of PaperMC (io.papermc.paperweight.util/file.kt) <3 14 | val FileSystemLocation.path: Path 15 | get() = asFile.toPath() 16 | 17 | val Provider.path: Path 18 | get() = get().path 19 | 20 | val Provider.pathOrNull: Path? 21 | get() = orNull?.path 22 | 23 | fun Project.paperApi(): Dependency = 24 | dependencies.create("io.papermc.paper:paper-api:${rootProject.providers.gradleProperty("version").get()}") 25 | -------------------------------------------------------------------------------- /core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.internal-conventions") 4 | id("asp.publishing-conventions") 5 | } 6 | 7 | dependencies { 8 | compileOnly(project(":api")) 9 | compileOnly(paperApi()) 10 | implementation(libs.zstd) 11 | } 12 | 13 | publishConfiguration { 14 | name = "Advanced Slime Paper Core" 15 | description = "Core logic for Advanced Slime Paper" 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/SlimeLogger.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | public class SlimeLogger { 7 | 8 | private static final Logger LOGGER = Logger.getLogger("ASWM-INTERNAL"); 9 | 10 | public static boolean DEBUG = false; 11 | 12 | public static void debug(String message) { 13 | if (DEBUG) { 14 | LOGGER.log(Level.WARNING, message); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/Util.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp; 2 | 3 | public final class Util { 4 | 5 | private Util() { 6 | throw new AssertionError(); 7 | } 8 | 9 | public static long chunkPosition(final int x, final int z) { 10 | return ((((long) x) << 32) | (z & 0xFFFFFFFFL)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/SlimeSerializationAdapterImpl.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization; 2 | 3 | import com.infernalsuite.asp.api.SlimeNMSBridge; 4 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 5 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 6 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 7 | import com.infernalsuite.asp.api.loaders.SlimeSerializationAdapter; 8 | import com.infernalsuite.asp.api.utils.SlimeFormat; 9 | import com.infernalsuite.asp.api.world.SlimeWorld; 10 | import com.infernalsuite.asp.api.world.SlimeWorldInstance; 11 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 12 | import com.infernalsuite.asp.serialization.slime.SlimeSerializer; 13 | import com.infernalsuite.asp.serialization.slime.reader.SlimeWorldReaderRegistry; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.io.IOException; 18 | 19 | public class SlimeSerializationAdapterImpl implements SlimeSerializationAdapter { 20 | 21 | @Override 22 | public byte[] serializeWorld(@NotNull SlimeWorld slimeWorld) { 23 | if(slimeWorld instanceof SlimeWorldInstance) { 24 | throw new IllegalArgumentException("SlimeWorldInstances cannot be serialized directly. Use SlimeWorldInstance.getSerializableCopy() instead."); 25 | } 26 | return SlimeSerializer.serialize(slimeWorld); 27 | } 28 | 29 | @Override 30 | public @NotNull SlimeWorld deserializeWorld(@NotNull String worldName, byte[] serializedWorld, @Nullable SlimeLoader loader, @NotNull SlimePropertyMap propertyMap, boolean readOnly) throws CorruptedWorldException, NewerFormatException, IOException { 31 | SlimeWorld slimeWorld = SlimeWorldReaderRegistry.readWorld(loader, worldName, serializedWorld, propertyMap, loader == null || readOnly); 32 | return SlimeNMSBridge.instance().getSlimeDataConverter().applyDataFixers(slimeWorld); 33 | } 34 | 35 | @Override 36 | public int getSlimeFormat() { 37 | return SlimeFormat.SLIME_VERSION; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/SlimeWorldReader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeWorld; 4 | 5 | public interface SlimeWorldReader { 6 | 7 | SlimeWorld readFromData(T data); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilImportData.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.anvil; 2 | 3 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.io.File; 7 | import java.nio.file.Path; 8 | 9 | public record AnvilImportData(Path worldDir, String newName, @Nullable SlimeLoader loader) { 10 | 11 | public static AnvilImportData legacy(File worldDir, String newName, @Nullable SlimeLoader loader) { 12 | return new AnvilImportData(worldDir.toPath(), newName, loader); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/ChunkPruner.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeChunk; 4 | import com.infernalsuite.asp.api.world.SlimeChunkSection; 5 | import com.infernalsuite.asp.api.world.SlimeWorld; 6 | import com.infernalsuite.asp.api.world.properties.SlimeProperties; 7 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 8 | import net.kyori.adventure.nbt.BinaryTagTypes; 9 | import net.kyori.adventure.nbt.CompoundBinaryTag; 10 | import net.kyori.adventure.nbt.ListBinaryTag; 11 | 12 | import java.util.List; 13 | 14 | public class ChunkPruner { 15 | 16 | public static boolean canBePruned(SlimeWorld world, SlimeChunk chunk) { 17 | SlimePropertyMap propertyMap = world.getPropertyMap(); 18 | if (propertyMap.getValue(SlimeProperties.SHOULD_LIMIT_SAVE)) { 19 | int minX = propertyMap.getValue(SlimeProperties.SAVE_MIN_X); 20 | int maxX = propertyMap.getValue(SlimeProperties.SAVE_MAX_X); 21 | 22 | int minZ = propertyMap.getValue(SlimeProperties.SAVE_MIN_Z); 23 | int maxZ = propertyMap.getValue(SlimeProperties.SAVE_MAX_Z); 24 | 25 | int chunkX = chunk.getX(); 26 | int chunkZ = chunk.getZ(); 27 | 28 | if (chunkX < minX || chunkX > maxX) { 29 | return true; 30 | } 31 | 32 | if (chunkZ < minZ || chunkZ > maxZ) { 33 | return true; 34 | } 35 | } 36 | 37 | String pruningSetting = world.getPropertyMap().getValue(SlimeProperties.CHUNK_PRUNING); 38 | if (pruningSetting.equals("aggressive")) { 39 | return chunk.getTileEntities().isEmpty() && chunk.getEntities().isEmpty() && areSectionsEmpty(chunk.getSections()); 40 | } 41 | 42 | return false; 43 | } 44 | 45 | // TAG_List("palette"): 1 entries of type TAG_Compound 46 | //[13:15:06 INFO]: { 47 | //[13:15:06 INFO]: TAG_Compound: 1 entries 48 | //[13:15:06 INFO]: { 49 | //[13:15:06 INFO]: TAG_String("Name"): minecraft:air 50 | //[13:15:06 INFO]: } 51 | private static boolean areSectionsEmpty(SlimeChunkSection[] sections) { 52 | for (SlimeChunkSection chunkSection : sections) { 53 | try { 54 | ListBinaryTag paletteTag = chunkSection.getBlockStatesTag().getList("palette"); 55 | if (paletteTag.elementType() != BinaryTagTypes.COMPOUND) { 56 | continue; // If the element type isn't a compound tag, consider the section empty 57 | } 58 | List palette = paletteTag.stream().map(tag -> (CompoundBinaryTag) tag).toList(); 59 | if (palette.size() > 1) return false; // If there is more than one palette, the section is not empty 60 | if (!palette.getFirst().getString("Name").equals("minecraft:air")) return false; // If the only palette entry is not air, the section is not empty 61 | } catch (final Exception e) { 62 | return false; 63 | } 64 | // The section is empty, continue to the next one 65 | } 66 | // All sections are empty, we can omit this chunk 67 | return true; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader; 2 | 3 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 4 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 5 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 6 | import com.infernalsuite.asp.api.utils.SlimeFormat; 7 | import com.infernalsuite.asp.api.world.SlimeWorld; 8 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 9 | import com.infernalsuite.asp.serialization.slime.reader.impl.v10.v10WorldFormat; 10 | import com.infernalsuite.asp.serialization.slime.reader.impl.v11.v11WorldFormat; 11 | import com.infernalsuite.asp.serialization.slime.reader.impl.v12.v12WorldFormat; 12 | import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9WorldFormat; 13 | 14 | import java.io.ByteArrayInputStream; 15 | import java.io.DataInputStream; 16 | import java.io.IOException; 17 | import java.util.Arrays; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class SlimeWorldReaderRegistry { 22 | 23 | private static final Map> FORMATS = new HashMap<>(); 24 | 25 | static { 26 | register(v1_9WorldFormat.FORMAT, 1, 2, 3, 4, 5, 6, 7, 8, 9); 27 | register(v10WorldFormat.FORMAT, 10); 28 | register(v11WorldFormat.FORMAT, 11); 29 | register(v12WorldFormat.FORMAT, 12); 30 | } 31 | 32 | private static void register(VersionedByteSlimeWorldReader format, int... bytes) { 33 | for (int value : bytes) { 34 | FORMATS.put((byte) value, format); 35 | } 36 | } 37 | 38 | public static SlimeWorld readWorld(SlimeLoader loader, String worldName, byte[] serializedWorld, SlimePropertyMap propertyMap, boolean readOnly) throws IOException, CorruptedWorldException, NewerFormatException { 39 | DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(serializedWorld)); 40 | byte[] fileHeader = new byte[SlimeFormat.SLIME_HEADER.length]; 41 | dataStream.read(fileHeader); 42 | 43 | if (!Arrays.equals(SlimeFormat.SLIME_HEADER, fileHeader)) { 44 | throw new CorruptedWorldException(worldName); 45 | } 46 | 47 | // File version 48 | byte version = dataStream.readByte(); 49 | 50 | if (version > SlimeFormat.SLIME_VERSION) { 51 | throw new NewerFormatException(version); 52 | } 53 | 54 | VersionedByteSlimeWorldReader reader = FORMATS.get(version); 55 | return reader.deserializeWorld(version, loader, worldName, dataStream, propertyMap, readOnly); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/VersionedByteSlimeWorldReader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader; 2 | 3 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 4 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 5 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 6 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.io.DataInputStream; 10 | import java.io.IOException; 11 | 12 | public interface VersionedByteSlimeWorldReader { 13 | 14 | T deserializeWorld(byte version, @Nullable SlimeLoader loader, String worldName, DataInputStream dataStream, SlimePropertyMap propertyMap, boolean readOnly) throws IOException, CorruptedWorldException, NewerFormatException; 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/SimpleWorldFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl; 2 | 3 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 4 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 5 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 6 | import com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader; 7 | import com.infernalsuite.asp.api.world.SlimeWorld; 8 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.io.DataInputStream; 12 | import java.io.IOException; 13 | 14 | public class SimpleWorldFormat implements VersionedByteSlimeWorldReader { 15 | 16 | private final com.infernalsuite.asp.serialization.SlimeWorldReader data; 17 | private final VersionedByteSlimeWorldReader reader; 18 | 19 | public SimpleWorldFormat(com.infernalsuite.asp.serialization.SlimeWorldReader data, VersionedByteSlimeWorldReader reader) { 20 | this.data = data; 21 | this.reader = reader; 22 | } 23 | 24 | @Override 25 | public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, String worldName, DataInputStream dataStream, SlimePropertyMap propertyMap, boolean readOnly) throws IOException, CorruptedWorldException, NewerFormatException { 26 | return this.data.readFromData(this.reader.deserializeWorld(version, loader, worldName, dataStream, propertyMap, readOnly)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10WorldFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v10; 2 | 3 | public interface v10WorldFormat { 4 | 5 | // Latest, returns same 6 | com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(data -> data, new v10SlimeWorldDeSerializer()); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11WorldFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v11; 2 | 3 | public interface v11WorldFormat { 4 | 5 | com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(data -> data, new v11SlimeWorldDeSerializer()); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12WorldFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v12; 2 | 3 | import com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat; 4 | 5 | public interface v12WorldFormat { 6 | 7 | SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v12SlimeWorldDeSerializer()); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/Upgrade.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; 2 | 3 | import com.infernalsuite.asp.api.SlimeDataConverter; 4 | 5 | public interface Upgrade { 6 | 7 | void upgrade(v1_9SlimeWorld world, SlimeDataConverter converter); 8 | 9 | } -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; 2 | 3 | import net.kyori.adventure.nbt.CompoundBinaryTag; 4 | 5 | import java.util.List; 6 | 7 | public final class v1_9SlimeChunk { 8 | public final String worldName; 9 | public final int x; 10 | public final int z; 11 | public v1_9SlimeChunkSection[] sections; 12 | public final int minY; 13 | public final int maxY; 14 | public CompoundBinaryTag heightMap; 15 | public int[] biomes; 16 | public List tileEntities; 17 | public List entities; 18 | // Used for 1.13 world upgrading 19 | public CompoundBinaryTag upgradeData; 20 | 21 | v1_9SlimeChunk(String worldName, 22 | int x, 23 | int z, 24 | v1_9SlimeChunkSection[] sections, 25 | int minY, 26 | int maxY, 27 | CompoundBinaryTag heightMap, 28 | int[] biomes, 29 | List tileEntities, 30 | List entities) { 31 | this.worldName = worldName; 32 | this.x = x; 33 | this.z = z; 34 | this.sections = sections; 35 | this.minY = minY; 36 | this.maxY = maxY; 37 | this.heightMap = heightMap; 38 | this.biomes = biomes; 39 | this.tileEntities = tileEntities; 40 | this.entities = entities; 41 | } 42 | 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; 2 | 3 | import com.infernalsuite.asp.api.utils.NibbleArray; 4 | import net.kyori.adventure.nbt.CompoundBinaryTag; 5 | import net.kyori.adventure.nbt.ListBinaryTag; 6 | 7 | public class v1_9SlimeChunkSection { 8 | 9 | // Pre 1.13 block data 10 | public final byte[] blocks; 11 | public final NibbleArray data; 12 | 13 | // Post 1.13 block data 14 | public ListBinaryTag palette; 15 | public final long[] blockStates; 16 | 17 | // Post 1.17 block data 18 | public CompoundBinaryTag blockStatesTag; 19 | public CompoundBinaryTag biomeTag; 20 | 21 | public final NibbleArray blockLight; 22 | public final NibbleArray skyLight; 23 | 24 | public v1_9SlimeChunkSection(byte[] blocks, NibbleArray data, ListBinaryTag palette, long[] blockStates, CompoundBinaryTag blockStatesTag, CompoundBinaryTag biomeTag, NibbleArray blockLight, NibbleArray skyLight) { 25 | this.blocks = blocks; 26 | this.data = data; 27 | this.palette = palette; 28 | this.blockStates = blockStates; 29 | this.blockStatesTag = blockStatesTag; 30 | this.biomeTag = biomeTag; 31 | this.blockLight = blockLight; 32 | this.skyLight = skyLight; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; 2 | 3 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 4 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 5 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 6 | import net.kyori.adventure.nbt.BinaryTag; 7 | 8 | import java.util.concurrent.ConcurrentMap; 9 | 10 | public class v1_9SlimeWorld { 11 | 12 | public byte version; 13 | 14 | public final String worldName; 15 | public final SlimeLoader loader; 16 | public final Long2ObjectMap chunks; 17 | public final ConcurrentMap extraCompound; 18 | public final SlimePropertyMap propertyMap; 19 | public final boolean readOnly; 20 | 21 | public v1_9SlimeWorld(byte version, 22 | String worldName, 23 | SlimeLoader loader, 24 | Long2ObjectMap chunks, 25 | ConcurrentMap extraCompound, 26 | SlimePropertyMap propertyMap, 27 | boolean readOnly) { 28 | this.version = version; 29 | this.worldName = worldName; 30 | this.loader = loader; 31 | this.chunks = chunks; 32 | this.extraCompound = extraCompound; 33 | this.propertyMap = propertyMap; 34 | this.readOnly = readOnly; 35 | } 36 | 37 | public int getDataVersion() { 38 | return switch (version) { 39 | case 0x01 -> 99;//1.8; 99 as 1.8 does not have a dataversion yet and 100 is the first one 40 | case 0x02 -> 184;//1.9.4 41 | case 0x03 -> 922;//1.11.2 42 | case 0x04 -> 1631;//1.13.2 43 | case 0x05 -> 1976;//1.14.4 44 | case 0x06 -> 2586;//1.16.5 45 | case 0x07 -> 2730;//1.17.1 46 | case 0x08 -> 2975;//1.18 47 | default -> throw new IllegalStateException("Unexpected value: " + version); 48 | }; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; 2 | 3 | public interface v1_9WorldFormat { 4 | 5 | com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(new v1_v9SlimeConverter(), new v1_9SlimeWorldDeserializer()); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSectionSkeleton.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.skeleton; 2 | 3 | import com.infernalsuite.asp.api.utils.NibbleArray; 4 | import com.infernalsuite.asp.api.world.SlimeChunkSection; 5 | import net.kyori.adventure.nbt.CompoundBinaryTag; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public record SlimeChunkSectionSkeleton(CompoundBinaryTag blockStates, CompoundBinaryTag biome, NibbleArray block, NibbleArray light) implements SlimeChunkSection { 9 | @Override 10 | public CompoundBinaryTag getBlockStatesTag() { 11 | return this.blockStates; 12 | } 13 | 14 | @Override 15 | public CompoundBinaryTag getBiomeTag() { 16 | return this.biome; 17 | } 18 | 19 | @Override 20 | public @Nullable NibbleArray getBlockLight() { 21 | return this.block; 22 | } 23 | 24 | @Override 25 | public NibbleArray getSkyLight() { 26 | return this.light; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.skeleton; 2 | 3 | import com.infernalsuite.asp.api.world.SlimeChunk; 4 | import com.infernalsuite.asp.api.world.SlimeChunkSection; 5 | import net.kyori.adventure.nbt.BinaryTag; 6 | import net.kyori.adventure.nbt.CompoundBinaryTag; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public record SlimeChunkSkeleton(int x, int z, SlimeChunkSection[] sections, 12 | CompoundBinaryTag heightMap, 13 | List blockEntities, 14 | List entities, 15 | Map extra, 16 | CompoundBinaryTag upgradeData) implements SlimeChunk { 17 | 18 | @Override 19 | public int getX() { 20 | return this.x; 21 | } 22 | 23 | @Override 24 | public int getZ() { 25 | return this.z; 26 | } 27 | 28 | @Override 29 | public SlimeChunkSection[] getSections() { 30 | return this.sections; 31 | } 32 | 33 | @Override 34 | public CompoundBinaryTag getHeightMaps() { 35 | return this.heightMap; 36 | } 37 | 38 | @Override 39 | public List getTileEntities() { 40 | return this.blockEntities; 41 | } 42 | 43 | @Override 44 | public List getEntities() { 45 | return this.entities; 46 | } 47 | 48 | @Override 49 | public Map getExtraData() { 50 | return this.extra; 51 | } 52 | 53 | @Override 54 | public CompoundBinaryTag getUpgradeData() { 55 | return this.upgradeData; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group=com.infernalsuite.asp 2 | apiVersion=4.0.0 3 | version=1.21.4-R0.1-SNAPSHOT 4 | 5 | mcVersion=1.21.4 6 | paperRef=a838a886dcbc93664283034a41673e802a6b3098 7 | 8 | org.gradle.caching=true 9 | org.gradle.parallel=true 10 | org.gradle.vfs.watch=false 11 | 12 | org.gradle.jvmargs=-Xmx10g -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | adventure = "4.17.0" 3 | annotations = "26.0.1" 4 | autoservice = "1.1.1" 5 | blossom = "2.1.0" 6 | bstats = "3.1.0" 7 | cloud-core = "2.0.0" 8 | cloud-minecraft = "2.0.0-beta.10" 9 | configurate = "4.1.2" 10 | indra-git = "3.1.3" 11 | gradle-profiles = "0.54.0" 12 | hikari = "6.2.1" 13 | kotlin = "1.9.25" 14 | lettuce = "6.5.1.RELEASE" 15 | lombok = "1.18.36" 16 | lombok-plugin = "8.11" 17 | mongo = "5.2.1" 18 | paperweight = "2.0.0-beta.14" 19 | plugin-yml-paper = "0.6.0" 20 | shadow = "8.3.5" 21 | slf4j = "2.0.16" 22 | zstd = "1.5.6-8" 23 | 24 | [plugins] 25 | blossom = { id = "net.kyori.blossom", version.ref = "blossom" } 26 | indragit = { id = "net.kyori.indra.git", version.ref = "indra-git" } 27 | profiles = { id = "org.kordamp.gradle.profiles", version.ref = "gradle-profiles" } 28 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 29 | lombok = { id = "io.freefair.lombok", version.ref = "lombok-plugin" } 30 | paperweight-patcher = { id = "io.papermc.paperweight.patcher", version.ref = "paperweight" } 31 | plugin-yml-paper = { id = "net.minecrell.plugin-yml.paper", version.ref = "plugin-yml-paper" } 32 | shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } 33 | 34 | [libraries] 35 | adventure-nbt = { module = "net.kyori:adventure-nbt", version.ref = "adventure" } 36 | annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } 37 | auto-service = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } 38 | auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoservice" } 39 | bstats = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } 40 | cloud-annotations = { module = "org.incendo:cloud-annotations", version.ref = "cloud-core" } 41 | cloud-bom = { module = "org.incendo:cloud-bom", version.ref = "cloud-core" } 42 | cloud-core = { module = "org.incendo:cloud-core", version.ref = "cloud-core" } 43 | cloud-minecraft-bom = { module = "org.incendo:cloud-minecraft-bom", version.ref = "cloud-minecraft" } 44 | cloud-minecraft-extras = { module = "org.incendo:cloud-minecraft-extras", version.ref = "cloud-minecraft" } 45 | cloud-paper = { module = "org.incendo:cloud-paper", version.ref = "cloud-minecraft" } 46 | configurate-core = { module = "org.spongepowered:configurate-core", version.ref = "configurate" } 47 | configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" } 48 | hikari = { module = "com.zaxxer:HikariCP", version.ref = "hikari" } 49 | lettuce = { module = "io.lettuce:lettuce-core", version.ref = "lettuce" } 50 | lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } 51 | mongo = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" } 52 | slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } 53 | zstd = { module = "com.github.luben:zstd-jni", version.ref = "zstd" } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfernalSuite/AdvancedSlimePaper/2b60acc0578293049d8837cd716ffd256f2e1090/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /importer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("com.gradleup.shadow") 4 | } 5 | 6 | dependencies { 7 | implementation(project(":api")) 8 | implementation(project(":core")) 9 | implementation(project(":aspaper-api")) 10 | } 11 | 12 | tasks { 13 | jar { 14 | manifest { 15 | attributes["Main-Class"] = "com.infernalsuite.asp.importer.SWMImporter" 16 | } 17 | } 18 | shadowJar { 19 | archiveClassifier.set("") 20 | minimize() 21 | } 22 | assemble { 23 | dependsOn(shadowJar) 24 | } 25 | } 26 | 27 | description = "asp-importer" 28 | -------------------------------------------------------------------------------- /loaders/api-loader/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | compileOnly(paperApi()) 9 | } 10 | 11 | publishConfiguration { 12 | name = "Advanced Slime Paper API loader" 13 | description = "HTTP-API based loader for Advanced Slime Paper" 14 | } 15 | -------------------------------------------------------------------------------- /loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/MapStructure.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.loaders.api; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class MapStructure { 7 | private String worldId; 8 | private String name; 9 | private int size; 10 | private long uploadTimestamp; 11 | private long updateTimestamp; 12 | private int version; 13 | private Map notes; 14 | private List authors; 15 | private Map pictureUrls; 16 | 17 | public String getWorldId() { 18 | return worldId; 19 | } 20 | 21 | public void setWorldId(String worldId) { 22 | this.worldId = worldId; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | public int getSize() { 34 | return size; 35 | } 36 | 37 | public void setSize(int size) { 38 | this.size = size; 39 | } 40 | 41 | public long getUploadTimestamp() { 42 | return uploadTimestamp; 43 | } 44 | 45 | public void setUploadTimestamp(long uploadTimestamp) { 46 | this.uploadTimestamp = uploadTimestamp; 47 | } 48 | 49 | public long getUpdateTimestamp() { 50 | return updateTimestamp; 51 | } 52 | 53 | public void setUpdateTimestamp(long updateTimestamp) { 54 | this.updateTimestamp = updateTimestamp; 55 | } 56 | 57 | public int getVersion() { 58 | return version; 59 | } 60 | 61 | public void setVersion(int version) { 62 | this.version = version; 63 | } 64 | 65 | public Map getNotes() { 66 | return notes; 67 | } 68 | 69 | public void setNotes(Map notes) { 70 | this.notes = notes; 71 | } 72 | 73 | public List getAuthors() { 74 | return authors; 75 | } 76 | 77 | public void setAuthors(List authors) { 78 | this.authors = authors; 79 | } 80 | 81 | public Map getPictureUrls() { 82 | return pictureUrls; 83 | } 84 | 85 | public void setPictureUrls(Map pictureUrls) { 86 | this.pictureUrls = pictureUrls; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /loaders/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | 9 | api(project(":loaders:api-loader")) 10 | api(project(":loaders:file-loader")) 11 | api(project(":loaders:mongo-loader")) 12 | api(project(":loaders:mysql-loader")) 13 | api(project(":loaders:redis-loader")) 14 | 15 | compileOnly(paperApi()) 16 | } 17 | 18 | publishConfiguration { 19 | name = "Advanced Slime Paper Loaders" 20 | description = "Default loaders for Advanced Slime Paper. There might be more loaders available then included in this BOM package" 21 | } 22 | -------------------------------------------------------------------------------- /loaders/file-loader/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | compileOnly(paperApi()) 9 | } 10 | 11 | publishConfiguration { 12 | name = "Advanced Slime Paper File Loader" 13 | description = "File loader for Advanced Slime Paper" 14 | } 15 | -------------------------------------------------------------------------------- /loaders/file-loader/src/main/java/com/infernalsuite/asp/loaders/file/FileLoader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.loaders.file; 2 | 3 | import com.infernalsuite.asp.api.exceptions.UnknownWorldException; 4 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.*; 9 | import java.nio.file.NotDirectoryException; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | public class FileLoader implements SlimeLoader { 15 | 16 | private static final FilenameFilter WORLD_FILE_FILTER = (dir, name) -> name.endsWith(".slime"); 17 | private static final Logger LOGGER = LoggerFactory.getLogger(FileLoader.class); 18 | 19 | private final File worldDir; 20 | 21 | public FileLoader(File worldDir) throws IllegalStateException { 22 | this.worldDir = worldDir; 23 | 24 | if (worldDir.exists() && !worldDir.isDirectory()) { 25 | LOGGER.warn("A file named '{}' has been deleted, as this is the name used for the worlds directory.", worldDir.getName()); 26 | if (!worldDir.delete()) throw new IllegalStateException("Failed to delete the file named '" + worldDir.getName() + "'."); 27 | } 28 | 29 | if (!worldDir.exists() && !worldDir.mkdirs()) throw new IllegalStateException("Failed to create the worlds directory."); 30 | } 31 | 32 | @Override 33 | public byte[] readWorld(String worldName) throws UnknownWorldException, IOException { 34 | if (!worldExists(worldName)) { 35 | throw new UnknownWorldException(worldName); 36 | } 37 | 38 | try (FileInputStream fis = new FileInputStream(new File(worldDir, worldName + ".slime"))){ 39 | return fis.readAllBytes(); 40 | } 41 | } 42 | 43 | @Override 44 | public boolean worldExists(String worldName) { 45 | return new File(worldDir, worldName + ".slime").exists(); 46 | } 47 | 48 | @Override 49 | public List listWorlds() throws NotDirectoryException { 50 | String[] worlds = worldDir.list(WORLD_FILE_FILTER); 51 | 52 | if (worlds == null) { 53 | throw new NotDirectoryException(worldDir.getPath()); 54 | } 55 | 56 | return Arrays.stream(worlds).map((c) -> c.substring(0, c.length() - 6)).collect(Collectors.toList()); 57 | } 58 | 59 | @Override 60 | public void saveWorld(String worldName, byte[] serializedWorld) throws IOException { 61 | try (FileOutputStream fos = new FileOutputStream(new File(worldDir, worldName + ".slime"))) { 62 | fos.write(serializedWorld); 63 | } 64 | } 65 | 66 | @Override 67 | public void deleteWorld(String worldName) throws UnknownWorldException, IOException { 68 | if (!worldExists(worldName)) { 69 | throw new UnknownWorldException(worldName); 70 | } else { 71 | if (!new File(worldDir, worldName + ".slime").delete()) { 72 | throw new IOException("Failed to delete the world file. File#delete() returned false."); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /loaders/mongo-loader/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | compileOnly(paperApi()) 9 | 10 | api(libs.mongo) 11 | } 12 | 13 | publishConfiguration { 14 | name = "Advanced Slime Paper MongoDB Loader" 15 | description = "MongoDB GridFS Loader for Advanced Slime Paper" 16 | } 17 | -------------------------------------------------------------------------------- /loaders/mysql-loader/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | 9 | api(libs.hikari) 10 | compileOnly(paperApi()) 11 | } 12 | 13 | publishConfiguration { 14 | name = "Advanced Slime Paper MySQL Loader" 15 | description = "MySQL loader for Advanced Slime Paper" 16 | } 17 | -------------------------------------------------------------------------------- /loaders/redis-loader/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | } 5 | 6 | dependencies { 7 | compileOnly(project(":api")) 8 | 9 | api(libs.lettuce) 10 | 11 | compileOnly(paperApi()) 12 | } 13 | 14 | publishConfiguration { 15 | name = "Advanced Slime Paper Redis Loader" 16 | description = "Redis loader for Advanced Slime Paper" 17 | } 18 | -------------------------------------------------------------------------------- /loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/RedisLoader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.loaders.redis; 2 | 3 | import com.infernalsuite.asp.api.exceptions.UnknownWorldException; 4 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 5 | import io.lettuce.core.RedisClient; 6 | import io.lettuce.core.api.sync.RedisCommands; 7 | import com.infernalsuite.asp.loaders.redis.util.StringByteCodec; 8 | 9 | import java.io.IOException; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | public class RedisLoader implements SlimeLoader { 15 | 16 | private static final String WORLD_DATA_PREFIX = "aswm:world:data:"; 17 | private static final String WORLD_LIST_PREFIX = "aswm:world:list"; 18 | 19 | private final RedisCommands connection; 20 | 21 | public RedisLoader(String uri) { 22 | this.connection = RedisClient 23 | .create(uri) 24 | .connect(StringByteCodec.INSTANCE) 25 | .sync(); 26 | } 27 | 28 | @Override 29 | public byte[] readWorld(String name) throws UnknownWorldException, IOException { 30 | byte[] data = connection.get(WORLD_DATA_PREFIX + name); 31 | if (data == null) { 32 | throw new UnknownWorldException(name); 33 | } 34 | return data; 35 | } 36 | 37 | @Override 38 | public boolean worldExists(String name) throws IOException { 39 | return connection.exists(WORLD_DATA_PREFIX + name) == 1; 40 | } 41 | 42 | @Override 43 | public List listWorlds() throws IOException { 44 | return connection.smembers(WORLD_LIST_PREFIX) 45 | .stream() 46 | .map(bytes -> new String(bytes, StandardCharsets.UTF_8)) 47 | .collect(Collectors.toList()); // We can't use .toList because this needs to be mutable. 48 | } 49 | 50 | @Override 51 | public void saveWorld(String worldName, byte[] bytes) throws IOException { 52 | connection.set(WORLD_DATA_PREFIX + worldName, bytes); 53 | 54 | // Also add to the world list set. We can't do this in one atomic operation (mset) because it's a set add 55 | connection.sadd(WORLD_LIST_PREFIX, worldName.getBytes(StandardCharsets.UTF_8)); 56 | } 57 | 58 | @Override 59 | public void deleteWorld(String worldName) throws UnknownWorldException, IOException { 60 | long deletedCount = connection.del(WORLD_DATA_PREFIX + worldName); 61 | 62 | // We're checking equal to zero, because the lock key doesn't have to exist 63 | if (deletedCount == 0) { 64 | throw new UnknownWorldException(worldName); 65 | } 66 | 67 | // Remove the world from the world list set 68 | connection.srem(WORLD_LIST_PREFIX, worldName.getBytes(StandardCharsets.UTF_8)); 69 | } 70 | } -------------------------------------------------------------------------------- /loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/util/StringByteCodec.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.loaders.redis.util; 2 | 3 | import io.lettuce.core.codec.RedisCodec; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.charset.Charset; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | public class StringByteCodec implements RedisCodec { 10 | 11 | public static final StringByteCodec INSTANCE = new StringByteCodec(); 12 | private static final byte[] EMPTY = new byte[0]; 13 | private final Charset charset = StandardCharsets.UTF_8; 14 | 15 | @Override 16 | public String decodeKey(final ByteBuffer bytes) { 17 | return charset.decode(bytes).toString(); 18 | } 19 | 20 | @Override 21 | public byte[] decodeValue(final ByteBuffer bytes) { 22 | return getBytes(bytes); 23 | } 24 | 25 | @Override 26 | public ByteBuffer encodeKey(final String key) { 27 | return charset.encode(key); 28 | } 29 | 30 | @Override 31 | public ByteBuffer encodeValue(final byte[] value) { 32 | if (value == null) { 33 | return ByteBuffer.wrap(EMPTY); 34 | } 35 | 36 | return ByteBuffer.wrap(value); 37 | } 38 | 39 | private static byte[] getBytes(final ByteBuffer buffer) { 40 | final byte[] b = new byte[buffer.remaining()]; 41 | buffer.get(b); 42 | return b; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("asp.base-conventions") 3 | id("asp.publishing-conventions") 4 | id("net.minecrell.plugin-yml.paper") 5 | id("com.gradleup.shadow") 6 | } 7 | 8 | dependencies { 9 | compileOnly(project(":api")) 10 | implementation(project(":loaders")) 11 | 12 | implementation(libs.configurate.yaml) 13 | implementation(libs.bstats) 14 | implementation(libs.cloud.paper) 15 | implementation(libs.cloud.minecraft.extras) 16 | implementation(libs.cloud.annotations) 17 | 18 | compileOnly(paperApi()) 19 | } 20 | 21 | tasks { 22 | withType { 23 | archiveBaseName.set("asp-plugin") 24 | } 25 | 26 | shadowJar { 27 | archiveClassifier.set("") 28 | 29 | relocate("org.bstats", "com.infernalsuite.asp.libs.bstats") 30 | relocate("org.spongepowered.configurate", "com.infernalsuite.asp.libs.configurate") 31 | relocate("com.zaxxer.hikari", "com.infernalsuite.asp.libs.hikari") 32 | relocate("com.mongodb", "com.infernalsuite.asp.libs.mongo") 33 | relocate("io.lettuce", "com.infernalsuite.asp.libs.lettuce") 34 | relocate("org.bson", "com.infernalsuite.asp.libs.bson") 35 | } 36 | 37 | assemble { 38 | dependsOn(shadowJar) 39 | } 40 | } 41 | 42 | paper { 43 | name = "ASPaperPlugin" 44 | description = "ASP plugin for Paper, providing utilities for the ASP platform" 45 | version = "\${gitCommitId}" 46 | apiVersion = "1.21" 47 | main = "com.infernalsuite.asp.plugin.SWPlugin" 48 | authors = listOf("InfernalSuite") 49 | } 50 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/SlimeCommand.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands; 2 | 3 | import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; 4 | import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; 5 | import com.infernalsuite.asp.api.exceptions.NewerFormatException; 6 | import com.infernalsuite.asp.api.exceptions.UnknownWorldException; 7 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 8 | import com.infernalsuite.asp.api.world.SlimeWorld; 9 | import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; 10 | import net.kyori.adventure.text.TextComponent; 11 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 12 | 13 | import java.io.IOException; 14 | 15 | public class SlimeCommand { 16 | public static final TextComponent COMMAND_PREFIX = LegacyComponentSerializer.legacySection().deserialize( 17 | "§9§lSWP §7§l>> §r" 18 | ); 19 | 20 | protected final CommandManager commandManager; 21 | protected final com.infernalsuite.asp.plugin.SWPlugin plugin; 22 | protected final AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance(); 23 | 24 | public SlimeCommand(CommandManager commandManager) { 25 | this.commandManager = commandManager; 26 | this.plugin = commandManager.getPlugin(); 27 | } 28 | 29 | // This method is here so that we can easily change the behavior in the future 30 | protected SlimeWorld getWorldReadyForCloning(String name, SlimeLoader loader, SlimePropertyMap propertyMap) throws CorruptedWorldException, NewerFormatException, UnknownWorldException, IOException { 31 | return asp.readWorld(loader, name, false, propertyMap); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/exception/MessageCommandException.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class MessageCommandException extends RuntimeException { 6 | 7 | private final Component component; 8 | 9 | public MessageCommandException(Component component) { 10 | this.component = component; 11 | } 12 | 13 | public Component getComponent() { 14 | return component; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/BukkitWorldParser.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.format.NamedTextColor; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.World; 8 | import org.bukkit.command.CommandSender; 9 | import org.checkerframework.checker.nullness.qual.NonNull; 10 | import org.incendo.cloud.context.CommandContext; 11 | import org.incendo.cloud.context.CommandInput; 12 | import org.incendo.cloud.parser.ArgumentParseResult; 13 | import org.incendo.cloud.parser.ArgumentParser; 14 | import org.incendo.cloud.suggestion.Suggestion; 15 | import org.incendo.cloud.suggestion.SuggestionProvider; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public class BukkitWorldParser implements ArgumentParser { 20 | @Override 21 | public @NonNull ArgumentParseResult<@NonNull World> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull CommandInput commandInput) { 22 | String input = commandInput.peekString(); 23 | World loaded = Bukkit.getWorld(input); 24 | 25 | if (loaded == null) { 26 | return ArgumentParseResult.failure(new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( 27 | Component.text("World " + input + " is not loaded!").color(NamedTextColor.RED) 28 | ))); 29 | } 30 | commandInput.readString(); 31 | return ArgumentParseResult.success(loaded); 32 | } 33 | 34 | @Override 35 | public @NonNull SuggestionProvider suggestionProvider() { 36 | return (context, input) -> CompletableFuture.supplyAsync(() -> 37 | Bukkit.getWorlds() 38 | .stream() 39 | .map(World::getName) 40 | .map(Suggestion::suggestion) 41 | .toList() 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoader.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 4 | 5 | public record NamedSlimeLoader(String name, SlimeLoader slimeLoader) { 6 | @Override 7 | public String toString() { 8 | return name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoaderParser.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.api.loaders.SlimeLoader; 4 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 5 | import com.infernalsuite.asp.plugin.loader.LoaderManager; 6 | import net.kyori.adventure.text.Component; 7 | import net.kyori.adventure.text.format.NamedTextColor; 8 | import org.bukkit.command.CommandSender; 9 | import org.checkerframework.checker.nullness.qual.NonNull; 10 | import org.incendo.cloud.context.CommandContext; 11 | import org.incendo.cloud.context.CommandInput; 12 | import org.incendo.cloud.parser.ArgumentParseResult; 13 | import org.incendo.cloud.parser.ArgumentParser; 14 | import org.incendo.cloud.suggestion.Suggestion; 15 | import org.incendo.cloud.suggestion.SuggestionProvider; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public class NamedSlimeLoaderParser implements ArgumentParser { 20 | 21 | private final LoaderManager loaderManager; 22 | 23 | public NamedSlimeLoaderParser(LoaderManager loaderManager) { 24 | this.loaderManager = loaderManager; 25 | } 26 | 27 | @Override 28 | public @NonNull ArgumentParseResult<@NonNull NamedSlimeLoader> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull CommandInput commandInput) { 29 | String input = commandInput.peekString(); 30 | SlimeLoader loader = loaderManager.getLoader(input); 31 | 32 | if (loader == null) { 33 | return ArgumentParseResult.failure(new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( 34 | Component.text("Unknown data source " + input + "!").color(NamedTextColor.RED) 35 | ))); 36 | } 37 | commandInput.readString(); 38 | return ArgumentParseResult.success(new NamedSlimeLoader(input, loader)); 39 | } 40 | 41 | @Override 42 | public @NonNull SuggestionProvider suggestionProvider() { 43 | return (commandContext, commandInput) -> CompletableFuture.supplyAsync(() -> 44 | loaderManager 45 | .getLoaders() 46 | .keySet() 47 | .stream() 48 | .map(Suggestion::suggestion) 49 | .toList() 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldData.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.plugin.config.WorldData; 4 | 5 | public record NamedWorldData(String name, WorldData worldData) { 6 | } 7 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldDataParser.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 4 | import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; 5 | import com.infernalsuite.asp.plugin.commands.parser.suggestion.KnownSlimeWorldSuggestionProvider; 6 | import com.infernalsuite.asp.plugin.config.ConfigManager; 7 | import com.infernalsuite.asp.plugin.config.WorldData; 8 | import net.kyori.adventure.text.Component; 9 | import net.kyori.adventure.text.format.NamedTextColor; 10 | import org.bukkit.command.CommandSender; 11 | import org.checkerframework.checker.nullness.qual.NonNull; 12 | import org.incendo.cloud.context.CommandContext; 13 | import org.incendo.cloud.context.CommandInput; 14 | import org.incendo.cloud.parser.ArgumentParseResult; 15 | import org.incendo.cloud.parser.ArgumentParser; 16 | import org.incendo.cloud.suggestion.SuggestionProvider; 17 | 18 | public class NamedWorldDataParser implements ArgumentParser { 19 | 20 | @Override 21 | public @NonNull ArgumentParseResult<@NonNull NamedWorldData> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull CommandInput commandInput) { 22 | String input = commandInput.peekString(); 23 | WorldData worldData = ConfigManager.getWorldConfig().getWorlds().get(input); 24 | 25 | if (worldData == null) { 26 | return ArgumentParseResult.failure(new MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( 27 | Component.text("Failed to find world " + input + "inside the worlds config file!").color(NamedTextColor.RED) 28 | ))); 29 | } 30 | commandInput.readString(); 31 | return ArgumentParseResult.success(new NamedWorldData(input, worldData)); 32 | } 33 | 34 | @Override 35 | public @NonNull SuggestionProvider suggestionProvider() { 36 | return new KnownSlimeWorldSuggestionProvider(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/SlimeWorldParser.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser; 2 | 3 | import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; 4 | import com.infernalsuite.asp.api.world.SlimeWorld; 5 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 6 | import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; 7 | import net.kyori.adventure.text.Component; 8 | import net.kyori.adventure.text.format.NamedTextColor; 9 | import org.bukkit.command.CommandSender; 10 | import org.checkerframework.checker.nullness.qual.NonNull; 11 | import org.incendo.cloud.context.CommandContext; 12 | import org.incendo.cloud.context.CommandInput; 13 | import org.incendo.cloud.parser.ArgumentParseResult; 14 | import org.incendo.cloud.parser.ArgumentParser; 15 | import org.incendo.cloud.suggestion.Suggestion; 16 | import org.incendo.cloud.suggestion.SuggestionProvider; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class SlimeWorldParser implements ArgumentParser { 21 | @Override 22 | public @NonNull ArgumentParseResult<@NonNull SlimeWorld> parse(@NonNull CommandContext<@NonNull CommandSender> commandContext, @NonNull CommandInput commandInput) { 23 | String input = commandInput.peekString(); 24 | SlimeWorld loaded = AdvancedSlimePaperAPI.instance().getLoadedWorld(input); 25 | 26 | if (loaded == null) { 27 | return ArgumentParseResult.failure(new MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( 28 | Component.text("World " + input + " is not loaded!").color(NamedTextColor.RED) 29 | ))); 30 | } 31 | commandInput.readString(); 32 | return ArgumentParseResult.success(loaded); 33 | } 34 | 35 | @Override 36 | public @NonNull SuggestionProvider suggestionProvider() { 37 | return (context, input) -> CompletableFuture.supplyAsync(() -> 38 | AdvancedSlimePaperAPI.instance().getLoadedWorlds() 39 | .stream() 40 | .map(SlimeWorld::getName) 41 | .map(Suggestion::suggestion) 42 | .toList() 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.parser.suggestion; 2 | 3 | import com.infernalsuite.asp.plugin.config.ConfigManager; 4 | import org.bukkit.command.CommandSender; 5 | import org.checkerframework.checker.nullness.qual.NonNull; 6 | import org.incendo.cloud.context.CommandContext; 7 | import org.incendo.cloud.context.CommandInput; 8 | import org.incendo.cloud.suggestion.Suggestion; 9 | import org.incendo.cloud.suggestion.SuggestionProvider; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class KnownSlimeWorldSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { 16 | return CompletableFuture.supplyAsync(() -> 17 | ConfigManager.getWorldConfig() 18 | .getWorlds() 19 | .keySet() 20 | .stream() 21 | .map(Suggestion::suggestion) 22 | .toList() 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/GotoCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.kyori.adventure.text.format.NamedTextColor; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.Location; 7 | import org.bukkit.World; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.incendo.cloud.annotations.Argument; 11 | import org.incendo.cloud.annotations.Command; 12 | import org.incendo.cloud.annotations.CommandDescription; 13 | import org.incendo.cloud.annotations.Permission; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public class GotoCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { 17 | 18 | public GotoCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { 19 | super(commandManager); 20 | } 21 | 22 | @Command("swp|aswm|swm goto [player]") 23 | @CommandDescription("Teleport yourself (or someone else) to a world.") 24 | @Permission("swm.goto") 25 | public void onCommand(CommandSender sender, @Argument(value = "world") World world, 26 | @Argument(value = "player") @Nullable Player target ) { 27 | Player finalTarget; 28 | 29 | if (target == null) { 30 | if (!(sender instanceof Player)) { 31 | throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( 32 | Component.text("The console cannot be teleported to a world! Please specify a player.").color(NamedTextColor.RED) 33 | )); 34 | } 35 | 36 | finalTarget = (Player) sender; 37 | } else { 38 | finalTarget = target; 39 | } 40 | 41 | if (target == null) { 42 | sender.sendMessage(COMMAND_PREFIX.append( 43 | Component.text("Teleporting yourself to ").color(NamedTextColor.GRAY) 44 | .append(Component.text(world.getName()).color(NamedTextColor.AQUA)) 45 | .append(Component.text("...")).color(NamedTextColor.GRAY) 46 | )); 47 | } else { 48 | sender.sendMessage(COMMAND_PREFIX.append( 49 | Component.text("Teleporting ").color(NamedTextColor.GRAY) 50 | .append(Component.text(target.getName()).color(NamedTextColor.YELLOW)) 51 | .append(Component.text(" to ").color(NamedTextColor.GRAY)) 52 | .append(Component.text(world.getName()).color(NamedTextColor.AQUA)) 53 | .append(Component.text("...")).color(NamedTextColor.GRAY) 54 | )); 55 | } 56 | 57 | Location spawnLocation; 58 | if (com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig().getWorlds().containsKey(world.getName())) { 59 | String spawn = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig().getWorlds().get(world.getName()).getSpawn(); 60 | String[] coords = spawn.split(", "); 61 | double x = Double.parseDouble(coords[0]); 62 | double y = Double.parseDouble(coords[1]); 63 | double z = Double.parseDouble(coords[2]); 64 | spawnLocation = new Location(world, x, y, z); 65 | } else { 66 | spawnLocation = world.getSpawnLocation(); 67 | } 68 | 69 | finalTarget.teleportAsync(spawnLocation); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/HelpCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.incendo.cloud.annotations.Argument; 5 | import org.incendo.cloud.annotations.Command; 6 | import org.incendo.cloud.annotations.CommandDescription; 7 | import org.incendo.cloud.minecraft.extras.MinecraftHelp; 8 | import org.incendo.cloud.paper.LegacyPaperCommandManager; 9 | import org.incendo.cloud.paper.PaperCommandManager; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class HelpCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { 13 | 14 | private final MinecraftHelp help; 15 | 16 | public HelpCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager, LegacyPaperCommandManager cloudCommandManager) { 17 | super(commandManager); 18 | this.help = MinecraftHelp.createNative("/swp help", cloudCommandManager); 19 | } 20 | 21 | @Command("swp|aswm|swm help [query]") 22 | @CommandDescription("Displays the help message.") 23 | public void help(CommandSender sender, @Argument(value = "query") @Nullable String[] query) { 24 | String parsedQuery = query == null ? "" : String.join(" ", query); 25 | help.queryCommands(parsedQuery, sender); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ReloadConfigCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | import com.infernalsuite.asp.plugin.commands.CommandManager; 4 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 5 | import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; 6 | import com.infernalsuite.asp.plugin.config.ConfigManager; 7 | import net.kyori.adventure.text.Component; 8 | import net.kyori.adventure.text.format.NamedTextColor; 9 | import org.bukkit.ChatColor; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.command.ConsoleCommandSender; 12 | import org.incendo.cloud.annotations.Command; 13 | import org.incendo.cloud.annotations.CommandDescription; 14 | import org.incendo.cloud.annotations.Permission; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.IOException; 19 | import java.util.concurrent.CompletableFuture; 20 | 21 | public class ReloadConfigCmd extends SlimeCommand { 22 | private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfigCmd.class); 23 | 24 | public ReloadConfigCmd(CommandManager commandManager) { 25 | super(commandManager); 26 | } 27 | 28 | @Command("swp|aswm|swm reload") 29 | @CommandDescription("Reloads the config files.") 30 | @Permission("swm.reload") 31 | public CompletableFuture reloadConfig(CommandSender sender) { 32 | return CompletableFuture.runAsync(() -> { 33 | try { 34 | ConfigManager.initialize(); 35 | } catch (IOException ex) { 36 | LOGGER.error("Failed to load config files:", ex); 37 | 38 | throw new MessageCommandException(COMMAND_PREFIX.append( 39 | Component.text("Failed to reload the config file. Take a look at the server console for more information.").color(NamedTextColor.RED) 40 | )); 41 | } 42 | 43 | sender.sendMessage(COMMAND_PREFIX.append( 44 | Component.text("Config reloaded.").color(NamedTextColor.GREEN) 45 | )); 46 | }); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SaveWorldCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | 4 | import com.infernalsuite.asp.api.world.SlimeWorld; 5 | import com.infernalsuite.asp.plugin.commands.CommandManager; 6 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 7 | import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; 8 | import net.kyori.adventure.text.Component; 9 | import net.kyori.adventure.text.format.NamedTextColor; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.ChatColor; 12 | import org.bukkit.World; 13 | import org.bukkit.command.CommandSender; 14 | import org.incendo.cloud.annotations.Argument; 15 | import org.incendo.cloud.annotations.Command; 16 | import org.incendo.cloud.annotations.CommandDescription; 17 | import org.incendo.cloud.annotations.Permission; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.io.IOException; 22 | 23 | public class SaveWorldCmd extends SlimeCommand { 24 | private static final Logger LOGGER = LoggerFactory.getLogger(SaveWorldCmd.class); 25 | 26 | public SaveWorldCmd(CommandManager commandManager) { 27 | super(commandManager); 28 | } 29 | 30 | @Command("swp|aswm|swm save ") 31 | @CommandDescription("Saves a world.") 32 | @Permission("swm.saveworld") 33 | public void saveWorld(CommandSender sender, @Argument(value = "world") SlimeWorld slimeWorld) { 34 | try { 35 | asp.saveWorld(slimeWorld); 36 | sender.sendMessage(COMMAND_PREFIX.append( 37 | Component.text("World " + slimeWorld.getName() + " saved.").color(NamedTextColor.GREEN) 38 | )); 39 | } catch (IOException e) { 40 | LOGGER.error("Failed to save world {}.", slimeWorld.getName(), e); 41 | throw new MessageCommandException(COMMAND_PREFIX.append( 42 | Component.text("Failed to save world " + slimeWorld.getName() + ".").color(NamedTextColor.RED) 43 | )); 44 | } 45 | 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SetSpawnCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | 4 | import com.infernalsuite.asp.plugin.commands.CommandManager; 5 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 6 | import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; 7 | import com.infernalsuite.asp.plugin.config.ConfigManager; 8 | import com.infernalsuite.asp.plugin.config.WorldData; 9 | import net.kyori.adventure.text.Component; 10 | import net.kyori.adventure.text.format.NamedTextColor; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.ChatColor; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.command.CommandSender; 16 | import org.bukkit.entity.Player; 17 | import org.incendo.cloud.annotations.Command; 18 | import org.incendo.cloud.annotations.CommandDescription; 19 | import org.incendo.cloud.annotations.Permission; 20 | 21 | public class SetSpawnCmd extends SlimeCommand { 22 | 23 | public SetSpawnCmd(CommandManager commandManager) { 24 | super(commandManager); 25 | } 26 | 27 | //TODO: It seems like originally this command was supposed to allow to set a spawnpoint based on a provided location, but it was never implemented. 28 | @Command("swp|aswm|swm setspawn") 29 | @CommandDescription("Set the spawnpoint of a world based on your location") 30 | @Permission("swm.setspawn") 31 | public void setSpawn(CommandSender sender) { 32 | if (!(sender instanceof Player player)) { 33 | throw new MessageCommandException(COMMAND_PREFIX.append( 34 | Component.text("This command is for players").color(NamedTextColor.RED) 35 | )); 36 | } 37 | 38 | Location location = player.getLocation(); 39 | World world = location.getWorld(); 40 | WorldData config = ConfigManager.getWorldConfig().getWorlds().get(world.getName()); 41 | 42 | if (config == null) { 43 | throw new MessageCommandException(COMMAND_PREFIX.append( 44 | Component.text("World ").color(NamedTextColor.RED) 45 | .append(Component.text(world.getName()).color(NamedTextColor.YELLOW)) 46 | .append(Component.text(" is not a registered slime world.")).color(NamedTextColor.RED) 47 | )); 48 | } 49 | 50 | world.setSpawnLocation(player.getLocation()); 51 | 52 | String spawnVerbose = player.getLocation().getX() + ", " + player.getLocation().getY() + ", " + player.getLocation().getZ(); 53 | 54 | config.setSpawn(spawnVerbose); 55 | ConfigManager.getWorldConfig().save(); //FIXME: An IO op should be done async 56 | 57 | sender.sendMessage(COMMAND_PREFIX.append( 58 | Component.text("Set spawn for ").color(NamedTextColor.GREEN) 59 | .append(Component.text(world.getName()).color(NamedTextColor.YELLOW)) 60 | .append(Component.text(".").color(NamedTextColor.GREEN)) 61 | )); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/VersionCmd.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.commands.sub; 2 | 3 | import com.infernalsuite.asp.plugin.SWPlugin; 4 | import com.infernalsuite.asp.api.utils.SlimeFormat; 5 | import com.infernalsuite.asp.plugin.commands.CommandManager; 6 | import com.infernalsuite.asp.plugin.commands.SlimeCommand; 7 | import net.kyori.adventure.text.Component; 8 | import net.kyori.adventure.text.format.NamedTextColor; 9 | import org.bukkit.command.CommandSender; 10 | import org.incendo.cloud.annotations.Command; 11 | import org.incendo.cloud.annotations.CommandDescription; 12 | 13 | public class VersionCmd extends SlimeCommand { 14 | 15 | public VersionCmd(CommandManager commandManager) { 16 | super(commandManager); 17 | } 18 | 19 | @Command("swp|aswm|swm version") 20 | @CommandDescription("Shows the plugin version.") 21 | public void showVersion(CommandSender sender) { 22 | sender.sendMessage(COMMAND_PREFIX.append( 23 | Component.text("This server is running SWM ").color(NamedTextColor.GRAY) 24 | .append(Component.text("v" + SWPlugin.getInstance().getDescription().getVersion()).color(NamedTextColor.YELLOW)) 25 | .append(Component.text(", which supports up to Slime Format ").color(NamedTextColor.GRAY)) 26 | .append(Component.text("v" + SlimeFormat.SLIME_VERSION).color(NamedTextColor.AQUA)) 27 | .append(Component.text(".").color(NamedTextColor.GRAY)) 28 | )); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/config/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.config; 2 | 3 | import com.infernalsuite.asp.plugin.SWPlugin; 4 | import io.leangen.geantyref.TypeToken; 5 | import org.spongepowered.configurate.loader.HeaderMode; 6 | import org.spongepowered.configurate.yaml.NodeStyle; 7 | import org.spongepowered.configurate.yaml.YamlConfigurationLoader; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | 13 | public class ConfigManager { 14 | 15 | private static final File PLUGIN_DIR = new File("plugins", "SlimeWorldManager"); 16 | private static final File WORLDS_FILE = new File(PLUGIN_DIR, "worlds.yml"); 17 | private static final File SOURCES_FILE = new File(PLUGIN_DIR, "sources.yml"); 18 | 19 | private static WorldsConfig worldConfig; 20 | private static YamlConfigurationLoader worldConfigLoader; 21 | 22 | private static DatasourcesConfig datasourcesConfig; 23 | 24 | public static void initialize() throws IOException { 25 | copyDefaultConfigs(); 26 | 27 | worldConfigLoader = YamlConfigurationLoader.builder().file(WORLDS_FILE) 28 | .nodeStyle(NodeStyle.BLOCK).headerMode(HeaderMode.PRESERVE).build(); 29 | worldConfig = worldConfigLoader.load().get(TypeToken.get(WorldsConfig.class)); 30 | 31 | YamlConfigurationLoader datasourcesConfigLoader = YamlConfigurationLoader.builder().file(SOURCES_FILE) 32 | .nodeStyle(NodeStyle.BLOCK).headerMode(HeaderMode.PRESERVE).build(); 33 | datasourcesConfig = datasourcesConfigLoader.load().get(TypeToken.get(DatasourcesConfig.class)); 34 | 35 | worldConfig.save(); 36 | datasourcesConfigLoader.save(datasourcesConfigLoader.createNode().set(TypeToken.get(DatasourcesConfig.class), datasourcesConfig)); 37 | } 38 | 39 | private static void copyDefaultConfigs() throws IOException { 40 | PLUGIN_DIR.mkdirs(); 41 | 42 | if (!WORLDS_FILE.exists()) { 43 | Files.copy(SWPlugin.getInstance().getResource("worlds.yml"), WORLDS_FILE.toPath()); 44 | } 45 | 46 | if (!SOURCES_FILE.exists()) { 47 | Files.copy(SWPlugin.getInstance().getResource("worlds.yml"), SOURCES_FILE.toPath()); 48 | } 49 | } 50 | 51 | public static DatasourcesConfig getDatasourcesConfig() { 52 | return datasourcesConfig; 53 | } 54 | 55 | public static WorldsConfig getWorldConfig() { 56 | return worldConfig; 57 | } 58 | 59 | static YamlConfigurationLoader getWorldConfigLoader() { 60 | return worldConfigLoader; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldsConfig.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.config; 2 | 3 | import io.leangen.geantyref.TypeToken; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.spongepowered.configurate.objectmapping.ConfigSerializable; 7 | import org.spongepowered.configurate.objectmapping.meta.Setting; 8 | 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | @ConfigSerializable 14 | public class WorldsConfig { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(WorldsConfig.class); 17 | 18 | @Setting("worlds") 19 | private final Map worlds = new HashMap<>(); 20 | 21 | public void save() { 22 | try { 23 | ConfigManager.getWorldConfigLoader().save(ConfigManager.getWorldConfigLoader().createNode().set(TypeToken.get(WorldsConfig.class), this)); 24 | } catch (IOException ex) { 25 | LOGGER.error("Failed to save worlds config file", ex); 26 | } 27 | } 28 | 29 | public Map getWorlds() { 30 | return worlds; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /plugin/src/main/java/com/infernalsuite/asp/plugin/util/ExecutorUtil.java: -------------------------------------------------------------------------------- 1 | package com.infernalsuite.asp.plugin.util; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | public class ExecutorUtil { 9 | 10 | // We usually use this method to make sure that any exceptions thrown by the runnable are propagated to the caller 11 | public static void runSyncAndWait(Plugin plugin, Runnable runnable) { 12 | if (Bukkit.isPrimaryThread()) { 13 | runnable.run(); 14 | return; 15 | } 16 | 17 | CountDownLatch latch = new CountDownLatch(1); 18 | RuntimeException[] runtimeException = new RuntimeException[1]; 19 | 20 | Bukkit.getScheduler().runTask(plugin, () -> { 21 | try { 22 | runnable.run(); 23 | } catch (RuntimeException e) { 24 | runtimeException[0] = e; 25 | } finally { 26 | latch.countDown(); 27 | } 28 | }); 29 | 30 | try { 31 | latch.await(); 32 | } catch (InterruptedException e) { 33 | throw new RuntimeException(e); // Rather propagate the interrupt (and thus prevent further execution) than continue 34 | } 35 | 36 | if (runtimeException[0] != null) { 37 | throw runtimeException[0]; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugin/src/main/resource-templates/paper-plugin.yml: -------------------------------------------------------------------------------- 1 | name: "SlimeWorldPlugin" 2 | version: "{{ version }}" 3 | main: "com.infernalsuite.aswm.plugin.SWPlugin" 4 | authors: 5 | - Grinderwolf 6 | - Paul19988 7 | - Jools 8 | - SoapTurtle 9 | - Gerolmed 10 | - b0ykoe 11 | - Owen 12 | - ComputerNerd100 13 | - kyngs 14 | api-version: "1.20" 15 | description: "Advanced Slime World Manager is a resource that implements the Slime Region Format, designed by the Hypixel Team to load and save worlds more efficiently." -------------------------------------------------------------------------------- /plugin/src/main/resources/main.yml: -------------------------------------------------------------------------------- 1 | # TO PREVENT NULL. 2 | updater: 3 | enabled: false 4 | onjoinmessage: true -------------------------------------------------------------------------------- /plugin/src/main/resources/sources.yml: -------------------------------------------------------------------------------- 1 | # Inside this file is the configuration options 2 | # for the data sources that ASP supports 3 | mysql: 4 | enabled: false 5 | host: 127.0.0.1 6 | port: 3306 7 | username: slimeworldmanager 8 | password: '' 9 | database: slimeworldmanager 10 | usessl: false 11 | mongodb: 12 | enabled: false 13 | host: 127.0.0.1 14 | port: 27017 15 | auth: admin 16 | username: slimeworldmanager 17 | password: '' 18 | database: slimeworldmanager 19 | collection: worlds 20 | uri: '' 21 | file: 22 | path: slime_worlds 23 | api: 24 | enabled: false 25 | ignoreSslCertificate: false 26 | username: '' 27 | token: '' 28 | url: '' -------------------------------------------------------------------------------- /plugin/src/main/resources/worlds.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration file for all the slime worlds 2 | # 3 | # Example configuration: 4 | # worlds: 5 | # world1: 6 | # source: file 7 | # difficulty: peaceful 8 | # spawn: 32, 40, 97 9 | # allowMonsters: false 10 | # allowAnimals: false 11 | # loadOnStartup: true 12 | # readOnly: true 13 | # world2: 14 | # source: mysql 15 | # difficulty: hard 16 | # spawn: -140, 38, 159 17 | # allowMonsters: false 18 | # allowAnimals: false 19 | # loadOnStartup: false 20 | # readOnly: true 21 | # world3: 22 | # source: seaweed 23 | # difficulty: easy 24 | # spawn: -59, 67, -2 25 | # allowMonsters: false 26 | # allowAnimals: true 27 | # loadOnStartup: true 28 | # readOnly: false 29 | # world4: 30 | # source: api 31 | # difficulty: easy 32 | # spawn: -59, 67, -2 33 | # allowMonsters: false 34 | # allowAnimals: true 35 | # loadOnStartup: true 36 | worlds: -------------------------------------------------------------------------------- /scripts/apatch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gitcmd="git -c commit.gpgsign=false" 4 | 5 | noapply=1 6 | isreject=0 7 | if [[ $1 == "--noapplied" ]]; then 8 | noapply=1 9 | shift 10 | fi 11 | 12 | if [ ! -z "$1" ]; then 13 | file="$1" 14 | elif [ -z "$1" ] && [ -f .git/rebase-apply/patch ]; then 15 | file=".git/rebase-apply/patch" 16 | noapply=1 17 | isreject=1 18 | else 19 | echo "Please specify a file" 20 | exit 1 21 | fi 22 | applied=$(echo $file | sed 's/.patch$/-applied\.patch/g') 23 | if [ "$1" == "--reset" ]; then 24 | $gitcmd am --abort 25 | $gitcmd reset --hard 26 | $gitcmd clean -f 27 | exit 0 28 | fi 29 | 30 | 31 | (test "$isreject" != "1" && $gitcmd am -3 $file) || ( 32 | echo "Failures - Wiggling" 33 | $gitcmd reset --hard 34 | $gitcmd clean -f 35 | errors=$($gitcmd apply --rej $file 2>&1) 36 | echo "$errors" >> ~/patch.log 37 | export missingfiles="" 38 | export summaryfail="" 39 | export summarygood="" 40 | for i in $(find . -name \*.rej); do 41 | base=$(echo "$i" | sed 's/.rej//g') 42 | if [ -f "$i" ]; then 43 | sed -e 's/^diff a\/\(.*\) b\/\(.*\)[[:space:]].*rejected.*$/--- \1\n+++ \2/' -i $i && wiggle -v -l --replace "$base" "$i" 44 | rm "$base.porig" "$i" 45 | else 46 | echo "No such file: $base" 47 | missingfiles="$missingfiles\n$base" 48 | fi 49 | done 50 | for i in $($gitcmd status --porcelain | awk '{print $2}'); do 51 | filedata=$(cat "$i") 52 | if [ -f "$file" ] && [[ "$filedata" == *"<<<<<"* ]]; then 53 | export summaryfail="$summaryfail\nFAILED TO APPLY: $i" 54 | else 55 | $gitcmd add --force "$i" 56 | export summarygood="$summarygood\nAPPLIED CLEAN: $i" 57 | fi 58 | done 59 | echo -e "$summarygood" 60 | echo -e "$summaryfail" 61 | if [[ "$errors" == *"No such file"* ]]; then 62 | echo "==========================="; 63 | echo " " 64 | echo " MISSING FILES" 65 | echo $(echo "$errors" | grep "No such file") 66 | echo -e "$missingfiles" 67 | echo " " 68 | echo "==========================="; 69 | fi 70 | $gitcmd status 71 | $gitcmd diff 72 | ) 73 | if [[ "$noapply" != "1" ]] && [[ "$file" != *-applied.patch ]]; then 74 | mv "$file" "$applied" 75 | fi -------------------------------------------------------------------------------- /scripts/upstreamCommit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # requires curl & jq 4 | 5 | # upstreamCommit 6 | # param: bashHash - the commit hash to use for comparing commits (baseHash...HEAD) 7 | 8 | ( 9 | set -e 10 | PS1="$" 11 | 12 | paper=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/PaperMC/Paper/compare/$1...HEAD | jq -r '.commits[] | "PaperMC/Paper@\(.sha[:7]) \(.commit.message | split("\r\n")[0] | split("\n")[0])"') 13 | 14 | updated="" 15 | logsuffix="" 16 | if [ ! -z "paper" ]; then 17 | logsuffix="$logsuffix\n\nPaper Changes:\n$paper" 18 | updated="Paper" 19 | fi 20 | disclaimer="Upstream has released updates that appear to apply and compile correctly" 21 | 22 | log="${UP_LOG_PREFIX}Updated Upstream ($updated)\n\n${disclaimer}${logsuffix}" 23 | 24 | echo -e "$log" | git commit -F - 25 | 26 | ) || exit 1 -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | maven("https://repo.papermc.io/repository/maven-public/") 7 | } 8 | } 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" 12 | } 13 | 14 | rootProject.name = "ASPaper" 15 | 16 | include(":gradle:platform") 17 | include(":api") 18 | include(":core") 19 | include(":importer") 20 | include(":loaders") 21 | include(":plugin") 22 | include(":aspaper-api") 23 | include(":aspaper-server") 24 | 25 | include("loaders:mongo-loader") 26 | findProject(":loaders:mongo-loader")?.name = "mongo-loader" 27 | include("loaders:api-loader") 28 | findProject(":loaders:api-loader")?.name = "api-loader" 29 | include("loaders:file-loader") 30 | findProject(":loaders:file-loader")?.name = "file-loader" 31 | include("loaders:mysql-loader") 32 | findProject(":loaders:mysql-loader")?.name = "mysql-loader" 33 | include("loaders:redis-loader") 34 | findProject(":loaders:redis-loader")?.name = "redis-loader" 35 | --------------------------------------------------------------------------------