From e6cbab2d3c12d8a16dd549dc3d2fff6dd5d3ad54 Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Thu, 24 Nov 2022 20:42:03 +0800 Subject: [PATCH] add a CLI program, which can read config, simple parse and echo a basic score - CLI add config read, from $HOME/.config/sekai-scores/sekai-scores.properties. - now it can read a pjsekai user id, and the sekai-scores database config - add a command `add` - it now can parse 9 params from args, or let user input them in cli, to build a basic score - it can echo the score, which looks just like the result score screen in game - now the sekai-cli build script supports 'application' implementation, and supports UTF-8 and uses spotbugs-annotations. --- sekai-cli/build.gradle | 38 +++++++++- sekai-cli/gradle.properties | 4 + .../java/cc/sukazyo/sekai_cli/ClientMain.java | 28 +++++++ .../java/cc/sukazyo/sekai_cli/Config.java | 74 +++++++++++++++++++ .../main/java/cc/sukazyo/sekai_cli/Log.java | 16 ++++ .../cc/sukazyo/sekai_cli/client/AddScore.java | 56 ++++++++++++++ .../sukazyo/sekai_cli/client/PrintScore.java | 34 +++++++++ 7 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 sekai-cli/gradle.properties create mode 100644 sekai-cli/src/main/java/cc/sukazyo/sekai_cli/ClientMain.java create mode 100644 sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Config.java create mode 100644 sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Log.java create mode 100644 sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/AddScore.java create mode 100644 sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/PrintScore.java diff --git a/sekai-cli/build.gradle b/sekai-cli/build.gradle index 0ae9b4a..d4f343d 100644 --- a/sekai-cli/build.gradle +++ b/sekai-cli/build.gradle @@ -1,16 +1,19 @@ +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets + plugins { id 'java' + id 'application' } -group 'cc.sukazyo' -version projVersion - repositories { mavenCentral() } dependencies { + compileOnly "com.github.spotbugs:spotbugs-annotations:${lib_spotbugs_v}" + implementation rootProject testImplementation "org.junit.jupiter:junit-jupiter-api:${lib_junit_v}" @@ -18,6 +21,35 @@ dependencies { } +final JavaVersion proj_java = JavaVersion.VERSION_17 +final Charset proj_source_encoding = StandardCharsets.UTF_8 + +group 'cc.sukazyo' +version moduleVersion + +application { + mainClass = "${group}.sekai_cli.ClientMain" +} + test { useJUnitPlatform() } + +java { + + sourceCompatibility proj_java + targetCompatibility proj_java + + withSourcesJar() + +} + +tasks.withType(JavaCompile) { + options.encoding = proj_source_encoding.name() +} + +tasks.withType(Javadoc) { + options.encoding = proj_source_encoding.name() + options.docEncoding = proj_source_encoding.name() + options.charSet = proj_source_encoding.name() +} diff --git a/sekai-cli/gradle.properties b/sekai-cli/gradle.properties new file mode 100644 index 0000000..a927e2b --- /dev/null +++ b/sekai-cli/gradle.properties @@ -0,0 +1,4 @@ +## Project Configurations + +# Proj Metadata +moduleVersion = 0.5 diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/ClientMain.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/ClientMain.java new file mode 100644 index 0000000..5252715 --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/ClientMain.java @@ -0,0 +1,28 @@ +package cc.sukazyo.sekai_cli; + +import cc.sukazyo.sekai_cli.client.AddScore; + +import java.util.Arrays; + +public class ClientMain { + + public static final Config config = Config.loadUserConfig(); + + public static void main (String[] args) { + + if (args.length > 0) { + if (args[0].equals("add")) { + AddScore.main(Arrays.copyOfRange(args, 1, args.length)); + $done(); + } + } + + System.out.println("Unknown function call.\n please check your program param."); + + } + + private static void $done () { + System.exit(0); + } + +} diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Config.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Config.java new file mode 100644 index 0000000..6baea15 --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Config.java @@ -0,0 +1,74 @@ +package cc.sukazyo.sekai_cli; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import static cc.sukazyo.sekai_cli.Log._debug; + +public class Config { + + private static final String CONFIG_ROOT_NAME = "sekai-scores"; + private static final String CONFIG_FILE = CONFIG_ROOT_NAME + ".properties"; + + public final long sekai_id; + @Nonnull public final String db_host; + @Nonnull public final String db_name; + @Nonnull public final String db_auth_user; + @Nonnull public final String db_auth_pwd; + @Nullable public final String db_prefix; + + private Config (Properties props) { + // user config field + this.sekai_id = Long.parseLong(props.getProperty("user.sekai-id")); + _debug("config field user.sekai-id set: " + this.sekai_id); + this.db_host = getNonnull(props, "db.server"); + _debug("config field db.server set: " + this.db_host); + this.db_name = getNonnull(props, "db.database"); + _debug("config field db.database set: " + this.db_name); + this.db_auth_user = getNonnull(props, "db.auth.user"); + _debug("config field db.auth.user set: " + this.db_auth_user); + this.db_auth_pwd = getNonnull(props, "db.auth.password"); + _debug("config field db.auth.password set."); + this.db_prefix = props.getProperty("db.table-prefix"); + _debug(this.db_prefix == null ? "config field db.prefix unset." : "config field db.table-prefix set: " + this.db_prefix); + } + + @Nonnull + private static String getNonnull (Properties props, String key) { + final String v = props.getProperty(key); + if (v == null) throw new NullPointerException("null at key " + key); + return v; + } + + static Config loadUserConfig() { + try { + + final Properties props = new Properties(); + final File propFile = Paths.get(getAppConfigPath(getSysUserConfigRoot()).toString(), CONFIG_FILE).toFile(); + props.load(new FileInputStream(propFile)); + _debug("loaded config file: " + propFile.getAbsolutePath()); + + return new Config(props); + + } catch (IOException e) { + throw new RuntimeException("error while load user config", e); + } + } + + private static Path getAppConfigPath(Path configRoot) { + return Paths.get(configRoot.toString(), CONFIG_ROOT_NAME); + } + + private static Path getSysUserConfigRoot () { + final Path userhome = Paths.get(System.getProperty("user.home"), ".config"); + _debug("read config from user home : " + userhome.toAbsolutePath()); + return userhome; + } + +} diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Log.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Log.java new file mode 100644 index 0000000..89b7da5 --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/Log.java @@ -0,0 +1,16 @@ +package cc.sukazyo.sekai_cli; + +public class Log { + + private static final boolean IS_DEBUG = true; + + public static void _debug (String m) { + if (!isDebug()) return; + m = "[DEBUG]" + m; + m = m.replaceAll("\\n", "'''''''"); + System.out.println(m); + } + + private static boolean isDebug () { return IS_DEBUG; } + +} diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/AddScore.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/AddScore.java new file mode 100644 index 0000000..f9d91df --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/AddScore.java @@ -0,0 +1,56 @@ +package cc.sukazyo.sekai_cli.client; + +import cc.sukazyo.sekai_scores.ScoreBase; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.InputMismatchException; +import java.util.Scanner; + +import static cc.sukazyo.sekai_cli.Log._debug; + +public class AddScore { + + public static void main (String[] args) { + + try { + + final int[] scores = new int[9]; + + if (args.length == 0) { + System.out.println("Input your score,"); + System.out.println(" score formatting: perfect ... miss late fast flick combo"); + System.out.print("> "); + Scanner input = new Scanner(System.in); + for (int i = 0; i < 9; i++) { + scores[i] = input.nextInt(); + } + } else { + for (int i = 0; i < 9; i++) { + scores[i] = Integer.parseInt(args[i]); + } + } + + for (int i : scores) { + if (i < 0 || i > 9999) throw new InputMismatchException(String.format("input %d out of range of [0, 9999]", i)); + } + + ScoreBase scoreBase = new ScoreBase( + scores[0], scores[1], scores[2], scores[3], scores[4], + scores[6], scores[5], scores[7], scores[8] + ); + + System.out.println(PrintScore.printTableScoreBase(scoreBase)); + + } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) { + System.out.println("unsupported yet."); + final StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + _debug(sw.toString()); + } catch (InputMismatchException e) { + System.out.println("unavailable input: " + e.getMessage()); + } + + } + +} diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/PrintScore.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/PrintScore.java new file mode 100644 index 0000000..feaf12c --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/PrintScore.java @@ -0,0 +1,34 @@ +package cc.sukazyo.sekai_cli.client; + +import cc.sukazyo.sekai_scores.ScoreBase; + +public class PrintScore { + + public static final String MESSAGE_AP = "ALL PERFECT!"; + public static final String MESSAGE_FC = "FULL COMBO!"; + + public static String getMessageQuote(String message) { + return "\\ " + message + " /"; + } + + public static String printTableScoreBase (ScoreBase score) { + return String.format(""" + COMBO %16s + ╭────────────────────┬─────────────────┬───────────────╮ + │ │ PERFECT %4d │ │ + │ │ GREAT %4d │ LATE FAST │ + │ %-4d │ GOOD %4d │ [%-4d %4d] │ + │ │ BAD %4d │ FLICK %4d │ + │ │ MISS %4d │ │ + ╰────────────────────┴─────────────────┴───────────────╯ + """, + score.noteCount() == score.combo() ? getMessageQuote((score.noteCount()==score.perfect()?MESSAGE_AP : MESSAGE_FC)) : "", + score.perfect(), + score.great(), + score.combo(), score.good(), score.slow(), score.fast(), + score.bad(), score.flick(), + score.miss() + ); + } + +}