├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── fileTemplates │ ├── code │ │ └── New Kotlin Function Body.kt │ └── includes │ │ └── File Header.java ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml └── runConfigurations │ └── io_github_sof3_graphmine_cli_MainKt.xml ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── graphmine-cli ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ └── io │ │ └── github │ │ └── sof3 │ │ └── graphmine │ │ └── cli │ │ ├── ConsoleReceiver.kt │ │ ├── ConsoleSender.kt │ │ ├── TerminalSignal.kt │ │ └── main.kt │ └── resources │ ├── config.kts │ ├── log4j2.component.properties │ └── log4j2.yml ├── graphmine-core ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── sof3 │ └── graphmine │ ├── HasLogger.kt │ ├── Server.kt │ ├── VersionInfo.kt │ ├── client │ ├── Client.kt │ └── ClientAttachable.kt │ ├── command │ ├── Command.kt │ ├── CommandExecutor.kt │ ├── CommandMap.kt │ ├── CommandReceiver.kt │ ├── CommandSender.kt │ ├── EmptyOverload.kt │ ├── Overload.kt │ ├── RegisteredOverload.kt │ ├── args │ │ ├── ClientArg.kt │ │ ├── CommandArg.kt │ │ ├── EnumArg.kt │ │ ├── number.kt │ │ └── string.kt │ ├── exception.kt │ ├── impl │ │ ├── HelpCommand.kt │ │ ├── SayCommand.kt │ │ ├── VersionCommand.kt │ │ └── package.md │ └── package.md │ ├── config │ ├── ConfigEntryDelegate.kt │ ├── ConfigGroupSpec.kt │ ├── ConfigSpec.kt │ ├── SimpleConfigEntry.kt │ ├── exception.kt │ └── spec.kt │ ├── entity │ ├── Entity.kt │ ├── EntityController.kt │ ├── EntityState.kt │ ├── EntityViewEvent.kt │ ├── ExternalView.kt │ ├── ExternalViewer.kt │ └── InternalViewer.kt │ ├── feature │ ├── FeatureEdge.kt │ ├── FeatureEvent.kt │ ├── FeatureGraph.kt │ ├── FeatureNode.kt │ └── FeatureNodeInstance.kt │ ├── scope │ ├── BaseScope.kt │ └── Scope.kt │ └── world │ ├── Block.kt │ ├── BlockLocation.kt │ ├── Blocks.kt │ ├── Location.kt │ ├── World.kt │ ├── WorldPartition.kt │ ├── WorldUser.kt │ └── WorldView.kt ├── graphmine-i18n-core ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── sof3 │ │ │ └── graphmine │ │ │ └── i18n │ │ │ └── core │ │ │ ├── CoreLang.kt │ │ │ └── CoreLangLoader.kt │ └── resources │ │ └── io │ │ └── github │ │ └── sof3 │ │ └── graphmine │ │ └── i18n │ │ └── core │ │ └── en_US.lang.kts │ └── test │ └── kotlin │ └── io │ └── github │ └── sof3 │ └── graphmine │ └── i18n │ └── core │ └── TranslationTest_en_US.kt ├── graphmine-i18n ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── sof3 │ └── graphmine │ └── i18n │ ├── Declaration.kt │ ├── GroupSpec.kt │ ├── I18n.kt │ ├── I18nable.kt │ ├── LangSpec.kt │ ├── LiteralI18N.kt │ └── SpecI18N.kt ├── graphmine-util ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── sof3 │ │ │ └── graphmine │ │ │ └── util │ │ │ ├── DataUtil.kt │ │ │ ├── DelegateProvider.kt │ │ │ ├── KtsBin.kt │ │ │ ├── KtsLoader.kt │ │ │ ├── VarDelegateProvider.kt │ │ │ ├── arrayMaps.kt │ │ │ ├── error.kt │ │ │ ├── misc.kt │ │ │ ├── qualifier │ │ │ ├── Qualifier.kt │ │ │ ├── QualifierClashException.kt │ │ │ ├── QualifierMap.kt │ │ │ └── package.md │ │ │ ├── ranges.kt │ │ │ ├── ref.kt │ │ │ └── string │ │ │ └── FormattedStringReader.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── javax.script.ScriptEngineFactory │ └── test │ └── kotlin │ └── io │ └── github │ └── sof3 │ └── graphmine │ └── util │ ├── string │ └── FormattedStringReaderSpec.kt │ └── testUtil.kt ├── settings.gradle.kts ├── util-math ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── sof3 │ └── graphmine │ └── util │ └── math │ ├── IntVector3.kt │ ├── IntVector3Range.kt │ ├── Side.kt │ ├── Vector3.kt │ ├── annotations.kt │ └── math.kt └── util-reflect-io ├── build.gradle.kts ├── module.md ├── settings.gradle.kts └── src └── main └── kotlin └── io └── github └── sof3 └── graphmine └── util └── reflectio └── Persistable.kt /.gitignore: -------------------------------------------------------------------------------- 1 | /data/ 2 | /logs/ 3 | /graphmine-cli/data/ 4 | /graphmine-cli/logs/ 5 | /*/out 6 | .idea/* 7 | !.idea/fileTemplates 8 | !.idea/inspectionProfiles 9 | !.idea/.name 10 | !.idea/codeStyles 11 | !.idea/runConfigurations 12 | !.idea/gradle.xml 13 | *.class 14 | *.log 15 | *.ctxt 16 | .mtj.tmp/ 17 | *.jar 18 | *.war 19 | *.nar 20 | *.ear 21 | *.zip 22 | *.tar.gz 23 | *.rar 24 | hs_err_pid* 25 | *~ 26 | .fuse_hidden* 27 | .directory 28 | .Trash-* 29 | .nfs* 30 | .DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | Icon 34 | ._* 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | .com.apple.timemachine.donotpresent 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | *.bak 48 | Thumbs.db 49 | ehthumbs.db 50 | ehthumbs_vista.db 51 | *.stackdump 52 | [Dd]esktop.ini 53 | $RECYCLE.BIN/ 54 | *.cab 55 | *.msi 56 | *.msix 57 | *.msm 58 | *.msp 59 | *.lnk 60 | .gradle 61 | /build/ 62 | /*/build/ 63 | gradle-app.setting 64 | !gradle-wrapper.jar 65 | .gradletasknamecache 66 | [._]*.s[a-v][a-z] 67 | [._]*.sw[a-p] 68 | [._]s[a-rt-v][a-z] 69 | [._]ss[a-gi-z] 70 | [._]sw[a-p] 71 | Session.vim 72 | .netrwhist 73 | tags 74 | [._]*.un~ 75 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | graphmine -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 14 | 113 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/fileTemplates/code/New Kotlin Function Body.kt: -------------------------------------------------------------------------------- 1 | TODO("not implemented") -------------------------------------------------------------------------------- /.idea/fileTemplates/includes/File Header.java: -------------------------------------------------------------------------------- 1 | /* 2 | * GraphMine 3 | * Copyright (C) 2018 SOFe 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 35 | 36 | -------------------------------------------------------------------------------- /.idea/runConfigurations/io_github_sof3_graphmine_cli_MainKt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | - oraclejdk8 5 | #- oraclejdk9 6 | env: 7 | global: 8 | - secure: "SuMHzb/Ua8UOH9ayTRyJ1H5xERrZq6pFG0kM8i1nvQcHmKTkfYcP/J/SaXYLhEueCch4m0OVeBCgSKZUxAFEhvTino2ULPpebGHUAtmeBzMIn2jIyOM3qY6oEXfQ6OXV1O3xXp2m/Q7WQXyrnGCiGTBNKJ4uEwvWzaMyocvw0g4RFRrGuhJT8xsceMrxKzyhqDr/Um/c7E2xD58nCc8OmfHXwAlJa1yOzcWwNAdU2jfAF/jFQIfFE/u2OmeT4sNkTgzrsGstiphhIQsUyLglwQrhQvqgaU5Bp//iRAAq0inreVjaA2HJAiU41ZhrYUjOEFa2S6z8JWicT8dsbCKDgspp7+u1NSHUs4k9mxhHhCo/1npbw5e/Uyr78Qin5fLYVEWUwdRDby0q6kJZgjVpbf+LtKMd1nNcDerSf4cI4wRORI0afAZ4mRQC7Fs13JjnUdJs+7ly8QB6/IF4pwziuVwXeBufQzj0VEF4jf7I83F0LWGUENh/98rknQB2UjI5EujLQLT6bU7TWdjXCqLH6QAp8ljGVMJKCXE3hODkXnye+hqjbUH2uYhJwCQjiFTTWOUtlWXnTNRQdOsFG+X6DmC8yi8otHt6Qz4s1/ZCTB78Gl1wSKgf01q+3HPoHrdVmoJFPxMEqo+UPwV6A5ScBRPwt+aLV8WW0xYHXW1jGFA=" 9 | before_cache: 10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 12 | cache: 13 | directories: 14 | - $HOME/.gradle/caches/ 15 | - $HOME/.gradle/wrapper/ 16 | script: 17 | - ./gradlew check 18 | after_script: 19 | - cd $TRAVIS_BUILD_DIR && ./gradlew codeCoverageReport 20 | - bash <(curl -s https://codecov.io/bash) 21 | before_deploy: 22 | - ./gradlew graphmine-cli:fatJar dokka generateDependencyGraph 23 | - wget -O ../gh-pages.tar.gz https://github.com/SOF3/GraphMine/archive/gh-pages.tar.gz 24 | - tar xzf ../gh-pages.tar.gz -C .. 25 | - cd ../GraphMine-gh-pages && rm -r api && mkdir api 26 | - cp -r $TRAVIS_BUILD_DIR/*/build/javadoc/* api 27 | - cp $TRAVIS_BUILD_DIR/build/reports/dependency-graph/dependency-graph.png depGraph.png 28 | - cp $TRAVIS_BUILD_DIR/graphmine-cli/build/libs/graphmine-cli-fat-*.jar latestMaster.jar 29 | deploy: 30 | skip-cleanup: true 31 | on: 32 | branch: master 33 | jdk: openjdk8 34 | provider: pages 35 | github-token: $GITHUB_TOKEN 36 | local-dir: $TRAVIS_BUILD_DIR/../GraphMine-gh-pages 37 | keep-history: true 38 | committer-from-gh: true 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | clean: 4 | rm -r build \ 5 | graphmine-cli/build \ 6 | graphmine-core/build \ 7 | graphmine-i18n/build \ 8 | graphmine-i18n-core/build \ 9 | graphmine-util/build \ 10 | util-math/build \ 11 | util-reflect-io/build \ 12 | 2>/dev/null 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphMine 2 | An experimental server software project. DO NOT TRY TO USE THIS. This is just a proof of concept. 3 | 4 | [![Build Status](https://travis-ci.org/SOF3/GraphMine.svg?branch=master)](https://travis-ci.org/SOF3/GraphMine) 5 | 6 | ## What is this? 7 | This is just yet another of SOFe's useless projects where he just writes the code and never tests it. **Do not expect a 8 | working server software from someone who doesn't even have a client.** 9 | 10 | Yes, there are unit tests, but there are no integration tests. 11 | 12 | ## So why is this here? 13 | Often, I have some cool ideas for PocketMine, but the idea is too big, complicated or impractical to be implemented on 14 | PocketMine. Since I am addicted in implementing crazy stuff, I created this repo to satisfy my own desires for 15 | sophistication. Most ideas here are probably too impractical, e.g. a lot of effort is put into making sure multiple 16 | players can control the same entity at the same time but see different things. And some effort is put into making sure 17 | the server can run fluently as a pure proxy, and the server can also run fluently as a pure backend without actual 18 | connecting players. Sound confusing and pointless? Yes, pointlessness is the point of this project. 19 | 20 | In addition, Kotlin is a cool language that I want to master, but I don't have cool ideas to work on, so I resorted to 21 | Minecraft server software again. I am too stupid to learn to make anything other than Minecraft-related stuff. 22 | 23 | No project is perfect, but I hope this one is going to *look* better than PocketMine. 24 | 25 | ## How to run the project? 26 | Development jar executables can be found in https://sof3.github.io/GraphMine/latestMaster.jar (but make sure you 27 | actually want to run it -- it doesn't do anything useful!) 28 | 29 | Oracle JDK 9 is not supported, but Oracle JDK 8 and OpenJDK 8 are both OK. 30 | 31 | #### Project dependency graph 32 | 33 | ![](https://sof3.github.io/GraphMine/depGraph.png) 34 | 35 | ## Why is it called GraphMine? 36 | GraphMine organizes features in a graph model. Features can be appended to or removed from the graph with a more 37 | intuitive approach. However, the exact modelling of this graph is still yet to be defined. 38 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension 2 | import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator 3 | import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin 4 | import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorTask 5 | import guru.nidi.graphviz.attribute.Color 6 | import guru.nidi.graphviz.attribute.Style 7 | import guru.nidi.graphviz.model.MutableNode 8 | import java.nio.file.Files 9 | import org.gradle.api.tasks.testing.logging.TestLogEvent 10 | import org.gradle.internal.impldep.org.testng.reporters.XMLUtils.xml 11 | import org.gradle.testing.jacoco.tasks.JacocoReport 12 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 13 | import org.jetbrains.dokka.gradle.DokkaTask 14 | 15 | /* 16 | * GraphMine 17 | * Copyright (C) 2018 SOFe 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU Affero General Public License as published 21 | * by the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU Affero General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU Affero General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | group = "io.github.sof3.graphmine" 34 | version = "1.0.0-SNAPSHOT" 35 | 36 | buildscript { 37 | repositories { 38 | jcenter() 39 | } 40 | dependencies { 41 | classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17") 42 | classpath("com.vanniktech:gradle-dependency-graph-generator-plugin:0.5.0") 43 | } 44 | } 45 | 46 | plugins { 47 | java 48 | kotlin("jvm") version "1.3.0" 49 | jacoco 50 | id("com.vanniktech.dependency.graph.generator") version "0.5.0" 51 | } 52 | 53 | allprojects { 54 | repositories { 55 | jcenter() 56 | } 57 | } 58 | 59 | subprojects { 60 | apply(plugin = "org.jetbrains.dokka") 61 | 62 | repositories { 63 | maven(url = "https://dl.bintray.com/spekframework/spek-dev") 64 | } 65 | 66 | tasks.withType { 67 | kotlinOptions.jvmTarget = "1.8" 68 | } 69 | 70 | tasks.withType { 71 | useJUnitPlatform { 72 | includeEngines("spek2") 73 | } 74 | testLogging { 75 | showStandardStreams = true 76 | showExceptions = true 77 | showCauses = true 78 | showStackTraces = true 79 | events(TestLogEvent.FAILED, TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR) 80 | } 81 | } 82 | 83 | tasks.withType { 84 | outputFormat = "jekyll" 85 | outputDirectory = "$buildDir/javadoc" 86 | 87 | File(project.projectDir, "module.md").takeIf { it.isFile }?.let { includes += it } 88 | Files.walk(File(project.projectDir, "src").toPath()) 89 | .filter { it.toFile().isDirectory } 90 | .map { it.resolve("package.md").toFile() } 91 | .filter { it.isFile } 92 | .forEach { includes += it.absolutePath } 93 | } 94 | } 95 | 96 | fun colorNode(node: MutableNode, name: String): MutableNode { 97 | val rgb = name.hashCode() 98 | node.add(Style.FILLED, Color.rgb(rgb)) 99 | val R = rgb shr 16 and 0xFF 100 | val G = rgb shr 8 and 0xFF 101 | val B = rgb and 0xFF 102 | val light = 0.299 * R + 0.587 * G + 0.114 * B 103 | if (light <= 152.0) { 104 | node.add("fontcolor", "#ffffff") 105 | } 106 | return node 107 | } 108 | 109 | val graphMineDepGraphGenerator = Generator( 110 | dependencyNode = { node, dependency -> colorNode(node, dependency.moduleGroup) }, 111 | projectNode = { node, project -> colorNode(node, project.group.toString()) } 112 | ) 113 | 114 | configure { 115 | generators = listOf(graphMineDepGraphGenerator) 116 | } 117 | 118 | tasks.withType { 119 | reports { 120 | xml.isEnabled = true 121 | xml.destination = File("build/reports/jacoco.xml") 122 | executionData(tasks.withType()) 123 | } 124 | } 125 | 126 | tasks { 127 | task("codeCoverageReport", type = JacocoReport::class) { 128 | subprojects.forEach { 129 | dependsOn(":${it.name}:test") 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOF3/GraphMine/ae7c3599e8d327c0077fa798adeaed8dbbf0577f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 06 21:36:00 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /graphmine-cli/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * GraphMine 3 | * Copyright (C) 2018 SOFe 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | plugins { 20 | java 21 | kotlin("jvm") 22 | application 23 | } 24 | 25 | group = "io.github.sof3.graphmine" 26 | version = "1.0.0-SNAPSHOT" 27 | 28 | dependencies { 29 | implementation(project(":graphmine-core")) 30 | implementation(kotlin("stdlib-jdk8")) 31 | implementation("commons-cli", "commons-cli", "1.4") 32 | implementation("commons-io", "commons-io", "2.6") 33 | testImplementation(kotlin("test")) 34 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 35 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 36 | } 37 | 38 | application { 39 | mainClassName = "io.github.sof3.graphmine.cli.MainKt" 40 | } 41 | 42 | val fatJar = task("fatJar", type = Jar::class) { 43 | baseName = "${project.name}-fat" 44 | manifest { 45 | attributes["Implementation-Title"] = "GraphMine" 46 | attributes["Implementation-Version"] = version 47 | attributes["Main-Class"] = "io.github.sof3.graphmine.cli.MainKt" 48 | } 49 | from(configurations.runtime.map { if (it.isDirectory) it else zipTree(it) }) 50 | with(tasks["jar"] as CopySpec) 51 | } 52 | -------------------------------------------------------------------------------- /graphmine-cli/module.md: -------------------------------------------------------------------------------- 1 | # Module graphmine-cli 2 | An executable project that wraps the core and exposes a user interface through standard I/O (command line interface for terminal users) 3 | 4 | ## Threading 5 | ### Main thread 6 | This module contains a `fun main` entry point. The Server object is constructed from this thread. This thread exits after the server has been fully initialized. 7 | 8 | ### Stdin thread 9 | This module starts a thread for reading input from stdin. 10 | 11 | ### Shutdown thread 12 | A thread is started when the user sends a shutdown signal e.g. SIGINT. The thread exits as soon as the signal has been 13 | sent to the server's corresponding handlers. 14 | -------------------------------------------------------------------------------- /graphmine-cli/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine-cli" 2 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/kotlin/io/github/sof3/graphmine/cli/ConsoleReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.cli 2 | 3 | import io.github.sof3.graphmine.command.CommandReceiver 4 | import io.github.sof3.graphmine.i18n.I18n 5 | import org.apache.logging.log4j.LogManager 6 | 7 | /* 8 | * GraphMine 9 | * Copyright (C) 2018 SOFe 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published 13 | * by the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | internal class ConsoleReceiver(private val prefix: String, private val locale: String) : CommandReceiver { 26 | override fun receiveMessage(message: I18n) { 27 | LogManager.getLogger(prefix).info(message[locale]) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/kotlin/io/github/sof3/graphmine/cli/ConsoleSender.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.cli 2 | 3 | import io.github.sof3.graphmine.command.CommandSender 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | internal object ConsoleSender : CommandSender { 24 | } 25 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/kotlin/io/github/sof3/graphmine/cli/TerminalSignal.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.cli 2 | 3 | /** 4 | * Any signals received from the abstract console are expressed as a [TerminalSignal]. 5 | */ 6 | sealed class TerminalSignal { 7 | /** 8 | * Represents an EOD (end of data) signal, implying that the input is closed 9 | */ 10 | object Eod : TerminalSignal() 11 | 12 | /** 13 | * Represents a SIGINT or otherwise force-shutdown signal. 14 | */ 15 | object Int : TerminalSignal() 16 | 17 | /** 18 | * Represents a line of command 19 | * @property line the command line with line endings trimmed 20 | */ 21 | class Cmd(val line: String) : TerminalSignal() 22 | } 23 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/kotlin/io/github/sof3/graphmine/cli/main.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.cli 2 | 3 | import io.github.sof3.graphmine.Server 4 | import io.github.sof3.graphmine.VersionInfo 5 | import io.github.sof3.graphmine.config.CoreConfig 6 | import io.github.sof3.graphmine.util.BooleanRef 7 | import io.github.sof3.graphmine.util.KtsLoader 8 | import org.apache.commons.cli.CommandLine 9 | import org.apache.commons.cli.DefaultParser 10 | import org.apache.commons.cli.HelpFormatter 11 | import org.apache.commons.cli.Options 12 | import org.apache.commons.io.IOUtils 13 | import org.apache.logging.log4j.LogManager 14 | import reactor.core.publisher.Flux 15 | import reactor.core.scheduler.Schedulers 16 | import java.io.File 17 | import java.io.FileOutputStream 18 | import java.io.FileReader 19 | import java.io.IOException 20 | import java.text.DateFormat 21 | 22 | /* 23 | * GraphMine 24 | * Copyright (C) 2018 SOFe 25 | * 26 | * This program is free software: you can redistribute it and/or modify 27 | * it under the terms of the GNU Affero General Public License as published 28 | * by the Free Software Foundation, either version 3 of the License, or 29 | * (at your option) any later version. 30 | * 31 | * This program is distributed in the hope that it will be useful, 32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34 | * GNU Affero General Public License for more details. 35 | * 36 | * You should have received a copy of the GNU Affero General Public License 37 | * along with this program. If not, see . 38 | */ 39 | 40 | internal lateinit var options: Options 41 | internal lateinit var cmd: CommandLine 42 | internal val logger = LogManager.getLogger()!! 43 | 44 | internal fun main(args: Array) { 45 | options = Options().apply { 46 | addOption("v", "version", false, "GraphMine version") 47 | addOption("h", "help", false, "Overload line description") 48 | addOption("d", "data", true, "Path to data directory") 49 | } 50 | cmd = DefaultParser().parse(options, args) 51 | 52 | if (metaArgs()) return 53 | 54 | val initNano = System.nanoTime() 55 | 56 | logger.debug("Setting up data directory...") 57 | val (dataDir, config) = setupDataDir() 58 | 59 | val server = Server( 60 | dataDir = dataDir, 61 | config = config, 62 | initNano = initNano 63 | ) 64 | 65 | logger.debug("Initializing stdin") 66 | val (signalFlux, shutdown) = createStdinFlux() 67 | signalFlux.subscribe { 68 | when (it) { 69 | is TerminalSignal.Cmd -> { 70 | val name = it.line.substringBefore(" ") 71 | server.commandMap.dispatch(it.line, ConsoleSender, ConsoleReceiver("Command:$name", server.locale)) 72 | } 73 | TerminalSignal.Eod -> server.shutdown() 74 | TerminalSignal.Int -> server.shutdown() 75 | } 76 | } 77 | server.shutdownHandlers += shutdown 78 | } 79 | 80 | private fun metaArgs() = when { 81 | cmd.hasOption("h") -> { 82 | HelpFormatter().printHelp("graphmine", options) 83 | true 84 | } 85 | cmd.hasOption("v") -> { 86 | println("GraphMine v${VersionInfo.VERSION}, built on ${DateFormat.getDateTimeInstance().format(VersionInfo.BUILD_DATE)}") 87 | true 88 | } 89 | else -> false 90 | } 91 | 92 | private fun setupDataDir(): Pair { 93 | val dataDir = File(cmd.getOptionValue("data", "data")) 94 | if (!dataDir.exists()) dataDir.mkdirs() 95 | 96 | val configFile = File(dataDir, "config.kts") 97 | if (!configFile.exists()) { 98 | logger.debug("Copying default config.kts") 99 | val default = object {}.javaClass.classLoader.getResourceAsStream("config.kts") 100 | FileOutputStream(configFile).use { IOUtils.copy(default, it) } 101 | } 102 | 103 | logger.debug("Loading CoreConfig classes") 104 | CoreConfig {} 105 | logger.debug("Loading ${configFile.canonicalPath}") 106 | val config = KtsLoader.load(FileReader(configFile)) 107 | logger.debug("Loaded ${configFile.canonicalPath}") 108 | return dataDir to config 109 | } 110 | 111 | private fun createStdinFlux(): Pair, () -> Unit> { 112 | var closing by BooleanRef(false) 113 | 114 | val flux = Flux.create { sink -> 115 | Runtime.getRuntime().addShutdownHook(Thread { sink.next(TerminalSignal.Int) }) 116 | 117 | try { 118 | while (!closing) { 119 | val line = readLine() ?: break 120 | sink.next(TerminalSignal.Cmd(line)) 121 | } 122 | } catch (e: IOException) { 123 | } 124 | if (!closing) sink.next(TerminalSignal.Eod) 125 | }.subscribeOn(Schedulers.newSingle("stdin")) 126 | 127 | return Pair(flux) { 128 | System.`in`.close() 129 | closing = true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/resources/config.kts: -------------------------------------------------------------------------------- 1 | import io.github.sof3.graphmine.config.CoreConfig 2 | 3 | CoreConfig { 4 | language = "en_US" 5 | server { 6 | port = 19132 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/resources/log4j2.component.properties: -------------------------------------------------------------------------------- 1 | log4j.configurationFile=log4j2.yml 2 | -------------------------------------------------------------------------------- /graphmine-cli/src/main/resources/log4j2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Configuration: 3 | name: GraphMineLogger 4 | status: WARN 5 | appenders: 6 | Console: 7 | name: stdout 8 | PatternLayout: 9 | Pattern: "%date{HH:mm:ss.SSS} %logger{2}@%thread %level %msg%n" 10 | RollingFile: 11 | name: file 12 | fileName: logs/server.log 13 | filePattern: "logs/archive/server.%{yyyy-MM-dd-HH-mm}.gz" 14 | PatternLayout: 15 | Pattern: "%d{MMM dd HH:mm:ss} [%thread] %level %logger - %msg%n" 16 | Filters: 17 | ThresholdFilter: 18 | level: info 19 | Policies: 20 | SizeBasedTriggeringPolicy: 21 | size: 16 KB 22 | DefaultRollOverStrategy: 23 | max: 64 24 | Loggers: 25 | # Logger: 26 | # - name: io.github.sof3.graphmine.Server 27 | Root: 28 | level: trace 29 | AppenderRef: 30 | - ref: stdout 31 | - ref: file 32 | ... 33 | -------------------------------------------------------------------------------- /graphmine-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import java.io.FileWriter 3 | import java.text.SimpleDateFormat 4 | import java.util.Date 5 | import java.util.Properties 6 | 7 | /* 8 | * GraphMine 9 | * Copyright (C) 2018 SOFe 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published 13 | * by the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | plugins { 26 | java 27 | kotlin("jvm") 28 | } 29 | 30 | group = "io.github.sof3.graphmine" 31 | version = "1.0.0-SNAPSHOT" 32 | 33 | dependencies { 34 | api(project(":graphmine-util")) 35 | api(project(":util-math")) 36 | api(project(":graphmine-i18n")) 37 | api(project(":graphmine-i18n-core")) 38 | 39 | api("org.apache.logging.log4j", "log4j-api", "2.11.1") 40 | runtimeOnly("org.apache.logging.log4j", "log4j-core", "2.11.1") 41 | 42 | api(kotlin("stdlib-jdk8")) 43 | api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1") 44 | api(kotlin("reflect")) 45 | 46 | api("io.projectreactor:reactor-core:3.2.3.RELEASE") 47 | 48 | implementation("com.fasterxml.jackson.module", "jackson-module-kotlin", "2.9.7") 49 | implementation("com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml", "2.9.7") 50 | implementation("com.fasterxml.jackson.core", "jackson-databind", "2.9.4") 51 | implementation("com.fasterxml.jackson.core", "jackson-annotations", "2.9.7") 52 | 53 | testImplementation(kotlin("test")) 54 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 55 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 56 | } 57 | 58 | open class CreateVersionProperties : DefaultTask() { 59 | @TaskAction 60 | fun create() { 61 | } 62 | } 63 | 64 | tasks { 65 | "createVersionProperties"(CreateVersionProperties::class) { 66 | dependsOn("processResources") 67 | doLast { 68 | val resDir = File(project.buildDir, "resources/main") 69 | resDir.mkdirs() 70 | FileWriter(File(resDir, "build.properties")).use { 71 | Properties().apply { 72 | this["version"] = project.version 73 | this["build-date"] = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'").format(Date()) 74 | }.store(it, "Generated by gradle build script") 75 | } 76 | } 77 | } 78 | "classes"{ 79 | dependsOn("createVersionProperties") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /graphmine-core/module.md: -------------------------------------------------------------------------------- 1 | # Module graphmine-core 2 | Main GraphMine code. All public classes and functions are stable API. 3 | 4 | ## Threading 5 | -------------------------------------------------------------------------------- /graphmine-core/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine-core" 2 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/HasLogger.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine 2 | 3 | import io.github.sof3.graphmine.i18n.I18n 4 | import org.apache.logging.log4j.Logger 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * This interface only exists to avoid the boilerplate of calling the logger. 26 | */ 27 | interface HasLogger { 28 | /** 29 | * The logger for this object 30 | */ 31 | val logger: Logger 32 | /** 33 | * @return the default locale to use 34 | */ 35 | val locale: String 36 | 37 | /** 38 | * Logs a DEBUG-level message 39 | */ 40 | fun debug(i18n: I18n) = logger.debug(i18n[locale]) 41 | 42 | /** 43 | * Logs a ERROR-level message 44 | */ 45 | fun error(i18n: I18n) = logger.error(i18n[locale]) 46 | 47 | /** 48 | * Logs a FATAL-level message 49 | */ 50 | fun fatal(i18n: I18n) = logger.fatal(i18n[locale]) 51 | 52 | /** 53 | * Logs a INFO-level message 54 | */ 55 | fun info(i18n: I18n) = logger.info(i18n[locale]) 56 | 57 | /** 58 | * Logs a WARN-level message 59 | */ 60 | fun warn(i18n: I18n) = logger.warn(i18n[locale]) 61 | } 62 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/Server.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine 2 | 3 | import io.github.sof3.graphmine.command.CommandMap 4 | import io.github.sof3.graphmine.command.impl.VersionCommand 5 | import io.github.sof3.graphmine.config.CoreConfig 6 | import io.github.sof3.graphmine.i18n.core.* 7 | import io.github.sof3.graphmine.i18n.core.CoreLang.Startup.LockedArg 8 | import io.github.sof3.graphmine.scope.BaseScope 9 | import io.github.sof3.graphmine.scope.Scope 10 | import org.apache.logging.log4j.LogManager 11 | import java.io.File 12 | import java.io.RandomAccessFile 13 | import java.nio.channels.FileLock 14 | 15 | /* 16 | * GraphMine 17 | * Copyright (C) 2018 SOFe 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU Affero General Public License as published 21 | * by the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU Affero General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU Affero General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | /** 34 | * The Server should be the object that links up different components of the server. 35 | * 36 | * To prevent cyclic dependency, instead of passing the Server object around, pass the objects that will actually be used, e.g. the logger, the config, etc. 37 | * 38 | * @property dataDir the server data directory 39 | * @property config the server config 40 | * @param initNano System.nanoTime() when the server start command was created 41 | * @param scope internal value, do not override 42 | */ 43 | class Server( 44 | val dataDir: File, 45 | val config: CoreConfig, 46 | 47 | initNano: Long = System.nanoTime(), 48 | 49 | private val scope: BaseScope = BaseScope(Server::class) 50 | ) : Scope by scope, HasLogger { 51 | /** 52 | * the logger used for the server scope. Plugins should use their own logger instead of this one. 53 | */ 54 | override val logger = LogManager.getLogger()!! 55 | /** 56 | * the default locale of the server. 57 | */ 58 | override val locale get() = config.language 59 | 60 | init { 61 | logger.debug("Checking config") 62 | config.checkAll() 63 | logger.debug("Loading en_US.lang.kts") 64 | loadCoreLang() 65 | logger.debug("Loaded en_US.lang.kts") 66 | 67 | lockDataDir() 68 | info(CoreLang.startup.version(CoreLang.Startup.VersionArg(version = VersionInfo.VERSION, ip = config.server.ip, port = config.server.port))) 69 | } 70 | 71 | private fun lockDataDir() { 72 | val file = File(dataDir, "server.lock") 73 | val ra = RandomAccessFile(file, "rw") 74 | val lock: FileLock? = ra.channel.tryLock() 75 | if (lock == null) { 76 | fatal(CoreLang.startup.locked(LockedArg(file))) 77 | throw RuntimeException("Server is already running") 78 | } 79 | } 80 | 81 | 82 | /** 83 | * List of [Runnable]s that are called when the server shuts down 84 | */ 85 | val shutdownHandlers = mutableListOf<() -> Unit>().apply { 86 | this += scope::dispose 87 | } 88 | 89 | 90 | /** 91 | * Stores the list of server-wide commands 92 | */ 93 | val commandMap = CommandMap().apply { 94 | // default commands 95 | addCommand(VersionCommand, this@Server) 96 | } 97 | 98 | 99 | init { 100 | info(CoreLang.startup.complete(CoreLang.Startup.CompleteArg(nano = System.nanoTime() - initNano))) 101 | } 102 | 103 | fun shutdown() { 104 | shutdownHandlers.forEach { it() } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/VersionInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.* 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Holds the static version information for this build 26 | */ 27 | object VersionInfo { 28 | /** 29 | * the API version from build.gradle 30 | */ 31 | val VERSION: String 32 | 33 | /** 34 | * the date the Gradle build for the API module was created 35 | */ 36 | val BUILD_DATE: Date 37 | 38 | init { 39 | val properties = javaClass.classLoader.getResourceAsStream("build.properties")?.use { 40 | Properties().apply { load(it) } 41 | } 42 | if (properties != null) { 43 | VERSION = properties["version"].toString() 44 | BUILD_DATE = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'").parse(properties["build-date"].toString()) 45 | } else { 46 | VERSION = "NO_GRADLE" 47 | BUILD_DATE = Date() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/client/Client.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.client 2 | 3 | import io.github.sof3.graphmine.feature.FeatureNode 4 | import io.github.sof3.graphmine.feature.FeatureNodeInstance 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | interface Client : FeatureNodeInstance { 25 | var attached: ClientAttachable 26 | 27 | companion object Node : FeatureNode 28 | 29 | override val node get() = Node 30 | } 31 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/client/ClientAttachable.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.client 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | interface ClientAttachable { 22 | } 23 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/Command.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.i18n.I18n 4 | import io.github.sof3.graphmine.i18n.i18n 5 | import io.github.sof3.graphmine.scope.Scope 6 | import io.github.sof3.graphmine.util.qualifier.Qualifier 7 | import io.github.sof3.graphmine.util.string.FormattedStringReader 8 | import kotlinx.coroutines.launch 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | /** 29 | * This class represents a command type. Each instance of Command should represent one registered command. 30 | * 31 | * Subclasses must initialize the [name] property. 32 | * 33 | * @param fn A lambda to initialize the command. 34 | * 35 | * @sample io.github.sof3.graphmine.command.impl.VersionCommand 36 | */ 37 | abstract class Command(fn: Command.() -> Unit) { 38 | /** 39 | * The scope that owns the command 40 | */ 41 | lateinit var scope: C 42 | internal set 43 | 44 | /** 45 | * The qualified name of the command. 46 | * 47 | * This property is intentionally not made as a delegation as string so as to remind command developers that the 48 | * name needs to be qualified. 49 | * 50 | * @see Qualifier 51 | */ 52 | lateinit var name: Qualifier 53 | /** 54 | * The description of the command, shown in action lists like /help. 55 | */ 56 | var description: I18n = "".i18n 57 | /** 58 | * The list of aliases 59 | */ 60 | var aliases = listOf() 61 | 62 | /** @suppress */ 63 | val overloads = mutableListOf() 64 | /** @suppress */ 65 | val handlers = mutableListOf) -> Unit>() 66 | 67 | /** 68 | * Executes the command. 69 | * 70 | * The command is run as a new coroutine in [the command's owner scope][scope]. 71 | * 72 | * @param reader the reader containing the command arguments. The reader pointer should start at the first character 73 | * of the command arguments. 74 | * @param sender the [CommandSender] that sent the command 75 | * @param receiver the object that accepts the command output and presents it to the sender. 76 | */ 77 | fun dispatch(reader: FormattedStringReader, sender: CommandSender, receiver: CommandReceiver) = scope.launch { 78 | try { 79 | for (overload in overloads) { 80 | val arg = overload.accept(reader) 81 | if (arg != null) { 82 | val executor = CommandExecutor(arg, sender, receiver, scope) 83 | handlers.forEach { it(executor) } 84 | return@launch 85 | } 86 | } 87 | throw WrongSyntaxException(name.toString(), overloads.map { it.i18n }) 88 | } catch (ex: CommandException) { 89 | receiver.receiveMessage(ex.i18n) 90 | } 91 | } 92 | 93 | /** 94 | * Adds a handler to the command. 95 | * 96 | * Type parameters [A] and [S] are explicitly specified to filter the overloads and sender types. 97 | * 98 | * If the command does not require any parameters, [EmptyOverload] can be used. 99 | * 100 | * @param A the restricted [Overload] type 101 | * @param S the restricted [CommandSender] type 102 | * 103 | * @param fn the handler function 104 | */ 105 | inline fun handle(crossinline fn: suspend CommandExecutor.() -> Unit) { 106 | overloads += RegisteredOverload(A::class) 107 | 108 | handlers += { 109 | val special = it.specialize() 110 | if (special != null) fn(special) 111 | } 112 | } 113 | 114 | init { 115 | fn() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/CommandExecutor.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.i18n.I18n 4 | import io.github.sof3.graphmine.scope.Scope 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * The `this` context of command handlers. 26 | * 27 | * Other modules are encouraged to create extension functions on this class for more convenient handling. 28 | * 29 | * @param A the type of overload. Can be any supertype of the actual overload. 30 | * @param S the type of command sender. Can be any supertype of the actual sender. 31 | * @param C the [scope][Scope] of the command execution. 32 | * 33 | * @property args the parsed overload instance 34 | * @property sender the sender that sent the command 35 | * @property receiver the object to send command output into 36 | * @property scope the scope that owns the command 37 | */ 38 | class CommandExecutor internal constructor( 39 | val args: A, 40 | val sender: S, 41 | val receiver: CommandReceiver, 42 | val scope: C 43 | ) { 44 | /** 45 | * Restricts the command executor to its subtypes. 46 | * 47 | * [SubA] and [SubS] are intentionally not forced to be subtypes of [A] and [S] because it is possible that [SubA] 48 | * is another interface that does not extend [A]. 49 | * 50 | * @param SubA the new expected overload type 51 | * @param SubS the new expected sender type 52 | * @return `this` if the types can be narrowed for this instance, `null` if types are incompatible 53 | */ 54 | @Suppress("UNCHECKED_CAST") 55 | inline fun specialize() = 56 | if (args is SubA && sender is SubS) this as CommandExecutor 57 | else null 58 | 59 | /** 60 | * Responds to the command sender. A shortcut for `receiver.receiveMessage`. 61 | * 62 | * @param message the i18nized message to be sent. 63 | */ 64 | fun respond(message: I18n) = receiver.receiveMessage(message) 65 | } 66 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/CommandMap.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.i18n.core.* 4 | import io.github.sof3.graphmine.i18n.core.CoreLang.Commands.Generic.NotFoundArg 5 | import io.github.sof3.graphmine.scope.Scope 6 | import io.github.sof3.graphmine.util.qualifier.QualifierMap 7 | import io.github.sof3.graphmine.util.string.FormattedStringReader 8 | 9 | /* 10 | * GraphMine 11 | * Copyright (C) 2018 SOFe 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published 15 | * by the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Affero General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | /** 28 | * Stores a list of commands. This class can also be instantiated at individual commands to provide a sub-command list. 29 | */ 30 | class CommandMap { 31 | private val map = QualifierMap>() 32 | 33 | /** 34 | * Adds a command to the map. 35 | * 36 | * @param command the command to be added 37 | * @param scope the [scope][Scope] that owns the command 38 | */ 39 | fun addCommand(command: Command, scope: C) = apply { 40 | command.scope = scope 41 | map[command.name] = command 42 | } 43 | 44 | /** 45 | * Dispatches a command. This function should only be used when the command name is unknown. 46 | * 47 | * @param 48 | */ 49 | fun dispatch(string: String, by: CommandSender, to: CommandReceiver) { 50 | val reader = FormattedStringReader(string) 51 | val (name) = reader.nextDelimiter() ?: return 52 | val command = map[name] 53 | if (command != null) { 54 | command.dispatch(reader, by, to) 55 | } else { 56 | to.receiveMessage(CoreLang.commands.generic.notFound(NotFoundArg(name))) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/CommandReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.i18n.I18n 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Accepts output from a command. 25 | */ 26 | interface CommandReceiver { 27 | /** 28 | * Receives a message from the command output and presents it to the client 29 | */ 30 | fun receiveMessage(message: I18n) 31 | 32 | // TODO add receiver-specific configurations here, similar to how tty detection works on Linux 33 | } 34 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/CommandSender.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents an object that sends the command. 23 | */ 24 | interface CommandSender 25 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/EmptyOverload.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * An empty overload, reused for commands that do not require arguments. 23 | */ 24 | class EmptyOverload : Overload() 25 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/Overload.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.client.Client 4 | import io.github.sof3.graphmine.command.args.* 5 | import kotlin.reflect.KClass 6 | 7 | /* 8 | * GraphMine 9 | * Copyright (C) 2018 SOFe 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published 13 | * by the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | /** 26 | * Defines the parameters of a command. 27 | * 28 | * Commands that have parameters should create classes extending Overload. The subclass should contain backing 29 | * properties delegated to one of the [CommandArg] factory methods like [string], [integer], etc. 30 | * 31 | * If another module wants to implement its own [CommandArg] types, it can create extension functions on [Overload] that 32 | * constructs the CommandArg, calls the [addArg] function and returns the new CommandArg instance. 33 | */ 34 | abstract class Overload { 35 | internal val args = mutableListOf>() 36 | 37 | /** 38 | * @see StringArg 39 | */ 40 | fun string(): CommandArg = addArg(StringArg()) 41 | 42 | /** 43 | * @see IntegerArg 44 | */ 45 | fun integer(): CommandArg = addArg(IntegerArg()) 46 | 47 | /** 48 | * @see NumberArg 49 | */ 50 | fun number(): CommandArg = addArg(NumberArg()) 51 | 52 | /** 53 | * @see ClientArg 54 | */ 55 | fun client(): CommandArg = addArg(ClientArg()) 56 | 57 | /** 58 | * @see EnumArg 59 | */ 60 | inline fun > enum() = enum(E::class) 61 | 62 | /** 63 | * @see EnumArg 64 | */ 65 | fun > enum(enumClass: KClass): CommandArg = addArg(EnumArg(enumClass)) 66 | 67 | /** 68 | * @see RawTextArg 69 | */ 70 | fun rawText(): CommandArg = addArg(RawTextArg()) 71 | 72 | /** 73 | * CommandArg factory extension functions must call this function before returning the argument 74 | */ 75 | fun addArg(arg: CommandArg): CommandArg { 76 | args += arg 77 | return arg 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/RegisteredOverload.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.command.args.CommandArg 4 | import io.github.sof3.graphmine.i18n.I18n 5 | import io.github.sof3.graphmine.i18n.I18nable 6 | import io.github.sof3.graphmine.util.string.FormattedStringReader 7 | import kotlin.reflect.KClass 8 | import kotlin.reflect.full.createInstance 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | /** 29 | * A factory that creates a new instance of a known command overload for each command execution. 30 | */ 31 | class RegisteredOverload(private val klass: KClass) : I18nable { 32 | override val i18n: I18n 33 | get() = TODO("not implemented") 34 | 35 | /** 36 | * The arguments applicable to this overload 37 | */ 38 | val args: List> 39 | 40 | init { 41 | val instance = klass.createInstance() 42 | args = instance.args 43 | } 44 | 45 | /** 46 | * Accepts a command and tries to parse it. 47 | */ 48 | fun accept(parser: FormattedStringReader): Overload? { 49 | val command = klass.createInstance() 50 | for (arg in command.args) { 51 | if (!arg.accept(parser)) return null // TODO use parser.exec{} 52 | } 53 | 54 | return command 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/args/ClientArg.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.args 2 | 3 | import io.github.sof3.graphmine.client.Client 4 | import io.github.sof3.graphmine.util.string.FormattedStringReader 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Accepts a client name 26 | */ 27 | class ClientArg : CommandArg() { 28 | override fun parse(parser: FormattedStringReader): Client? { 29 | TODO("not implemented") 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/args/CommandArg.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.args 2 | 3 | import io.github.sof3.graphmine.command.Overload 4 | import io.github.sof3.graphmine.util.DelegateProvider 5 | import io.github.sof3.graphmine.util.string.FormattedStringReader 6 | import kotlin.properties.ReadOnlyProperty 7 | import kotlin.reflect.KProperty 8 | 9 | /* 10 | * GraphMine 11 | * Copyright (C) 2018 SOFe 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published 15 | * by the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Affero General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | /** 28 | * Superclass of a command argument. 29 | */ 30 | abstract class CommandArg : DelegateProvider { 31 | /** 32 | * Reads the next value in the parser into the value for the argument. 33 | * @return whether the value is valid for this argument 34 | */ 35 | fun accept(parser: FormattedStringReader): Boolean { 36 | value = parse(parser) ?: return false 37 | return true 38 | } 39 | 40 | /** 41 | * Reads the next value in the parser into the value for the argument 42 | * 43 | * @return the parsed value, or `null` if it is missing or cannot be parsed 44 | */ 45 | abstract fun parse(parser: FormattedStringReader): T? 46 | 47 | 48 | override fun provideDelegate(thisRef: Overload, property: KProperty<*>) = object : ReadOnlyProperty { 49 | override fun getValue(thisRef: Overload, property: KProperty<*>) = value 50 | } 51 | 52 | /** 53 | * Whether the command arg can be skipped 54 | */ 55 | var optional = false 56 | /** 57 | * Sets the default value of the command. Implicitly sets [optional] to `true` (even if the set value is `null`) 58 | */ 59 | var default: T? = null 60 | set(value) { 61 | field = value 62 | optional = true 63 | } 64 | 65 | /** 66 | * Sets the default value of the command. Implicitly sets [optional] to `true` (even if the set value is `null`) 67 | */ 68 | fun default(value: T) = apply { default = value } 69 | 70 | /** 71 | * The received value of the argument 72 | */ 73 | lateinit var value: T 74 | protected set 75 | } 76 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/args/EnumArg.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.args 2 | 3 | import io.github.sof3.graphmine.util.string.FormattedStringReader 4 | import kotlin.reflect.KClass 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Accepts a value that names one of the constants in the enum class 26 | */ 27 | class EnumArg>(private val enumClass: KClass) : CommandArg() { 28 | /** 29 | * Match enum constant names case-insensitively. 30 | * 31 | * This may cause problems if multiple enum constants have the same name with different cases. (However this is not 32 | * encouraged in Java coding style anyway) 33 | */ 34 | var ignoreCase = true 35 | 36 | /** 37 | * The policy for comparing symbols in enum names 38 | */ 39 | var symbolPolicy = SymbolPolicy.ANY 40 | 41 | override fun parse(parser: FormattedStringReader): E? { 42 | val name = parser.nextDelimiter()?.content ?: return null 43 | val given = symbolPolicy.fn(name) 44 | for (enum in enumClass.java.enumConstants!!) { 45 | val actual = symbolPolicy.fn(enum.name) 46 | if (actual.equals(given, ignoreCase)) return enum 47 | } 48 | return null 49 | } 50 | 51 | /** 52 | * The policy to use for comparing symbols in enum names 53 | */ 54 | enum class SymbolPolicy(internal val fn: (String) -> String) { 55 | /** 56 | * Compares names as-is 57 | */ 58 | AS_IS({ it }), 59 | /** 60 | * Any strings of one or more non-alphanumeric symbols are considered the same, and leading and trailing symbols are always 61 | * ignored 62 | */ 63 | ANY({ it.replace(Regex("[^A-Za-z0-9]+"), "_").trim('_') }), 64 | /** 65 | * Any non-alphanumeric symbols are ignored 66 | */ 67 | IGNORE({ it.replace(Regex("[^A-Za-z0-9]+"), "") }), 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/args/number.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.args 2 | 3 | import io.github.sof3.graphmine.util.string.FormattedStringReader 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Accepts integer arguments. 25 | */ 26 | class IntegerArg : CommandArg() { 27 | override fun parse(parser: FormattedStringReader) = try { 28 | parser.nextDelimiter()?.content?.toInt() 29 | } catch (e: NumberFormatException) { 30 | null 31 | } 32 | } 33 | 34 | /** 35 | * Accepts any finite real-numeric arguments. 36 | */ 37 | class NumberArg : CommandArg() { 38 | override fun parse(parser: FormattedStringReader) = try { 39 | parser.nextDelimiter()?.content?.toDouble() 40 | } catch (e: NumberFormatException) { 41 | null 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/args/string.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.args 2 | 3 | import io.github.sof3.graphmine.util.string.FormattedStringReader 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Accepts the next value in the line. Multi-word arguments can be quoted by `""` or their spaces escaped by `\` before. 25 | */ 26 | class StringArg : CommandArg() { 27 | override fun parse(parser: FormattedStringReader) = parser.nextQuoted()?.inner 28 | } 29 | 30 | /** 31 | * Accepts the rest of the whole line literally 32 | */ 33 | class RawTextArg : CommandArg() { 34 | override fun parse(parser: FormattedStringReader) = parser.remaining 35 | } 36 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/exception.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command 2 | 3 | import io.github.sof3.graphmine.i18n.I18n 4 | import io.github.sof3.graphmine.i18n.I18nable 5 | import io.github.sof3.graphmine.i18n.core.* 6 | import io.github.sof3.graphmine.i18n.core.CoreLang.Commands.Generic.WrongSyntaxArg 7 | 8 | /* 9 | * GraphMine 10 | * Copyright (C) 2018 SOFe 11 | * 12 | * This program is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU Affero General Public License as published 14 | * by the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | /** 27 | * Represents any user-friendly errors reported from commands. 28 | * 29 | * Commands are not required to use this exception. It is only here for more convenient control flow (e.g. can throw 30 | * exception directly instead of calling response() then return) 31 | */ 32 | abstract class CommandException : Exception(), I18nable { 33 | /** 34 | * A user-friendly [I18n] describing the problem 35 | */ 36 | abstract override val i18n: I18n 37 | } 38 | 39 | /** 40 | * Thrown when a command with wrong syntax is sent. 41 | * @param name command name 42 | * @param syntax list of syntax possible for this command 43 | */ 44 | class WrongSyntaxException(private val name: String, private val syntax: List) : CommandException() { 45 | override val i18n get() = CoreLang.commands.generic.wrongSyntax(WrongSyntaxArg(name, syntax)) 46 | } 47 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/impl/HelpCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.impl 2 | 3 | import io.github.sof3.graphmine.Server 4 | import io.github.sof3.graphmine.command.Command 5 | import io.github.sof3.graphmine.command.CommandSender 6 | import io.github.sof3.graphmine.command.Overload 7 | import io.github.sof3.graphmine.i18n.i18n 8 | import io.github.sof3.graphmine.util.qualifier.qualify 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | internal object HelpCommand : Command({ 29 | name = "graphmine.help".qualify() 30 | 31 | description = "Shows help information".i18n // TODO localize 32 | 33 | aliases += "h" 34 | aliases += "?" 35 | 36 | class ByPage : Overload() { 37 | val page by integer().default(1) 38 | } 39 | handle { 40 | TODO("implement") 41 | } 42 | 43 | class ByName : Overload() { 44 | val name by string() 45 | } 46 | handle { 47 | TODO("implement") 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/impl/SayCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.impl 2 | 3 | import io.github.sof3.graphmine.Server 4 | import io.github.sof3.graphmine.command.Command 5 | import io.github.sof3.graphmine.command.CommandSender 6 | import io.github.sof3.graphmine.command.Overload 7 | import io.github.sof3.graphmine.i18n.i18n 8 | import io.github.sof3.graphmine.util.qualifier.qualify 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | internal class SayCommand : Overload() { 29 | companion object : Command({ 30 | name = "graphmine.say".qualify() 31 | aliases += "announce" 32 | 33 | description = "".i18n // TODO 34 | 35 | handle { 36 | TODO("Implement") 37 | } 38 | }) 39 | 40 | val message by rawText() 41 | } 42 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/impl/VersionCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.command.impl 2 | 3 | import io.github.sof3.graphmine.Server 4 | import io.github.sof3.graphmine.VersionInfo 5 | import io.github.sof3.graphmine.command.Command 6 | import io.github.sof3.graphmine.command.CommandSender 7 | import io.github.sof3.graphmine.command.EmptyOverload 8 | import io.github.sof3.graphmine.i18n.core.* 9 | import io.github.sof3.graphmine.i18n.core.CoreLang.Commands.Version.VersionResponse 10 | import io.github.sof3.graphmine.util.qualifier.qualify 11 | 12 | /* 13 | * GraphMine 14 | * Copyright (C) 2018 SOFe 15 | * 16 | * This program is free software: you can redistribute it and/or modify 17 | * it under the terms of the GNU Affero General Public License as published 18 | * by the Free Software Foundation, either version 3 of the License, or 19 | * (at your option) any later version. 20 | * 21 | * This program is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU Affero General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Affero General Public License 27 | * along with this program. If not, see . 28 | */ 29 | 30 | /** 31 | * Implements the /version command 32 | */ 33 | object VersionCommand : Command({ 34 | name = "graphmine.version".qualify() 35 | aliases += "v" 36 | 37 | description = CoreLang.commands.version.description(Unit) 38 | 39 | handle { 40 | respond(CoreLang.commands.version.response(VersionResponse(VersionInfo.VERSION))) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/impl/package.md: -------------------------------------------------------------------------------- 1 | # Package io.github.sof3.graphmine.command.impl 2 | This package implements the default commands in GraphMine. Except for [VersionCommand] that serves as the example command, all other commands are internal. 3 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/command/package.md: -------------------------------------------------------------------------------- 1 | # Package io.github.sof3.graphmine.command 2 | This package contains the API for declaring commands. 3 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/ConfigEntryDelegate.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | import kotlin.properties.ReadWriteProperty 4 | import kotlin.reflect.KProperty 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * The delegate class to a config entry 26 | * @see ConfigSpec.entry 27 | */ 28 | class ConfigEntryDelegate internal constructor(internal var validator: (T) -> String?) : ReadWriteProperty { 29 | internal lateinit var name: () -> String 30 | internal var set = false 31 | internal var value: T? = null 32 | 33 | /** 34 | * Validates the config value. 35 | * 36 | * This method assumes that the value is sufficiently set. 37 | */ 38 | internal fun validate() = validator(value!!) 39 | 40 | override operator fun getValue(thisRef: ConfigSpec, property: KProperty<*>) = value!! 41 | 42 | override operator fun setValue(thisRef: ConfigSpec, property: KProperty<*>, value: T) { 43 | set = true 44 | val err = validator(value) 45 | if (err != null) throw IllegalArgumentException("Config entry ${name()} is missing") 46 | this.value = value 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/ConfigGroupSpec.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Superclass of type specifications of groups in a [ConfigSpec] 23 | * 24 | * @see ConfigSpec.group 25 | */ 26 | abstract class ConfigGroupSpec> : ConfigSpec() { 27 | internal lateinit var parent: ConfigSpec 28 | internal lateinit var groupName: String 29 | 30 | override val path get() = parent.path + groupName 31 | 32 | /** 33 | * @see Any.run 34 | */ 35 | @Suppress("UNCHECKED_CAST") 36 | operator fun invoke(fn: Self.() -> Unit) = (this as Self).run(fn) 37 | } 38 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/ConfigSpec.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | import io.github.sof3.graphmine.util.Ref 4 | import io.github.sof3.graphmine.util.notNull 5 | import kotlin.reflect.KProperty 6 | 7 | /* 8 | * GraphMine 9 | * Copyright (C) 2018 SOFe 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published 13 | * by the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | /** 26 | * Superclass for type specifications of .kts config files 27 | */ 28 | abstract class ConfigSpec { 29 | internal open var entries = mutableMapOf>() 30 | internal val groups = mutableMapOf>() 31 | internal open val path = emptyList() 32 | 33 | /** 34 | * Declares a required entry 35 | * @param T type of entry 36 | * @param validator returns an error string if the config value is incorrect, `null` otherwise 37 | */ 38 | protected fun entry(validator: (T) -> String? = { null }) = SimpleConfigEntry(validator) 39 | 40 | /** 41 | * Declares an entry with a default value 42 | * @param T type of entry 43 | * @param default default value to use if the config does not specify this entry. 44 | * @param validator returns an error string if the config value is incorrect, `null` otherwise 45 | * @sample CoreConfig.language 46 | */ 47 | protected fun entry(default: T, validator: (T) -> String? = { null }): SimpleConfigEntry = entry(validator).apply { 48 | delegate.set = true 49 | delegate.value = default 50 | } 51 | 52 | /** 53 | * Used in property delegation. Config groups are included in the parent group by property delegation. 54 | * @param G the actual group spec class 55 | * @see ConfigSpec.group 56 | */ 57 | protected inner class GroupDelegate>(private val group: G) { 58 | /** 59 | * Provides delegation to the backing group. 60 | */ 61 | operator fun provideDelegate(thisRef: ConfigSpec, property: KProperty<*>): Ref { 62 | groups[property.name] = group 63 | group.groupName = property.name 64 | return Ref(group) 65 | } 66 | } 67 | 68 | /** 69 | * Config groups should be included by delegation to this call 70 | * @param group the config to be added 71 | * @return the delegate that can be included by property delegation 72 | * @sample CoreConfig.server 73 | */ 74 | protected fun > group(group: G) = GroupDelegate(group.also { 75 | group.parent = this 76 | entries.putAll(group.entries) 77 | group.entries = entries 78 | }) 79 | 80 | /** 81 | * Validates the whole config 82 | * @throws ConfigMissingException if a config value is not set 83 | * @throws ConfigValidationException if a config value is invalid 84 | */ 85 | fun checkAll() { 86 | for ((_, entry) in entries) { 87 | if (!entry.set) throw ConfigMissingException("The config entry ${entry.name} is required but is not set") 88 | entry.validate().notNull { err -> 89 | throw ConfigValidationException("The config entry ${entry.name} is invalid: $err") 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/SimpleConfigEntry.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | import kotlin.reflect.KProperty 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * @see ConfigSpec.entry 25 | */ 26 | class SimpleConfigEntry internal constructor(validator: (T) -> String?) { 27 | internal val delegate = ConfigEntryDelegate(validator) 28 | 29 | operator fun provideDelegate(thisRef: ConfigSpec, property: KProperty<*>): ConfigEntryDelegate { 30 | thisRef.entries[property.name] = delegate 31 | delegate.name = { (thisRef.path + property.name).joinToString(".") } 32 | return delegate 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/exception.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Thrown when a config has some problems 23 | * @param message the detailed exception message 24 | */ 25 | open class ConfigException(message: String) : RuntimeException(message) 26 | 27 | /** 28 | * Thrown when a required entry is missing from a config 29 | * @param message the detailed exception message 30 | */ 31 | open class ConfigMissingException(message: String) : ConfigException(message) 32 | 33 | /** 34 | * Thrown when an entry from a config is invalid 35 | * @param message the detailed exception message 36 | */ 37 | open class ConfigValidationException(message: String) : ConfigException(message) 38 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/config/spec.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.config 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Model for the server config.yml 23 | */ 24 | class CoreConfig(fn: CoreConfig.() -> Unit) : ConfigSpec() { 25 | companion object { 26 | const val VERSION = 1 27 | } 28 | 29 | /** 30 | * The default language to use 31 | */ 32 | var language by entry("en_US") 33 | 34 | /** 35 | * Settings for the server 36 | */ 37 | val server by group(ServerConfig()) 38 | 39 | init { 40 | apply(fn) 41 | } 42 | } 43 | 44 | /** 45 | * Settings for the server 46 | */ 47 | class ServerConfig : ConfigGroupSpec() { 48 | /** 49 | * The IP to listen on 50 | * 51 | * Keep this as `0.0.0.0` unless the server has multiple networks. 52 | */ 53 | var ip by entry("0.0.0.0") 54 | /** 55 | * The port to listen on 56 | */ 57 | var port by entry(19132) { if (it !in 0..65535) "Port must be between 0 and 65535" else null } 58 | } 59 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/Entity.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * An entity is a mobile object that exists in a world 23 | * 24 | * An entity follows the ICES structure: 25 | * - a set of Internal viewers that sees what the entity sees 26 | * - a set of Controllers that controls the behaviour of the entity 27 | * - a nullable External viewer that exposes the entity to other viewers 28 | */ 29 | class Entity { 30 | /** 31 | * The list of viewers that can view the world from the entity's perspective, i.e. using the entity as a camera 32 | */ 33 | val internalViewers = mutableSetOf() 34 | /** 35 | * The list of objects that control the entity's motion and behaviour. They may gain or lose control on the entity, 36 | * or co-work with other controllers to control the entity. 37 | */ 38 | val controllers = mutableSetOf() 39 | /** 40 | * The adapter that determines how this entity looks to other viewers, e.g. which model to look like. 41 | */ 42 | var externalViewer: ExternalViewer? = null 43 | /** 44 | * Stores the entity-specific information. Can be directly written to or read from disk. 45 | */ 46 | var state: EntityState? = null 47 | } 48 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/EntityController.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Controls the actions of an entity. 23 | * 24 | * Each instance of EntityController should only be applied on one entity. 25 | */ 26 | interface EntityController { 27 | /** 28 | * Invoked when the controller is added to the entity 29 | */ 30 | fun onGainControl() 31 | 32 | /** 33 | * Invoked when the controller is removed from the entity 34 | */ 35 | fun onLoseControl() 36 | } 37 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/EntityState.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | import io.github.sof3.graphmine.util.math.Vector3 4 | import io.github.sof3.graphmine.world.World 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Contains the savable data of an entity 26 | * @property world the world that the entity belongs to 27 | * @param vector the entity's initial position 28 | * @property vector the position of the entity 29 | */ 30 | open class EntityState( 31 | var world: World, 32 | var vector: Vector3 33 | ) 34 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/EntityViewEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Events that an entity's view changes. This may be due to changes in entity external views, blocks in world views, or 23 | * other reasons. 24 | */ 25 | interface EntityViewEvent 26 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/ExternalView.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents one of the appearances of an entity. 23 | */ 24 | interface ExternalView 25 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/ExternalViewer.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | import io.github.sof3.graphmine.world.WorldView 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Provides an adapter that determines how this entity looks to other entities. 25 | */ 26 | interface ExternalViewer { 27 | /** 28 | * 29 | * @param worldView the world view that views the entity 30 | */ 31 | fun provideView(worldView: WorldView): ExternalView 32 | } 33 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/entity/InternalViewer.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.entity 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Acts like the receiver to the camera signals of an entity 23 | */ 24 | interface InternalViewer { 25 | /** 26 | * Handles a change in what the entity sees. 27 | */ 28 | fun receiveEvent(event: EntityViewEvent) 29 | } 30 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/feature/FeatureEdge.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.feature 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents the implementation of a feature. 23 | * 24 | * FeatureGraph.handle() provides a convenience inline function for implementing this. 25 | * @see FeatureGraph.handle 26 | */ 27 | interface FeatureEdge< 28 | Node1 : FeatureNode, 29 | Node2 : FeatureNode, 30 | Inst1 : FeatureNodeInstance, 31 | Inst2 : FeatureNodeInstance 32 | > { 33 | /** 34 | * Represents one of the endpoint FeatureNodes. Swapping node1 and node2 does not matter. 35 | */ 36 | val node1: Node1 37 | /** 38 | * Represents one of the endpoint FeatureNodes. Swapping node1 and node2 does not matter. 39 | */ 40 | val node2: Node2 41 | 42 | /** 43 | * The implementation to handle the FeatureEvent. 44 | */ 45 | fun handle(inst1: Inst1, inst2: Inst2, event: FeatureEvent) 46 | } 47 | 48 | /** 49 | * Convenience implementation of self-looping FeatureEdge 50 | */ 51 | interface SingleFeatureEdge< 52 | Node : FeatureNode, 53 | Inst : FeatureNodeInstance> 54 | : FeatureEdge { 55 | /** 56 | * the node that the edge incidents with 57 | */ 58 | val node: Node 59 | override val node1 get() = node 60 | override val node2 get() = node 61 | 62 | override fun handle(inst1: Inst, inst2: Inst, event: FeatureEvent) { 63 | assert(inst1 === inst2) 64 | return handle(inst1, event) 65 | } 66 | 67 | /** 68 | * The implementation to handle the FeatureEvent. 69 | */ 70 | fun handle(inst: Inst, event: FeatureEvent) 71 | } 72 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/feature/FeatureEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.feature 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents an event on some node(s). Implementations shall contain mutable and immutable properties for FeatureEdges 23 | * to read and modify, except the FeatureNodeInstances do not need to be in the event since 24 | * they are passed to the event in a different way. 25 | */ 26 | interface FeatureEvent 27 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/feature/FeatureGraph.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.feature 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * The Feature Graph is the main registry of features in the server. It is an undirected multi-graph that allows 23 | * FeatureEdges (the features themselves) to handle interaction events (FeatureEvent) between FeatureNodes (the objects 24 | * of features, e.g. entities, blocks, or abstract concepts like commands), or self-loop edges that handle events on 25 | * a single object (e.g. player join). 26 | */ 27 | class FeatureGraph { 28 | /** 29 | * the features registered on this server 30 | */ 31 | val edges: MutableMap, 32 | MutableMap, 33 | MutableSet>>> = hashMapOf() 34 | 35 | /** 36 | * Registers a FeatureNode so that edges can be added upon it 37 | */ 38 | fun addNode(node: FeatureNode<*, *>) { 39 | edges[node] = mutableMapOf() 40 | } 41 | 42 | /** 43 | * Registers a FeatureEdge 44 | */ 45 | fun addEdge(edge: FeatureEdge< 46 | out FeatureNode<*, *>, 47 | out FeatureNode<*, *>, 48 | out FeatureNodeInstance<*, *>, 49 | out FeatureNodeInstance<*, *>> 50 | ) { 51 | edges[edge.node1]!![edge.node2]!!.add(edge) 52 | edges[edge.node2]!![edge.node1]!!.add(edge) 53 | } 54 | 55 | /** 56 | * Dispatch a single-node event 57 | */ 58 | fun , Inst2 : FeatureNodeInstance> 59 | dispatch(inst1: Inst1, inst2: Inst2, event: FeatureEvent) { 60 | getEdges(inst1, inst1)?.forEach { it.handle(inst1, inst1, event) } 61 | getEdges(inst1, inst2)?.forEach { it.handle(inst1, inst2, event) } 62 | // getEdges(inst2, inst1)?.forEach { it.handle(inst2, inst1, event) } // edges should have been registered bidirectionally 63 | getEdges(inst2, inst2)?.forEach { it.handle(inst2, inst2, event) } 64 | } 65 | 66 | /** 67 | * Dispatch a two-node event 68 | */ 69 | fun > dispatch(inst: Inst, event: FeatureEvent) { 70 | getEdges(inst, inst)?.forEach { it.handle(inst, inst, event) } 71 | } 72 | 73 | @Suppress("UNCHECKED_CAST") 74 | private fun , Inst2 : FeatureNodeInstance> getEdges(inst1: Inst1, inst2: Inst2): 75 | MutableSet, out FeatureNode<*, Inst2>, Inst1, Inst2>>? = 76 | edges[inst1.node]!![inst2.node] as MutableSet, out FeatureNode<*, Inst2>, Inst1, Inst2>>? 77 | } 78 | 79 | /** 80 | * A convenient wrapper for FeatureGraph.addEdge and SingleFeatureEdge construction (for one node) 81 | */ 82 | inline fun , Inst : FeatureNodeInstance, reified Ev : FeatureEvent> 83 | FeatureGraph.handle(node: Node, crossinline fn: (inst: Inst, event: Ev) -> Unit) { 84 | addEdge(SingleFeatureHandler(node) { inst, event -> if (event is Ev) fn(inst, event) }) 85 | } 86 | 87 | /** @internal */ 88 | class SingleFeatureHandler, Inst : FeatureNodeInstance>( 89 | override val node: Node, 90 | private val fn: (inst: Inst, event: FeatureEvent) -> Unit 91 | ) : SingleFeatureEdge { 92 | override fun handle(inst: Inst, event: FeatureEvent) = fn(inst, event) 93 | } 94 | 95 | /** 96 | * A convenient wrapper for FeatureGraph.addEdge and FeatureEdge construction (for two nodes) 97 | */ 98 | inline fun , Inst1 : FeatureNodeInstance, 99 | Node2 : FeatureNode, Inst2 : FeatureNodeInstance, 100 | reified Ev : FeatureEvent> 101 | FeatureGraph.handle(node1: Node1, node2: Node2, crossinline fn: (inst1: Inst1, inst2: Inst2, event: Ev) -> Unit) { 102 | addEdge(DoubleFeatureHandler(node1, node2) { inst1, inst2, event -> if (event is Ev) fn(inst1, inst2, event) }) 103 | } 104 | 105 | /** @internal */ 106 | class DoubleFeatureHandler, Inst1 : FeatureNodeInstance, 107 | Node2 : FeatureNode, Inst2 : FeatureNodeInstance>( 108 | override val node1: Node1, 109 | override val node2: Node2, 110 | private val fn: (Inst1, Inst2, FeatureEvent) -> Unit 111 | ) : FeatureEdge { 112 | override fun handle(inst1: Inst1, inst2: Inst2, event: FeatureEvent) = fn(inst1, inst2, event) 113 | } 114 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/feature/FeatureNode.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.feature 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents a concrete or abstract concept that can interact with itself or other FeatureNodes, such as an entity, 23 | * a block, a client or a command. 24 | * 25 | * If the implementation is a singleton, it is recommended that FeatureNode be implemented in the companion object of 26 | * the corresponding FeatureNodeInstance. 27 | * 28 | * @param Self the actual class implementing FeatureNode. It is not mandatory to pass the actual instance type, but it 29 | * is recommended if possible to improve type prediction 30 | * @param Inst the FeatureNodeInstance class corresponding to Self. 31 | */ 32 | interface FeatureNode, Inst : FeatureNodeInstance> 33 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/feature/FeatureNodeInstance.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.feature 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents an instance of FeatureNode. The FeatureNode represents the type in general, while FeatureNodeInstance 23 | * represents each instance of the type. It is valid for FeatureNodeInstance to be singleton or even same as the 24 | * FeatureNode. For example, each instance of the Client class represents one client, while the singleton Client.Node 25 | * companion object represents the client type. 26 | */ 27 | interface FeatureNodeInstance, Node : FeatureNode> { 28 | /** 29 | * the corresponding node for the instance. 30 | */ 31 | val node: Node 32 | } 33 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/scope/BaseScope.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.scope 2 | 3 | import kotlinx.coroutines.Job 4 | import kotlin.coroutines.CoroutineContext 5 | import kotlin.reflect.KClass 6 | 7 | /** 8 | * Basic implementation of BaseScope 9 | * 10 | * @property name the scope name 11 | * @property parent the parent scope (if any) 12 | */ 13 | open class BaseScope(final override val name: String, val parent: BaseScope? = null) : Scope { 14 | /** 15 | * @param klass the class to be used in the scope name 16 | * @param name the scope name 17 | * @param parent the parent scope (if any) 18 | */ 19 | constructor(klass: KClass, name: String, parent: BaseScope? = null) : this("${klass.qualifiedName}:$name", parent) 20 | 21 | /** 22 | * @param klass the class to be used in the scope name 23 | * @param parent the parent scope (if any) 24 | */ 25 | constructor(klass: KClass, parent: BaseScope? = null) : this(klass.qualifiedName!!, parent) 26 | 27 | override var isDisposed = false 28 | protected set 29 | 30 | private var disposalHooks = mutableListOf<() -> Unit>() 31 | 32 | override val coroutineContext: CoroutineContext 33 | get() = if (parent != null) parent.coroutineContext + Job() else Job() 34 | 35 | @Suppress("OVERRIDE_BY_INLINE") 36 | override fun addOnDispose(fn: () -> Unit) { 37 | disposalHooks.add(fn) 38 | } 39 | 40 | /** 41 | * Marks the scope as disposed. 42 | */ 43 | open fun dispose() { 44 | isDisposed = true 45 | 46 | disposalHooks.forEach { it() } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/scope/Scope.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.scope 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * A Scope represents some period of persistence. Features are enabled only during the scope is not disposed. Builtin 25 | * scopes include: 26 | * 27 | * - Server scope: Until the server is stopped 28 | * - Player scope: Until the player leaves the server 29 | * - Plugin scope: While the plugin is enabled 30 | * - World scope: While the world is loaded 31 | * - World partition scope: While the world partition is loaded 32 | * 33 | * Plugins may create their own scopes too, e.g. a Hunger Games plugin may create a scope that lasts during the Hunger Games tournament 34 | * 35 | * A scope is also a coroutine scope. Coroutines may be started from the scope. 36 | */ 37 | interface Scope : CoroutineScope { 38 | val name: String 39 | 40 | /** 41 | * whether the scope has been disposed 42 | */ 43 | val isDisposed: Boolean 44 | 45 | /** 46 | * Adds an action to execute when the scope is disposed 47 | */ 48 | fun addOnDispose(fn: () -> Unit) 49 | } 50 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/Block.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | import io.github.sof3.graphmine.feature.FeatureNode 4 | import io.github.sof3.graphmine.feature.FeatureNodeInstance 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Represents a block type. 26 | * 27 | * This class is only used as a wrapper for the block ID along with FeatureNode identification. In other words, it can 28 | * be said that this class only exists for documentation purpose. 29 | */ 30 | data class Block( 31 | /** 32 | * The block ID. This may be changed in the future. 33 | */ 34 | val id: Int 35 | ) : FeatureNode 36 | 37 | /** 38 | * Represents a block type at a certain location. The existence persistence of this block is irrelevant to whether the 39 | * block is really at the location, ever at the location or has been removed. This is a pure value class. 40 | */ 41 | data class BlockInstance( 42 | /** 43 | * the block type 44 | */ 45 | val block: Block, 46 | /** 47 | * the expected location of the block 48 | */ 49 | val location: BlockLocation 50 | ) : FeatureNodeInstance { 51 | override val node get() = block 52 | } 53 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/BlockLocation.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | import io.github.sof3.graphmine.util.math.IntVector3 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * A unique identifier for an integer location. 25 | * 26 | * Out-of-bounds block locations are intentionally allowed, because normally out-of-bounds locations like negative 27 | * locations may be valid for certain world formats. While they are not valid for the client, the World implementation 28 | * is responsible for translating coordinates to something the client can see. This should not interfere with plugins' 29 | * ability to interpret the world in a sensible manner. 30 | */ 31 | data class BlockLocation( 32 | /** 33 | * The positional vector of the location 34 | */ 35 | val vector: IntVector3, 36 | /** 37 | * The world that the location is in 38 | */ 39 | val world: World 40 | ) 41 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/Blocks.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | /** 4 | * The list of default Block instances. Plugins are not expected to add custom block types to this list 5 | */ 6 | object Blocks { 7 | /***/ 8 | val AIR = Block(0) 9 | /***/ 10 | val STONE = Block(1) 11 | // TODO generate more block types 12 | } 13 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/Location.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | import io.github.sof3.graphmine.util.math.Vector3 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Represents a point in the 3D space of a world. 25 | */ 26 | data class Location( 27 | /** 28 | * The position vector for the location 29 | */ 30 | val vector: Vector3, 31 | /** 32 | * The world that the location is in 33 | */ 34 | val world: World 35 | ) 36 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/World.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Each world has its set of 3D space. Usually, this is implemented as one saved map, but plugins may create virtual 23 | * worlds, remote worlds or anything that features the characteristics as required by the interface. 24 | * 25 | * As the World object itself is used as the identifier, implementations should not override the equals() and hashCode() 26 | * methods. 27 | */ 28 | interface World { 29 | 30 | } 31 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/WorldPartition.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents a partition of a World. World partitioning is controlled by the server based on WorldUser activity. 23 | */ 24 | interface WorldPartition { 25 | } 26 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/WorldUser.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Represents a user of a world that holds resources from being freed. 23 | */ 24 | interface WorldUser { 25 | } 26 | -------------------------------------------------------------------------------- /graphmine-core/src/main/kotlin/io/github/sof3/graphmine/world/WorldView.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.world 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * An adapter that intercepts between a world viewer and the actual world. 23 | */ 24 | interface WorldView { 25 | } 26 | -------------------------------------------------------------------------------- /graphmine-i18n-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * GraphMine 3 | * Copyright (C) 2018 SOFe 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | plugins { 20 | java 21 | kotlin("jvm") 22 | } 23 | 24 | group = "io.github.sof3.graphmine" 25 | version = "1.0.0-SNAPSHOT" 26 | 27 | dependencies { 28 | implementation(kotlin("stdlib-jdk8")) 29 | implementation(project(":graphmine-i18n")) 30 | implementation(project(":graphmine-util")) 31 | testImplementation(kotlin("test")) 32 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 33 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 34 | } 35 | -------------------------------------------------------------------------------- /graphmine-i18n-core/module.md: -------------------------------------------------------------------------------- 1 | # Module graphmine-i18n-core 2 | This module is an implementation of graphmine-i18n that contains the declarations required for the core. 3 | -------------------------------------------------------------------------------- /graphmine-i18n-core/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine-i18n-core" 2 | 3 | -------------------------------------------------------------------------------- /graphmine-i18n-core/src/main/kotlin/io/github/sof3/graphmine/i18n/core/CoreLang.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n.core 2 | 3 | import io.github.sof3.graphmine.i18n.GroupSpec 4 | import io.github.sof3.graphmine.i18n.I18n 5 | import io.github.sof3.graphmine.i18n.LangSpec 6 | import java.io.File 7 | 8 | /* 9 | * GraphMine 10 | * Copyright (C) 2018 SOFe 11 | * 12 | * This program is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU Affero General Public License as published 14 | * by the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | object CoreLang : LangSpec() { 27 | val serverName by accept() 28 | 29 | val startup by group(Startup) 30 | 31 | object Startup : GroupSpec() { 32 | val locked by accept() 33 | 34 | data class LockedArg(val file: File) 35 | 36 | val version by accept() 37 | 38 | data class VersionArg(val version: String, val ip: String, val port: Int) 39 | 40 | val complete by accept() 41 | 42 | data class CompleteArg(val nano: Long) 43 | } 44 | 45 | val commands by group(Commands) 46 | 47 | object Commands : GroupSpec() { 48 | val generic by group(Generic) 49 | 50 | object Generic : GroupSpec() { 51 | val notFound by accept() 52 | 53 | data class NotFoundArg(val command: String) 54 | 55 | val wrongSyntax by accept() 56 | 57 | data class WrongSyntaxArg(val command: String, val syntax: List) 58 | } 59 | 60 | val version by group(Version) 61 | 62 | object Version : GroupSpec() { 63 | val description by accept() 64 | val response by accept() 65 | 66 | data class VersionResponse(val version: String) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /graphmine-i18n-core/src/main/kotlin/io/github/sof3/graphmine/i18n/core/CoreLangLoader.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n.core 2 | 3 | import io.github.sof3.graphmine.i18n.loadLangScript 4 | 5 | val availableLocales = listOf( 6 | "en_US" 7 | ) 8 | 9 | fun loadCoreLang() { 10 | loadLangScript(availableLocales) 11 | } 12 | -------------------------------------------------------------------------------- /graphmine-i18n-core/src/main/resources/io/github/sof3/graphmine/i18n/core/en_US.lang.kts: -------------------------------------------------------------------------------- 1 | import io.github.sof3.graphmine.i18n.core.* 2 | import kotlin.math.round 3 | 4 | /* 5 | * GraphMine 6 | * Copyright (C) 2018 SOFe 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published 10 | * by the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | CoreLang("en_US") { 23 | serverName { "GraphMine" } 24 | startup { 25 | locked { "Another server is already running in the data directory $file" } 26 | version { "Starting GraphMine version $version on $ip:$port" } 27 | complete { "Startup completed in ${round(nano / 1e+6) / 1e+3}s" } 28 | } 29 | commands { 30 | generic { 31 | notFound { """Command "$command" not found""" } 32 | wrongSyntax { 33 | """Wrong format for "$command". """ + 34 | if (syntax.size == 1) "Correct format: ${syntax[0]}" 35 | else syntax.joinToString("\n", "Possible format:\n") 36 | } 37 | } 38 | version { 39 | description { "Shows the server version" } 40 | response { "The server is running GraphMine version $version" } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /graphmine-i18n-core/src/test/kotlin/io/github/sof3/graphmine/i18n/core/TranslationTest_en_US.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n.core 2 | 3 | import io.github.sof3.graphmine.i18n.Declaration 4 | import org.spekframework.spek2.Spek 5 | import org.spekframework.spek2.style.gherkin.Feature 6 | import org.spekframework.spek2.style.gherkin.ScenarioBody 7 | import kotlin.test.assertEquals 8 | 9 | /* 10 | * GraphMine 11 | * Copyright (C) 2018 SOFe 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published 15 | * by the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Affero General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | object TranslationTest_en_US : Spek({ 28 | fun ScenarioBody.expect(decl: Declaration, arg: Arg, expected: String) { 29 | lateinit var actual: String 30 | Given("${decl.pathJoined} with ${arg.toString()}") { actual = decl(arg)["en_US"] } 31 | Then("Translation should be \"$expected\"") { assertEquals(expected, actual) } 32 | } 33 | 34 | Feature("GraphMine en_US translation") { 35 | val lang by memoized { loadCoreLang(); CoreLang } 36 | 37 | fun ScenarioBody.expect(decl: Declaration, s: String) = expect(decl, Unit, s) 38 | 39 | Scenario("serverName") { expect(lang.serverName, "GraphMine") } 40 | } 41 | }) 42 | 43 | -------------------------------------------------------------------------------- /graphmine-i18n/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * GraphMine 3 | * Copyright (C) 2018 SOFe 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | plugins { 20 | java 21 | kotlin("jvm") 22 | } 23 | 24 | group = "io.github.sof3.graphmine" 25 | version = "1.0.0-SNAPSHOT" 26 | 27 | dependencies { 28 | api(project(":graphmine-util")) 29 | implementation(kotlin("stdlib-jdk8")) 30 | } 31 | -------------------------------------------------------------------------------- /graphmine-i18n/module.md: -------------------------------------------------------------------------------- 1 | # Module graphmine-i18n 2 | This module provides a kotlin-flavoured i18n (internationalization) framework. 3 | 4 | ## Main Concepts 5 | ### Locale 6 | A locale refers to a human language. It does not need to be an ISO-639 language code; the convention should be determined by the user of this module based on its context, as long as it can be represented in a simple `kotlin.String`. 7 | 8 | ### i18n 9 | An i18n object represents a text displayed differently given different locales. In code, it is represented with the `I18n` interface 10 | 11 | Logically, i18n is the conversion `(locale: String) -> (humanReadable: String)` 12 | 13 | ### Declaration 14 | A declaration is the specification that a dynamic i18n is required by a module based on an argument of type `Arg`. This `Arg` type should either be `Unit` or a kotlin `data class`. 15 | 16 | Logically, a declaration has the conversions: 17 | - `(arg: Arg) -> i18n` 18 | - `(arg: Arg, locale: String) -> (humanReadable: String)` 19 | - `(locale: Locale) -> Translation` (in the next section) 20 | 21 | ### Translation 22 | A translation is the specialization of a declaration in a particular locale. In code, it is represented with the lambda function `(arg: Arg) -> (humanReadable: String)`. 23 | 24 | Translations may perform any memoryless operations, i.e. given the same `Arg` and the same current timestamp, a translation should always return the same string. 25 | 26 | ### Path 27 | A path is the unique identifier of a declaration within a module, composed of one or multiple valid JVM identifier (including those that require the `backtick` syntax in kotlin) strings. In code, a path is represented as an `Array`. 28 | 29 | ## Usage 30 | A module may require translations by declaring a LangSpec class: 31 | 32 | ```kotlin 33 | class MySpecName : LangSpec() { 34 | val declOne by accept() // creates the declaration with path "declOne" and arg type "Unit", i.e. it does not require an argument. 35 | 36 | val declTwo by accept() // creates the declaration with path "declTwo" and arg type "ArgTwo" 37 | data class ArgTwo(val one: String, val two: Int) // translations can reference them as $one and $two 38 | 39 | val groupThree by group(GroupThree()) 40 | class GroupThree : GroupSpec() { 41 | val subFour by accept() // creates the declaration with path "groupThree.subFour" 42 | val subFive by accept() // arg types may be reused. 43 | } 44 | } 45 | ``` 46 | 47 | Also create a convenience infix function: 48 | 49 | ```kotlin 50 | infix fun String.translatesMySpecName (fn: MySpecName.() -> Unit) = MySpecName().apply { 51 | locale = this@translatesMySpecName 52 | fn() 53 | } 54 | ``` 55 | 56 | Translations scripts can then invoke strings with lambdas on this class: 57 | 58 | ```kotlin 59 | "en_US" translatesMySpecName { // provides translations in the locale "en_US" 60 | declOne { "Hello world!" } 61 | declTwo { "arg1 is $one and arg2 is half of ${two * 2}" } 62 | groupThree { 63 | subFour = "this is an inner translation" 64 | subFive = "I don't actually need to use all the args provided, because I only like $two but not one." 65 | } 66 | } 67 | ``` 68 | 69 | Not all messages need to be translated. If it is not provided in one locale, it fallbacks to the first locale registered, then the second one, vice versa. If it is not found in any locales, the message path will be used.. 70 | 71 | ```kotlin 72 | "zh_HK" translatesMySpecName { 73 | declOne { "你好,世界!" } 74 | declTwo { "第一個參數是$one,而第二個參數是${two}的一半" } 75 | groupThree { 76 | // we skipped subFour, and this is OK 77 | subFive = "英文翻譯中沒有用${one},但是在其他語言中也可以用。" 78 | } 79 | } 80 | ``` 81 | 82 | Note that in locales without spaces, translations may need to wrap variable references with `${}`. In this zh_HK example, normal Chinese characters like `的一半` are parsed as part of the variable name without the `{}`, while Chinese punctuation characters are not. Since the translator may be unsure whether a character is a valid JVM identifier, it is recommended that variables always be quoted (even unnecessarily). 83 | 84 | Scripts should be placed in the spec class resource path, i.e. if the LangSpec subclass is `io.github.sof3.graphmine.i18n.core.CoreSpec`, the lang scripts should be placed in `io/github/sof3/graphmine/i18n/core/{locale}.lang.kts` of the corresponding resources directory. 85 | 86 | To load the scripts into a `LangSpec` object, call `io.github.sof3.graphmine.i18n.loadLangScript` with the `LangSpec` subclass as the type parameter and a list of locale names as the argument: 87 | 88 | ```kotlin 89 | val spec = loadLangScript(listOf( 90 | "en_US", 91 | "zh_HK" 92 | )) 93 | ``` 94 | 95 | One of the instances of `MySpecName` will be returned (it is undefined which one is returned). Whichever instance it returns, the API is the same: To retrieve the I18nable representation of a message `path.to.decl`, simply call `spec.path.to.decl.i18n()` (or `spec.path.to.decl.i18n(arg)` if it requires an argument). 96 | 97 | Note: Although a `LangSpec` object is created for every locale, the `.i18n` accessor works the same in any locale of `LangSpec`, so just take any `LangSpec` returned by the translation scripts. 98 | 99 | -------------------------------------------------------------------------------- /graphmine-i18n/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine-i18n" 2 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/Declaration.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | import kotlin.properties.ReadOnlyProperty 4 | import kotlin.reflect.KProperty 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | typealias Translation = T.() -> String 25 | 26 | class Declaration(private val spec: () -> LangSpec<*>, private val pathGet: () -> List) : ReadOnlyProperty, Declaration> { 27 | val translations = linkedMapOf>() 28 | val path by lazy { pathGet() } 29 | val pathJoined by lazy { path.joinToString(".") } 30 | 31 | /** 32 | * Dummy function to satisfy the ReadOnlyProperty interface 33 | */ 34 | override fun getValue(thisRef: LangSpec<*>, property: KProperty<*>) = this 35 | 36 | /** 37 | * To be called by translation scripts to provide a translation. 38 | */ 39 | operator fun invoke(fn: Translation) { 40 | translations[spec().locale!!] = fn 41 | } 42 | 43 | /** 44 | * To be called by translation users to obtain a specific I18n for this declaration. 45 | */ 46 | operator fun invoke(arg: Arg): I18n { 47 | return SpecI18N(arg, this) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/GroupSpec.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | abstract class GroupSpec> : LangSpec() { 22 | lateinit var parent: LangSpec<*> 23 | lateinit var name: String 24 | 25 | override val rootSpec: LangSpec<*> get() = parent 26 | override var locale: String? 27 | get() = parent.locale 28 | set(_) = throw RuntimeException("Cannot set locale on group spec") 29 | override val path get() = parent.path + name 30 | 31 | operator fun invoke(fn: Self.() -> Unit) { 32 | @Suppress("UNCHECKED_CAST") fn(this as Self) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/I18n.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | import io.github.sof3.graphmine.util.KtsLoader 4 | import java.io.InputStreamReader 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | interface I18n { 25 | operator fun get(locale: String): String 26 | } 27 | 28 | inline fun > loadLangScript(locales: Iterable) { 29 | for (locale in locales) { 30 | val url = T::class.java.getResource("$locale.lang.kts")!! 31 | 32 | KtsLoader.load(InputStreamReader(url.openStream()!!)) // no caching for singletons :( 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/I18nable.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * An object that can be expressed i18nly 23 | */ 24 | interface I18nable { 25 | /** 26 | * the i18nized expression of the object 27 | */ 28 | val i18n: I18n 29 | } 30 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/LangSpec.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | import io.github.sof3.graphmine.util.DelegateProvider 4 | import io.github.sof3.graphmine.util.Ref 5 | import kotlin.properties.ReadOnlyProperty 6 | import kotlin.reflect.KProperty 7 | 8 | /* 9 | * GraphMine 10 | * Copyright (C) 2018 SOFe 11 | * 12 | * This program is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU Affero General Public License as published 14 | * by the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | abstract class LangSpec> { 27 | open val rootSpec: LangSpec<*> get() = this 28 | open var locale: String? = null 29 | open val path = emptyList() 30 | 31 | val declarations = hashMapOf>() 32 | val groups = hashMapOf>() 33 | 34 | // to be invoked from translation 35 | inline operator fun invoke(locale: String, fn: Self.() -> Unit): LangSpec { 36 | if (this.locale != null) throw ConcurrentModificationException("Concurrent calls to forLocale()") 37 | try { 38 | this.locale = locale 39 | @Suppress("UNCHECKED_CAST") fn(this as Self) 40 | } finally { 41 | this.locale = null 42 | } 43 | return this 44 | } 45 | 46 | /** 47 | * Invoked by declaration subclasses to create a single declaration 48 | */ 49 | fun accept() = object : DelegateProvider> { 50 | override fun provideDelegate(thisRef: Self, property: KProperty<*>): ReadOnlyProperty> { 51 | val declaration = Declaration({ rootSpec }, { path + property.name }) 52 | declarations[property.name] = declaration 53 | return declaration 54 | } 55 | } 56 | 57 | /** 58 | * Invoked by declaration subclasses to create a declaration group 59 | */ 60 | fun > group(group: Grp) = object : DelegateProvider { 61 | override fun provideDelegate(thisRef: Self, property: KProperty<*>): ReadOnlyProperty { 62 | group.parent = this@LangSpec 63 | group.name = property.name 64 | groups[property.name] = group 65 | return Ref(group) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/LiteralI18N.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | class LiteralI18N(val string: String) : I18n { 22 | override fun get(locale: String) = string 23 | } 24 | 25 | val String.i18n get() = LiteralI18N(this) 26 | -------------------------------------------------------------------------------- /graphmine-i18n/src/main/kotlin/io/github/sof3/graphmine/i18n/SpecI18N.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.i18n 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | class SpecI18N internal constructor(private val arg: Arg, private val declaration: Declaration) : I18n { 22 | override fun get(locale: String): String { 23 | val best = declaration.translations[locale] 24 | if (best != null) return best(arg) 25 | 26 | val iter = declaration.translations.iterator() 27 | if (iter.hasNext()) return iter.next().value(arg) 28 | 29 | return declaration.pathJoined 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /graphmine-util/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * GraphMine 3 | * Copyright (C) 2018 SOFe 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | plugins { 20 | java 21 | kotlin("jvm") 22 | } 23 | 24 | group = "io.github.sof3.graphmine" 25 | version = "1.0.0-SNAPSHOT" 26 | 27 | dependencies { 28 | implementation(kotlin("stdlib-jdk8")) 29 | implementation(kotlin("reflect")) 30 | 31 | implementation("commons-io:commons-io:2.6") 32 | 33 | runtimeOnly(kotlin("compiler-embeddable")) 34 | runtimeOnly(kotlin("script-util")) 35 | 36 | testImplementation(kotlin("test")) 37 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 38 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 39 | } 40 | -------------------------------------------------------------------------------- /graphmine-util/module.md: -------------------------------------------------------------------------------- 1 | # Module graphmine-util 2 | This module provides general-purpose that cannot be classified. Most of them are to supplement the Kotlin language API. 3 | -------------------------------------------------------------------------------- /graphmine-util/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine-util" 2 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/DataUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import java.io.DataInputStream 4 | import java.io.DataOutputStream 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | fun DataInputStream.readString(): String { 25 | val length = readInt() 26 | val buf = ByteArray(length) 27 | read(buf) 28 | return String(buf) 29 | } 30 | 31 | fun DataOutputStream.writeString(string: String) { 32 | val buf = string.toByteArray() 33 | writeInt(buf.size) 34 | write(buf) 35 | } 36 | 37 | inline fun DataInputStream.readMap(keyFn: (DataInputStream) -> K, valueFn: (DataInputStream) -> V) = readMap(mutableMapOf(), keyFn, valueFn) 38 | inline fun DataInputStream.readMap(map: MutableMap, keyFn: (DataInputStream) -> K, valueFn: (DataInputStream) -> V): MutableMap { 39 | val size = readInt() 40 | repeat(size) { 41 | val key = keyFn(this) 42 | val value = valueFn(this) 43 | map[key] = value 44 | } 45 | return map 46 | } 47 | 48 | inline fun DataOutputStream.writeMap(map: MutableMap, keyFn: (DataOutputStream, K) -> Unit, valueFn: (DataOutputStream, V) -> Unit): MutableMap { 49 | writeInt(map.size) 50 | for ((k, v) in map) { 51 | keyFn(this, k) 52 | valueFn(this, v) 53 | } 54 | return map 55 | } 56 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/DelegateProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import kotlin.properties.ReadOnlyProperty 4 | import kotlin.reflect.KProperty 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * An interface to help completing the `provideDelegate` operator function for `val` property delegation. 26 | */ 27 | interface DelegateProvider { 28 | /** 29 | * Provide the [ReadOnlyProperty] for delegation. Could use [Ref] if no special logic is required. 30 | */ 31 | operator fun provideDelegate(thisRef: R, property: KProperty<*>): ReadOnlyProperty 32 | } 33 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/KtsBin.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import java.io.DataInputStream 4 | import java.io.DataOutputStream 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | interface KtsBin, F : KtsBin.Factory> { 25 | fun write(dis: DataOutputStream) 26 | 27 | interface Factory, F : Factory> { 28 | fun read(dis: DataInputStream): T 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/KtsLoader.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import org.apache.commons.io.IOUtils 4 | import org.apache.commons.io.input.TeeInputStream 5 | import java.io.* 6 | import java.security.DigestOutputStream 7 | import java.security.MessageDigest 8 | import javax.script.ScriptEngineManager 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | /** 29 | * State object to ensure system properties are set before loading a script 30 | */ 31 | object KtsLoader { 32 | init { 33 | System.setProperty("idea.io.use.fallback", "true") 34 | } 35 | 36 | /** 37 | * Loads a .kts file, optionally from cache and stores to cache 38 | * 39 | * @param T the type 40 | */ 41 | inline fun , F : KtsBin.Factory> loadSerializable(factory: F, fis: InputStream, path: String, cacheDir: File, rVersion: Int): T { 42 | cacheDir.mkdirs() 43 | val cacheFile = File(cacheDir, "kts-" + path.hashCode().toString(16)) 44 | 45 | val digestBuf = ByteArrayOutputStream() 46 | val digest = DigestOutputStream(digestBuf, MessageDigest.getInstance("MD5")!!) 47 | val tee = TeeInputStream(fis, digest) 48 | 49 | val raw = ByteArrayOutputStream() 50 | tee.use { IOUtils.copy(tee, raw) } 51 | 52 | val md5 = digestBuf.toByteArray()!! 53 | assert(md5.size == 16) 54 | val load = loadCache(md5, cacheFile, rVersion) { factory.read(it) } 55 | if (load != null) return load 56 | val loaded = load(StringReader(String(raw.toByteArray()))) 57 | writeCache(cacheFile, md5, rVersion, loaded) 58 | return loaded 59 | } 60 | 61 | fun loadCache(md5: ByteArray, cacheFile: File, rVersion: Int, read: (DataInputStream) -> T): T? { 62 | if (!cacheFile.isFile) return null 63 | return DataInputStream(FileInputStream(cacheFile)).use { 64 | val buffer = ByteArray(16) 65 | it.read(buffer) 66 | if (!buffer.contentEquals(md5)) return null 67 | val version = it.readInt() 68 | if (version != rVersion) return null 69 | read(it) 70 | } 71 | } 72 | 73 | fun writeCache(cacheFile: File, md5: ByteArray, rVersion: Int, value: KtsBin<*, *>) { 74 | return DataOutputStream(FileOutputStream(cacheFile)).use { 75 | it.write(md5) 76 | it.writeInt(rVersion) 77 | value.write(it) 78 | } 79 | } 80 | 81 | /** 82 | * Loads a script from a reader 83 | * 84 | * @param R the expected return type from the script 85 | * @return the value in the script 86 | */ 87 | inline fun load(reader: Reader): R { 88 | val engine = ScriptEngineManager().getEngineByExtension("kts") 89 | return reader.use { engine.eval(it) as R } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/VarDelegateProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import kotlin.properties.ReadWriteProperty 4 | import kotlin.reflect.KProperty 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * An interface to help completing the `provideDelegate` operator function for `var` property delegation. 26 | */ 27 | interface VarDelegateProvider { 28 | /** 29 | * Provide the [ReadWriteProperty] for delegation. Could use [Ref] if no special logic is required. 30 | */ 31 | operator fun provideDelegate(thisRef: R, property: KProperty<*>): ReadWriteProperty 32 | } 33 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/error.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package io.github.sof3.graphmine.util 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Always throws [AssertionError] stating that control flow should never reach this point. Useful in `when` blocks when 25 | * Kotlin is unable to detect that the conditions are exhaustive 26 | */ 27 | inline val DEADCODE: Nothing get() = DEADCODE() 28 | 29 | /** 30 | * Always throws [AssertionError] stating that control flow should never reach this point. Useful in `when` blocks when 31 | * Kotlin is unable to detect that the conditions are exhaustive 32 | * @param message a string to use as the error message 33 | */ 34 | inline fun DEADCODE(message: String = "Control flow error"): Nothing = throw AssertionError(message) 35 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/misc.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Executes [fn] if [this] is not null 23 | */ 24 | inline fun T?.notNull(fn: (T) -> Unit) { 25 | if (this != null) fn(this) 26 | } 27 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/qualifier/Qualifier.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.qualifier 2 | 3 | import io.github.sof3.graphmine.util.get 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Represents a qualified identifier. 25 | * 26 | * @param string the dot-delimited qualified identifier 27 | */ 28 | class Qualifier(string: String) { 29 | /** 30 | * The string parts of the qualifier 31 | */ 32 | val parts = string.split(".") 33 | 34 | /** 35 | * The last part of the qualifier, which is also the most frequently used one 36 | */ 37 | val simple get() = parts.last() 38 | 39 | /** 40 | * Executes [fn] on each possible representation of this qualifier 41 | */ 42 | inline fun forEachPermutation(fn: (List) -> Unit) { 43 | for (i in 0 until parts.size) { 44 | fn(parts[i, parts.size]) 45 | } 46 | } 47 | 48 | /** 49 | * Returns the dot-delimited representation of the qualifier 50 | */ 51 | override fun toString() = parts.joinToString(".") 52 | 53 | private val hashCode by lazy { parts.map(String::hashCode).reduce { a, b -> a * 31 + b } } 54 | /** 55 | * Returns the lazily-evaluated hash code of the qualifier only based on the contents of [parts] 56 | */ 57 | override fun hashCode() = hashCode 58 | 59 | /** 60 | * Checks if two identifiers are fully equal based on the contents of [parts] 61 | */ 62 | override fun equals(other: Any?) = other is Qualifier && parts == other.parts 63 | } 64 | 65 | /** 66 | * Converts a dot-delimited string to a qualifier 67 | * 68 | * @receiver a dot-delimited string 69 | */ 70 | fun String.qualify() = Qualifier(this) 71 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/qualifier/QualifierClashException.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.qualifier 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Thrown when there is a qualifier clash 23 | * @property values a map of [Qualifier]s to their respective values. The value type of the map can be safely assumed to be 24 | * the type T in the [QualifierMap] that throws the exception. 25 | */ 26 | class QualifierClashException(val values: Map) : Exception() 27 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/qualifier/QualifierMap.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.qualifier 2 | 3 | import java.util.* 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * A thread-safe store for qualified entries allowing search by qualified name 25 | */ 26 | class QualifierMap { 27 | private val map = hashMapOf>() 28 | 29 | /** 30 | * Adds a qualified entry to the map 31 | * @param qualifier the qualified name 32 | * @param value the value to add 33 | */ 34 | operator fun set(qualifier: Qualifier, value: T) { 35 | qualifier.forEachPermutation { slice -> 36 | val key = slice.joinToString(".") 37 | lateinit var map: MutableMap 38 | synchronized(this.map) { 39 | if (key !in this.map) this.map[key] = hashMapOf() 40 | map = this.map[key]!! 41 | } 42 | synchronized(map) { map[qualifier] = value } 43 | } 44 | } 45 | 46 | /** 47 | * Gets an entry using a qualified key 48 | * @param key any qualified key 49 | */ 50 | operator fun get(key: String): T? { 51 | val map = synchronized(map) { map[key] } ?: return null 52 | val clone: Map = synchronized(map) { 53 | if (map.size == 1) return map.iterator().next().value 54 | Collections.unmodifiableMap(map) 55 | } 56 | throw QualifierClashException(clone) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/qualifier/package.md: -------------------------------------------------------------------------------- 1 | # Package io.github.sof3.graphmine.util.qualifier 2 | This package provides the Qualifier API, which is used to resolve name clashes. 3 | 4 | When multiple modules add to the same registry with the same name, a name clash occurs. The module identifier can be 5 | used to distinguish others from multiple modules. On the other hand, it is inconvenient if users have to type the whole 6 | name from the module identifier. Therefore, a gradual approach is used: If the base name does not have a clash, only 7 | typing the base name is enough. Otherwise, the user has to add one more part of the qualifier. This process repeats 8 | until there is no longer any ambiguity. 9 | 10 | The qualifier string should be dot-separated. By convention, it starts with the module identifier (usually the module 11 | package), then the object name. Since the same module should not register two objects with the same name, it is not 12 | necessary to subdivide under the package. Moreover, to make the second part of the object name (which usually 13 | distinguishes most of the collision) more intuitive, it should be the module name. 14 | 15 | For example, if there is a plugin called "ChestLock" and two plugins called "PortKey" that implement a "key" command, 16 | they might have qualified command names like this: 17 | 18 | ``` 19 | io.github.foo.ChestLock.key 20 | com.gmail.bar.PortKey.key 21 | com.example.qux.PortKey.key 22 | ``` 23 | 24 | When the user uses the "key" command, there are three possibilities, so typing "ChestLock.key" instead (or usually the 25 | case-insensitive situation) would distinguish that the user wants to run the key command from ChestLock. Otherwise, the 26 | user wants to run the key command from the PortKey plugin, but there are two PortKey plugins (which is quite strange 27 | and unlikely), so the user types the full package name of the PortKey plugin to specify which plugin to use. 28 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/ranges.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Syntactic sugar for [CharSequence.substring] so that it looks like `string[start:end]` in Python 23 | */ 24 | operator fun CharSequence.get(start: Int, end: Int) = substring(start, end) 25 | 26 | /** 27 | * Syntactic sugar for [CharSequence.substring] so that it looks like `list[start:end]` in Python 28 | */ 29 | operator fun List.get(start: Int, end: Int) = slice(start until end) 30 | 31 | /** 32 | * A more intuitive way of checking if an offset is in bounds of a string 33 | */ 34 | fun String.hasOffset(offset: Int) = offset in 0 until length 35 | 36 | /** 37 | * A more intuitive way of checking if an offset is in bounds of a list 38 | */ 39 | fun List<*>.hasOffset(offset: Int) = offset in 0 until size 40 | -------------------------------------------------------------------------------- /graphmine-util/src/main/kotlin/io/github/sof3/graphmine/util/ref.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import kotlin.properties.ReadOnlyProperty 4 | import kotlin.properties.ReadWriteProperty 5 | import kotlin.reflect.KProperty 6 | 7 | /* 8 | * GraphMine 9 | * Copyright (C) 2018 SOFe 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published 13 | * by the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | /** 26 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 27 | * [VarDelegateProvider] 28 | * @property value the backing value 29 | */ 30 | class Ref(var value: T) : ReadWriteProperty, ReadOnlyProperty { 31 | /** Used for delegation to the backing [value] */ 32 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 33 | 34 | /** Used for delegation to the backing [value] */ 35 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 36 | this.value = value 37 | } 38 | } 39 | 40 | /** 41 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 42 | * [VarDelegateProvider] 43 | * @property value the backing value 44 | */ 45 | class ByteRef(var value: Byte) : ReadWriteProperty, ReadOnlyProperty { 46 | /** Used for delegation to the backing [value] */ 47 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 48 | 49 | /** Used for delegation to the backing [value] */ 50 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Byte) { 51 | this.value = value 52 | } 53 | } 54 | 55 | /** 56 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 57 | * [VarDelegateProvider] 58 | * @property value the backing value 59 | */ 60 | class CharRef(var value: Char) : ReadWriteProperty, ReadOnlyProperty { 61 | /** Used for delegation to the backing [value] */ 62 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 63 | 64 | /** Used for delegation to the backing [value] */ 65 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Char) { 66 | this.value = value 67 | } 68 | } 69 | 70 | /** 71 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 72 | * [VarDelegateProvider] 73 | * @property value the backing value 74 | */ 75 | class ShortRef(var value: Short) : ReadWriteProperty, ReadOnlyProperty { 76 | /** Used for delegation to the backing [value] */ 77 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 78 | 79 | /** Used for delegation to the backing [value] */ 80 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Short) { 81 | this.value = value 82 | } 83 | } 84 | 85 | /** 86 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 87 | * [VarDelegateProvider] 88 | * @property value the backing value 89 | */ 90 | class IntRef(var value: Int) : ReadWriteProperty, ReadOnlyProperty { 91 | /** Used for delegation to the backing [value] */ 92 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 93 | 94 | /** Used for delegation to the backing [value] */ 95 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { 96 | this.value = value 97 | } 98 | } 99 | 100 | /** 101 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 102 | * [VarDelegateProvider] 103 | * @property value the backing value 104 | */ 105 | class LongRef(var value: Long) : ReadWriteProperty, ReadOnlyProperty { 106 | /** Used for delegation to the backing [value] */ 107 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 108 | 109 | /** Used for delegation to the backing [value] */ 110 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { 111 | this.value = value 112 | } 113 | } 114 | 115 | /** 116 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 117 | * [VarDelegateProvider] 118 | * @property value the backing value 119 | */ 120 | class FloatRef(var value: Float) : ReadWriteProperty, ReadOnlyProperty { 121 | /** Used for delegation to the backing [value] */ 122 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 123 | 124 | /** Used for delegation to the backing [value] */ 125 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) { 126 | this.value = value 127 | } 128 | } 129 | 130 | /** 131 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 132 | * [VarDelegateProvider] 133 | * @property value the backing value 134 | */ 135 | class DoubleRef(var value: Double) : ReadWriteProperty, ReadOnlyProperty { 136 | /** Used for delegation to the backing [value] */ 137 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 138 | 139 | /** Used for delegation to the backing [value] */ 140 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Double) { 141 | this.value = value 142 | } 143 | } 144 | 145 | /** 146 | * Useful for passing by reference or as return value in delegation providers [DelegateProvider] and 147 | * [VarDelegateProvider] 148 | * @property value the backing value 149 | */ 150 | class BooleanRef(var value: Boolean) : ReadWriteProperty, ReadOnlyProperty { 151 | /** Used for delegation to the backing [value] */ 152 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = value 153 | 154 | /** Used for delegation to the backing [value] */ 155 | override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { 156 | this.value = value 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /graphmine-util/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory: -------------------------------------------------------------------------------- 1 | org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory 2 | -------------------------------------------------------------------------------- /graphmine-util/src/test/kotlin/io/github/sof3/graphmine/util/testUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util 2 | 3 | import org.spekframework.spek2.dsl.TestBody 4 | import org.spekframework.spek2.style.gherkin.ScenarioBody 5 | import kotlin.random.Random 6 | import kotlin.test.assertEquals 7 | import kotlin.test.assertNotEquals 8 | 9 | /* 10 | * GraphMine 11 | * Copyright (C) 2018 SOFe 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published 15 | * by the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Affero General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | infix fun Ref.shouldBe(expected: T) = assertEquals(expected, value) 28 | infix fun Ref.shouldNotBe(expected: T) = assertNotEquals(expected, value) 29 | infix fun T.shouldBe(expected: T) = assertEquals(expected, this) 30 | infix fun T.shouldNotBe(expected: T) = assertNotEquals(expected, this) 31 | 32 | private typealias InScenarioBody = ScenarioBody.() -> Unit 33 | 34 | //inline fun ScenarioBody.If(condition: String, boolean: Boolean, then: InScenarioBody, or: InScenarioBody) { 35 | // if (boolean) { 36 | // When("\"$condition\" is true") {} 37 | // then(this) 38 | // } else { 39 | // When("\"$condition\" is false", {}) 40 | // or(this) 41 | // } 42 | //} 43 | 44 | inline fun ScenarioBody.GivenRandom( 45 | lengths: IntRange, 46 | crossinline stringToParam: (String) -> T, 47 | crossinline ctx: () -> C, 48 | name: String = "random strings of length [${lengths.start}, ${lengths.endInclusive}]", 49 | eachTimes: Int = 10, 50 | charset: List = ALPHA_NUMERAL, 51 | crossinline then: RandomContext.() -> Unit 52 | ) { 53 | val list = mutableListOf>>() 54 | Given(name) { 55 | randomRegularStrings(lengths, eachTimes, charset) { list += Triple(it, stringToParam(it), Ref(ctx())) } 56 | } 57 | val context = RandomContext().apply(then) 58 | context.runs.forEach { it(this, list) } 59 | } 60 | 61 | typealias RandomTest = TestBody.(String, T, Ref) -> Unit 62 | private typealias RandomContextParam = List>> 63 | 64 | class RandomContext { 65 | val runs = mutableListOf) -> Unit>() 66 | 67 | inline fun When(description: String, crossinline fn: RandomTest) { 68 | runs.add { list -> When(description) { list.forEach { fn(it.first, it.second, it.third) } } } 69 | } 70 | 71 | inline fun Then(description: String, crossinline fn: RandomTest) { 72 | runs.add { list -> Then(description) { list.forEach { fn(it.first, it.second, it.third) } } } 73 | } 74 | 75 | inline fun And(description: String, crossinline fn: RandomTest) { 76 | runs.add { list -> And(description) { list.forEach { fn(it.first, it.second, it.third) } } } 77 | } 78 | 79 | inline fun If( 80 | condition: String, crossinline predicate: (String) -> Boolean, 81 | thenMessage: String, crossinline then: RandomTest, 82 | orMessage: String, crossinline or: RandomTest) { 83 | runs.add { list -> 84 | lateinit var t: RandomContextParam 85 | lateinit var f: RandomContextParam 86 | When("\"$condition\" is true") { 87 | val pair = list.partition { predicate(it.first) } 88 | t = pair.first 89 | f = pair.second 90 | } 91 | Then(thenMessage) { t.forEach { then(it.first, it.second, it.third) } } 92 | When("\"$condition\" is false") {} 93 | Then(orMessage) { f.forEach { or(it.first, it.second, it.third) } } 94 | } 95 | } 96 | } 97 | 98 | val ALPHA_UPPER = ('A'..'Z').toList() 99 | val ALPHA_LOWER = ('a'..'z').toList() 100 | val NUMERAL = ('0'..'9').toList() 101 | val ALPHA_NUMERAL = ALPHA_UPPER + ALPHA_LOWER + NUMERAL 102 | val ALPHA_NUMERAL_SPACE = ALPHA_NUMERAL + ' ' 103 | 104 | inline fun randomRegularStrings( 105 | lengths: IntRange, 106 | eachTimes: Int, 107 | charset: List = ALPHA_NUMERAL, 108 | fn: (String) -> Unit 109 | ) { 110 | for (length in lengths) { 111 | repeat(eachTimes) { 112 | fn(randomRegularString(length, charset)) 113 | } 114 | } 115 | } 116 | 117 | fun randomRegularString(length: Int, charset: List = ALPHA_NUMERAL): String { 118 | val chars = CharArray(length) 119 | repeat(length) { chars[it] = charset[Random.nextInt(charset.size)] } 120 | return String(chars) 121 | } 122 | 123 | fun allPermutations(vararg values: T, yields: Int = values.size, fn: (List) -> Unit) = allPermutations(emptyList(), values.toList(), yields, fn) 124 | fun allPermutations(front: List, values: List, levels: Int, fn: (List) -> Unit) { 125 | if (levels == 0) { 126 | fn(front) 127 | return 128 | } 129 | 130 | for (i in 0 until values.size) { 131 | val clone = values.toMutableList() 132 | val rem = clone.removeAt(i) 133 | allPermutations(front + rem, clone, levels - 1, fn) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "graphmine" 2 | 3 | include("graphmine-util") 4 | include("graphmine-i18n") 5 | include("graphmine-i18n-core") 6 | include("graphmine-core") 7 | include("graphmine-cli") 8 | include("util-math") 9 | include("util-reflect-io") 10 | -------------------------------------------------------------------------------- /util-math/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | plugins { 22 | java 23 | kotlin("jvm") 24 | } 25 | 26 | group = "io.github.sof3.graphmine" 27 | version = "1.0.0-SNAPSHOT" 28 | 29 | dependencies { 30 | implementation(kotlin("stdlib-jdk8")) 31 | implementation(kotlin("reflect")) 32 | api(project(":graphmine-util")) 33 | testImplementation(kotlin("test")) 34 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 35 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 36 | } 37 | 38 | tasks.withType { 39 | kotlinOptions.freeCompilerArgs += "-XXLanguage:+InlineClasses" 40 | } 41 | -------------------------------------------------------------------------------- /util-math/module.md: -------------------------------------------------------------------------------- 1 | # Module util-math 2 | This module provides a math library that targets at world geometry management. 3 | -------------------------------------------------------------------------------- /util-math/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "util-math" 2 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/IntVector3.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | import io.github.sof3.graphmine.util.mapDoubleArray 4 | import io.github.sof3.graphmine.util.mapIntArray 5 | import kotlin.math.abs 6 | import kotlin.math.max 7 | import kotlin.math.min 8 | import kotlin.math.sqrt 9 | 10 | /* 11 | * GraphMine 12 | * Copyright (C) 2018 SOFe 13 | * 14 | * This program is free software: you can redistribute it and/or modify 15 | * it under the terms of the GNU Affero General Public License as published 16 | * by the Free Software Foundation, either version 3 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Affero General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Affero General Public License 25 | * along with this program. If not, see . 26 | */ 27 | 28 | /** 29 | * A 3-dimensional vector in integers. Intended to store aligned positions e.g. block positions. 30 | * 31 | * The class is a value class. Values in this class cannot be changed. Therefore, no copy method is present. 32 | */ 33 | inline class IntVector3(internal val values: IntArray) { 34 | /** 35 | * This object caches some common instances for efficient reuse 36 | */ 37 | companion object Common { 38 | /** A zero vector */ 39 | val ZERO = IntVector3(0, 0, 0) 40 | /** A unit vector in the direction of *x* (east) */ 41 | val UNIT_X = IntVector3(1, 0, 0) 42 | /** A unit vector in the direction of *-x* (west) */ 43 | val UNIT_X_NEG = IntVector3(-1, 0, 0) 44 | /** A unit vector in the direction of *y* (up) */ 45 | val UNIT_Y = IntVector3(0, 1, 0) 46 | /** A unit vector in the direction of *-y* (up) */ 47 | val UNIT_Y_NEG = IntVector3(0, -1, 0) 48 | /** A unit vector in the direction of *z* (south) */ 49 | val UNIT_Z = IntVector3(0, 0, 1) 50 | /** A unit vector in the direction of *-z* (north) */ 51 | val UNIT_Z_NEG = IntVector3(0, 0, -1) 52 | } 53 | 54 | /** 55 | * Consrtucts a [Vector3] by specifying all coordinates 56 | */ 57 | constructor(x: Int, y: Int, z: Int) : this(intArrayOf(x, y, z)) 58 | 59 | /** The *x*-component value */ 60 | val x get() = values[0] 61 | /** The *y*-component value, which is orthogonal to *x* */ 62 | val y get() = values[1] 63 | /** The *z*-component value, which is in the direction of *x*×*y* */ 64 | val z get() = values[2] 65 | 66 | /** 67 | * Converts to a [Vector3] with approximately equivalent positions 68 | */ 69 | fun toDouble() = Vector3(values.mapDoubleArray(Int::toDouble)) 70 | 71 | /** 72 | * Calculates the squared length of this vector 73 | */ 74 | val lengthSquared get() = values.mapIntArray { it * it }.sum() 75 | /** 76 | * Calculates the length (a.k.a. norm or modulus) of this vector. 77 | * 78 | * Use [lengthSquared] if only comparison is needed for less computation. 79 | */ 80 | val length get() = sqrt(lengthSquared.toDouble()) 81 | 82 | /** 83 | * @return a unit vector (length = 1) in the same direction as this vector 84 | */ 85 | val unit get() = if (lengthSquared == 0) Vector3.ZERO else toDouble() / length 86 | 87 | /** 88 | * Calculates the dot product of `this` and [v] 89 | */ 90 | infix fun dot(v: IntVector3) = biMap(v) { a, b -> a * b }.values.sum() 91 | 92 | /** 93 | * Calculates the cross product of `this` and [v] 94 | */ 95 | infix fun cross(v: IntVector3) = IntVector3( 96 | y * v.z - z * v.y, 97 | z * v.x - x * v.z, 98 | x * v.y - y * v.x 99 | ) 100 | 101 | /** Calculates `this` + [that]*/ 102 | operator fun plus(that: IntVector3) = biMap(that) { a, b -> a + b } 103 | 104 | /** Calculates `this` - [that] */ 105 | operator fun minus(that: IntVector3) = biMap(that) { a, b -> a + b } 106 | 107 | /** 108 | * Multiplies the components of `this` by [scalar] 109 | * 110 | * Use `this.toDouble() * scalar` if [scalar] is a double. 111 | * 112 | * Use `this.toDouble() / scalar` if division is intended. 113 | */ 114 | operator fun times(scalar: Int) = map { it * scalar } 115 | 116 | /** @return this vector */ 117 | operator fun unaryPlus() = this 118 | 119 | /** @return this vector in the opposite direction and same length */ 120 | operator fun unaryMinus() = map(Int::unaryMinus) 121 | 122 | /** @return this vector with all components flipped if negative */ 123 | fun abs(): IntVector3 = map(::abs) 124 | 125 | /** 126 | * Calculates the squared distance between `this` and [that] 127 | */ 128 | fun distanceSquared(that: IntVector3) = (this - that).lengthSquared 129 | 130 | /** 131 | * Calculates the distance between `this` and [that]. 132 | * 133 | * Use [distanceSquared] if only comparison is needed for less computation. 134 | */ 135 | infix fun distanceTo(that: IntVector3) = (this - that).length 136 | 137 | /** 138 | * Returns the minimum [IntVector3Range] that inclusively contains both `this` and [that] 139 | */ 140 | operator fun rangeTo(that: IntVector3) = IntVector3Range(biMap(that, ::min), biMap(that, ::max)) 141 | 142 | /** @return the neighbour of `this` on the side [side] */ 143 | fun side(side: Side) = this + side.intVector 144 | 145 | /** @return `this` moved towards [side] by [steps] steps */ 146 | fun side(side: Side, steps: Int) = this + side.intVector * steps 147 | 148 | /** @return executes [fn] on each of the 6 neighbours of `this` */ 149 | fun eachSide(fn: (IntVector3) -> Unit) = Side.ALL.forEach { fn(side(it)) } 150 | 151 | internal inline fun map(fn: (Int) -> Int) = IntVector3(values.mapIntArray(fn)) 152 | 153 | internal inline fun biMap(that: IntVector3, fn: (Int, Int) -> Int): IntVector3 { 154 | val new = IntArray(3) 155 | for (i in 0 until values.size) new[i] = fn(values[i], that.values[i]) 156 | return IntVector3(new) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/IntVector3Range.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * A unit-aligned cuboid region in the 3D space. 23 | * 24 | * In integer-aligned space, this is a closed range [[min], [max]] such that the region joined with another region at 25 | * [max] + 1 on any axis will be fully continuous. 26 | * 27 | * In non-integer-aligned space, this is a semi-open range [[min], [max] + 1) such that the same rule applies. It can be 28 | * thought as the vector at `(x, y, z)` covering the range `([x, x + 1), [y, y + 1), [z, z + 1))`. 29 | * 30 | * @property min the minimum possible point in the region 31 | * @property max the maximum possible point in the region 32 | */ 33 | class IntVector3Range internal constructor(val min: IntVector3, val max: IntVector3) : Iterable { 34 | /** 35 | * Specifies the iteration order. 36 | * @property a the first axis to iterate over (increments when [b] resets, stops iteration after reaching [max]) 37 | * @property b the second axis to iterate over (increments when [c] resets, resets after reaching [max]) 38 | * @property c the third axis to iterate over (increments every time, resets after reaching [max]) 39 | */ 40 | enum class IterationOrder(val a: Int, val b: Int, val c: Int) { 41 | /** Iterate bottom-up, for each column from west to east, yields from north to south */ 42 | YXZ(1, 0, 2), 43 | /** Iterate from west to east, for each row from north to south, yields bottom-up */ 44 | XZY(0, 2, 1), 45 | /** Iterate bottom-up, for each column from north to south, yields from west to east */ 46 | YZX(1, 2, 0), 47 | /** Iterate from morth to south, for each row from west to east, yields bottom-up */ 48 | ZXY(2, 0, 1), 49 | } 50 | 51 | /** 52 | * Specifies the iteration order before using as an iterable. 53 | * 54 | * Example ues: `for(vector in range with IterationOrder.ZXY) { ... }` 55 | */ 56 | infix fun with(order: IterationOrder) = object : Iterable { 57 | override fun iterator(): Iterator = kotlin.sequences.iterator { 58 | for (a in min.values[order.a]..max.values[order.a]) { 59 | for (b in min.values[order.b]..max.values[order.b]) { 60 | for (c in min.values[order.c]..max.values[order.c]) { 61 | val array = intArrayOf(3, 3, 3) 62 | array[order.a] = a 63 | array[order.b] = b 64 | array[order.c] = c 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * Iterates over every integer-aligned point in this range. 73 | */ 74 | override fun iterator() = with(IterationOrder.YXZ).iterator() 75 | 76 | /** 77 | * Checks if a vector is inside the range in integer-aligned space. 78 | */ 79 | operator fun contains(vector: IntVector3) = (0..2).all { vector.values[it] in min.values[it]..max.values[it] } 80 | 81 | /** 82 | * Checks if a vector is inside the range in non-integer-aligned space. 83 | * 84 | * See the [IntVector3Range] definition of the range in non-integer-aligned space. 85 | */ 86 | operator fun contains(vector: Vector3) = (0..2).all { 87 | vector.values[it] >= min.values[it].toDouble() && 88 | vector.values[it] < max.values[it].toDouble() + 1.0 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/Side.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | import io.github.sof3.graphmine.util.DEADCODE 4 | 5 | /* 6 | * GraphMine 7 | * Copyright (C) 2018 SOFe 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published 11 | * by the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | /** 24 | * Represents one of the 6 directions in a 3D space. 25 | * 26 | * @property id the ID of the direction 27 | */ 28 | class Side private constructor(val id: Int) { 29 | /** 30 | * Contains the default sides 31 | */ 32 | companion object Sides { 33 | /** The DOWN side */ 34 | val DOWN = Side(0) 35 | /** The UP side */ 36 | val UP = Side(1) 37 | /** The NORTH side */ 38 | val NORTH = Side(2) 39 | /** The SOUTH side */ 40 | val SOUTH = Side(3) 41 | /** The WEST side */ 42 | val WEST = Side(4) 43 | /** The EAST side */ 44 | val EAST = Side(5) 45 | 46 | /** A list of all sides */ 47 | val ALL = (0..5).map { Side(it) } 48 | 49 | /** Access a side by ID */ 50 | operator fun get(id: Int) = ALL[id] 51 | } 52 | 53 | /** 54 | * Expresses the side as an [IntVector3] 55 | */ 56 | val intVector = when (id) { 57 | 0 -> IntVector3(0, -1, 0) 58 | 1 -> IntVector3(0, 1, 0) 59 | 2 -> IntVector3(0, 0, -1) 60 | 3 -> IntVector3(0, 0, 1) 61 | 4 -> IntVector3(-1, 0, 0) 62 | 5 -> IntVector3(1, 0, 0) 63 | else -> DEADCODE 64 | } 65 | 66 | /** 67 | * Expresses the side as a [Vector3] 68 | */ 69 | val vector = when (id) { 70 | 0 -> Vector3(0.0, -1.0, 0.0) 71 | 1 -> Vector3(0.0, 1.0, 0.0) 72 | 2 -> Vector3(0.0, 0.0, -1.0) 73 | 3 -> Vector3(0.0, 0.0, 1.0) 74 | 4 -> Vector3(-1.0, 0.0, 0.0) 75 | 5 -> Vector3(1.0, 0.0, 0.0) 76 | else -> DEADCODE 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/Vector3.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | import io.github.sof3.graphmine.util.mapDoubleArray 4 | import io.github.sof3.graphmine.util.mapIntArray 5 | import kotlin.Double.Companion.NEGATIVE_INFINITY 6 | import kotlin.Double.Companion.POSITIVE_INFINITY 7 | import kotlin.math.* 8 | 9 | /* 10 | * GraphMine 11 | * Copyright (C) 2018 SOFe 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published 15 | * by the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope v it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Affero General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | /** 28 | * A 3-dimensional vector in floating point precision. Intended to store non-aligned positions, e.g. entity positions. 29 | * 30 | * The class is a value class. Values in this class cannot be changed. Therefore, no copy method is present. 31 | */ 32 | inline class Vector3(internal val values: DoubleArray) { 33 | /** 34 | * This object caches some common instances for efficient reuse 35 | */ 36 | companion object Common { 37 | /** A zero vector */ 38 | val ZERO = Vector3(0.0, 0.0, 0.0) 39 | /** A unit vector in the direction of *x* (east) */ 40 | val UNIT_X = Vector3(1.0, 0.0, 0.0) 41 | /** A unit vector in the direction of *y* (up) */ 42 | val UNIT_Y = Vector3(0.0, 1.0, 0.0) 43 | /** A unit vector in the direction of *z* (south) */ 44 | val UNIT_Z = Vector3(0.0, 0.0, 1.0) 45 | /** A unit vector in the direction of *-x* (west) */ 46 | val UNIT_X_NEG = Vector3(-1.0, 0.0, 0.0) 47 | /** A unit vector in the direction of *-y* (down) */ 48 | val UNIT_Y_NEG = Vector3(0.0, -1.0, 0.0) 49 | /** A unit vector in the direction of *-z* (north) */ 50 | val UNIT_Z_NEG = Vector3(0.0, 0.0, -1.0) 51 | /** A vector pointing to the infinity in the (+,+,+) octant */ 52 | val INF = Vector3(POSITIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY) 53 | /** A vector pointing to the infinity in the (-,-,-) octant */ 54 | val INF_NEG = Vector3(NEGATIVE_INFINITY, NEGATIVE_INFINITY, NEGATIVE_INFINITY) 55 | } 56 | 57 | /** 58 | * Constructs a [Vector3] by specifying all coordinates 59 | */ 60 | constructor(x: Double, y: Double, z: Double) : this(doubleArrayOf(x, y, z)) 61 | 62 | /** The *x*-component value */ 63 | val x get() = values[0] 64 | /** The *y*-component value, which is orthogonal to *x* */ 65 | val y get() = values[1] 66 | /** The *z*-component value, which is in the direction of *x*×*y* */ 67 | val z get() = values[2] 68 | 69 | /** 70 | * Rounds this vector to an [IntVector3] 71 | */ 72 | fun round() = IntVector3(values.mapIntArray(Double::roundToInt)) 73 | 74 | /** 75 | * Rounds this vector to [dp] decimal places 76 | */ 77 | fun round(dp: Int): Vector3 { 78 | val factor = dp.pow10() 79 | return map { round(it * factor) / factor } 80 | } 81 | 82 | /** 83 | * Rounds down this vector to an [IntVector3] 84 | */ 85 | fun floor() = IntVector3(values.mapIntArray { floor(it).toInt() }) 86 | 87 | /** 88 | * Rounds up this vector to an [IntVector3] 89 | */ 90 | fun ceil() = IntVector3(values.mapIntArray { ceil(it).toInt() }) 91 | 92 | /** 93 | * Calculates the squared length of this vector 94 | */ 95 | val lengthSquared get() = values.mapDoubleArray { it * it }.sum() 96 | /** 97 | * Calculates the length (a.k.a. norm or modulus) of this vector. 98 | * 99 | * Use [lengthSquared] if only comparison is needed for less computation. 100 | */ 101 | val length get() = sqrt(lengthSquared.toDouble()) 102 | 103 | /** 104 | * @return a unit vector (length = 1) in the same direction as this vector 105 | */ 106 | val unit get() = this / length 107 | 108 | /** 109 | * Calculates the dot product of `this` and [v] 110 | */ 111 | infix fun dot(v: Vector3) = biMap(v) { a, b -> a * b }.values.sum() 112 | 113 | /** 114 | * Calculates the cross product of `this` and [v] 115 | */ 116 | infix fun cross(v: Vector3) = Vector3( 117 | y * v.z - z * v.y, 118 | z * v.x - x * v.z, 119 | x * v.y - y * v.x 120 | ) 121 | 122 | /** Calculates `this` + [that] */ 123 | operator fun plus(that: Vector3) = biMap(that) { a, b -> a + b } 124 | 125 | /** Calculates `this` - [that] */ 126 | operator fun minus(that: Vector3) = biMap(that) { a, b -> a + b } 127 | 128 | /** Multiplies the components of `this` by [scalar] */ 129 | operator fun times(scalar: Double) = map { it * scalar } 130 | 131 | /** 132 | * Divides the components of `this` by [scalar] 133 | * 134 | * @throws ArithmeticException if [scalar] is zero 135 | */ 136 | operator fun div(scalar: Double) = map { it / scalar } 137 | 138 | /** @return this vector */ 139 | operator fun unaryPlus() = this 140 | 141 | /** @return this vector in the opposite direction and same length */ 142 | operator fun unaryMinus() = map(Double::unaryMinus) 143 | 144 | /** @return this vector with all components flipped if negative */ 145 | fun abs(): Vector3 = map(::abs) 146 | 147 | /** 148 | * Calculates the squared distance between `this` and [that] 149 | */ 150 | fun distanceSquared(that: Vector3) = (this - that).lengthSquared 151 | 152 | /** 153 | * Calculates the distance between `this` and [that]. 154 | * 155 | * Use [distanceSquared] if only comparison is needed for less computation. 156 | */ 157 | infix fun distanceTo(that: Vector3) = (this - that).length 158 | 159 | /** @return this vector moved towards the side [side] by one unit */ 160 | fun side(side: Side) = this + side.vector 161 | 162 | /** @return this vector moved towards the side [side] by [steps] units */ 163 | fun side(side: Side, steps: Double) = this + side.vector * steps 164 | 165 | private inline fun map(fn: (Double) -> Double): Vector3 { 166 | val new = DoubleArray(values.size) 167 | for (i in 0 until values.size) new[i] = fn(values[i]) 168 | return Vector3(new) 169 | } 170 | 171 | private inline fun biMap(v: Vector3, fn: (Double, Double) -> Double): Vector3 { 172 | val new = DoubleArray(3) 173 | for (i in 0 until values.size) new[i] = fn(values[i], v.values[i]) 174 | return Vector3(new) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/annotations.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | import kotlin.annotation.AnnotationRetention.BINARY 4 | import kotlin.annotation.AnnotationTarget.* 5 | 6 | /* 7 | * GraphMine 8 | * Copyright (C) 2018 SOFe 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published 12 | * by the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | /** 25 | * Applied on a [Vector3] or [IntVector3] to indicate that it represents a positional vector, e.g. the position of an 26 | * object 27 | */ 28 | @Target(PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, FUNCTION) 29 | @Retention(BINARY) 30 | @MustBeDocumented 31 | annotation class Positional 32 | 33 | /** 34 | * Applied on a [Vector3] or [IntVector3] to indicate that it represents a relative vector, e.g. the distance between 35 | * two vectors 36 | */ 37 | @Target(PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, FUNCTION) 38 | @Retention(BINARY) 39 | @MustBeDocumented 40 | annotation class Relative 41 | -------------------------------------------------------------------------------- /util-math/src/main/kotlin/io/github/sof3/graphmine/util/math/math.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.math 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * Calculates 10 raised to the power of this 23 | */ 24 | fun Int.pow10() = Math.pow(10.0, toDouble()) 25 | -------------------------------------------------------------------------------- /util-reflect-io/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | plugins { 22 | java 23 | kotlin("jvm") 24 | } 25 | 26 | group = "io.github.sof3.graphmine" 27 | version = "1.0.0-SNAPSHOT" 28 | 29 | dependencies { 30 | api(project(":graphmine-util")) 31 | implementation(kotlin("stdlib-jdk8")) 32 | implementation(kotlin("reflect")) 33 | testImplementation(kotlin("test")) 34 | testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.0-alpha.2") 35 | testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.0-alpha.2") 36 | } 37 | -------------------------------------------------------------------------------- /util-reflect-io/module.md: -------------------------------------------------------------------------------- 1 | # Module util-reflect-io 2 | This module enables persisting data using a reflection-based API. 3 | -------------------------------------------------------------------------------- /util-reflect-io/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "util-reflect-io" 2 | -------------------------------------------------------------------------------- /util-reflect-io/src/main/kotlin/io/github/sof3/graphmine/util/reflectio/Persistable.kt: -------------------------------------------------------------------------------- 1 | package io.github.sof3.graphmine.util.reflectio 2 | 3 | /* 4 | * GraphMine 5 | * Copyright (C) 2018 SOFe 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | interface Persistable 22 | --------------------------------------------------------------------------------