diff --git a/.gitignore b/.gitignore index 848c5e82..85a49b2e 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,9 @@ bin .project .idea *.code-workspace +*.ipr +*.iws # Generated resources -common/src/main/resources/generated/* +**/src/main/resources/generated/* +**/src/gen/ diff --git a/build.gradle b/build.gradle index 829256f1..ac1726cd 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,13 @@ plugins { // pom packaging for root id "java-platform" + + id "idea" } allprojects { group = "com.dumbdogdiner" - version = "3.0.2" + version = "3.1.0" // java plugin is applied in subprojects apply plugin: "jacoco" @@ -172,6 +174,10 @@ subprojects { // Maven Central is defined in allprojects (for JaCoCo) maven { url "https://oss.sonatype.org/content/repositories/snapshots" } maven { url "https://papermc.io/repo/repository/maven-public/" } + maven { url "https://repo.codemc.io/repository/maven-public/" } + + maven { url "https://repo.pl3x.net/" } + maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots"} // Note: Subprojects can't have repo overrides to we have to put it here // Define a Ivy repo for the font width data (that way we don't need another plugin!) @@ -219,11 +225,16 @@ subprojects { } } - task sources(type: Jar, dependsOn: classes) { + task sourcesJar(type: Jar, dependsOn: classes) { archiveClassifier.set("sources") from sourceSets.main.allSource } + task javadocJar(type: Jar, dependsOn: javadoc) { + from javadoc + archiveClassifier.set('javadoc') + } + // Javadoc Fixes // Some environments (such as the builder image) do not use UTF-8 as the default encoding! @@ -238,22 +249,25 @@ subprojects { // Run the linter before compiling the source code. tasks.compileJava.dependsOn spotlessApply - tasks.build.dependsOn sources + tasks.build.dependsOn sourcesJar + tasks.javadoc.finalizedBy(javadocJar) + + // Per-module publishing (eg. stickyapi-common) - if (project.name != "serverversion") { // Ignore :common:serverversion (already included in :common) - publishing { - publications { - gprSubprojects(MavenPublication) { - artifactId "${rootProject.name}-${project.name}" - println("[debug] ${project.name} found component: " + components.java) - - from(components.java) - artifact sources // Publish the output of the sources task - } + publishing { + publications { + gprSubprojects(MavenPublication) { + artifactId "${rootProject.name}-${project.name}" + println("[debug] ${project.name} found component: " + components.java) + + from(components.java) + artifact sourcesJar // Publish the output of the sourcesJar task + artifact javadocJar // Publish the output of the javadocJar task } } } + } // Root build: include dependencies (see below) @@ -296,7 +310,8 @@ task copySubprojectJars(type: Copy, dependsOn: subprojects.jar) { // Copy subproject jar and sources from(subprojects.jar) - from(subprojects.sources) + from(subprojects.sourcesJar) + from(subprojects.javadocJar) into rootProject.file("build/libs/modules") } diff --git a/buildSrc/src/main/groovy/DownloadTask.groovy b/buildSrc/src/main/groovy/DownloadTask.groovy new file mode 100644 index 00000000..280e0fc2 --- /dev/null +++ b/buildSrc/src/main/groovy/DownloadTask.groovy @@ -0,0 +1,17 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +class DownloadTask extends DefaultTask { + @Input + String sourceUrl + + @OutputFile + File target + + @TaskAction + void download() { + ant.get(src: sourceUrl, dest: target) + } +} diff --git a/bukkit/build.gradle b/bukkit/build.gradle index f16710ed..6de14d0b 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -1,15 +1,18 @@ dependencies { api project(":common") // api - transistively expose common dependency when implementing :bukkit + // I don't know why i need to do this rn, i have to figure it out??? + //compileOnly project(":common").sourceSets.serverVersion.output testImplementation project(":common").sourceSets.test.output + // 3.0.2: serverversion is not transistive from common // Since we depend on common, the class is still available at runtime // ------------------------- // Why: "compileOnly" | This typically includes dependencies which are shaded when found at runtime. // Source: https://docs.gradle.org/6.8.3/userguide/java_library_plugin.html - compileOnly project(":common:serverversion") - compileOnly "com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT" + compileOnly("net.pl3x.purpur:purpur-api:1.16.5-R0.1-SNAPSHOT") + // Fun fact: paper/spigot/etc doesn't include netty! (as it's from NMS) // So instead let's use the netty version that we were using before (inherited from bungeecord) diff --git a/common/build.gradle b/common/build.gradle index 0462850a..184bdf7a 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,9 +1,78 @@ +buildscript { + repositories { + mavenCentral(); + } + dependencies { + classpath group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '2.2.0.201212191850-r' + classpath("com.squareup:javapoet:1.13.0") + classpath("com.google.guava:guava:30.1-jre") + classpath("de.skuzzle:semantic-version:2.1.0") + classpath("org.projectlombok:lombok:1.18.20") + } +} + +/* TODO: use features, and publish only the combined feature with BOTH serverversion and genreated + For now this is fine */ plugins { // Nemerosa Versioning Plugin for the build info id "net.nemerosa.versioning" version "2.14.0" // for "api" in dependencies { } id "java-library" + + // For IntelliJ + id "idea" +} + +// For now: we will just combine the output directory of each sourceset +sourceSets { + generated { +// java.outputDir = file('build/classes/main') + java.srcDir file('src/gen/java') + resources.srcDir file('src/gen/resources') + //compileClasspath += main.compileClasspath + } + + serverVersion { +// java.outputDir = file('build/classes/main') + java.srcDir file('src/serverVersion/java') + + // No resources necessary + //resources.srcDir file('src/serverVersion/resources') + } + + main { + java.outputDir = file('build/classes/main') + resources.srcDirs += file('src/gen/resources') + /*compileClasspath += generated.output + runtimeClasspath += generated.output + compileClasspath += serverVersion.output + runtimeClasspath += serverVersion.output*/ + } + + test { + compileClasspath += generated.output + runtimeClasspath += generated.output +// compileClasspath += serverVersion.output +// runtimeClasspath += serverVersion.output +// resources.srcDirs += file('src/gen/resources') + } +} +//System.out.println(sourceSets.generated.getApiConfigurationName()) +configurations { + generatedCompile.extendsFrom(compile) + + serverVersionCompile.extendsFrom(compile) + generatedApi + +// +// generatedApi{ +// extendsFrom(generatedCompile) +// canBeResolved = false +// canBeConsumed = true +// } + + testImplementation.extendsFrom compileOnly } dependencies { @@ -11,6 +80,9 @@ dependencies { api project(":config") // api - transistively expose config dependency when implementing :common testImplementation project(":config").sourceSets.test.output + api sourceSets.serverVersion.output + api sourceSets.generated.output + // LocaleProviderTest - add snakeyaml so YamlProvider (from the :config project) can work properly // testRuntimeOnly = only accessible during the test job testRuntimeOnly "org.yaml:snakeyaml:1.27" @@ -18,68 +90,55 @@ dependencies { // Font width data (see above) dddResource "dumbdogdiner:mc-font-extractor:main:mojangles_width_data@json" - implementation "com.google.code.gson:gson:2.8.6" + // Explicit GSON + api "com.google.code.gson:gson:2.8.6" // Dependencies available in both bukkit and bungee compileOnly "net.md-5:bungeecord-chat:1.16-R0.5-SNAPSHOT" // Only included in paper? (ShortID.java) compileOnly "commons-lang:commons-lang:2.6" + + // Needed for Buildinfo, explicitly + generatedCompileClasspath("org.jetbrains:annotations:20.1.0") + + // For buildinfo versioning; we want api so that it gets exposed to consuming thingies + api("de.skuzzle:semantic-version:2.1.0") + generatedImplementation("de.skuzzle:semantic-version:2.1.0") + + // Include the most generic forms possible, used only by ServerVersion + serverVersionCompileOnly "net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT" + serverVersionCompileOnly "org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT" + + testImplementation "net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT" + testImplementation "org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT" + testImplementation ('org.eclipse.jgit:org.eclipse.jgit:2.2.0.201212191850-r') +} + + + +test { + // Dynamically we can have it grab allllllll of the other dependancy types, this is a todo } + + // Use --add-opens to ensure functionality of FieldUtil [java.lang.reflect.Field] (required in Java 16+) // This will export java.lang.reflect to unnamed modules (eg. stickyapi) so that FieldUtil can still function. // Alternative: ["--illegal-access=warn"] (same functionality as pre Java 16) // JEP: https://openjdk.java.net/jeps/396 test.jvmArgs = ["--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"] -/* - Build Info - ---------- - The following lines (and the processSourceTokens task) serve to embed build information - such as versions, timestamps, commit details, as well as branch and working tree status - into StickyAPI during build-time. - - When using StickyAPI, these values can be retrieved from their respective getters at - com.dumbdogdiner.stickyapi.Stickyapi - ---------- +idea { + module { + sourceDirs += file('src/gen/java') + generatedSourceDirs += file('src/gen/java') + } +} - The processSourceTokens task will also print out a list of all the tokens to be added - to the build, for debugging reasons (eg. to ensure the info is correct) - These is also an accompanying test at (test src) com.dumbdogdiner.stickyapi.StickyAPITest - */ -// Set the timestamp format -def dataBuildTimestamp = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" - -// Import the filter -import org.apache.tools.ant.filters.ReplaceTokens - -// Define the map containing the tokens we want to replace -def tokensMap = [ - BUILDINFO_VERSION: project.rootProject.version, - BUILDINFO_DATEFORMAT: dataBuildTimestamp, - BUILDINFO_TIMESTAMP: new java.text.SimpleDateFormat(dataBuildTimestamp).format(new Date()), - BUILDINFO_COMMIT: versioning.info.commit, - BUILDINFO_BRANCH: versioning.info.branch, - BUILDINFO_ISDIRTY: versioning.info.dirty.toString() - ] - -// Create task to replace the tokens with their actual values -// NOTE: At the moment this replaces tokens *globally* (format eg. @BUILDINFO_COMMIT@ in source code) -task processSourceTokens(type: Sync) { - from sourceSets.main.java - into "build/processed/src/main/java" - filter(ReplaceTokens, tokens: tokensMap) - - // Pretty print the build info - println("\n----- (Common) StickyAPI Build Info -----\n") - tokensMap.each { println "${String.format("%1\$-" + 10 + "s", it.key.replace("BUILDINFO_", "").toLowerCase())}\t${it.value}" } -} -// Use the filter task as the input for compileJava -compileJava.source = processSourceTokens.outputs /* @@ -91,33 +150,45 @@ compileJava.source = processSourceTokens.outputs // Font Width Info task copyMCFontExtractor(type: Copy) { - def path = project.configurations.dddResource.find {it.name.startsWith("mc-font-extractor") } + def path = project.configurations.dddResource.find { it.name.startsWith("mc-font-extractor") } println("common: Found font data at: " + path) from file(path) - // into file("src/main/resources") - // - Please keep this comment for future reference. - // - This is how we would do this if we weren't also adding build info (see processSourceTokens, above comments) - destinationDir file("src/main/resources/generated/") + into file("src/gen/resources/") rename "mc-font-extractor-main-mojangles_width_data.json", "mojangles_width_data.json" } +tasks.processGeneratedResources.dependsOn(copyMCFontExtractor) + // Run the font data copier tasks.processResources.dependsOn copyMCFontExtractor +apply from: 'versioning-buildinfo.gradle' +tasks.compileJava.dependsOn(buildInfo) -// Common build: create a jar from the :common & :common:serverversion projects +// Common build: create a jar jar { + dependsOn generatedClasses + dependsOn serverVersionClasses + dependsOn classes from sourceSets.main.output - from project(":common:serverversion").sourceSets.main.output + from sourceSets.generated.output + from sourceSets.serverVersion.output + setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) } // Common build: create uber sources from subproject sources task commonSources(type: Jar, dependsOn: classes) { + dependsOn jar archiveClassifier.set("sources") - // Use source code from :common & :common:serverversion projects from sourceSets.main.allSource - from project(":common:serverversion").sourceSets.main.allSource + from sourceSets.generated.allSource + from sourceSets.serverVersion.allSource + setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) +} + +task cleanGenerated(type: Delete){ + delete 'src/gen/' } -// Common build: override the usual sources output with our one containing ServerVersion -tasks.sources.finalizedBy commonSources +tasks.clean.finalizedBy(cleanGenerated) +tasks.sourcesJar.finalizedBy commonSources diff --git a/common/serverversion/build.gradle b/common/serverversion/build.gradle deleted file mode 100644 index abff66bb..00000000 --- a/common/serverversion/build.gradle +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - compileOnly "net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT" - compileOnly "com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT" -} diff --git a/common/serverversion/lombok.config b/common/serverversion/lombok.config deleted file mode 100644 index 189c0bef..00000000 --- a/common/serverversion/lombok.config +++ /dev/null @@ -1,3 +0,0 @@ -# This file is generated by the 'io.freefair.lombok' Gradle plugin -config.stopBubbling = true -lombok.addLombokGeneratedAnnotation = true diff --git a/common/serverversion/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/common/serverversion/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index ca6ee9ce..00000000 --- a/common/serverversion/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline \ No newline at end of file diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java b/common/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java index c4d33844..2afae57a 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java @@ -31,82 +31,59 @@ private StickyAPI() { @Setter private static ExecutorService pool = Executors.newCachedThreadPool(); - // Build Info Start + // Build Info Start (OLD) /** * Get the current version of API. - * + * @deprecated Use {@link BuildInfo#getVersion()} * @since 3.0 * @return {@link String} version - * */ @Getter - private static final String version = "@BUILDINFO_VERSION@"; - - // Getter not required - private static final String dateFormat = "@BUILDINFO_DATEFORMAT@"; - - // Custom Getter (see below) - private static final String timestamp = "@BUILDINFO_TIMESTAMP@"; + @Deprecated + private static final String version = BuildInfo.getVersion().toString(); /** * Get a string with the latest commit (id) at API's build-time. - * + * @deprecated Use {@link BuildInfo#getCommit()} * @since 3.0 * @return {@link String} commit id */ @Getter - private static final String commit = "@BUILDINFO_COMMIT@"; + @Deprecated + private static final String commit = BuildInfo.getCommit(); /** * Get a string with the current branch at API's build-time. - * + * @deprecated Use {@link BuildInfo#getBranch()} * * @since 3.0 * @return {@link String} branch name */ @Getter - private static final String branch = "@BUILDINFO_BRANCH@"; - - // Custom Getter (see below) - private static final String isDirty = "@BUILDINFO_ISDIRTY@"; + @Deprecated + private static final String branch = BuildInfo.getBranch(); - /** - * Get a Date object showing the current time at API's build-time. - * + * Get a boolean showing if the working tree was dirty at API's build-time. + *

