├── .github └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── LICENCE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── settings.gradle └── src ├── main ├── java │ └── me │ │ └── mrnavastar │ │ └── sqlib │ │ ├── SQLib.java │ │ ├── api │ │ ├── DataContainer.java │ │ ├── DataStore.java │ │ ├── database │ │ │ ├── AuthenticatedDatabase.java │ │ │ ├── Database.java │ │ │ ├── MySQL.java │ │ │ ├── PostgreSQL.java │ │ │ └── SQLite.java │ │ └── types │ │ │ ├── AdventureTypes.java │ │ │ ├── GsonTypes.java │ │ │ ├── JavaTypes.java │ │ │ ├── MinecraftTypes.java │ │ │ └── SQLibType.java │ │ └── impl │ │ ├── ByteParser.java │ │ ├── SQLConnection.java │ │ ├── SQLPrimitive.java │ │ ├── SoundParser.java │ │ ├── TextParser.java │ │ └── config │ │ ├── Config.java │ │ ├── Fabric.java │ │ ├── NonMinecraft.java │ │ ├── Quilt.java │ │ └── Velocity.java └── resources │ ├── assets │ └── sqlib │ │ └── icon.png │ ├── fabric.mod.json │ ├── sqlib.toml │ └── velocity-plugin.json └── testMod ├── java └── me │ └── mrnavastar │ └── sqlib │ └── TestMod.java └── resources └── fabric.mod.json /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release New Version 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-java@v4 12 | with: 13 | distribution: temurin 14 | java-version: 21 15 | 16 | - name: Set Version 17 | run: | 18 | sed -i 's/debug-build/${{ github.event.release.tag_name }}/g' src/main/resources/fabric.mod.json 19 | sed -i 's/debug-build/${{ github.event.release.tag_name }}/g' src/main/resources/velocity-plugin.json 20 | 21 | - name: Setup Gradle 22 | uses: gradle/gradle-build-action@v3 23 | 24 | - name: Execute Gradle build 25 | run: ./gradlew build 26 | 27 | - name: Rename Files 28 | run: | 29 | mv ${{ github.workspace }}/build/libs/sqlib-all.jar ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}.jar; 30 | mv ${{ github.workspace }}/build/libs/sqlib-javadoc.jar ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}-javadoc.jar; 31 | mv ${{ github.workspace }}/build/libs/sqlib-sources.jar ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}-sources.jar; 32 | 33 | - name: Publish Release 34 | uses: Kir-Antipov/mc-publish@v3.3.0 35 | with: 36 | modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 37 | modrinth-featured: true 38 | files: | 39 | ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}.jar 40 | ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}-javadoc.jar 41 | ${{ github.workspace }}/sqlib-${{ github.event.release.tag_name }}-sources.jar 42 | java: 21 43 | game-versions: | 44 | >=1.16.5 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | on: 3 | push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-java@v4 11 | with: 12 | distribution: temurin 13 | java-version: 21 14 | 15 | - name: Setup Gradle 16 | uses: gradle/gradle-build-action@v3 17 | 18 | - name: Run Test Mod 19 | run: ./gradlew runTestMod 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity) 2 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 3 | 4 | [modrinth](https://modrinth.com/plugin/sqlib) 5 | 6 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/G2G4DZF4D) 7 | 8 | 9 | 10 | # SQLib 11 | SQLib is the easiest way to store data for all your minecraft needs! A simple sql wrapper made with a focus on minecraft use cases. 12 | 13 | # Important Note: 14 | This library is not a full-fledged sql wrapper, and does not provide full access to many sql features. 15 | The main focus of this library is to provide an easy and simple way to store data in your mods. 16 | If you are looking for a more advanced database I recommend taking a look at something like [Nitrite](https://github.com/nitrite/nitrite-java). 17 | 18 | # Config 19 | The mod generates a config on first time start that allows you to configure the database used by all mods relying on sqlib. 20 | The default database is a sqlite database running in the sqlib directory. 21 | 22 | # Datatypes 23 | The datatypes can be accessed via `JavaTpes`, `MinecraftTypes` or the `AdventureTypes` classes. I tend to add support for new types as I run into them in my projects. If you would like one added, pleade make an issue! 24 | | Standard | Minecraft | Adventure | 25 | |----------|-------------|-----------| 26 | | Byte | Vec3i | Key | 27 | | Byte[] | BlockPos | Component | 28 | | Bool | ChunkPos | | 29 | | Short | Text | | 30 | | Int | Identifier | | 31 | | Float | Sound | | 32 | | Double | Json | | 33 | | Long | NbtElement | | 34 | | String | | | 35 | | Char | | | 36 | | Date | | | 37 | | Color | | | 38 | | UUID | | | 39 | | URI | | | 40 | | URL | | | 41 | 42 | You can also add your own custom types as seen here: 43 | 44 | # Setup 45 | In your build.gradle include: 46 | ``` gradle 47 | repositories { 48 | maven { url "https://api.modrinth.com/maven" } 49 | } 50 | 51 | dependencies { 52 | modImplementation("maven.modrinth:sqlib:3.2.2") 53 | } 54 | ``` 55 | 56 | # Developer Usage 57 | This example uses the built-in database managed by sqlib. for 99% of mods, using the built-in database is good, however 58 | further down are examples for custom database management. 59 | ```java 60 | // Do not call SQLib.getDatabase() in a early mod initializer. Doing so will likely crash your mod. 61 | // Calling in or after the regular mod initializer is ok. 62 | Database db = SQLib.getDatabase(); 63 | 64 | DataStore store = db.dataStore("myModId", "userdata"); 65 | 66 | DataContainer playerData = store.createDataContainer(); 67 | playerData.put(JavaTypes.STRING, "username", "CoolGuy123"); 68 | playerData.put(MinecraftTypes.BLOCKPOS, "home", new BlockPos(304, 62, 37)); 69 | playerData.put(MinecraftTypes.NBT, "nbt", new NbtCompound()); 70 | 71 | System.out.println(playerdata.get(JavaTypes.STRING, "username")); 72 | System.out.println(playerdata.get(MinecraftTypes.BLOCKPOS, "home")); 73 | System.out.println(playerdata.get(MinecraftTypes.NBT, "nbt")); 74 | ``` 75 | 76 | # Custom Database Management 77 | ```java 78 | Postgres db = new Postgres("name", "192.168.1.69", "3306", "cooluser", "radman"); 79 | // OR 80 | MySQL db = new MySQL( "name", "192.168.1.69", "3306", "cooluser", "radman"); 81 | // OR 82 | SQLite db = new SQLite("name", "some/dir"); 83 | ``` 84 | 85 | # Transaction Support 86 | This approach will bach sql commands into one command for faster read/writes of large amounts of data. 87 | ```java 88 | DataStore store = db.dataStore("modId", "userdata"); 89 | 90 | DataContainer playerData = table.createDataContainer(); 91 | playerData.transaction().put("username", "CoolGuy123").put("home", new BlockPos(304, 62, 37).commit(); 92 | ``` 93 | 94 | # Custom Types 95 | You can add a custom type by following the implentations in `JavaTypes`, `MinecraftTypes` and `AdventureTypes`. Then you can use it like any other native SQLib type. 96 | ```java 97 | // The SQLPrimitive is the base type to serialize to, and the two function lambdas are to serialize and deserialize from it 98 | public static final SQLibType JSON = new SQLibType<>(SQLPrimitive.STRING, JsonElement::toString, JsonParser::parseString); 99 | 100 | // You can also extend a type like this: 101 | public static final SQLibType IDENTIFIER = new SQLibType<>(SQLPrimitive.STRING, Identifier::toString, Identifier::tryParse); 102 | public static final SQLibType SOUND = new SQLibType<>(IDENTIFIER, SoundEvent::getId, SoundEvent::of); 103 | ``` 104 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.10.5' 3 | id "xyz.jpenilla.run-velocity" version "2.3.1" 4 | id "io.github.goooler.shadow" version "8.1.8" 5 | id 'io.freefair.lombok' version '8.13.1' 6 | } 7 | 8 | def targetJavaVersion = 21 9 | group = project.maven_group 10 | 11 | sourceSets { 12 | testMod { 13 | compileClasspath += main.compileClasspath 14 | runtimeClasspath += main.runtimeClasspath 15 | } 16 | } 17 | 18 | loom { 19 | runs { 20 | testMod { 21 | server() 22 | ideConfigGenerated project.rootProject == project 23 | name = "TestMod" 24 | source sourceSets.testMod 25 | } 26 | } 27 | } 28 | 29 | loom.mods.register("testMod") { 30 | sourceSet sourceSets.testMod 31 | } 32 | 33 | 34 | repositories { 35 | maven { url "https://maven.quiltmc.org/repository/release" } 36 | maven { url "https://api.modrinth.com/maven" } 37 | maven { url "https://repo.papermc.io/repository/maven-public/" } 38 | maven { 39 | name = "sonatype-oss-snapshots1" 40 | url = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 41 | mavenContent { snapshotsOnly() } 42 | } 43 | } 44 | 45 | dependencies { 46 | testModImplementation sourceSets.main.output 47 | 48 | // Fabric 49 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 50 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 51 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 52 | modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" 53 | // Quilt 54 | modCompileOnly "org.quiltmc:quilt-loader:0.28.1" 55 | //Velocity 56 | compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' 57 | annotationProcessor 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' 58 | // Adventure 59 | modImplementation "net.kyori:adventure-platform-fabric:${project.adventure_version}" 60 | // Config 61 | shadow implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-toml:${project.jackson_version}") 62 | // SQL 63 | shadow implementation("com.zaxxer:HikariCP:${project.hikari_version}") 64 | shadow implementation("org.jdbi:jdbi3-core:${project.jdbi_version}") 65 | shadow implementation("org.xerial:sqlite-jdbc:${project.sqlite_version}") 66 | shadow implementation("org.mariadb.jdbc:mariadb-java-client:${project.mariadb_version}") 67 | shadow implementation("org.postgresql:postgresql:${project.postgres_version}") 68 | // Extra 69 | compileOnly "org.projectlombok:lombok:1.18.38" 70 | annotationProcessor "org.projectlombok:lombok:1.18.38" 71 | modImplementation "maven.modrinth:easy-eula:1.1.1-fabric" 72 | } 73 | 74 | shadowJar { 75 | archiveBaseName.set('sqlib') 76 | configurations = [project.configurations.shadow] 77 | 78 | relocate 'com.fasterxml', 'me.mrnavastar.sqlib.libs.com.fasterxml' 79 | relocate 'com.github', 'me.mrnavastar.sqlib.libs.com.github' 80 | relocate 'com.google.errorprone', 'me.mrnavastar.sqlib.libs.com.google.errorprone' 81 | relocate 'com.zaxxer', 'me.mrnavastar.sqlib.libs.com.zaxxer' 82 | 83 | relocate 'io', 'me.mrnavastar.sqlib.libs.io' 84 | relocate 'waffle', 'me.mrnavastar.sqlib.libs.waffle' 85 | relocate 'org.checkerframework', 'me.mrnavastar.sqlib.libs.org.checkerframework' 86 | relocate 'org.jdbi', 'me.mrnavastar.sqlib.libs.org.jdbi' 87 | relocate 'me.mrnavastar.r', 'me.mrnavastar.sqlib.libs.me.mrnavastar.r' 88 | //relocate 'org.sqlite', 'me.mrnavastar.sqlib.libs.org.sqlite' 89 | //relocate 'org.mariadb', 'me.mrnavastar.sqlib.libs.org.mariadb' 90 | //relocate 'org.postgresql', 'me.mrnavastar.sqlib.libs.org.postgresql' 91 | 92 | dependencies { 93 | mergeServiceFiles() 94 | exclude(dependency('org.slf4j:.*:.*')) 95 | exclude(dependency('net.java.dev.jna:.*:.*')) 96 | } 97 | } 98 | jar.finalizedBy('shadowJar') 99 | 100 | tasks.withType(JavaCompile).configureEach { 101 | it.options.encoding = "UTF-8" 102 | } 103 | 104 | java { 105 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 106 | if (JavaVersion.current() < javaVersion) { 107 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 108 | } 109 | archivesBaseName = project.archives_base_name 110 | 111 | javadoc.options.addStringOption('Xdoclint:none', '-quiet') 112 | withJavadocJar() 113 | withSourcesJar() 114 | } 115 | 116 | sourcesJar { 117 | exclude { 118 | sourceSets.main.allSource.contains it.file 119 | } 120 | from delombok 121 | } 122 | 123 | jar { 124 | from("LICENSE") { 125 | rename { "${it}_${project.archivesBaseName}" } 126 | } 127 | } 128 | 129 | tasks { 130 | runVelocity { 131 | velocityVersion(project.velocity_version) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | 3 | # Fabric 4 | # check these on https://fabricmc.net/develop/ 5 | minecraft_version=1.21.4 6 | yarn_mappings=1.21.4+build.2 7 | loader_version=0.16.14 8 | fabric_version=0.112.2+1.21.4 9 | 10 | velocity_version=3.3.0-SNAPSHOT 11 | adventure_version=6.2.0 12 | 13 | # Mod Properties 14 | maven_group=mrnavastar 15 | archives_base_name=sqlib 16 | 17 | # Dependencies 18 | # https://central.sonatype.com/artifact/com.zaxxer/HikariCP 19 | hikari_version=6.3.0 20 | # https://github.com/zsoltherpai/fluent-jdbc 21 | jdbi_version=3.49.4 22 | # https://github.com/xerial/sqlite-jdbc/releases 23 | sqlite_version=3.49.1.0 24 | # https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client 25 | mariadb_version=3.5.3 26 | # https://github.com/pgjdbc/pgjdbc/releases 27 | postgres_version=42.7.6 28 | # https://github.com/FasterXML/jackson-dataformats-text 29 | jackson_version=2.19.0 30 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrNavaStar/SQLib/88e7b33555d1f978e67445577c512074196846a2/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.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /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= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 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 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", ":disableDependencyDashboard" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | mavenCentral() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/SQLib.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib; 2 | 3 | import me.mrnavastar.sqlib.api.database.Database; 4 | import me.mrnavastar.sqlib.impl.config.Config; 5 | 6 | import java.util.List; 7 | 8 | public class SQLib { 9 | 10 | protected static Database database; 11 | 12 | public static Database getDatabase() { 13 | Config.load(); 14 | return database; 15 | } 16 | 17 | public static List getDatabases() { 18 | return Database.getDatabases(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/DataContainer.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api; 2 | 3 | import lombok.*; 4 | import me.mrnavastar.sqlib.api.types.SQLibType; 5 | import me.mrnavastar.sqlib.impl.SQLConnection; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @AllArgsConstructor 12 | @EqualsAndHashCode 13 | public class DataContainer { 14 | 15 | @Getter 16 | private final DataStore store; 17 | @Getter 18 | private final int id; 19 | private final SQLConnection connection; 20 | 21 | @AllArgsConstructor 22 | public static class Transaction { 23 | 24 | public record Put(SQLibType type, String field, Object value) {} 25 | 26 | private final DataContainer container; 27 | private final SQLConnection connection; 28 | private final ArrayList puts = new ArrayList<>(); 29 | 30 | public Transaction put(SQLibType type, @NonNull String field, T value) { 31 | puts.add(new Put(type, field, type.serialize(value))); 32 | return this; 33 | } 34 | 35 | public DataContainer commit() { 36 | connection.writeField(container.store, container.id, puts); 37 | return container; 38 | } 39 | } 40 | 41 | public Transaction transaction() { 42 | return new Transaction(this, connection); 43 | } 44 | 45 | public void put(SQLibType type, @NonNull String field, T value) { 46 | connection.writeField(store, id, List.of(new Transaction.Put(type, field, type.serialize(value)))); 47 | } 48 | 49 | public Optional get(SQLibType type, @NonNull String field) { 50 | Object value = connection.readField(store, id, field, type.getType().getClazz()); 51 | if (value == null) return Optional.empty(); 52 | return Optional.ofNullable(type.deserialize(value)); 53 | } 54 | 55 | public void clear(@NonNull String field) { 56 | connection.clearField(store, id, field); 57 | } 58 | 59 | /** 60 | * Delete the {@link DataContainer} from the database 61 | */ 62 | public void delete() { 63 | connection.deleteRow(store, id); 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/DataStore.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.NonNull; 6 | import lombok.SneakyThrows; 7 | import me.mrnavastar.sqlib.api.database.Database; 8 | import me.mrnavastar.sqlib.api.types.SQLibType; 9 | import me.mrnavastar.sqlib.impl.SQLConnection; 10 | 11 | import java.util.*; 12 | import java.util.function.Consumer; 13 | 14 | /** 15 | * This class represents a table in a {@link Database}. 16 | */ 17 | @EqualsAndHashCode 18 | public class DataStore { 19 | 20 | @Getter 21 | private final String modId; 22 | @Getter 23 | private final String name; 24 | @Getter 25 | private final Database database; 26 | private final SQLConnection connection; 27 | 28 | public DataStore(@NonNull String modId, @NonNull String name, @NonNull Database database, @NonNull SQLConnection connection) { 29 | this.modId = modId; 30 | this.name = name; 31 | this.database = database; 32 | this.connection = connection; 33 | connection.createTable(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return modId + "_" + name; 39 | } 40 | 41 | /** 42 | * Creates a new {@link DataContainer} with a unique id. 43 | */ 44 | @SneakyThrows 45 | public DataContainer createContainer() { 46 | return new DataContainer(this, connection.createRow(this), connection); 47 | } 48 | 49 | /** 50 | * Tries to get a {@link DataContainer} or creates a new {@link DataContainer} if it is missing. 51 | * Note that the created {@link DataContainer} is not guaranteed to have the same id as the one passed in. 52 | */ 53 | public DataContainer getOrCreateContainer(int id) { 54 | return getContainer(id).orElseGet(this::createContainer); 55 | } 56 | 57 | /** 58 | * Tries to get a {@link DataContainer} or creates a new {@link DataContainer} if it is missing. 59 | * Note that the created {@link DataContainer} is not guaranteed to have the same id as the one passed in. 60 | * 61 | * @param onCreate A function that gets run only when the container is created. This is useful for setting things 62 | * such as a container id or other elements that are only set once. 63 | */ 64 | public DataContainer getOrCreateContainer(int id, Consumer onCreate) { 65 | return getContainer(id).orElseGet(() -> { 66 | DataContainer newContainer = createContainer(); 67 | onCreate.accept(newContainer); 68 | return newContainer; 69 | }); 70 | } 71 | 72 | /** 73 | * Tries to get a {@link DataContainer} with a matching key value pair or creates a new {@link DataContainer} if it is missing. 74 | */ 75 | public DataContainer getOrCreateContainer(@NonNull String field, @NonNull Object value) { 76 | return getContainer(field, value).orElseGet(this::createContainer); 77 | } 78 | 79 | /** 80 | * Tries to get a {@link DataContainer} with a matching key value pair or creates a new {@link DataContainer} if it is missing. 81 | * 82 | * @param onCreate A function that gets run only when the container is created. This is useful for setting things 83 | * such as a container id or other elements that are only set once. 84 | */ 85 | public DataContainer getOrCreateContainer(@NonNull String field, @NonNull Object value, Consumer onCreate) { 86 | return getContainer(field, value).orElseGet(() -> { 87 | DataContainer newContainer = createContainer(); 88 | onCreate.accept(newContainer); 89 | return newContainer; 90 | }); 91 | } 92 | 93 | /** 94 | * Tries to get a {@link DataContainer} with a matching key value pair or creates a new {@link DataContainer} with 95 | * a matching key value pair if it is missing. 96 | */ 97 | public DataContainer getOrCreateDefaultContainer(@NonNull SQLibType type, @NonNull String field, @NonNull T value) { 98 | return getOrCreateContainer(field, value, c -> c.put(type, field, value)); 99 | } 100 | 101 | /** 102 | * @return A {@link DataContainer}'s with a matching key value pair or null if one does not exist. 103 | */ 104 | @SneakyThrows 105 | public Optional getContainer(@NonNull String field, @NonNull Object value) { 106 | return getContainers(field, value).stream().findFirst(); 107 | } 108 | 109 | /** 110 | * @return A {@link DataContainer} or null if it is missing. 111 | */ 112 | @SneakyThrows 113 | public Optional getContainer(int id) { 114 | return connection.rowExists(this, id) ? Optional.of(new DataContainer(this, id, connection)) : Optional.empty(); 115 | } 116 | 117 | /** 118 | * @return A list of all the {@link DataContainer}'s in this table. 119 | */ 120 | @SneakyThrows 121 | public List getContainers() { 122 | // Done this way to ensure that we always match the true state of the database in the case that the database is modified externally 123 | return connection.listIds(this).stream().map(id -> new DataContainer(this, id, connection)).toList(); 124 | } 125 | 126 | /** 127 | * @return A list of all the {@link DataContainer}'s with a matching key value pair. 128 | */ 129 | @SneakyThrows 130 | public List getContainers(@NonNull String field, @NonNull Object value) { 131 | // Done this way to ensure that we always match the true state of the database in the case that the database is modified externally 132 | return connection.findRows(this, field, value).stream().map(id -> new DataContainer(this, id, connection)).toList(); 133 | } 134 | 135 | /** 136 | * @return A list of every key present in this {@link DataStore}. 137 | */ 138 | public List getKeys() { 139 | return connection.listColumns(this); 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/database/AuthenticatedDatabase.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.database; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.Properties; 6 | 7 | @Getter 8 | public abstract class AuthenticatedDatabase extends Database { 9 | 10 | protected final String address; 11 | protected final String port; 12 | protected final String username; 13 | protected final String password; 14 | 15 | public AuthenticatedDatabase(String name, String address, String port, String username, String password) { 16 | super(name); 17 | this.address = address; 18 | this.port = port; 19 | this.username = username; 20 | this.password = password; 21 | connect(); 22 | } 23 | 24 | @Override 25 | public Properties getConnectionProperties() { 26 | Properties properties = new Properties(); 27 | properties.setProperty("user", username); 28 | properties.setProperty("password", password); 29 | properties.setProperty("ssl", "true"); 30 | properties.put("config_timeout", true); 31 | return properties; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/database/Database.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.database; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | import me.mrnavastar.sqlib.api.DataStore; 6 | import me.mrnavastar.sqlib.impl.SQLConnection; 7 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 8 | import me.mrnavastar.sqlib.impl.config.Config; 9 | 10 | import java.util.*; 11 | 12 | /** 13 | * This class can be extended to allow for new database implementations 14 | */ 15 | public abstract class Database { 16 | 17 | private static final HashSet databases = new HashSet<>(); 18 | 19 | static { 20 | Runtime.getRuntime().addShutdownHook(new Thread(() -> databases.forEach(Database::close))); 21 | Config.load(); 22 | } 23 | 24 | public static List getDatabases() { 25 | return List.copyOf(databases); 26 | } 27 | 28 | @Getter 29 | protected final String name; 30 | protected SQLConnection connection; 31 | 32 | public Database(@NonNull String name) { 33 | this.name = name; 34 | databases.add(this); 35 | } 36 | 37 | protected abstract String getConnectionUrl(); 38 | 39 | protected Properties getConnectionProperties() { 40 | return new Properties(); 41 | } 42 | 43 | public abstract String getTableCreationQuery(String tableName); 44 | 45 | public String getRowCreationQuery(String rowName) { 46 | return "INSERT INTO %s DEFAULT VALUES RETURNING SQLIB_AUTO_ID".formatted(rowName); 47 | } 48 | 49 | public abstract String getColumnListQuery(String tableName); 50 | 51 | public abstract String getDataType(SQLPrimitive dataType); 52 | 53 | protected void connect() { 54 | connection = new SQLConnection(getConnectionUrl(), getConnectionProperties()); 55 | } 56 | 57 | public void close() { 58 | connection.close(); 59 | } 60 | 61 | public DataStore dataStore(String modId, String name) { 62 | return new DataStore(modId, name, this, connection); 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/database/MySQL.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.database; 2 | 3 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 4 | 5 | public class MySQL extends AuthenticatedDatabase { 6 | 7 | public MySQL(String name, String address, String port, String username, String password) { 8 | super(name, address, port, username, password); 9 | } 10 | 11 | @Override 12 | public String getConnectionUrl() { 13 | return "jdbc:mariadb://%s:%s/%s".formatted(address, port, name); 14 | } 15 | 16 | @Override 17 | public String getTableCreationQuery(String tableName) { 18 | return "CREATE TABLE IF NOT EXISTS %s (SQLIB_AUTO_ID INT AUTO_INCREMENT, PRIMARY KEY (SQLIB_AUTO_ID))".formatted(tableName); 19 | } 20 | 21 | @Override 22 | public String getRowCreationQuery(String rowName) { 23 | return "INSERT INTO %s () VALUES () RETURNING SQLIB_AUTO_ID".formatted(rowName); 24 | } 25 | 26 | @Override 27 | public String getColumnListQuery(String tableName) { 28 | return "SELECT * FROM %s WHERE 1 = 0".formatted(tableName); 29 | } 30 | 31 | @Override 32 | public String getDataType(SQLPrimitive type) { 33 | return switch (type.getType()) { 34 | case BYTE, BOOL -> "TINYINT"; 35 | case BYTES -> "LONGBLOB"; 36 | case SHORT -> "SMALLINT"; 37 | case LONG -> "BIGINT"; 38 | case STRING -> "LONGTEXT"; 39 | case CHAR -> "CHAR(2)"; 40 | default -> type.getType().name(); 41 | }; 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/database/PostgreSQL.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.database; 2 | 3 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 4 | 5 | public class PostgreSQL extends AuthenticatedDatabase { 6 | 7 | public PostgreSQL(String name, String address, String port, String username, String password) { 8 | super(name, address, port, username, password); 9 | } 10 | 11 | @Override 12 | public String getConnectionUrl() { 13 | return "jdbc:postgresql://%s:%s/%s".formatted(address, port, name); 14 | } 15 | 16 | @Override 17 | public String getTableCreationQuery(String tableName) { 18 | return "CREATE TABLE IF NOT EXISTS %s (SQLIB_AUTO_ID BIGSERIAL PRIMARY KEY)".formatted(tableName); 19 | } 20 | 21 | @Override 22 | public String getRowCreationQuery(String rowName) { 23 | return "INSERT INTO %s VALUES(DEFAULT) RETURNING SQLIB_AUTO_ID".formatted(rowName); 24 | } 25 | 26 | @Override 27 | public String getColumnListQuery(String tableName) { 28 | return "SELECT * FROM %s WHERE 1 = 0".formatted(tableName); 29 | } 30 | 31 | @Override 32 | public String getDataType(SQLPrimitive type) { 33 | return switch (type.getType()) { 34 | case BYTE, BOOL -> "TINYINT"; 35 | case BYTES -> "BYTEA"; 36 | case SHORT -> "SMALLINT"; 37 | case INT -> "INT"; 38 | case DOUBLE -> "DOUBLE PRECISION"; 39 | case LONG -> "BIGINT"; 40 | case STRING -> "TEXT"; 41 | case CHAR -> "CHAR(2)"; 42 | default -> type.getType().name(); 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/database/SQLite.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.database; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | import lombok.SneakyThrows; 6 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 7 | 8 | import java.io.File; 9 | 10 | @Getter 11 | public class SQLite extends Database { 12 | 13 | private final String directory; 14 | private Mode mode = Mode.WAL2; 15 | 16 | public enum Mode { 17 | DELETE, 18 | TRUNCATE, 19 | PERSIST, 20 | MEMORY, 21 | WAL, 22 | WAL2, 23 | OFF 24 | } 25 | 26 | public SQLite(@NonNull String name, @NonNull String directory) { 27 | super(name); 28 | this.directory = directory; 29 | connect(); 30 | connection.getSql().useHandle(h -> h.execute("PRAGMA journal_mode = %s;".formatted(mode))); 31 | } 32 | 33 | @Override 34 | public String getConnectionUrl() { 35 | return "jdbc:sqlite:" + new File(directory + "/" + name + ".db"); 36 | } 37 | 38 | @Override 39 | public String getTableCreationQuery(String tableName) { 40 | return "CREATE TABLE IF NOT EXISTS %s (SQLIB_AUTO_ID INTEGER PRIMARY KEY UNIQUE)".formatted(tableName); 41 | } 42 | 43 | @Override 44 | public String getColumnListQuery(String tableName) { 45 | return "SELECT NAME FROM PRAGMA_TABLE_INFO('%s')".formatted(tableName); 46 | } 47 | 48 | @Override 49 | public String getDataType(SQLPrimitive type) { 50 | return switch (type.getType()) { 51 | case BYTE, BOOL -> "TINYINT"; 52 | case BYTES -> "BLOB"; 53 | case SHORT -> "SMALLINT"; 54 | case LONG -> "BIGINT"; 55 | case STRING -> "TEXT"; 56 | case CHAR -> "CHARACTER"; 57 | default -> type.getType().name(); 58 | }; 59 | } 60 | 61 | @SneakyThrows 62 | public void setMode(@NonNull Mode mode) { 63 | this.mode = mode; 64 | connection.getSql().useHandle(h -> h.execute("PRAGMA journal_mode = %s;".formatted(mode))); 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/types/AdventureTypes.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.types; 2 | 3 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 4 | import net.kyori.adventure.key.Key; 5 | import net.kyori.adventure.text.Component; 6 | import net.kyori.adventure.text.minimessage.MiniMessage; 7 | 8 | public class AdventureTypes { 9 | public static final SQLibType KEY = new SQLibType<>(SQLPrimitive.STRING, Key::asMinimalString, Key::key); 10 | public static final SQLibType COMPONENT = new SQLibType<>(SQLPrimitive.STRING, v -> MiniMessage.miniMessage().serialize(v), v -> MiniMessage.miniMessage().deserialize(v)); 11 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/types/GsonTypes.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.types; 2 | 3 | import com.google.gson.*; 4 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 5 | 6 | public class GsonTypes { 7 | public static final SQLibType ELEMENT = new SQLibType<>(SQLPrimitive.STRING, JsonElement::toString, JsonParser::parseString); 8 | public static final SQLibType OBJECT = new SQLibType<>(ELEMENT, v -> v, v -> (JsonObject) v); 9 | public static final SQLibType ARRAY = new SQLibType<>(ELEMENT, v -> v, v -> (JsonArray) v); 10 | public static final SQLibType PRIMITIVE = new SQLibType<>(ELEMENT, v -> v, v -> (JsonPrimitive) v); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/types/JavaTypes.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.types; 2 | 3 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 4 | 5 | import java.awt.*; 6 | import java.net.*; 7 | import java.util.Date; 8 | 9 | public class JavaTypes { 10 | // Primitives 11 | public static final SQLibType BYTE = new SQLibType<>(SQLPrimitive.INT, Byte::intValue, Integer::byteValue); 12 | public static final SQLibType BYTES = new SQLibType<>(SQLPrimitive.BYTES, v -> v, v -> v); 13 | public static final SQLibType BOOL = new SQLibType<>(SQLPrimitive.INT, v -> v ? 1 : 0, v -> v == 1); 14 | public static final SQLibType SHORT = new SQLibType<>(SQLPrimitive.SHORT, v -> v, v -> v); 15 | public static final SQLibType INT = new SQLibType<>(SQLPrimitive.INT, v -> v, v -> v); 16 | public static final SQLibType FLOAT = new SQLibType<>(SQLPrimitive.FLOAT, v -> v, v -> v); 17 | public static final SQLibType DOUBLE = new SQLibType<>(SQLPrimitive.DOUBLE, v -> v, v -> v); 18 | public static final SQLibType LONG = new SQLibType<>(SQLPrimitive.LONG, v -> v, v -> v); 19 | public static final SQLibType STRING = new SQLibType<>(SQLPrimitive.STRING, v -> v, v -> v); 20 | public static final SQLibType CHAR = new SQLibType<>(SQLPrimitive.CHAR, v -> v, v -> v); 21 | // Java Data Types 22 | public static final SQLibType DATE = new SQLibType<>(SQLPrimitive.LONG, Date::getTime, Date::new); 23 | public static final SQLibType COLOR = new SQLibType<>(SQLPrimitive.INT, Color::getRGB, Color::new); 24 | public static final SQLibType UUID = new SQLibType<>(SQLPrimitive.STRING, java.util.UUID::toString, java.util.UUID::fromString); 25 | public static final SQLibType URI = new SQLibType<>(SQLPrimitive.STRING, java.net.URI::toString, java.net.URI::create); 26 | public static final SQLibType URL = new SQLibType<>(URI, v -> { 27 | try { 28 | return v.toURI(); 29 | } catch (URISyntaxException e) { 30 | throw new RuntimeException(e); 31 | } 32 | }, v -> { 33 | try { 34 | return v.toURL(); 35 | } catch (MalformedURLException e) { 36 | throw new RuntimeException(e); 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/types/MinecraftTypes.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.types; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 5 | import me.mrnavastar.sqlib.impl.SoundParser; 6 | import me.mrnavastar.sqlib.impl.TextParser; 7 | import net.minecraft.nbt.NbtElement; 8 | import net.minecraft.nbt.StringNbtReader; 9 | import net.minecraft.sound.SoundEvent; 10 | import net.minecraft.text.Text; 11 | import net.minecraft.util.Identifier; 12 | import net.minecraft.util.math.BlockPos; 13 | import net.minecraft.util.math.ChunkPos; 14 | import net.minecraft.util.math.Vec3i; 15 | 16 | public class MinecraftTypes { 17 | public static final SQLibType VEC3I = new SQLibType<>(SQLPrimitive.LONG, v -> BlockPos.asLong(v.getX(), v.getY(), v.getZ()), v -> new Vec3i(BlockPos.unpackLongX(v), BlockPos.unpackLongY(v), BlockPos.unpackLongZ(v))); 18 | public static final SQLibType BLOCKPOS = new SQLibType<>(SQLPrimitive.LONG, BlockPos::asLong, BlockPos::fromLong); 19 | public static final SQLibType CHUNKPOS = new SQLibType<>(SQLPrimitive.LONG, ChunkPos::toLong, ChunkPos::new); 20 | 21 | public static final SQLibType TEXT = new SQLibType<>(SQLPrimitive.STRING, TextParser::textToString, TextParser::stringToText); 22 | public static final SQLibType IDENTIFIER = new SQLibType<>(SQLPrimitive.STRING, Identifier::toString, Identifier::tryParse); 23 | public static final SQLibType SOUND = new SQLibType<>(IDENTIFIER, SoundParser::getId, SoundEvent::of); 24 | 25 | public static final SQLibType NBT = new SQLibType<>(SQLPrimitive.STRING, NbtElement::toString, v -> { 26 | try { 27 | return StringNbtReader.parse(v); 28 | } catch (CommandSyntaxException e) { 29 | throw new RuntimeException(e); 30 | } 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/api/types/SQLibType.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.api.types; 2 | 3 | import lombok.Getter; 4 | import me.mrnavastar.sqlib.impl.SQLPrimitive; 5 | 6 | import java.util.function.Function; 7 | 8 | public class SQLibType { 9 | @Getter 10 | private final SQLPrimitive type; 11 | private final Function serializer; 12 | private final Function deserializer; 13 | 14 | public SQLibType(SQLPrimitive type, Function serializer, Function deserializer) { 15 | this.type = type; 16 | this.serializer = (Function) serializer; 17 | this.deserializer = (Function) deserializer; 18 | } 19 | 20 | public SQLibType(SQLibType type, Function serializer, Function deserializer) { 21 | this.type = type.getType(); 22 | this.serializer = serializer.andThen(type.serializer); 23 | this.deserializer = type.deserializer.andThen(deserializer); 24 | } 25 | 26 | public Object serialize(T value) { 27 | return serializer.apply(value); 28 | } 29 | 30 | public T deserialize(Object value) { 31 | return deserializer.apply(value); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/ByteParser.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.charset.StandardCharsets; 5 | 6 | public class ByteParser { 7 | 8 | public static byte[] primToBytes(Object object) { 9 | if (object instanceof Byte) return new byte[]{(Byte) object}; 10 | if (object instanceof byte[] by) return by; 11 | if (object instanceof Boolean b) return b ? new byte[1] : new byte[0]; 12 | if (object instanceof Short s) return ByteBuffer.allocate(Short.BYTES).putShort(s).array(); 13 | if (object instanceof Integer i) return ByteBuffer.allocate(Integer.BYTES).putInt(i).array(); 14 | if (object instanceof Float f) return ByteBuffer.allocate(Float.BYTES).putFloat(f).array(); 15 | if (object instanceof Double d) return ByteBuffer.allocate(Double.BYTES).putDouble(d).array(); 16 | if (object instanceof Long l) return ByteBuffer.allocate(Long.BYTES).putLong(l).array(); 17 | if (object instanceof Character c) return ByteBuffer.allocate(Character.BYTES).putInt(c).array(); 18 | if (object instanceof String s) return ByteBuffer.allocate(s.length() + 1).putInt(s.length()).put(s.getBytes(StandardCharsets.UTF_8)).array(); 19 | return null; 20 | } 21 | 22 | public static T bytesToPrim(byte[] bytes, Class clazz) { 23 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 24 | if (clazz == Byte.class) return clazz.cast(bytes[0]); 25 | if (clazz == byte[].class) return clazz.cast(bytes); 26 | if (clazz == Boolean.class) return clazz.cast(bytes.length > 0); 27 | if (clazz == Short.class) return clazz.cast(buffer.getShort()); 28 | if (clazz == Integer.class) return clazz.cast(buffer.getInt()); 29 | if (clazz == Float.class) return clazz.cast(buffer.getFloat()); 30 | if (clazz == Double.class) return clazz.cast(buffer.getDouble()); 31 | if (clazz == Long.class) return clazz.cast(buffer.getLong()); 32 | if (clazz == Character.class) return clazz.cast(buffer.getChar()); 33 | if (clazz == String.class) { 34 | byte[] stringBytes = new byte[buffer.getInt()]; 35 | buffer.get(stringBytes); 36 | return clazz.cast(new String(stringBytes, StandardCharsets.UTF_8)); 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/SQLConnection.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl; 2 | 3 | import com.zaxxer.hikari.HikariConfig; 4 | import com.zaxxer.hikari.HikariDataSource; 5 | import lombok.Getter; 6 | import me.mrnavastar.sqlib.api.DataContainer; 7 | import me.mrnavastar.sqlib.api.DataStore; 8 | import me.mrnavastar.sqlib.impl.config.Config; 9 | import org.jdbi.v3.core.Handle; 10 | import org.jdbi.v3.core.Jdbi; 11 | 12 | import java.util.*; 13 | 14 | 15 | public class SQLConnection { 16 | 17 | private final HikariDataSource ds; 18 | @Getter 19 | private final Jdbi sql; 20 | 21 | public SQLConnection(String connectionUrl, Properties properties) { 22 | HikariConfig config = new HikariConfig(); 23 | config.setJdbcUrl(connectionUrl); 24 | config.setUsername(properties.getProperty("user")); 25 | config.setPassword(properties.getProperty("password")); 26 | config.setMaximumPoolSize(50); 27 | config.setConnectionTimeout(Config.INSTANCE.database.timeout * 1000L); 28 | config.setMaxLifetime(1800000); // 30 min 29 | config.addDataSourceProperty("cachePrepStmts", "true"); 30 | config.addDataSourceProperty("useServerPrepStmts", "true"); 31 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 32 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 33 | config.addDataSourceProperty("db.log.active", "false"); 34 | ds = new HikariDataSource(config); 35 | sql = Jdbi.create(ds); 36 | 37 | // TODO: This probably doesn't work lol - fix it 38 | if (properties.getProperty("config_timout") == null) return; 39 | sql.useHandle(h -> { 40 | // Try and set the connection's max lifetime to be half of the databases wait_timeout 41 | Integer timeout = h.select("SHOW VARIABLES LIKE 'wait_timeout';").mapTo(Integer.class).one(); 42 | if (timeout != null) config.setMaxLifetime(timeout / 2); 43 | }); 44 | } 45 | 46 | public void close() { 47 | if (ds != null) ds.close(); 48 | } 49 | 50 | public void createTable(DataStore store) { 51 | sql.useHandle(h -> h.execute(store.getDatabase().getTableCreationQuery(store.toString()))); 52 | } 53 | 54 | public int createRow(DataStore store) { 55 | try (Handle h = sql.open()) { 56 | return h.select(store.getDatabase().getRowCreationQuery(store.toString())) 57 | .mapTo(Integer.class) 58 | .one(); 59 | } 60 | } 61 | 62 | public void deleteRow(DataStore store, int id) { 63 | sql.useHandle(h -> h.execute("DELETE FROM %s WHERE SQLIB_AUTO_ID = ?".formatted(store.toString()), id)); 64 | } 65 | 66 | public boolean rowExists(DataStore store, int id) { 67 | try (Handle h = sql.open()) { 68 | return h.select("SELECT 1 FROM %s WHERE SQLIB_AUTO_ID = ?".formatted(store.toString()), id) 69 | .mapTo(Integer.class) 70 | .findFirst() 71 | .isPresent(); 72 | } 73 | } 74 | 75 | public List findRows(DataStore store, String field, Object value) { 76 | try (Handle h = sql.open()) { 77 | return h.select("SELECT SQLIB_AUTO_ID FROM %s WHERE _%s = ?".formatted(store.toString(), field), value).mapTo(Integer.class).list(); 78 | } catch(Exception ignore) { 79 | return new ArrayList<>(); 80 | } 81 | } 82 | 83 | public List listIds(DataStore store) { 84 | try (Handle h = sql.open()) { 85 | return h.select("SELECT SQLIB_AUTO_ID FROM %s".formatted(store.toString())) 86 | .mapTo(Integer.class) 87 | .list(); 88 | } 89 | } 90 | 91 | public List listColumns(DataStore store) { 92 | try (Handle h = sql.open()) { 93 | return h.select(store.getDatabase().getColumnListQuery(store.toString())) 94 | .mapTo(String.class) 95 | .filter(name -> !name.equals("SQLIB_AUTO_ID")) 96 | .map(name -> name.substring(1)) 97 | .list(); 98 | } 99 | } 100 | 101 | public T readField(DataStore store, int id, String field, Class clazz) { 102 | try (Handle h = sql.open()) { 103 | return h.select("SELECT _%s FROM %s WHERE SQLIB_AUTO_ID = ?".formatted(field, store.toString()), id) 104 | .mapTo(clazz) 105 | .one(); 106 | } catch (Exception ignore) { 107 | return null; 108 | } 109 | } 110 | 111 | public void writeField(DataStore store, int id, List puts) { 112 | sql.useHandle(h -> { 113 | StringJoiner fields = new StringJoiner(","); 114 | ArrayList values = new ArrayList<>(); 115 | 116 | for (DataContainer.Transaction.Put put : puts) { 117 | fields.add("_%s = ?".formatted(put.field())); 118 | values.add(put.value()); 119 | } 120 | values.add(id); 121 | 122 | String update = "UPDATE %s SET %s WHERE SQLIB_AUTO_ID = ?".formatted(store.toString(), fields); 123 | try { 124 | h.execute(update, values.toArray()); 125 | } catch (Exception ignore) { 126 | h.inTransaction(t -> { 127 | for (DataContainer.Transaction.Put put : puts) 128 | t.execute("ALTER TABLE %s ADD COLUMN _%s %s".formatted(store.toString(), put.field(), store.getDatabase().getDataType(put.type().getType()))); 129 | return t.execute(update, values.toArray()); 130 | }); 131 | } 132 | }); 133 | } 134 | 135 | public void clearField(DataStore store, int id, String field) { 136 | sql.useHandle(h -> h.execute("UPDATE %s SET %s = NULL WHERE SQLIB_AUTO_ID = ?".formatted(store.toString(), field), id)); 137 | } 138 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/SQLPrimitive.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 9 | public class SQLPrimitive { 10 | 11 | public enum Type { 12 | BYTES, BYTE, BOOL, SHORT, INT, FLOAT, DOUBLE, LONG, STRING, CHAR, DATE, DATETIME, TIME, TIMESTAMP 13 | } 14 | 15 | private final Type type; 16 | private final Class clazz; 17 | 18 | public static final SQLPrimitive BYTES = new SQLPrimitive<>(Type.BYTES, byte[].class); 19 | public static final SQLPrimitive SHORT = new SQLPrimitive<>(Type.SHORT, Short.class); 20 | public static final SQLPrimitive INT = new SQLPrimitive<>(Type.INT, Integer.class); 21 | public static final SQLPrimitive FLOAT = new SQLPrimitive<>(Type.FLOAT, Float.class); 22 | public static final SQLPrimitive DOUBLE = new SQLPrimitive<>(Type.DOUBLE, Double.class); 23 | public static final SQLPrimitive LONG = new SQLPrimitive<>(Type.LONG, Long.class); 24 | public static final SQLPrimitive CHAR = new SQLPrimitive<>(Type.CHAR, Character.class); 25 | public static final SQLPrimitive STRING = new SQLPrimitive<>(Type.STRING, String.class); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/SoundParser.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl; 2 | 3 | import net.minecraft.sound.SoundEvent; 4 | import net.minecraft.util.Identifier; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | // This class is used for backwards compatibility as 1.21.2 changed SoundEvent to a record 9 | public class SoundParser { 10 | 11 | private static Field id = null; 12 | 13 | static { 14 | try { 15 | id = SoundEvent.class.getDeclaredField("comp_3319"); 16 | } catch (NoSuchFieldException ignore) {} 17 | 18 | // DEV 19 | try { 20 | id = SoundEvent.class.getDeclaredField("id"); 21 | } catch (NoSuchFieldException ignore) {} 22 | 23 | id.setAccessible(true); 24 | } 25 | 26 | public static Identifier getId(SoundEvent event) { 27 | try { 28 | return (Identifier) id.get(event); 29 | } catch (IllegalAccessException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/TextParser.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import lombok.SneakyThrows; 6 | import lombok.experimental.UtilityClass; 7 | import net.minecraft.registry.BuiltinRegistries; 8 | import net.minecraft.registry.RegistryWrapper; 9 | import net.minecraft.text.Text; 10 | 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | 14 | @UtilityClass 15 | public class TextParser { 16 | 17 | private static final Gson GSON = new Gson(); 18 | private static Method serialize = null; 19 | private static Method deserialize = null; 20 | 21 | static { 22 | // 1.20.5 & up 23 | try { 24 | Class clazz = Class.forName("net/minecraft/class_2561$class_2562"); 25 | serialize = clazz.getMethod("method_10867", Text.class, RegistryWrapper.WrapperLookup.class); 26 | deserialize = clazz.getMethod("method_10872", JsonElement.class, RegistryWrapper.WrapperLookup.class); 27 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 28 | 29 | // 1.20.3 & 1.20.4 30 | try { 31 | Class clazz = Class.forName("net/minecraft/class_2561$class_2562"); 32 | serialize = clazz.getMethod("method_10867", Text.class); 33 | deserialize = clazz.getMethod("method_10872", JsonElement.class); 34 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 35 | 36 | // 1.20.2 & down 37 | try { 38 | Class clazz = Class.forName("net/minecraft/class_2561$class_8822"); 39 | serialize = clazz.getMethod("method_10867", Text.class); 40 | deserialize = clazz.getMethod("method_10872", JsonElement.class); 41 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 42 | 43 | // DEV - 1.20.5 & up 44 | try { 45 | Class clazz = Class.forName("net.minecraft.text.Text$Serialization"); 46 | serialize = clazz.getDeclaredMethod("toJsonString", Text.class, RegistryWrapper.WrapperLookup.class); 47 | deserialize = clazz.getDeclaredMethod("fromJsonTree", JsonElement.class, RegistryWrapper.WrapperLookup.class); 48 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 49 | 50 | // DEV - 1.20.3 & 1.20.4 51 | try { 52 | Class clazz = Class.forName("net.minecraft.text.Text$Serialization"); 53 | serialize = clazz.getDeclaredMethod("toJsonString", Text.class, RegistryWrapper.WrapperLookup.class); 54 | deserialize = clazz.getDeclaredMethod("fromJsonTree", JsonElement.class, RegistryWrapper.WrapperLookup.class); 55 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 56 | 57 | // DEV - 1.20.2 & down 58 | try { 59 | Class clazz = Class.forName("net.minecraft.text.Text$Serializer"); 60 | serialize = clazz.getMethod("toJson", Text.class); 61 | deserialize = clazz.getMethod("fromJson", JsonElement.class); 62 | } catch (NoSuchMethodException | ClassNotFoundException ignore) {} 63 | } 64 | 65 | public static String textToString(Text text) { 66 | try { 67 | if (serialize.getParameterCount() > 1) { 68 | return (String) serialize.invoke(null, text, BuiltinRegistries.createWrapperLookup()); 69 | } 70 | return (String) serialize.invoke(null, text); 71 | } catch (InvocationTargetException | IllegalAccessException e) { 72 | throw new RuntimeException(e); 73 | } 74 | } 75 | 76 | @SneakyThrows 77 | public static Text stringToText(String s) { 78 | if (serialize.getParameterCount() > 1) { 79 | return (Text) deserialize.invoke(null, GSON.fromJson(s, JsonElement.class), BuiltinRegistries.createWrapperLookup()); 80 | } 81 | return (Text) deserialize.invoke(null, GSON.fromJson(s, JsonElement.class)); 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/config/Config.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl.config; 2 | 3 | import com.fasterxml.jackson.dataformat.toml.TomlMapper; 4 | import lombok.SneakyThrows; 5 | import me.mrnavastar.sqlib.SQLib; 6 | import me.mrnavastar.sqlib.api.database.MySQL; 7 | import me.mrnavastar.sqlib.api.database.PostgreSQL; 8 | import me.mrnavastar.sqlib.api.database.SQLite; 9 | import org.apache.logging.log4j.Level; 10 | import org.apache.logging.log4j.LogManager; 11 | 12 | import java.io.File; 13 | import java.io.FileWriter; 14 | import java.io.IOException; 15 | import java.nio.file.Path; 16 | import java.util.Objects; 17 | 18 | public class Config { 19 | 20 | public static Config INSTANCE; 21 | 22 | public Database database; 23 | public Local local; 24 | public Server server; 25 | 26 | public static class Database { 27 | public String name; 28 | public String type; 29 | public int timeout; 30 | 31 | public boolean validate() { 32 | return name != null && !name.isEmpty() && type != null && !type.isEmpty() && timeout > 0; 33 | } 34 | } 35 | 36 | public static class Local { 37 | public String directory; 38 | 39 | public boolean validate() { 40 | return directory != null && !directory.isEmpty() && new File(directory).exists(); 41 | } 42 | } 43 | 44 | public static class Server { 45 | public String address; 46 | public int port = -1; 47 | public String username; 48 | public String password; 49 | 50 | public boolean validate() { 51 | return address != null && !address.isEmpty() && port != -1 && username != null && !username.isEmpty() && password != null && !password.isEmpty(); 52 | } 53 | } 54 | 55 | public boolean validate() { 56 | if (database == null || !database.validate()) return false; 57 | 58 | if (database.type.equalsIgnoreCase("sqlite") && local.validate()) return true; 59 | if (database.type.equalsIgnoreCase("mysql") && server.validate()) return true; 60 | if (database.type.equalsIgnoreCase("mariadb") && server.validate()) return true; 61 | return database.type.equalsIgnoreCase("postgres") && server.validate(); 62 | } 63 | 64 | @SneakyThrows 65 | public static void load() { 66 | if (INSTANCE != null) return; 67 | 68 | Class.forName("org.sqlite.JDBC"); 69 | Class.forName("org.mariadb.jdbc.Driver"); 70 | Class.forName("org.postgresql.Driver"); 71 | 72 | try { 73 | Class.forName("net.fabricmc.loader.api.FabricLoader"); 74 | Fabric.load(); 75 | return; 76 | } catch (ClassNotFoundException ignore) {} 77 | 78 | try { 79 | Class.forName("org.quiltmc.loader.api.QuiltLoader"); 80 | Quilt.load(); 81 | return; 82 | } catch (ClassNotFoundException ignore) {} 83 | 84 | try { 85 | Class.forName("com.velocitypowered.api.plugin.Plugin"); 86 | Velocity.load(); 87 | } catch (ClassNotFoundException ignore) {} 88 | 89 | if (!NonMinecraft.load()) throw new RuntimeException("SQLib currently only supports Fabric, Quilt, and Velocity!"); 90 | } 91 | 92 | public static me.mrnavastar.sqlib.api.database.Database load(Path localDir, Path configDir) { 93 | localDir.toFile().mkdirs(); 94 | 95 | try { 96 | File configFile = new File(configDir + "/sqlib.toml"); 97 | if (!configFile.exists()) { 98 | configFile.getParentFile().mkdirs(); 99 | String data = new String(Objects.requireNonNull(SQLib.class.getResourceAsStream("/sqlib.toml")).readAllBytes()).replace("${local_path}", localDir.toString().replace("\\", "/")); 100 | try (FileWriter writer = new FileWriter(configFile)) { 101 | writer.write(data); 102 | } 103 | } 104 | INSTANCE = new TomlMapper().readValue(configFile, Config.class); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | 109 | if (!INSTANCE.validate()) { 110 | log(Level.ERROR, "Invalid config - Stopping"); 111 | System.exit(1); 112 | } 113 | 114 | return switch (INSTANCE.database.type.toLowerCase()) { 115 | case "sqlite" -> new SQLite(INSTANCE.database.name, INSTANCE.local.directory); 116 | case "mysql", "mariadb" -> new MySQL(INSTANCE.database.name, INSTANCE.server.address, String.valueOf(INSTANCE.server.port), INSTANCE.server.username, INSTANCE.server.password); 117 | case "postgres" -> new PostgreSQL(INSTANCE.database.name, INSTANCE.server.address, String.valueOf(INSTANCE.server.port), INSTANCE.server.username, INSTANCE.server.password); 118 | default -> null; 119 | }; 120 | } 121 | 122 | public static void log(Level level, String message) { 123 | LogManager.getLogger().log(level, "[SQLib]: " + message); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/config/Fabric.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl.config; 2 | 3 | import me.mrnavastar.sqlib.SQLib; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | 6 | import java.nio.file.Path; 7 | 8 | public class Fabric extends SQLib { 9 | 10 | public static void load() { 11 | if (database == null) database = Config.load(Path.of(FabricLoader.getInstance().getGameDir() + "/sqlib"), FabricLoader.getInstance().getConfigDir()); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/config/NonMinecraft.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl.config; 2 | 3 | import me.mrnavastar.sqlib.SQLib; 4 | 5 | import java.nio.file.Path; 6 | 7 | public class NonMinecraft extends SQLib { 8 | 9 | private static Path databaseDir; 10 | private static Path config; 11 | 12 | public static void init(Path defaultDatabaseDir, Path configDir) { 13 | databaseDir = defaultDatabaseDir; 14 | config = configDir; 15 | } 16 | 17 | public static boolean load() { 18 | if (databaseDir == null || config == null) return false; 19 | if (database == null) database = Config.load(databaseDir, config); 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/config/Quilt.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl.config; 2 | 3 | import me.mrnavastar.sqlib.SQLib; 4 | import org.quiltmc.loader.api.QuiltLoader; 5 | 6 | import java.nio.file.Path; 7 | 8 | public class Quilt extends SQLib { 9 | 10 | public static void load() { 11 | if (database == null) database = Config.load(Path.of(QuiltLoader.getGameDir() + "/sqlib"), QuiltLoader.getConfigDir()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/mrnavastar/sqlib/impl/config/Velocity.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib.impl.config; 2 | 3 | import me.mrnavastar.sqlib.SQLib; 4 | 5 | import java.nio.file.Path; 6 | 7 | public class Velocity extends SQLib { 8 | 9 | public static void load() { 10 | if (database != null) return; 11 | Path dir = Path.of("plugins/sqlib"); 12 | database = Config.load(dir,dir); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/assets/sqlib/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrNavaStar/SQLib/88e7b33555d1f978e67445577c512074196846a2/src/main/resources/assets/sqlib/icon.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "sqlib", 4 | "version": "debug-build", 5 | "name": "SQLib", 6 | "description": "A simple SQL wrapper with a focus on Minecraft use cases", 7 | "authors": ["MrNavaStar"], 8 | "contact": {}, 9 | "license": "CC0-1.0", 10 | "icon": "assets/sqlib/icon.png", 11 | "environment": "*", 12 | "depends": { 13 | "java": "21", 14 | "minecraft": ">=1.16.5" 15 | }, 16 | "custom": { 17 | "modmanager": { 18 | "modrinth": "IM84wwxs" 19 | }, 20 | "mc-publish": { 21 | "modrinth": "IM84wwxs", 22 | "loaders": ["fabric", "quilt", "velocity"] 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/resources/sqlib.toml: -------------------------------------------------------------------------------- 1 | # Config File for SQLib 2 | 3 | # Settings that apply regardless of database used 4 | [database] 5 | name = "default" 6 | # Allowed Values: sqlite, mysql, mariadb, postgres 7 | type = "sqlite" 8 | # Transaction timeout value in seconds 9 | timeout = 30 10 | 11 | # Used for SQLite 12 | [local] 13 | # Can be any path that exists on this system 14 | directory = "${local_path}" 15 | 16 | # Used for MySQL, MariaDB, and Postgres 17 | [server] 18 | address = "127.0.0.1" 19 | port = 3306 20 | username = "user" 21 | password = "pass" -------------------------------------------------------------------------------- /src/main/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":"sqlib", 3 | "name":"SQLib", 4 | "version":"debug-build", 5 | "description":"A simple SQL wrapper with a focus on Minecraft use cases", 6 | "authors":["MrNavaStar"], 7 | "main": "me.mrnavastar.sqlib.impl.config.Velocity" 8 | } -------------------------------------------------------------------------------- /src/testMod/java/me/mrnavastar/sqlib/TestMod.java: -------------------------------------------------------------------------------- 1 | package me.mrnavastar.sqlib; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.SneakyThrows; 5 | import me.mrnavastar.easyeula.EasyEula; 6 | import me.mrnavastar.sqlib.api.DataContainer; 7 | import me.mrnavastar.sqlib.api.types.AdventureTypes; 8 | import me.mrnavastar.sqlib.api.types.JavaTypes; 9 | import me.mrnavastar.sqlib.api.types.MinecraftTypes; 10 | import me.mrnavastar.sqlib.api.types.GsonTypes; 11 | import me.mrnavastar.sqlib.api.DataStore; 12 | import me.mrnavastar.sqlib.impl.SoundParser; 13 | import net.fabricmc.api.ModInitializer; 14 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; 15 | import net.kyori.adventure.key.Key; 16 | import net.kyori.adventure.text.Component; 17 | import net.kyori.adventure.text.minimessage.MiniMessage; 18 | import net.minecraft.nbt.NbtCompound; 19 | import net.minecraft.sound.SoundEvent; 20 | import net.minecraft.sound.SoundEvents; 21 | import net.minecraft.text.Text; 22 | import net.minecraft.util.Identifier; 23 | import net.minecraft.util.math.BlockPos; 24 | import net.minecraft.util.math.ChunkPos; 25 | import net.minecraft.util.math.Vec3i; 26 | 27 | import java.awt.*; 28 | import java.util.Arrays; 29 | import java.util.Date; 30 | import java.util.UUID; 31 | 32 | public class TestMod implements ModInitializer { 33 | 34 | private void assertEquals(Object expected, Object actual) { 35 | if (!expected.equals(actual) || !actual.getClass().equals(expected.getClass())) throw new RuntimeException("Expected " + expected.getClass().getName() + " but got " + actual.getClass().getName()); 36 | } 37 | 38 | private void assertNotEquals(Object o1, Object o2) { 39 | if (o1.equals(o2)) throw new RuntimeException("Expected objects to be equal, but they are the same"); 40 | } 41 | 42 | private void assertTrue(boolean bool) { 43 | if (!bool) throw new RuntimeException("Expected True but got False"); 44 | } 45 | 46 | private void assertFalse(boolean bool) { 47 | if (bool) throw new RuntimeException("Expected False but got True"); 48 | } 49 | 50 | private void testAllTransactions() { 51 | DataContainer container = SQLib.getDatabase().dataStore("test", "store1").createContainer(); 52 | 53 | // Test Byte 54 | container.put(JavaTypes.BYTE, "byte", Byte.MAX_VALUE); 55 | assertEquals(Byte.MAX_VALUE, container.get(JavaTypes.BYTE, "byte").orElseThrow()); 56 | container.put(JavaTypes.BYTE, "byte", Byte.MIN_VALUE); 57 | assertEquals(Byte.MIN_VALUE, container.get(JavaTypes.BYTE, "byte").orElseThrow()); 58 | container.put(JavaTypes.BYTE, "byte", (byte) 0); 59 | assertEquals((byte) 0, container.get(JavaTypes.BYTE, "byte").orElseThrow()); 60 | 61 | // Test Bytes 62 | byte[] bytes = new byte[]{1, 2, 3, 4}; 63 | container.put(JavaTypes.BYTES, "bytes", bytes); 64 | if (!Arrays.equals(bytes, container.get(JavaTypes.BYTES, "bytes").orElseThrow())) { 65 | throw new RuntimeException("Expected bytes, got " + Arrays.toString(bytes)); 66 | } 67 | 68 | // Test Bool 69 | container.put(JavaTypes.BOOL, "bool", true); 70 | assertTrue(container.get(JavaTypes.BOOL, "bool").orElseThrow()); 71 | container.put(JavaTypes.BOOL, "bool", false); 72 | assertFalse(container.get(JavaTypes.BOOL, "bool").orElseThrow()); 73 | 74 | // Test Short 75 | container.put(JavaTypes.SHORT, "short", Short.MAX_VALUE); 76 | assertEquals(Short.MAX_VALUE, container.get(JavaTypes.SHORT, "short").orElseThrow()); 77 | container.put(JavaTypes.SHORT, "short", Short.MIN_VALUE); 78 | assertEquals(Short.MIN_VALUE, container.get(JavaTypes.SHORT, "short").orElseThrow()); 79 | container.put(JavaTypes.SHORT, "short", (short) 0); 80 | assertEquals((short) 0, container.get(JavaTypes.SHORT, "short").orElseThrow()); 81 | 82 | // Test Int 83 | container.put(JavaTypes.INT, "int", Integer.MAX_VALUE); 84 | assertEquals(Integer.MAX_VALUE, container.get(JavaTypes.INT, "int").orElseThrow()); 85 | container.put(JavaTypes.INT, "int", Integer.MIN_VALUE); 86 | assertEquals(Integer.MIN_VALUE, container.get(JavaTypes.INT, "int").orElseThrow()); 87 | container.put(JavaTypes.INT, "int", 0); 88 | assertEquals(0, container.get(JavaTypes.INT, "int").orElseThrow()); 89 | 90 | // Test Float 91 | container.put(JavaTypes.FLOAT, "float", Float.MAX_VALUE); 92 | assertEquals(Float.MAX_VALUE, container.get(JavaTypes.FLOAT, "float").orElseThrow()); 93 | container.put(JavaTypes.FLOAT, "float", Float.MIN_VALUE); 94 | assertEquals(Float.MIN_VALUE, container.get(JavaTypes.FLOAT, "float").orElseThrow()); 95 | container.put(JavaTypes.FLOAT, "float", 0F); 96 | assertEquals(0F, container.get(JavaTypes.FLOAT, "float").orElseThrow()); 97 | 98 | // Test Double 99 | container.put(JavaTypes.DOUBLE, "double", Double.MAX_VALUE); 100 | assertEquals(Double.MAX_VALUE, container.get(JavaTypes.DOUBLE, "double").orElseThrow()); 101 | container.put(JavaTypes.DOUBLE, "double", Double.MIN_VALUE); 102 | assertEquals(Double.MIN_VALUE, container.get(JavaTypes.DOUBLE, "double").orElseThrow()); 103 | container.put(JavaTypes.DOUBLE, "double", 0.0); 104 | assertEquals(0.0, container.get(JavaTypes.DOUBLE, "double").orElseThrow()); 105 | 106 | // Test Long 107 | container.put(JavaTypes.LONG, "long", Long.MAX_VALUE); 108 | assertEquals(Long.MAX_VALUE, container.get(JavaTypes.LONG, "long").orElseThrow()); 109 | container.put(JavaTypes.LONG, "long", Long.MIN_VALUE); 110 | assertEquals(Long.MIN_VALUE, container.get(JavaTypes.LONG, "long").orElseThrow()); 111 | container.put(JavaTypes.LONG, "long", 0L); 112 | assertEquals(0L, container.get(JavaTypes.LONG, "long").orElseThrow()); 113 | 114 | // Test String 115 | container.put(JavaTypes.STRING, "string", "Test"); 116 | assertEquals("Test", container.get(JavaTypes.STRING, "string").orElseThrow()); 117 | 118 | // Test Char 119 | container.put(JavaTypes.CHAR, "char", 'c'); 120 | assertEquals('c', container.get(JavaTypes.CHAR, "char").orElseThrow()); 121 | 122 | // Test Date 123 | Date date = new Date(); 124 | container.put(JavaTypes.DATE, "date", date); 125 | assertEquals(date, container.get(JavaTypes.DATE, "date").orElseThrow()); 126 | 127 | // Test Color 128 | Color color = new Color(1, 2, 3); 129 | container.put(JavaTypes.COLOR, "color", color); 130 | assertEquals(color, container.get(JavaTypes.COLOR, "color").orElseThrow()); 131 | 132 | // Test UUID 133 | UUID uuid = UUID.randomUUID(); 134 | container.put(JavaTypes.UUID, "uuid", uuid); 135 | assertEquals(uuid, container.get(JavaTypes.UUID, "uuid").orElseThrow()); 136 | 137 | // Test Vec3i 138 | Vec3i vec3i = new Vec3i(7, 8, 9); 139 | container.put(MinecraftTypes.VEC3I, "vec3i", vec3i); 140 | assertEquals(vec3i, container.get(MinecraftTypes.VEC3I, "vec3i").orElseThrow()); 141 | 142 | // Test BlockPos 143 | BlockPos blockPos = new BlockPos(7, 8, 9); 144 | container.put(MinecraftTypes.BLOCKPOS, "blockpos", blockPos); 145 | assertEquals(blockPos, container.get(MinecraftTypes.BLOCKPOS, "blockpos").orElseThrow()); 146 | 147 | // Test ChunkPos 148 | ChunkPos chunkPos = new ChunkPos(1, 2); 149 | container.put(MinecraftTypes.CHUNKPOS, "chunkpos", chunkPos); 150 | assertEquals(chunkPos, container.get(MinecraftTypes.CHUNKPOS, "chunkpos").orElseThrow()); 151 | 152 | // Test JSON 153 | JsonObject jsonElement = new JsonObject(); 154 | container.put(GsonTypes.OBJECT, "json", jsonElement); 155 | assertEquals(jsonElement, container.get(GsonTypes.OBJECT, "json").orElseThrow()); 156 | 157 | // Test NBT 158 | NbtCompound nbtCompound = new NbtCompound(); 159 | container.put(MinecraftTypes.NBT, "nbt", nbtCompound); 160 | assertEquals(nbtCompound, container.get(MinecraftTypes.NBT, "nbt").orElseThrow()); 161 | 162 | // Test Text 163 | Text text = Text.of("Test"); 164 | container.put(MinecraftTypes.TEXT, "text", text); 165 | assertEquals(text, container.get(MinecraftTypes.TEXT, "text").orElseThrow()); 166 | 167 | // Test Identifier 168 | Identifier identifier = Identifier.tryParse("cool:guys"); 169 | container.put(MinecraftTypes.IDENTIFIER, "identifier", identifier); 170 | assertEquals(identifier, container.get(MinecraftTypes.IDENTIFIER, "identifier").orElseThrow()); 171 | 172 | SoundEvent soundEvent = SoundEvents.BLOCK_BARREL_OPEN; 173 | container.put(MinecraftTypes.SOUND, "sound", soundEvent); 174 | assertEquals(SoundParser.getId(soundEvent), SoundParser.getId(container.get(MinecraftTypes.SOUND, "sound").orElseThrow())); 175 | 176 | // Test Adventure Key 177 | Key key = Key.key("cool:guys"); 178 | container.put(AdventureTypes.KEY, "key", key); 179 | assertEquals(key, container.get(AdventureTypes.KEY, "key").orElseThrow()); 180 | 181 | // Test Adventure Component 182 | Component component = MiniMessage.miniMessage().deserialize("test"); 183 | container.put(AdventureTypes.COMPONENT, "component", component); 184 | assertEquals(component, container.get(AdventureTypes.COMPONENT, "component").orElseThrow()); 185 | 186 | // Test Transaction 187 | container.transaction() 188 | .put(JavaTypes.STRING, "gamer", "epic") 189 | .put(MinecraftTypes.IDENTIFIER, "pog", Identifier.tryParse("pog:champ")) 190 | .put(JavaTypes.BOOL, "what", false) 191 | .commit(); 192 | } 193 | 194 | private void testStoreFunctions() { 195 | DataStore store = SQLib.getDatabase().dataStore("test", "store1"); 196 | 197 | System.out.println(store.getKeys()); 198 | } 199 | 200 | @Override 201 | @SneakyThrows 202 | public void onInitialize() { 203 | EasyEula.acceptEula(); 204 | ServerLifecycleEvents.SERVER_STARTED.register(server -> server.stop(false)); 205 | 206 | System.out.println("---------- Starting Tests ----------"); 207 | System.out.println("Starting Transactions"); 208 | testAllTransactions(); 209 | System.out.println("Starting Store Functions"); 210 | testStoreFunctions(); 211 | System.out.println("--------------- Done ----------------"); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/testMod/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "testmod", 4 | "version": "debug-build", 5 | "name": "TestMod", 6 | "description": "SQLib Test Mod", 7 | "authors": ["MrNavaStar"], 8 | "contact": {}, 9 | "license": "CC0-1.0", 10 | "environment": "*", 11 | "entrypoints": { 12 | "main": ["me.mrnavastar.sqlib.TestMod"] 13 | }, 14 | "depends": { 15 | "java": "21", 16 | "minecraft": ">=1.16.5" 17 | } 18 | } --------------------------------------------------------------------------------