Merge pull request #661 from BTreeNewBee/maaHTTP

feta.Maa-HTTP+Websocket接口实现
This commit is contained in:
MistEO
2022-06-01 00:06:28 +08:00
committed by GitHub
22 changed files with 1745 additions and 162 deletions

View File

@@ -0,0 +1,18 @@
@echo off
cd /d %~dp0
set javaPath=%cd%
cd ..
::显示上级目录路径
::设置要临时加入到path环境变量中的路径
set My_PATH=%cd%
set PATH=%PATH%;%My_PATH%
cd javaPath
::下面写你其它脚本命令
java -jar .\Maa-HTTP-0.0.1.jar -port=8848
pause

562
src/Java/Maaj/Readme.md Normal file
View File

@@ -0,0 +1,562 @@
# Maa-HTTP接口实现
本项目为通过JNA(Java-Native-Access)调用Maa核心实现的HTTP+WebSocket接口实现.
## 接口实现情况
| 功能名称 | 接口路径(命令) | HTTP实现 | WebSocket实现 |
|:-------|:--------------|:-------|:------------|
| 获取版本号 | getVersion | Yes | Yes |
| 创建连接 | connect | Yes | Yes |
| 添加任务 | appendTask | Yes | Yes |
| 设定任务参数 | setTaskParams | Yes | Yes |
| 开始执行 | start | Yes | Yes |
| 停止执行 | stop | Yes | Yes |
| 销毁实例 | destroy | Yes | Yes |
| 列出实例连接 | listInstance | Yes | Yes |
| 消息回调通知 | callBack | No | Yes |
## 环境依赖
1. JDK1.8 +
2. Maa v3.9.0-alpha +
## 如何编译
.\gradlew.bat jar
## 使用方法
1. 把他丢进Maa文件夹下,形成如下文件结构.
```
MeoAssistantArknights_v3.9.0-beta.8
│ MeoAsstGui.exe
│ MeoAssistant.dll
│ ...
└───Java-HTTP
│ Maa-HTTP-0.0.1.jar
│ Maa-HTTP-Server-startup.bat
```
2. 双击Maa-HTTP-Server-startup.bat启动.
3. 默认端口号8848钛金端口,如需修改,请修改 Maa-HTTP-Server-startup.bat 中的-port=8848 .
4. 当你看到如下内容则启动成功.
```
2022-05-31 22:20:03.000 [main] INFO Application - Autoreload is disabled because the development mode is off.
2022-05-31 22:20:03.407 [main] INFO Application - Application started in 0.458 seconds.
2022-05-31 22:20:03.407 [main] INFO Application - Application started: io.ktor.server.application.Application@fac80
2022-05-31 22:20:03.717 [DefaultDispatcher-worker-1] INFO Application - Responding at http://0.0.0.0:8848
```
## 接口文档
### HTTP部分
提供了如下功能
| 功能名称 | 接口路径 |
|:-------|:--------------|
| 获取版本号 | getVersion |
| 创建连接 | connect |
| 添加任务 | appendTask |
| 设定任务参数 | setTaskParams |
| 开始执行 | start |
| 停止执行 | stop |
| 销毁实例 | destroy |
| 列出连接 | listInstance |
---
#### 接口名称 获取版本号
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/getVersion
###### 2) 调用方式HTTP get
###### 3) 请求参数: 无
###### 4) 返回结果示例:
```json
{
"data": {
"version": "v3.9.0-beta"
},
"code": 0,
"message": "success"
}
```
---
##### 接口名称 创建连接
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/connect
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"adbPath": "C:\\MeoAssistantArknights3\\platform-tools\\adb.exe",
"host": "127.0.0.1:62001",
"detailJson": ""
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |-----------------|--------|-----|-----|
| adbPath | ADB地址 | String | Y | - |
| host | 实例地址 | String | Y | - |
| detailJson | 有什么用我也不知道,反正就是有 | String | N | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"result": true
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|---------|---- |
| id | 给连接实例创建的ID | String | - |
| result | 是否成功 | Boolean | - |
---
---
##### 接口名称 c
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/appendTask
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"type": "Fight",
"params": {
"stage": "LastBattle"
}
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |-------------------------------------------------------------------------------------------------------------------------------|--------|---- |-----|
| id | 创建连接返回的实例ID | String | Y | - |
| type | 任务类型,此次示例为刷理智战斗,详情参考[集成文档](https://raw.githubusercontent.com/MaaAssistantArknights/MaaAssistantArknights/master/docs/集成文档.md) | String | Y | - |
| params | 任务参数 | Json | Y | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"taskId": 0
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|------------|--------|---- |
| id | 实例ID | String | - |
| taskId | 任务ID,为0则失败 | Int | - |
---
##### 接口名称 设定任务参数
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/setTaskParams
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"type": "Fight",
"taskId": 0,
"params": {
"stage": "LastBattle"
}
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |-------------------------------------------------------------------------------------------------------------------------------|--------|---- |-----|
| id | 创建连接返回的实例ID | String | Y | - |
| taskId | 任务ID | Int | Y | - |
| type | 任务类型,此次示例为刷理智战斗,详情参考[集成文档](https://raw.githubusercontent.com/MaaAssistantArknights/MaaAssistantArknights/master/docs/集成文档.md) | String | Y | - |
| params | 任务参数 | Json | Y | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"result": false
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|---------|---- |
| id | 给连接实例创建的ID | String | - |
| result | 是否成功 | Boolean | - |
---
##### 接口名称 开始执行
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/start
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3"
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |--------|--------|---- |-----|
| id | 创建连接返回的实例ID | String | Y | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3",
"result": true
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|---------|---- |
| id | 给连接实例创建的ID | String | - |
| result | 是否成功 | Boolean | - |
---
##### 接口名称 停止执行,清空任务链
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/stop
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3"
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |--------|--------|---- |-----|
| id | 创建连接返回的实例ID | String | Y | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3",
"result": true
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|---------|---- |
| id | 给连接实例创建的ID | String | - |
| result | 是否成功 | Boolean | - |
---
##### 接口名称 销毁实例
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/destroy
###### 2) 调用方式HTTP post
###### 3) 请求参数:
```json
{
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3"
}
```
###### Post参数:
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |--------|--------|---- |-----|
| id | 创建连接返回的实例ID | String | Y | - |
###### 4) 请求返回结果:
```json
{
"data": {
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3",
"result": true
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|---------|---- |
| id | 给连接实例创建的ID | String | - |
| result | 是否成功 | Boolean | - |
---
##### 接口名称 列出实例连接
###### 1) 请求地址
> http://127.0.0.1:8848/API/V1/listInstance
###### 2) 调用方式HTTP get
###### 3) 请求参数: 无
###### 4) 请求返回结果:
```json
{
"data": {
"list": [
{
"id": "ccd76e0c367511158ca774ff951a22e8bb62f5d3",
"host": "127.0.0.1:62026",
"adbPath": "C:\\Users\\atmzx\\Desktop\\MeoAssistantArknights3\\platform-tools\\adb.exe",
"uuid": "",
"status": 0
}
]
},
"code": 0,
"message": "success"
}
```
###### 5) 请求返回结果参数说明:
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-------------|--------|---- |
| id | 实例ID | String | - |
| host | 连接的Host | String | - |
| adbPath | 使用的Adb地址 | String | - |
| uuid | 设备备ID保留 | String | - |
| status | 0待机中,1任务执行中 | Int | - |
### WebSocket部分
请求地址(没错跟HTTP同样是8848钛金端口
> ws://127.0.0.1:8848/API/V1
提供了如下功能
| 功能名称 | 命令 |
|:-------|:--------------|
| 获取版本号 | getVersion |
| 创建连接 | connect |
| 添加任务 | appendTask |
| 设定任务参数 | setTaskParams |
| 开始执行 | start |
| 停止执行 | stop |
| 销毁实例 | destroy |
| 列出实例连接 | listInstance |
| 消息回调通知 | callBack |
所有的的WebSocket接口跟HTTP接口请求响应参数均完全一致
---
##### 接口名称 获取版本号
```json
{
"command": "getVersion",
"msgId": 114514,
"data": {}
}
```
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |-----------------------------------|--------|---- |-----|
| command | 操作命令 | String |,Y | - |
| msgId | 消息ID服务端收到此次请求会用同一个ID答复便于请求响应关联 | Int | Y | - |
| data | 请求参数请参考同名接口的HTTP接口文档与HTTP接口完全一致 | Json | Y | - |
###### 请求返回结果:
```json
{
"data": {
"version": "v3.9.0-beta"
},
"command": "getVersion",
"msgId": 114514,
"code": 0,
"message": "success"
}
```
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-----------------------------------|--------|---- |
| command | 操作命令,与请求一致 | String | - |
| msgId | 消息ID此次请求的消息ID为114514则响应也是114514 | Int | - |
| data | 响应内容,请参考同名接口的HTTP接口文档与HTTP接口完全一致 | Json | - |
---
##### 接口名称 创建连接
```json
{
"command": "connect",
"msgId": 114514,
"data": {
"adbPath": "C:\\Users\\atmzx\\Desktop\\MeoAssistantArknights3\\platform-tools\\adb.exe",
"host": "127.0.0.1:62001"
}
}
```
| 字段名称 | 字段说明 | 类型 | 必填 | 备注 |
| ---- |---------------------------------|--------|---- |-----|
| command | 操作命令 | String |,Y | - |
| msgId | 消息ID服务端收到此次请求会用同一个ID答复便于请求响应关联 | Int | Y | - |
| data | 请求参数请参考同名接口的HTTP文档与HTTP接口完全一致 | Json | Y | - |
###### 请求返回结果:
```json
{
"data": {
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"result": true
},
"command": "connect",
"msgId": 114514,
"code": 0,
"message": "success"
}
```
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-----------------------------------|--------|---- |
| command | 操作命令,与请求一致 | String | - |
| msgId | 消息ID此次请求的消息ID为114514则响应也是114514 | Int | - |
| data | 响应内容,请参考同名接口的HTTP接口文档与HTTP接口完全一致 | Json | - |
appendTask setTaskParams start stop等接口不再描述均可遵循以上规则参考HTTP接口文档实现。
---
##### 接口名称 回调消息
###### 返回结果示例:
```json
{
"data": {
"id": "46b9d5cd60382f100b336b17d6817f8eda255f73",
"logId": 1,
"msg": 2,
"details": {
"uuid": "",
"details": {
"adb": "C:\\MeoAssistantArknights3\\platform-tools\\adb.exe",
"address": "127.0.0.1:62001",
"config": "General",
"width": 1280,
"height": 720
},
"what": "ResolutionGetted",
"why": ""
}
},
"command": "callBack",
"msgId": 0,
"code": 0,
"message": "success"
}
```
| 字段名称 | 字段说明 | 类型 | 备注 |
|------|-----------------------------------|--------|---- |
| command | 操作命令,callBack类型 | String | - |
| msgId | 消息IDcallBack 一律为0 | Int | - |
| data | 响应内容,详情参考[集成文档](https://raw.githubusercontent.com/MaaAssistantArknights/MaaAssistantArknights/master/docs/集成文档.md) | Json | - |

View File

@@ -1,18 +1,103 @@
val ktor_version = "2.0.2"
val kotlin_version = "1.6.10"
val logback_version = "1.2.11"
val appName = "Maa-HTTP"
plugins {
id("java")
application
kotlin("jvm") version "1.6.10"
kotlin("plugin.serialization") version "1.6.10"
id("edu.sc.seis.launch4j") version "2.5.3"
}
version = "1.0-SNAPSHOT"
group = "com.iguigui"
version = "0.0.1"
application {
mainClass.set("com.iguigui.maaj.ApplicationKt")
val isDevelopment: Boolean = project.ext.has("development")
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
}
repositories {
mavenCentral()
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/ktor/eap") }
}
dependencies {
//JNA
implementation("net.java.dev.jna:jna:5.11.0")
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("io.ktor:ktor-server-cors:$ktor_version")
implementation("io.ktor:ktor-server-websockets:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-server-call-logging:$ktor_version")
implementation("io.ktor:ktor-server-double-receive:$ktor_version")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
}
tasks.getByName<Test>("test") {
useJUnitPlatform()
tasks.jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes(
mapOf(
"Main-Class" to "com.iguigui.maaj.ApplicationKt",
"Implementation-Title" to "Maa-HTTP",
"Manifest-Version" to "1.0.0"
)
)
}
from(configurations.runtimeClasspath.get().map {
if (it.isDirectory) it else zipTree(it)
})
val sourcesMain = sourceSets.main.get()
sourcesMain.allSource.forEach { println("add from sources: ${it.name}") }
from(sourcesMain.output)
}
launch4j {
mainClassName = "com.iguigui.maaj.ApplicationKt"
icon = "${projectDir}/icons/myApp.ico"
}
tasks.withType<edu.sc.seis.launch4j.tasks.DefaultLaunch4jTask> {
outfile = "${appName}.exe"
mainClassName = "com.iguigui.maaj.ApplicationKt"
icon = "$projectDir/icon.ico"
productName = "${appName}"
}
tasks.register<edu.sc.seis.launch4j.tasks.Launch4jLibraryTask>("createFastStart") {
outfile = "${appName}.exe"
mainClassName = "com.iguigui.maaj.ApplicationKt"
icon = "$projectDir/icon.ico"
fileDescription = "The lightning fast implementation"
}
tasks.register<edu.sc.seis.launch4j.tasks.Launch4jLibraryTask>("MyApp-memory") {
fileDescription = "The default implementation with increased heap size"
maxHeapPercent = 50
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}

257
src/Java/Maaj/gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh
#!/usr/bin/env sh
#
# Copyright © 2015-2021 the original authors.
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,101 +17,67 @@
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -121,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -132,7 +98,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -140,95 +106,80 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=$( cygpath --unix "$JAVACMD" )
JAVACMD=`cygpath --unix "$JAVACMD"`
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

View File

@@ -1,2 +1,16 @@
rootProject.name = "Maaj"
pluginManagement {
repositories {
maven { url = uri("https://repo.spring.io/milestone") }
maven { url = uri("https://repo.spring.io/snapshot") }
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "org.springframework.boot") {
useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
}
}
}
}
rootProject.name = "Maa-HTTP"

View File

@@ -0,0 +1,80 @@
package com.iguigui.maaj
import com.iguigui.maaj.routing.httpRouting
import com.iguigui.maaj.routing.wsRouting
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.doublereceive.*
import io.ktor.server.request.*
import io.ktor.server.websocket.*
import io.ktor.util.logging.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import org.slf4j.event.Level
fun main(args: Array<String>): Unit = EngineMain.main(args)
fun Application.module() {
configure()
httpRouting()
wsRouting()
}
lateinit var logger : Logger
fun Application.configure() {
logger = log
//Input and output serialize
install(ContentNegotiation) {
json(Json {
encodeDefaults = true
isLenient = true
allowSpecialFloatingPointValues = true
allowStructuredMapKeys = true
prettyPrint = false
useArrayPolymorphism = false
})
}
install(CORS) {
allowCredentials = true
anyHost()
}
install(WebSockets) {
pingPeriod = java.time.Duration.ofSeconds(15)
timeout = java.time.Duration.ofSeconds(15)
maxFrameSize = Long.MAX_VALUE
masking = false
}
//Provides the ability to receive a request body several times.
install(DoubleReceive) {
}
//Logging request body.
install(CallLogging) {
level = Level.INFO
format { call ->
val status = call.response.status()
val httpMethod = call.request.httpMethod
val queryParameters = call.request.rawQueryParameters
when (httpMethod) {
HttpMethod.Get -> "Status: $status, HTTP method: $httpMethod, Uri: ${call.request.uri}, QueryParameters: $queryParameters "
HttpMethod.Post -> runBlocking {
"Status: $status, HTTP method: $httpMethod, Uri: ${call.request.uri} ,Body: ${
call.receive<JsonElement>().toString()
} "
}
else -> {
"Status: $status, HTTP method: $httpMethod, Uri: ${call.request.uri}, QueryParameters: $queryParameters "
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
package com.iguigui.maaj.dto
/* Global Info */
val INTERNAL_ERROR = 0 // 内部错误
val INIT_FAILED = 1 // 初始化失败
val CONNECTION_INFO = 2 // 连接相关信息
val ALL_TASKS_COMPLETED = 3 // 全部任务完成
/* TaskChain Info */
val TASK_CHAIN_ERROR = 10000 // 任务链执行/识别错误
val TASK_CHAIN_START = 10001 // 任务链开始
val TASK_CHAIN_COMPLETED = 10002 // 任务链完成
val TASK_CHAIN_EXTRA_INFO = 10003 // 任务链额外信息
/* SubTask Info */
val SUB_TASK_ERROR = 20000 // 原子任务执行/识别错误
val SUB_TASK_START = 20001 // 原子任务开始
val SUB_TASK_COMPLETED = 20002 // 原子任务完成
val SUB_TASK_EXTRA_INFO = 20003 // 原子任务额外信息

View File

@@ -0,0 +1,179 @@
package com.iguigui.maaj.dto
import com.iguigui.maaj.util.Json
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonElement
@Serializable
data class ConnectRequest(
@SerialName("adbPath")
val adbPath: String,
@SerialName("host")
val host: String,
@SerialName("detailJson")
val detailJson: String = ""
)
@Serializable
data class AppendTaskRequest(
@SerialName("id")
val id: String,
@SerialName("type")
val type: String,
@SerialName("params")
val params: JsonElement
)
@Serializable
data class SetTaskParamsRequest(
@SerialName("id")
val id: String,
@SerialName("type")
val type: String,
@SerialName("taskId")
val taskId: Int,
@SerialName("params")
val params: JsonElement
)
@Serializable
sealed class Task
@Serializable
data class Fight(
@SerialName("medicine")
val medicine: Int?,
@SerialName("penguin_id")
val penguinId: String?,
@SerialName("report_to_penguin")
val reportToPenguin: Boolean?,
@SerialName("server")
val server: String?,
@SerialName("stage")
val stage: String?,
@SerialName("stone")
val stone: Int?,
@SerialName("times")
val times: Int?
) : Task()
@Serializable
data class Recruit(
@SerialName("confirm")
val confirm: List<Int>,
@SerialName("expedite")
val expedite: Boolean?,
@SerialName("expedite_times")
val expediteTimes: Int?,
@SerialName("refresh")
val refresh: Boolean?,
@SerialName("select")
val select: List<Int>,
@SerialName("set_time")
val setTime: Boolean?,
@SerialName("times")
val times: Int?
) : Task()
@Serializable
data class Infrast(
@SerialName("drones")
val drones: String?,
@SerialName("facility")
val facility: List<String>,
@SerialName("mode")
val mode: Int?,
@SerialName("replenish")
val replenish: Boolean?,
@SerialName("threshold")
val threshold: Double?
) : Task()
@Serializable
class Visit : Task()
@Serializable
data class Mall(
@SerialName("blacklist")
val blacklist: List<String>?,
@SerialName("buy_first")
val buyFirst: List<String>?,
@SerialName("shopping")
val shopping: Boolean?
) : Task()
@Serializable
class Award : Task()
@Serializable
data class Roguelike(
@SerialName("mode")
val mode: Int?,
@SerialName("opers")
val opers: List<Oper>
) : Task()
@Serializable
data class Oper(
@SerialName("name")
val name: String,
@SerialName("skill")
val skill: Int?,
@SerialName("skill_usage")
val skillUsage: Int?
)
@Serializable
data class Copilot(
@SerialName("filename")
val filename: String,
@SerialName("formation")
val formation: Boolean,
@SerialName("stage_name")
val stageName: String?
)
@Serializable
data class StartRequest(
@SerialName("id")
val id: String,
)
@Serializable
data class StopRequest(
@SerialName("id")
val id: String,
)
@Serializable
data class DestroyRequest(
@SerialName("id")
val id: String,
)
@SerialName("connect")
@Serializable
data class WsRequest(
@SerialName("data")
val `data`: JsonElement,
@SerialName("command")
val command: String,
@SerialName("msgId")
val msgId: Int = 0
)
fun Task.toJsonString() = Json.encodeToString(this)

View File

@@ -0,0 +1,141 @@
package com.iguigui.maaj.dto
import com.iguigui.maaj.util.Json
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
@Serializable
data class HttpResponse(
@SerialName("data")
val data: JsonElement = EmptyBaseData.toJsonElement(),
@SerialName("code")
val code: Int = 0,
@SerialName("message")
val message: String = "success"
)
@Serializable
data class WsResponse(
@SerialName("data")
val data: JsonElement,
@SerialName("command")
val command: String,
@SerialName("msgId")
val msgId: Int = 0,
@SerialName("code")
val code: Int = 0,
@SerialName("message")
val message: String = "success"
)
@Serializable
sealed class BaseData
@Serializable
data class GetVersionResponse(
@SerialName("version")
val version: String
) : BaseData()
@Serializable
data class ConnectResponse(
@SerialName("id")
val id: String,
@SerialName("result")
val result: Boolean
) : BaseData()
@Serializable
data class ListInstanceResponse(
@SerialName("list")
val list: List<MaaInstanceInfo>
) : BaseData()
@Serializable
data class MaaInstanceInfo(
@SerialName("id")
val id: String,
@SerialName("host")
val host: String,
@SerialName("adbPath")
val adbPath: String,
@SerialName("uuid")
val uuid: String,
@SerialName("status")
val status: Int,
) : BaseData()
@Serializable
data class AppendTaskResponse(
@SerialName("id")
val id: String,
@SerialName("taskId")
val taskId: Int
) : BaseData()
@Serializable
data class SetTaskParamsResponse(
@SerialName("id")
val id: String,
@SerialName("result")
val result: Boolean
) : BaseData()
@Serializable
data class StartResponse(
@SerialName("id")
val id: String,
@SerialName("result")
val result: Boolean
) : BaseData()
@Serializable
data class StopResponse(
@SerialName("id")
val id: String,
@SerialName("result")
val result: Boolean
) : BaseData()
@Serializable
data class CallBackLog(
@SerialName("id")
val id: String,
@SerialName("logId")
val logId: Long,
@SerialName("msg")
val msg: Int,
@SerialName("details")
val details: JsonElement,
) : BaseData()
@Serializable
object EmptyBaseData : BaseData()
fun BaseData.toJsonElement() =
Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" })
fun BaseData.wapperToHttpResponse() =
HttpResponse(Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }))
fun BaseData.wapperToWsResponse(command: String, msgId: Int) =
WsResponse(
Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }),
command,
msgId
)
fun WsResponse.toJsonString() = Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }).toString()

View File

@@ -1,4 +1,4 @@
package com.iguigui.maaj;
package com.iguigui.maaj.easySample;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
@@ -8,7 +8,7 @@ import java.util.Scanner;
public class MaaJavaSample {
/**
* Maa的Java调用实例实现项目需依赖JNA库
* Maa的Java调用实例实现最简示例仅需依赖第三方JNA库
* 仅在Windows下进行过测试未在Linux下测试过(相信强大的Linux用户自己可以解决问题
*
* --------特别提醒----------

View File

@@ -1,4 +1,4 @@
package com.iguigui.maaj;
package com.iguigui.maaj.easySample;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary;

View File

@@ -0,0 +1,68 @@
package com.iguigui.maaj.routing
import com.iguigui.maaj.dto.*
import com.iguigui.maaj.service.MaaService
import com.iguigui.maaj.service.MaaService.appendTask
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.jsonObject
fun Application.httpRouting() {
routing {
route("/API/V1") {
get("/getVersion") {
val version = MaaService.getVersion()
call.respond(GetVersionResponse(version).wapperToHttpResponse())
}
post("/connect") {
with(call.receive<ConnectRequest>()) {
val connect = MaaService.connect(adbPath, host, detailJson)
call.respond(connect.wapperToHttpResponse())
}
}
post("/appendTask") {
with(call.receive<AppendTaskRequest>()) {
val result = appendTask(
id,
type,
params.jsonObject.toString()
)
call.respond(AppendTaskResponse(id, result).wapperToHttpResponse())
}
}
post("/setTaskParams") {
with(call.receive<SetTaskParamsRequest>()) {
val result = MaaService.setTaskParams(
id,
taskId,
params.jsonObject.toString()
)
call.respond(SetTaskParamsResponse(id, result).wapperToHttpResponse())
}
}
post("/start") {
val startRequest = call.receive<StartRequest>()
val result = MaaService.start(startRequest.id)
call.respond(StartResponse(startRequest.id, result).wapperToHttpResponse())
}
post("/stop") {
val stopRequest = call.receive<StopRequest>()
val result = MaaService.stop(stopRequest.id)
call.respond(StartResponse(stopRequest.id, result).wapperToHttpResponse())
}
post("/destroy") {
val destroy = call.receive<DestroyRequest>()
MaaService.destroy(destroy.id)
call.respond(EmptyBaseData.wapperToHttpResponse())
}
get("/listInstance") {
val list = MaaService.listInstance()
call.respond(ListInstanceResponse(list).wapperToHttpResponse())
}
}
}
}

View File

@@ -0,0 +1,101 @@
package com.iguigui.maaj.routing
import com.iguigui.maaj.dto.*
import com.iguigui.maaj.logger
import com.iguigui.maaj.service.Connection
import com.iguigui.maaj.service.MaaService
import com.iguigui.maaj.service.MaaService.addConnection
import com.iguigui.maaj.service.MaaService.appendTask
import com.iguigui.maaj.service.MaaService.removeConnection
import com.iguigui.maaj.util.Json
import io.ktor.websocket.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.serialization.json.jsonObject
import java.util.*
import kotlin.collections.LinkedHashSet
fun Application.wsRouting() {
routing {
webSocket("/API/V1") {
val connection = Connection(this)
logger.info("Ws connection connected ! connection : ${connection.session} ")
addConnection(connection)
try {
for (frame in incoming) {
frame as? Frame.Text ?: continue
val receivedText = frame.readText()
logger.info(receivedText)
val wsRequest = Json.decodeFromString(WsRequest.serializer(), receivedText)
var response: BaseData? = null
when (wsRequest.command) {
"getVersion" -> {
response = GetVersionResponse(MaaService.getVersion())
}
"connect" -> {
with(Json.decodeFromJsonElement(ConnectRequest.serializer(), wsRequest.data)) {
response = MaaService.connect(adbPath, host, detailJson)
}
}
"appendTask" -> {
with(Json.decodeFromJsonElement(AppendTaskRequest.serializer(), wsRequest.data)) {
val result = appendTask(
id,
type,
params.jsonObject.toString()
)
response = AppendTaskResponse(id, result)
}
}
"setTaskParams" -> {
with(Json.decodeFromJsonElement(SetTaskParamsRequest.serializer(), wsRequest.data)) {
val result = MaaService.setTaskParams(
id,
taskId,
params.jsonObject.toString()
)
response = SetTaskParamsResponse(id, result)
}
}
"start" -> {
val startRequest =
Json.decodeFromJsonElement(StartRequest.serializer(), wsRequest.data)
val result = MaaService.start(startRequest.id)
response = StartResponse(startRequest.id, result)
}
"stop" -> {
val stopRequest =
Json.decodeFromJsonElement(StopRequest.serializer(), wsRequest.data)
val result = MaaService.stop(stopRequest.id)
response = StopResponse(stopRequest.id, result)
}
"destroy" -> {
val destroyRequest =
Json.decodeFromJsonElement(DestroyRequest.serializer(), wsRequest.data)
MaaService.destroy(destroyRequest.id)
response = EmptyBaseData
}
"listInstance" -> {
val list = MaaService.listInstance()
response = ListInstanceResponse(list)
}
else -> {}
}
response?.let {
send(it.wapperToWsResponse(wsRequest.command, wsRequest.msgId).toJsonString())
}
}
} catch (e: Exception) {
logger.warn("Ws Exception $e.message")
logger.warn(e.stackTraceToString())
} finally {
logger.info("Ws connection disconnected ! connection : $connection ")
removeConnection(connection)
}
}
}
}

View File

@@ -0,0 +1,11 @@
package com.iguigui.maaj.service
import io.ktor.websocket.*
import java.util.concurrent.atomic.AtomicInteger
class Connection(val session: DefaultWebSocketSession) {
companion object {
var lastId = AtomicInteger(0)
}
val name = "user${lastId.getAndIncrement()}"
}

View File

@@ -0,0 +1,85 @@
package com.iguigui.maaj.service
import com.iguigui.maaj.dto.CallBackLog
import com.iguigui.maaj.dto.*
import com.iguigui.maaj.easySample.MeoAssistant
import com.iguigui.maaj.logger
import com.iguigui.maaj.util.Json
import com.sun.jna.Pointer
import io.ktor.server.application.*
import java.util.concurrent.atomic.AtomicLong
import kotlin.reflect.KFunction1
class MaaInstance(
private val instance: MeoAssistant,
val id: String,
val adbPath: String,
val host: String,
private val detailJson: String,
val callBackAction: KFunction1<CallBackLog, Unit>
) :
MeoAssistant.AsstApiCallback {
// constructor(
// instance: MeoAssistant,
// id: String,
// adbPath: String,
// host: String,
// pointer: Pointer,
// detailJson: String
// ) : this(
// instance,
// id,
// adbPath,
// host,
// detailJson
// ) {
// this.pointer = pointer
// }
lateinit var pointer: Pointer
var uuid = ""
//状态码0初始化未运行 1运行中
var status = 0
// private var logQueue: ArrayBlockingQueue<CallBackLog> = ArrayBlockingQueue(100)
//
private var msgId = AtomicLong(0)
override fun callback(msg: Int, detail_json: String, custom_arg: String) {
val callBackLog = CallBackLog(id, msgId.getAndIncrement(), msg, Json.parseToJsonElement(detail_json))
logger.info("callBackLog = $callBackLog")
callBackAction.call(callBackLog)
when (msg) {
INTERNAL_ERROR,
INIT_FAILED,
ALL_TASKS_COMPLETED,
TASK_CHAIN_ERROR,
TASK_CHAIN_COMPLETED,
SUB_TASK_ERROR,
SUB_TASK_COMPLETED -> status = 0
//也不知道啥算正在运行,姑且认为任务链开始和原子任务开始肯定是在运行中吧
TASK_CHAIN_START,
SUB_TASK_START -> status = 1
else -> {}
}
}
fun connect(): Boolean = instance.AsstConnect(pointer, adbPath, host, detailJson)
fun appendTask(type: String, params: String) = instance.AsstAppendTask(pointer, type, params)
fun setTaskParams(taskId: Int, params: String) = instance.AsstSetTaskParams(pointer, taskId, params)
fun start(): Boolean {
msgId.set(0)
return instance.AsstStart(pointer)
}
fun stop() = instance.AsstStop(pointer)
fun destroy() = instance.AsstDestroy(pointer)
}

View File

@@ -0,0 +1,98 @@
package com.iguigui.maaj.service
import com.iguigui.maaj.dto.*
import com.iguigui.maaj.easySample.MeoAssistant
import com.iguigui.maaj.logger
import com.sun.jna.Native
import io.ktor.websocket.*
import kotlinx.coroutines.runBlocking
import java.io.File
import java.security.MessageDigest
import java.util.*
import java.util.concurrent.ConcurrentHashMap
object MaaService {
private var instancePool: ConcurrentHashMap<String, MaaInstance> = ConcurrentHashMap()
private val wsConnection = Collections.synchronizedSet<Connection?>(LinkedHashSet())
val meoAssistant: MeoAssistant by lazy {
var maaPath = File(File("").absolutePath).parent
logger.info("maaPath $maaPath")
// maaPath = "C:\\Users\\atmzx\\Desktop\\MeoAssistantArknights3"
System.setProperty("jna.library.path", maaPath)
val load = Native.load("MeoAssistant", MeoAssistant::class.java)
load.AsstLoadResource(maaPath)
load
}
fun connect(adbPath: String, host: String, detailJson: String): ConnectResponse {
val id = sha1(host)
// if (instancePool.containsKey(id)) {
// return ConnectResponse(id, true)
// }
val maaInstance = MaaInstance(meoAssistant, id, adbPath, host, detailJson, ::callBackLog)
maaInstance.pointer = meoAssistant.AsstCreateEx(maaInstance, maaInstance.id)
val connect = maaInstance.connect()
if (!connect) {
return ConnectResponse("", false)
}
instancePool[id] = maaInstance
return ConnectResponse(id, true)
}
fun appendTask(id: String, type: String, detailJson: String) =
instancePool[id]?.appendTask(type, detailJson) ?: 0
fun setTaskParams(id: String, taskId: Int, detailJson: String) =
instancePool[id]?.setTaskParams(taskId, detailJson) ?: false
fun start(id: String) = instancePool[id]?.start() ?: false
fun stop(id: String) = instancePool[id]?.stop() ?: false
fun getVersion(): String = meoAssistant.AsstGetVersion()
private fun sha1(password: String): String {
val messageDigest = MessageDigest.getInstance("SHA")
val digests = messageDigest.digest(password.toByteArray())
val stringBuilder = StringBuilder()
for (i in digests.indices) {
var halfbyte = digests[i].toInt() ushr 4 and 0x0F
for (j in 0..1) {
stringBuilder.append(
if (0 <= halfbyte && halfbyte <= 9) ('0'.code + halfbyte).toChar() else ('a'.code + (halfbyte - 10)).toChar()
)
halfbyte = digests[i].toInt() and 0x0F
}
}
return stringBuilder.toString()
}
fun listInstance(): List<MaaInstanceInfo> =
instancePool.values.map { e -> MaaInstanceInfo(e.id, e.host, e.adbPath, e.uuid, e.status) }.toList()
fun destroy(id: String) {
instancePool[id]?.destroy()
instancePool.remove(id)
}
fun callBackLog(message: CallBackLog) {
runBlocking {
wsConnection.forEach { it.session.send(message.wapperToWsResponse("callBack", 0).toJsonString()) }
}
}
fun addConnection(connection: Connection) {
this.wsConnection += connection
}
fun removeConnection(connection: Connection) {
this.wsConnection -= connection
}
}

View File

@@ -0,0 +1,10 @@
package com.iguigui.maaj.util
import kotlinx.serialization.json.Json
val Json by lazy {
Json {
ignoreUnknownKeys = true
encodeDefaults = true
}
}

View File

@@ -0,0 +1,8 @@
ktor {
deployment {
port = 8848
}
application {
modules = [ com.iguigui.maaj.ApplicationKt.module ]
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,13 @@
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>Maaj.log</file>
<append>true</append>
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE"/>
</root>
<logger name="io.netty" level="INFO"/>
</configuration>

View File

@@ -0,0 +1,32 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="io.netty" level="INFO"/>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/maa-http.log </file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>maa-http-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>10MB</maxFileSize>
<maxHistory>10</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<append>true</append>
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="ROLLING"/>
</root>
</configuration>

View File

@@ -0,0 +1,95 @@
package com.iguigui.maaj.routing;
import com.iguigui.maaj.configure
import com.iguigui.maaj.dto.GetVersionResponse
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.HttpStatusCode.Companion.OK
import io.ktor.server.testing.*
import org.junit.Assert
import org.junit.Before
import kotlin.test.Test
class HttpRoutingKtTest {
// @Test
// fun testGetApiV1Getversion() = testApplication {
// application {
// httpRouting()
// }
// client.get("/API/V1/getVersion").apply {
// Assert.assertEquals(status,OK)
// }
// }
//
// @Test
// fun testPostApiV1Connect() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/connect").apply {
//
// }
// }
//
// @Test
// fun testPostApiV1Appendtask() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/appendTask").apply {
// TODO("Please write your test here")
// }
// }
//
// @Test
// fun testPostApiV1Settaskparams() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/setTaskParams").apply {
// TODO("Please write your test here")
// }
// }
//
// @Test
// fun testPostApiV1Start() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/start").apply {
// TODO("Please write your test here")
// }
// }
//
// @Test
// fun testPostApiV1Stop() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/stop").apply {
// TODO("Please write your test here")
// }
// }
//
// @Test
// fun testPostApiV1Destroy() = testApplication {
// application {
// httpRouting()
// }
// client.post("/API/V1/destroy").apply {
// TODO("Please write your test here")
// }
// }
//
// @Test
// fun testGetApiV1Listinstance() = testApplication {
// application {
// httpRouting()
// }
// client.get("/API/V1/listInstance").apply {
// TODO("Please write your test here")
// }
// }
}