+ * If the working directory was dirty, this will return true, meaning there were modified tracked files and staged changes at build-time. + * @deprecated Use {@link BuildInfo#getBranch()} * @since 3.0 - * @return {@link Date} date + * @return {@link Boolean} isDirty */ - public static Date getTimestamp() { - SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); - try { - Date date = formatter.parse(timestamp); - return date; - } catch (ParseException e) { - e.printStackTrace(); - return null; - } - } + @Deprecated + @Getter + private static final boolean isDirty = BuildInfo.isDirty(); + /** * Get a string with the latest commit sha at API's build-time. - * + * @deprecated This method does not work the same way Github's does, this can be fixed by contributing to the versioning plugin and then addign to {@link BuildInfo} * @since 3.0 * @return {@link String} sha */ + @Deprecated public static String getSha() { return commit.substring(0, 7); } - - /** - * Get a boolean showing if the working tree was dirty at API's build-time. - *

- * If the working directory was dirty, this will return true, meaning there were modified tracked files and staged changes at build-time. - * - * @since 3.0 - * @return {@link Boolean} isDirty - */ - public static Boolean getIsDirty() { - return Boolean.parseBoolean(isDirty); - } } diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java index 1ca0d78a..16840605 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java @@ -6,32 +6,40 @@ import com.dumbdogdiner.stickyapi.StickyAPI; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.gson.Gson; import lombok.Data; +import lombok.Getter; import lombok.NonNull; +import lombok.experimental.UtilityClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.TextComponent; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Set; +import java.util.StringJoiner; /** * Utilities for text, such as in chat, books, and signs * * @since 3.0 (rewrite) */ +@UtilityClass public class TextUtil { - private TextUtil() { - } - - /** Offset for bold (in each direction) */ - public static final float BOLD_OFFSET = 0.5f; - /** Offset for shadows (in each direction) */ - public static final float SHADOW_OFFSET = 0.5f; + /** Offset for bold (in each direction) in half-pixels */ + public static final int BOLD_OFFSET = 1; + @Getter + public static boolean fallback = false; /** Number of pixels per line of a book */ public static final int PIXELS_PER_BOOK_LINE = 113; @@ -46,9 +54,16 @@ private TextUtil() { /** Number of pixels per line of a sign */ // TODO verify via minecraft source code public static final int PIXELS_PER_SIGN_LINE = 96; + /** Number of half-pixels per line of a sign */ + public static final int HALF_PIXELS_PER_SIGN_LINE = PIXELS_PER_SIGN_LINE * 2; /** Number of lines per sign */ public static final int LINES_PER_SIGN = 4; + /** Number of pixels per line of chat */ + public static final int PIXELS_PER_CHAT_LINE = 250; + /** Number of half-pixels per line of chat */ + public static final int HALF_PIXELS_PER_CHAT_LINE = PIXELS_PER_CHAT_LINE * 2; + private static final HashMap widths = new HashMap<>(); /** @@ -64,13 +79,14 @@ private static class WidthEntry { static { Gson gson = new Gson(); - try (InputStream input = ClassLoader.getSystemResource("generated/mojangles_width_data.json").openStream()) { + try (InputStream input = TextUtil.class.getResourceAsStream("/mojangles_width_data.json")) { WidthEntry[] entries = gson.fromJson(new InputStreamReader(input), WidthEntry[].class); for (WidthEntry entry : entries) { if (entry.getId() == null) entry.setId(0); widths.put((char) (entry.getId() != null ? entry.getId() : '\0'), entry.width); } } catch (Exception e) { + fallback = true; StickyAPI.getLogger().severe(e.getMessage()); // fallback to a minimal configuration, in case anything fails for (char c = 32; c <= 126; ++c) { @@ -123,31 +139,67 @@ public static int getCharacterWidth(char c) throws IllegalArgumentException{ * Measure the width of a string, assuming it is in the default Minecraft font. * * @param text The string to measure - * @param isBold If the string is bold * @return The width of the string, in half-pixels */ - public static int getStringWidth(@NonNull String text, boolean isBold) { - text = ChatColor.stripColor(text); + public static int getStringWidth(@NonNull String text) { int width = 0; - - for (char c : text.toCharArray()) { - width += TextUtil.getCharacterWidth(c); + BaseComponent [] components = TextComponent.fromLegacyText(text); + for(BaseComponent component : components){ + for (char c : component.toPlainText().toCharArray()) { + width += getCharacterWidth(c); + } + // TODO double check this calculation, based off of koda's code and something i read but cant find anymore + if(component.isBold()) { + width += (component.toPlainText().length() + 1) * 2; + } } - if (isBold) - width += 2 * text.length(); - return width; } - /** - Measure the width of a string, assuming it is in the default Minecraft font. - * - * @param text The string to measure - * @return The width of the string, in half-pixels - * @see #getStringWidth(String, boolean) - */ - public static int getStringWidth(@NonNull String text) { - return getStringWidth(text, false); + public static String[] splitLines(String ... lines){ + List processedLines = new ArrayList<>(); + for(String line : lines) { + StringBuilder processedLine = new StringBuilder(); + boolean chatCode = false; + ChatColor lineColor = ChatColor.RESET; + int width = 0; + for(char c : line.toCharArray()) { + if(c == ChatColor.COLOR_CHAR) { + chatCode = true; + processedLine.append(c); + } else if(chatCode) { + lineColor = ChatColor.getByChar(c); + chatCode = false; + processedLine.append(c); + } else { + int charWidth = getCharacterWidth(c); + if(width + charWidth > HALF_PIXELS_PER_CHAT_LINE) { + // Start a new line! + processedLines.add(processedLine.toString()); + processedLine = new StringBuilder(); + width = 0; + + // Don't forget to continue formatting! FIXME this is bugged for now I need to test how stupid components work aaa + processedLine.append(lineColor); + } + // Append to the current (possibly new) line + processedLine.append(c); + width += charWidth; + } + } + } + + return (String[]) processedLines.toArray(); + } + + public static String [] prettifyText(String ... text) { + text = splitLines(text); + for(int i = 0; i < text.length; i++){ + int width = getStringWidth(text[i]); + int pad = (HALF_PIXELS_PER_CHAT_LINE - width) / 2; + text[i] = Strings.padStart(text[i], pad, ' '); + } + return text; } } diff --git a/common/serverversion/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java b/common/src/serverVersion/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java similarity index 63% rename from common/serverversion/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java rename to common/src/serverVersion/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java index 2caf8826..66080ee5 100644 --- a/common/serverversion/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java +++ b/common/src/serverVersion/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java @@ -4,15 +4,15 @@ */ package com.dumbdogdiner.stickyapi.common; +import lombok.experimental.UtilityClass; +import static com.dumbdogdiner.stickyapi.common.ServerVersion.ServerType.*; /** * Utility class for fetching version data. */ +@UtilityClass public final class ServerVersion { - private ServerVersion() { - } - public enum ServerType { - BUKKIT, SPIGOT, PAPER, BUNGEE, WATERFALL + BUKKIT, SPIGOT, PAPER, TUINITY, AIRPLANE, PURPUR, BUNGEE, WATERFALL } /** @@ -22,20 +22,64 @@ public enum ServerType { */ public static ServerType getServerType() { if (isBukkit()) { - if (isPaper()) { - return ServerType.PAPER; - } - if (isSpigot()) { - return ServerType.SPIGOT; - } - return ServerType.BUKKIT; + if(isPurpur()) + return PURPUR; + else if(isAirplane()) + return AIRPLANE; + else if(isTuinity()) + return TUINITY; + else if (isPaper()) + return PAPER; + else if (isSpigot()) + return SPIGOT; + else + return BUKKIT; + } else if(isBungee()) { + if (isWaterfall()) + return WATERFALL; + return BUNGEE; + } + + throw new UnsupportedOperationException("Unknown server type, is this even a server??"); + } + + /** + * Returns true if the server is running tuinity. + * + * @return Whether or not the server is running tuinity + */ + public static boolean isTuinity() { + try { + return Class.forName("com.tuinity.tuinity.config.TuinityConfig") != null; + } catch (NoClassDefFoundError | ClassNotFoundException e) { + return false; } + } - if (isWaterfall()) { - return ServerType.WATERFALL; + /** + * Returns true if the server is running airplane. + * + * @return Whether or not the server is running airplane + */ + public static boolean isAirplane() { + try { + return Class.forName("gg.airplane.AirplaneCommand") != null; + } catch (NoClassDefFoundError | ClassNotFoundException e) { + return false; } + } - return ServerType.BUNGEE; + /** + * Returns true if the server is running purpur. + * + * @return Whether or not the server is running purpur + */ + public static boolean isPurpur() { + try { + return Class.forName("net.pl3x.purpur.PurpurVersionFetcher") != null; + } catch (NoClassDefFoundError | ClassNotFoundException e) { + return false; + } } /** diff --git a/common/src/test/java/com/dumbdogdiner/stickyapi/BuildInfoTest.java b/common/src/test/java/com/dumbdogdiner/stickyapi/BuildInfoTest.java new file mode 100644 index 00000000..6409bf55 --- /dev/null +++ b/common/src/test/java/com/dumbdogdiner/stickyapi/BuildInfoTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// TODO Use JGit to verify + +public class BuildInfoTest { + @Test + public void debugPrintAll() { + System.out.printf("Version\t %s", BuildInfo.getVersion()); + System.out.printf("\nTimestamp\t %s", BuildInfo.getTimestamp()); + System.out.printf("\nUnix Timestamp\t %d", BuildInfo.getBuiltAt().getEpochSecond()); + System.out.printf("\nCommit\t %s", BuildInfo.getCommit()); + System.out.printf("\nBranch\t %s", BuildInfo.getBranch()); + System.out.printf("\nDirty ws?\t %b", BuildInfo.isDirty()); + } + + + @Test + void getVersion() { + assertEquals(BuildInfo.getVersion().getMajor(), BuildInfo.getMajor()); + assertEquals(BuildInfo.getVersion().getMinor(), BuildInfo.getMinor()); + assertEquals(BuildInfo.getVersion().getMajor(), BuildInfo.getMajor()); + } + + @Test + public void testGetVersion() { + String version = BuildInfo.getVersion().toString(); + + assertNotNull(version); + assertFalse(version.length() == 0); + } + + @Test + public void testGetCommit() { + assertEquals(40, BuildInfo.getCommit().length()); + } + + @Test + public void testGetBranch() { + String branch = BuildInfo.getBranch(); + + assertNotNull(branch); + assertTrue(branch.length() != 0); + } +} diff --git a/common/src/test/java/com/dumbdogdiner/stickyapi/StickyAPITest.java b/common/src/test/java/com/dumbdogdiner/stickyapi/StickyAPITest.java index abe7cccb..2f837cab 100644 --- a/common/src/test/java/com/dumbdogdiner/stickyapi/StickyAPITest.java +++ b/common/src/test/java/com/dumbdogdiner/stickyapi/StickyAPITest.java @@ -16,10 +16,10 @@ public class StickyAPITest { @Test public void debugPrintAll() { System.out.printf("Version\t %s", StickyAPI.getVersion()); - System.out.printf("\nTimestamp\t %s", StickyAPI.getTimestamp().toString()); + System.out.printf("\nTimestamp\t %s", BuildInfo.getTimestamp().toString()); System.out.printf("\nCommit\t %s", StickyAPI.getCommit()); System.out.printf("\nBranch\t %s", StickyAPI.getBranch()); - System.out.printf("\nDirty ws?\t %b", StickyAPI.getIsDirty()); + System.out.printf("\nDirty ws?\t %b", StickyAPI.isDirty()); } @Test @@ -30,11 +30,6 @@ public void testGetVersion() { assertFalse(version.length() == 0); } - @Test - public void testGetTimestamp() { - assertNotNull(StickyAPI.getTimestamp()); - } - @Test public void testGetCommit() { assertEquals(40, StickyAPI.getCommit().length()); @@ -55,10 +50,4 @@ public void testGetBranch() { assertNotNull(branch); assertTrue(branch.length() != 0); } - - @Test - // This test is kinda useless? - public void testGetIsDirty() { - assertTrue(StickyAPI.getIsDirty() instanceof Boolean); - } } diff --git a/common/serverversion/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java b/common/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java similarity index 93% rename from common/serverversion/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java rename to common/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java index 11452fcb..cd7df0c0 100644 --- a/common/serverversion/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java +++ b/common/src/test/java/com/dumbdogdiner/stickyapi/common/ServerVersionTest.java @@ -7,8 +7,11 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.*; @@ -79,6 +82,7 @@ public void testGetServerTypeWaterfall() { try (MockedStatic mocked = mockStatic(ServerVersion.class)) { // Set stub return values when methods are called mocked.when(ServerVersion::isBukkit).thenReturn(false); + mocked.when(ServerVersion::isBungee).thenReturn(true); mocked.when(ServerVersion::isWaterfall).thenReturn(true); mocked.when(ServerVersion::getServerType).thenCallRealMethod(); // Bypass mocking @@ -96,6 +100,7 @@ public void testGetServerTypeBungee() { // Set stub return values when methods are called mocked.when(ServerVersion::isBukkit).thenReturn(false); mocked.when(ServerVersion::isWaterfall).thenReturn(false); + mocked.when(ServerVersion::isBungee).thenReturn(true); mocked.when(ServerVersion::getServerType).thenCallRealMethod(); // Bypass mocking assertTrue(ServerVersion.getServerType().equals(ServerType.BUNGEE)); diff --git a/common/src/test/java/com/dumbdogdiner/stickyapi/common/util/TextUtilTest.java b/common/src/test/java/com/dumbdogdiner/stickyapi/common/util/TextUtilTest.java index 397fe385..1e389463 100644 --- a/common/src/test/java/com/dumbdogdiner/stickyapi/common/util/TextUtilTest.java +++ b/common/src/test/java/com/dumbdogdiner/stickyapi/common/util/TextUtilTest.java @@ -29,6 +29,7 @@ public void testIsCharacterSupportedTrueBasicPlaneDecimal() { @Test public void testIsCharacterSupportedTrueEmojiUtf16() { // 0x1F5E1 | "Dagger Knife" Emoji 🗡️ + assertFalse(TextUtil.isFallback(), "should not be in fallback mode"); assertTrue(TextUtil.isCharacterSupported((char) 0x1F5E1)); } diff --git a/common/versioning-buildinfo.gradle b/common/versioning-buildinfo.gradle new file mode 100644 index 00000000..6328c4d1 --- /dev/null +++ b/common/versioning-buildinfo.gradle @@ -0,0 +1,164 @@ +/** + * This file allows the generation of a source set and Java file that contains information about the version of StickyAPI + */ + +buildscript { + repositories { + mavenCentral(); + } + dependencies { + classpath("com.squareup:javapoet:1.13.0") + classpath("com.google.guava:guava:30.1-jre") + classpath("de.skuzzle:semantic-version:2.1.0") + classpath("org.projectlombok:lombok:1.18.20") + classpath("org.jetbrains:annotations:20.1.0") + } +} + +import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import de.skuzzle.semantic.Version +import lombok.Getter +import lombok.experimental.UtilityClass +import org.jetbrains.annotations.NotNull; +import java.time.Instant; + +import javax.lang.model.element.Modifier +import java.text.SimpleDateFormat + +class BuildInfo extends DefaultTask { + @TaskAction + void doTask() { + FieldSpec builtAt = FieldSpec.builder(Instant.class, "builtAt") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) + .addAnnotation(Getter.class) + .addAnnotation(NotNull.class) + .initializer("\$T.ofEpochSecond(\$L)", Instant.class, Instant.now().getEpochSecond()) + .addJavadoc("@return When the code was built") + .build() + FieldSpec dateFormat = FieldSpec.builder(SimpleDateFormat.class, "dateFormat") + .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) + .addAnnotation(NotNull.class) + .addJavadoc("An {@link \$T} in ISO 8601 format", SimpleDateFormat.class) + .initializer("new \$T(\$S)", SimpleDateFormat.class, "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + .build() + FieldSpec version = FieldSpec.builder(Version.class, "version") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) + .addAnnotation(Getter.class) + .addAnnotation(NotNull.class) + .addJavadoc("@return What version of the project this is") + .initializer("\$T.parseVersion(\$S)", Version.class, project.rootProject.version) + .build() + MethodSpec getTimeStamp = MethodSpec.methodBuilder("getTimestamp") + .addJavadoc(CodeBlock.builder() + .addStatement("Gets the timestamp (as a String) of when the project was built") + .addStatement("@return The timestamp string of when the project was built") + .build() + ) + .returns(String) + .addCode(CodeBlock.builder() + .addStatement("return \$N.format(\$T.from(\$N))", dateFormat, Date.class, builtAt) + .build() + ) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + FieldSpec branch = FieldSpec.builder(String, "branch") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) + .addAnnotation(Getter) + .addAnnotation(NotNull.class) + .initializer("\$S", project.versioning.info.branch) + .addJavadoc("@return Which branch the build is from") + .build() + FieldSpec dirty = FieldSpec.builder(TypeName.BOOLEAN, "dirty") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) + .addAnnotation(Getter) + .initializer("\$L", project.versioning.info.dirty) + .addJavadoc("@return If the branch built from is considered dirty") + .build() + FieldSpec commit = FieldSpec.builder(String.class, "commit") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) + .addAnnotation(Getter.class) + .addAnnotation(NotNull.class) + .addJavadoc("@return The hash of the commit the build is from") + .initializer("\$S", project.versioning.info.commit) + .build() + MethodSpec getMajor = MethodSpec.methodBuilder("getMajor") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addJavadoc("@return the major version of the code") + .returns(int.class) + .addCode(CodeBlock.builder() + .addStatement("return \$N.getMajor()", version) + .build() + ) + .build() + MethodSpec getMinor = MethodSpec.methodBuilder("getMinor") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addJavadoc("@return the minor version of the code") + .returns(int.class) + .addCode(CodeBlock.builder() + .addStatement("return \$N.getMinor()", version) + .build() + ) + .build() + MethodSpec getPatch = MethodSpec.methodBuilder("getPatch") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addJavadoc("@return the patch version of the code") + .returns(int.class) + .addCode(CodeBlock.builder() + .addStatement("return \$N.getPatch()", version) + .build() + ) + .build() + + TypeSpec buildInfoClass = TypeSpec.classBuilder("BuildInfo") + .addJavadoc(CodeBlock.builder() + .addStatement("This class is generated at build-time and contains info about the current build") + .addStatement("@since @3.1.0") + .build() + ) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addAnnotation(UtilityClass.class) + .addField(version) + .addField(dirty) + .addField(builtAt) + .addField(branch) + .addField(dateFormat) + .addField(commit) + .addMethod(getTimeStamp) + .addMethod(getMajor) + .addMethod(getMinor) + .addMethod(getPatch) + .build() + JavaFile jfile = JavaFile.builder((getProject().getRootProject().getGroup() as String) + "." + getProject().getRootProject().getName(), buildInfoClass).build(); + jfile.writeTo(getProject().getProjectDir().toPath().resolve("src/gen/java").toFile()); + } +} + +tasks.register('buildInfo', BuildInfo) + +buildInfo { + doFirst { + // Set the timestamp format + def dataBuildTimestamp = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + + // Define the map containing the tokens we want to replace + def tokensMap = [ + BUILDINFO_VERSION : project.rootProject.version, + BUILDINFO_DATEFORMAT: dataBuildTimestamp, + BUILDINFO_TIMESTAMP : new SimpleDateFormat(dataBuildTimestamp).format(new Date()), + BUILDINFO_COMMIT : versioning.info.commit, + BUILDINFO_BRANCH : versioning.info.branch, + BUILDINFO_ISDIRTY : versioning.info.dirty.toString(), + ] + + // Pretty print the build info + println("\n----- (Common) StickyAPI Build Info -----\n") + tokensMap.each { println "${String.format("%1\$-" + 10 + "s", it.key.replace("BUILDINFO_", "").toLowerCase())}\t${it.value}" } + } + group("build") + dependsOn(versionDisplay) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index d107c168..1f499a73 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ rootProject.name = "stickyapi" include ":common" -include ":common:serverversion" // subproject that's shaded into common include ":config" // common subproject; not included in common jar include ":bukkit" include ":bungee"