refactor: 重构开发环境
BREAKING CHANGE: 由com.r3944realms.dg_lab_api -> com.r3944realms.dg_lab.api
This commit is contained in:
commit
4b15d70e06
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
19
.idea/gradle.xml
Normal file
19
.idea/gradle.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$PROJECT_DIR$/../projEnv/gradle/gradle-8.12" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/Common" />
|
||||
<option value="$PROJECT_DIR$/CommonApi" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/misc.xml
Normal file
14
.idea/misc.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ASMIdeaPluginConfiguration">
|
||||
<asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" />
|
||||
<groovy codeStyle="LEGACY" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
90
Common/build.gradle
Normal file
90
Common/build.gradle
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'java-library'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
group = project_group
|
||||
version = project_version
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = "${project_name}-${common_suffix}"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Common 依赖 CommonApi
|
||||
api project(':CommonApi')
|
||||
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
|
||||
implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.23.1'
|
||||
implementation 'org.realityforge.org.jetbrains.annotations:org.jetbrains.annotations:1.7.0'
|
||||
implementation 'org.apache.commons:commons-lang3:3.17.0'
|
||||
implementation 'com.google.guava:guava:33.3.0-jre'
|
||||
implementation 'io.netty:netty-all:4.1.109.Final'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'org.slf4j:slf4j-api:2.0.16'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
// Sources JAR
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allSource
|
||||
archiveClassifier = 'sources'
|
||||
}
|
||||
|
||||
// Javadoc JAR
|
||||
task javadocJar(type: Jar) {
|
||||
dependsOn javadoc
|
||||
from javadoc.destinationDir
|
||||
archiveClassifier = 'javadoc'
|
||||
}
|
||||
|
||||
// Shadow JAR: 只把 CommonApi 打包进 Common
|
||||
shadowJar {
|
||||
// 只把 CommonApi 的源代码 + Common 自己的源代码打包
|
||||
from sourceSets.main.output // Common 自己的代码
|
||||
from project(':CommonApi').sourceSets.main.output // CommonApi 的代码
|
||||
|
||||
// 不要包含 runtimeClasspath 的依赖
|
||||
configurations = [] // 空数组,不包含其它依赖
|
||||
|
||||
mergeServiceFiles()
|
||||
archiveClassifier.set('') // 覆盖默认 jar
|
||||
}
|
||||
|
||||
|
||||
// 将 shadowJar 绑定到 build
|
||||
build.dependsOn shadowJar
|
||||
|
||||
// Javadoc 配置
|
||||
javadoc {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
// 发布配置
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact shadowJar
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
groupId = project.group.toString()
|
||||
artifactId = base.archivesName.get()
|
||||
version = project.version.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Common/src/main/java/com/r3944realms/dg_lab/DgLab.java
Normal file
43
Common/src/main/java/com/r3944realms/dg_lab/DgLab.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab;
|
||||
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* The type Dg lab.
|
||||
*/
|
||||
public class DgLab {
|
||||
/**
|
||||
* The constant LOGGING_HANDLER.
|
||||
*/
|
||||
public static LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
|
||||
|
||||
|
||||
/**
|
||||
* Sets logging handler level.
|
||||
*
|
||||
* @param level the level
|
||||
*/
|
||||
public void setLoggingHandlerLevel(LogLevel level) {
|
||||
LOGGING_HANDLER = new LoggingHandler(level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.manager;
|
||||
|
||||
import com.r3944realms.dg_lab.api.manager.Status;
|
||||
import com.r3944realms.dg_lab.websocket.PowerBoxWSClient;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ClientPowerBoxSharedData;
|
||||
|
||||
/**
|
||||
* The type Dgpb client manager.
|
||||
*/
|
||||
public class DGPBClientManager implements IDGLabManager {
|
||||
/**
|
||||
* The Client.
|
||||
*/
|
||||
protected final PowerBoxWSClient client;
|
||||
|
||||
/**
|
||||
* Instantiates a new Dgpb client manager.
|
||||
*
|
||||
* @param client the client
|
||||
*/
|
||||
public DGPBClientManager(PowerBoxWSClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
@Override
|
||||
public void start() {
|
||||
client.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPowerBoxSharedData getSharedData() {
|
||||
return client.getSharedData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return client.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(Status status) {
|
||||
client.setStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets address.
|
||||
*
|
||||
* @param address the address
|
||||
*/
|
||||
public void setAddress(String address) {
|
||||
client.setAddress(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets port.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
client.setPort(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets address and port.
|
||||
*
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public void setAddressAndPort(String address, int port) {
|
||||
client.setAddressAndPort(address, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send.
|
||||
*
|
||||
* @param message the message
|
||||
*/
|
||||
public void send(Message message) {
|
||||
client.send(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.manager;
|
||||
|
||||
import com.r3944realms.dg_lab.api.manager.IDGLabManager;
|
||||
import com.r3944realms.dg_lab.api.manager.Status;
|
||||
import com.r3944realms.dg_lab.websocket.PowerBoxWSServer;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ServerPowerBoxSharedData;
|
||||
|
||||
/**
|
||||
* The type Dgpb server manager.
|
||||
*/
|
||||
public class DGPBServerManager implements IDGLabManager {
|
||||
/**
|
||||
* The Server.
|
||||
*/
|
||||
protected final PowerBoxWSServer server;
|
||||
|
||||
/**
|
||||
* Instantiates a new Dgpb server manager.
|
||||
*
|
||||
* @param server the server
|
||||
*/
|
||||
public DGPBServerManager(PowerBoxWSServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
@Override
|
||||
public void start() {
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return server.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(Status status) {
|
||||
server.setStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPowerBoxSharedData getSharedData() {
|
||||
return server.getSharedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send.
|
||||
*
|
||||
* @param connectId the connect id
|
||||
* @param message the message
|
||||
*/
|
||||
public void send(String connectId, Message message) {
|
||||
server.send(connectId, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets port.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
server.setPort(port);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.manager;
|
||||
|
||||
import com.r3944realms.dg_lab.utils.unSafe.AtomicClass;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* DGLab管理接口
|
||||
*/
|
||||
public interface IDGLabManager extends com.r3944realms.dg_lab.api.manager.IDGLabManager {
|
||||
/**
|
||||
* The constant CLIENT_INSTANCE.
|
||||
*/
|
||||
AtomicClass<DGPBClientManager> CLIENT_INSTANCE = new AtomicClass<>(null);
|
||||
/**
|
||||
* The constant SERVER_INSTANCE.
|
||||
*/
|
||||
AtomicClass<DGPBServerManager> SERVER_INSTANCE = new AtomicClass<>(null);
|
||||
/**
|
||||
* The constant logger.
|
||||
*/
|
||||
Logger logger = LoggerFactory.getLogger(IDGLabManager.class);
|
||||
|
||||
/**
|
||||
* 设置单例客户端管理类
|
||||
*
|
||||
* @param instance 单例客户端管理类
|
||||
*/
|
||||
static void setClientManagerInstance(DGPBClientManager instance) {
|
||||
CLIENT_INSTANCE.set(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单例服务器管理类
|
||||
*
|
||||
* @param instance 单例服务器管理类
|
||||
*/
|
||||
static void setServerManagerInstance(DGPBServerManager instance) {
|
||||
SERVER_INSTANCE.set(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前单例客户端管理类
|
||||
*
|
||||
* @return 单例客户端管理类 client manager instance
|
||||
*/
|
||||
static DGPBClientManager getClientManagerInstance() {
|
||||
DGPBClientManager dgpbClientManager = CLIENT_INSTANCE.get();
|
||||
if (dgpbClientManager == null)
|
||||
throw new IllegalStateException("Client manager is not set or is null");
|
||||
return dgpbClientManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前单例服务器管理类
|
||||
*
|
||||
* @return 单例服务器管理类 server manager instance
|
||||
*/
|
||||
static DGPBServerManager getServerManagerInstance() {
|
||||
DGPBServerManager dgpbServerManager = SERVER_INSTANCE.get();
|
||||
if (dgpbServerManager == null)
|
||||
throw new IllegalStateException("Server manager is not set or is null");
|
||||
return dgpbServerManager;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.annoation;
|
||||
|
||||
/**
|
||||
* 需要未来完善
|
||||
*/
|
||||
public @interface NeedCompletedInFuture {
|
||||
/**
|
||||
* Future target string.
|
||||
*
|
||||
* @return 未来完善的方向 string
|
||||
*/
|
||||
String futureTarget() default "";
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.enums;
|
||||
|
||||
/**
|
||||
* The enum Send mode.
|
||||
*/
|
||||
public enum SendMode {
|
||||
/**
|
||||
* 仅文本 <br/>
|
||||
* 即双端均用统一的格式发送Json信息<br/>
|
||||
* <br/>
|
||||
* PowerBox通讯<br/>Json如下:<br/>
|
||||
* <code>{"type":"XXX",clientId:"XXX-XXX-XXX-XXX",targetId:"XXX-XXX-XXX-XXX","message":"XXX"}</code>
|
||||
* <br/>详细可见{@link com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData}
|
||||
*/
|
||||
OnlyText,
|
||||
/**
|
||||
* 对客户端使用包含更多信息Message<br/>
|
||||
* 仅客户端与服务器通讯采用,app端仍然为原Text格式<br/>
|
||||
* 客户端与服务器端PowerBox通讯<br/>Json如下: <br/>
|
||||
* <code>{"commandType":"XXX","direction":{"sender":{"name":"uuid","type":"T_CLIENT"},"receiver":{"name":"uuid","type":"T_SERVER"}},"payload":{"timer_A":0,"timer_B":0,"type":"","clientId":"XXX-XXX-XXX-XXX","targetId":"XXX-XXX-XXX-XXX","message":"XXX"}}</code>
|
||||
* <br/>详细可见{@link com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage}
|
||||
*/
|
||||
ClientMessage
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.stringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type String handler util.
|
||||
*/
|
||||
public class StringHandlerUtil {
|
||||
/**
|
||||
* Gets char for string.
|
||||
*
|
||||
* @param str the str
|
||||
* @param index the index
|
||||
* @return the char for string
|
||||
*/
|
||||
public static char getCharForString(String str, int index) {
|
||||
return str.charAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build web socket url string.
|
||||
*
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
* @param ssl the ssl
|
||||
* @return the string
|
||||
*/
|
||||
public static String buildWebSocketURL(String address, int port, boolean ssl) {
|
||||
String scheme = ssl ? "wss" : "ws";
|
||||
return String.format("%s://%s:%d/", scheme, address, port).replace("%","%25");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add quotes string [ ].
|
||||
*
|
||||
* @param arr the arr
|
||||
* @return the string [ ]
|
||||
*/
|
||||
public static String[] addQuotes(String[] arr) {
|
||||
String[] newArr = new String[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
newArr[i] = "\"" + arr[i] + "\"";
|
||||
}
|
||||
return newArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove quotes string [ ].
|
||||
*
|
||||
* @param arr the arr
|
||||
* @return the string [ ]
|
||||
*/
|
||||
public static String[] removeQuotes(String[] arr) {
|
||||
String[] newArr = new String[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
newArr[i] = arr[i].substring(1, arr[i].length() - 1);
|
||||
}
|
||||
return newArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String[] 转化为 <code>["xxxxxxxxxxxxxxxx",......,"xxxxxxxxxxxxxxxx"]</code><br/><br/>
|
||||
* <b>例 :</b><code>[ff00ff, ac013021, 23990123]</code> -> <code>["ff00ff","ac013021","23990123"]</code>
|
||||
*
|
||||
* @param waveDataList the wave data list
|
||||
* @return the string
|
||||
*/
|
||||
public static String reformWaveDataList(String[] waveDataList) {
|
||||
List<String> b = new ArrayList<>(Arrays.asList(addQuotes(waveDataList)));
|
||||
return b.toString().replace(" ","");//去除空格
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.stringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* The type Url validator.
|
||||
*/
|
||||
public class UrlValidator {
|
||||
private static final String DOMAIN_REGEX = "^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$";
|
||||
private static final String IP_REGEX = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
|
||||
|
||||
private static final Pattern DOMAIN_PATTERN = Pattern.compile(DOMAIN_REGEX);
|
||||
private static final Pattern IP_PATTERN = Pattern.compile(IP_REGEX);
|
||||
|
||||
/**
|
||||
* Is valid address boolean.
|
||||
*
|
||||
* @param address the address
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isValidAddress(String address) {
|
||||
return isValidDomain(address) || isValidIP(address);
|
||||
}
|
||||
|
||||
private static boolean isValidDomain(String domain) {
|
||||
String[] parts = domain.split("\\.");
|
||||
if (parts.length < 2) {
|
||||
return false;
|
||||
}
|
||||
for (String part : parts) {
|
||||
Matcher matcher = DOMAIN_PATTERN.matcher(part);
|
||||
if (!matcher.matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isValidIP(String ip) {
|
||||
Matcher matcher = IP_PATTERN.matcher(ip);
|
||||
return matcher.matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid port boolean.
|
||||
*
|
||||
* @param Port the port
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isValidPort(int Port) {
|
||||
return Port >= 0 && Port <= 65535;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.timeTask;
|
||||
|
||||
|
||||
import com.r3944realms.dg_lab.utils.annoation.NeedCompletedInFuture;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.PlaceholderRole;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketClientRole;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.util.TimerTask;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The type Dg lab timer task.
|
||||
*/
|
||||
public class DgLabTimerTask extends TimerTask {
|
||||
/**
|
||||
* The Client.
|
||||
*/
|
||||
final ChannelHandlerContext client;
|
||||
/**
|
||||
* The Target.
|
||||
*/
|
||||
final ChannelHandlerContext target;
|
||||
/**
|
||||
* The Client id.
|
||||
*/
|
||||
String clientId, /**
|
||||
* The Target id.
|
||||
*/
|
||||
targetId;
|
||||
/**
|
||||
* The Total sends.
|
||||
*/
|
||||
Integer totalSends;
|
||||
/**
|
||||
* The Send message data.
|
||||
*/
|
||||
final Message sendMessageData;
|
||||
/**
|
||||
* The Timer consumer.
|
||||
*/
|
||||
final Consumer<Object> timerConsumer;
|
||||
/**
|
||||
* The Channel.
|
||||
*/
|
||||
final char channel;
|
||||
|
||||
/**
|
||||
* Instantiates a new Dg lab timer task.
|
||||
*
|
||||
* @param client the client
|
||||
* @param target the target
|
||||
* @param sendMessageData the send message data
|
||||
* @param totalSends the total sends
|
||||
* @param timerConsumer the timer consumer
|
||||
* @param channel the channel
|
||||
*/
|
||||
public DgLabTimerTask(ChannelHandlerContext client, ChannelHandlerContext target, Message sendMessageData, Integer totalSends, Consumer<Object> timerConsumer, char channel) {
|
||||
this.channel = channel;
|
||||
this.client = client;
|
||||
this.target = target;
|
||||
this.sendMessageData = sendMessageData;
|
||||
this.totalSends = totalSends;
|
||||
this.timerConsumer = timerConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets id.
|
||||
*
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
*/
|
||||
public void setId(String clientId, String targetId) {
|
||||
this.clientId = clientId;
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if(totalSends > 0) {//一一个固定频率发送特定的客户端
|
||||
send(target,sendMessageData);
|
||||
totalSends--;
|
||||
}
|
||||
if(totalSends <= 0) {//达到发送上限
|
||||
PowerBoxMessage over = PowerBoxMessage.createPowerBoxMessage("clientMsg", clientId, targetId, "send-over", new PlaceholderRole("Server"), new WebSocketClientRole("Cl" + clientId));
|
||||
send(client, over);
|
||||
timerConsumer.accept(null);
|
||||
}
|
||||
}
|
||||
@NeedCompletedInFuture
|
||||
private void send(ChannelHandlerContext target, Message message) {
|
||||
target.writeAndFlush(message.getDataJson());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.utils.unSafe;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* The type Atomic class.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
public class AtomicClass<T> {
|
||||
private static final VarHandle VALUE;
|
||||
private volatile T value;
|
||||
|
||||
/**
|
||||
* Instantiates a new Atomic class.
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
public AtomicClass(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
static {
|
||||
try {
|
||||
Field valueField = AtomicClass.class.getDeclaredField("value");
|
||||
VALUE = MethodHandles.lookup().unreflectVarHandle(valueField);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get t.
|
||||
*
|
||||
* @return the t
|
||||
*/
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set.
|
||||
*
|
||||
* @param newValue the new value
|
||||
*/
|
||||
public void set(T newValue) {
|
||||
while (true) {
|
||||
//noinspection unchecked
|
||||
T prev = (T) VALUE.getVolatile(this);
|
||||
if (VALUE.compareAndSet(this, prev, newValue)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket;
|
||||
|
||||
import com.r3944realms.dg_lab.DgLab;
|
||||
import com.r3944realms.dg_lab.api.manager.Status;
|
||||
import com.r3944realms.dg_lab.utils.stringUtils.StringHandlerUtil;
|
||||
import com.r3944realms.dg_lab.utils.stringUtils.UrlValidator;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 主要是处理netty逻辑以建立客户端
|
||||
*/
|
||||
public abstract class AbstractWebSocketClient {
|
||||
/**
|
||||
* 日志
|
||||
*/
|
||||
protected static final Logger logger = LoggerFactory.getLogger(AbstractWebSocketClient.class);
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
protected volatile String Address;
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
protected volatile int Port;
|
||||
/**
|
||||
* 客户端启动类
|
||||
*/
|
||||
private Bootstrap ClientBootstrap;
|
||||
/**
|
||||
* 客户端事件线程组
|
||||
*/
|
||||
private EventLoopGroup ClientEventLoopGroup;
|
||||
/**
|
||||
* The Client channel.
|
||||
*/
|
||||
protected Channel ClientChannel;
|
||||
|
||||
private Thread WebSocketClientThread;
|
||||
/**
|
||||
* The Client status.
|
||||
*/
|
||||
protected volatile Status ClientStatus = Status.WAITING_FOR_INIT;
|
||||
|
||||
/**
|
||||
* Gets status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return this.ClientStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets status.
|
||||
*
|
||||
* @param status the status
|
||||
*/
|
||||
public void setStatus(Status status) {
|
||||
this.ClientStatus = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract web socket client.
|
||||
*/
|
||||
protected AbstractWebSocketClient() {
|
||||
try {
|
||||
this.Address = InetAddress.getLocalHost().getHostAddress();
|
||||
this.Port = 9000;
|
||||
} catch (UnknownHostException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract web socket client.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
protected AbstractWebSocketClient(int port) {
|
||||
try {
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
this.Address = localHost.getHostAddress();
|
||||
this.Port = UrlValidator.isValidPort(port) ? port : 9000;
|
||||
} catch (UnknownHostException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract web socket client.
|
||||
*
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
protected AbstractWebSocketClient(String address, int port) {
|
||||
try {
|
||||
this.Address = UrlValidator.isValidAddress(address) ? address : InetAddress.getLocalHost().getHostAddress();
|
||||
this.Port = UrlValidator.isValidPort(port) ? port : 9000;
|
||||
} catch (UnknownHostException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets port.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
if (this.ClientStatus != Status.WAITING_FOR_INIT && this.ClientStatus != Status.STOPPED) {
|
||||
logger.error("Unable to change port to {}", port);
|
||||
return;
|
||||
}
|
||||
this.Port = UrlValidator.isValidPort(port) ? port : 9000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets address.
|
||||
*
|
||||
* @param address the address
|
||||
*/
|
||||
public void setAddress(String address) {
|
||||
if (this.ClientStatus != Status.WAITING_FOR_INIT && this.ClientStatus != Status.STOPPED) {
|
||||
logger.error("Unable to change address to {}", address);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.Address = UrlValidator.isValidAddress(address) ? address : InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets address and port.
|
||||
*
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public void setAddressAndPort(String address, int port) {
|
||||
try {
|
||||
setAddress(address);
|
||||
setPort(port);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets url.
|
||||
*
|
||||
* @return the url
|
||||
*/
|
||||
public String getUrl() {
|
||||
return StringHandlerUtil.buildWebSocketURL(Address, Port, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供实现此处添加对 Message处理的 Handler
|
||||
*
|
||||
* @param pipeline 管道
|
||||
*/
|
||||
protected abstract void MessagePipeLineHandler(ChannelPipeline pipeline);
|
||||
|
||||
/**
|
||||
* Init thread 0.
|
||||
*/
|
||||
protected void initThread0() {}
|
||||
|
||||
/**
|
||||
* Init thread.
|
||||
*/
|
||||
protected final void initThread() {
|
||||
ClientEventLoopGroup = new NioEventLoopGroup();
|
||||
WebSocketClientThread = new Thread(() -> {
|
||||
ClientStatus = Status.STARTING;
|
||||
ClientBootstrap = new Bootstrap();
|
||||
ClientBootstrap.group(ClientEventLoopGroup);
|
||||
ClientBootstrap.channel(NioSocketChannel.class);
|
||||
ClientBootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(DgLab.LOGGING_HANDLER,
|
||||
new HttpClientCodec(),
|
||||
new HttpObjectAggregator(65536),
|
||||
new WebSocketClientProtocolHandler(
|
||||
WebSocketClientHandshakerFactory.newHandshaker(
|
||||
URI.create(StringHandlerUtil.buildWebSocketURL(Address, Port, false)),
|
||||
WebSocketVersion.V13,
|
||||
null,
|
||||
false,
|
||||
new DefaultHttpHeaders()
|
||||
)
|
||||
),
|
||||
new WebSocketFrameAggregator(65536));
|
||||
MessagePipeLineHandler(pipeline);
|
||||
}
|
||||
});
|
||||
try {
|
||||
ChannelFuture channelFuture = ClientBootstrap.connect(Address, Port);
|
||||
ClientStatus = Status.RUNNING;
|
||||
started();
|
||||
ClientChannel = channelFuture.sync().channel();
|
||||
ClientChannel.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
startingError();
|
||||
ClientStatus = Status.ERROR;
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
if(ClientStatus == Status.ERROR || ClientStatus == Status.RUNNING) stop();
|
||||
}
|
||||
|
||||
}, "WebSocketClient");
|
||||
initThread0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start 0.
|
||||
*/
|
||||
protected void start0() {}
|
||||
|
||||
/**
|
||||
* Start.
|
||||
*/
|
||||
public final void start() {
|
||||
starting();
|
||||
switch (ClientStatus) {
|
||||
case STARTING -> {
|
||||
startingError();
|
||||
logger.info("Client is already starting.");
|
||||
}
|
||||
case RUNNING -> {
|
||||
startingError();
|
||||
logger.info("Client is already running.");
|
||||
}
|
||||
case STOPPING -> {
|
||||
startingError();
|
||||
logger.info("Client is stopping");
|
||||
}
|
||||
case STOPPED, WAITING_FOR_INIT -> {
|
||||
initThread();
|
||||
WebSocketClientThread.start();
|
||||
start0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop 0 completable future.
|
||||
*
|
||||
* @return the completable future
|
||||
*/
|
||||
protected CompletableFuture<Void> stop0() {
|
||||
return CompletableFuture.runAsync(() -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting.
|
||||
*/
|
||||
protected abstract void starting();
|
||||
|
||||
/**
|
||||
* Starting error.
|
||||
*/
|
||||
protected abstract void startingError();
|
||||
|
||||
/**
|
||||
* Started.
|
||||
*/
|
||||
protected abstract void started();
|
||||
|
||||
/**
|
||||
* Stopping.
|
||||
*/
|
||||
protected abstract void stopping();
|
||||
|
||||
/**
|
||||
* Stopping error.
|
||||
*/
|
||||
protected abstract void stoppingError();
|
||||
|
||||
/**
|
||||
* Stopped.
|
||||
*/
|
||||
protected abstract void stopped();
|
||||
|
||||
/**
|
||||
* Stop.
|
||||
*/
|
||||
public final void stop() {
|
||||
stopping();
|
||||
switch (ClientStatus) {
|
||||
case WAITING_FOR_INIT -> {
|
||||
stoppingError();
|
||||
logger.warn("Not Init. (It shouldn't be happened)");
|
||||
}
|
||||
case STARTING -> {
|
||||
stoppingError();
|
||||
logger.info("Client is starting, please waiting.");
|
||||
}
|
||||
case RUNNING, ERROR -> {
|
||||
ClientStatus = Status.STOPPING;
|
||||
CompletableFuture<Void> CliChanClose = CompletableFuture.runAsync(() -> {
|
||||
if (ClientChannel != null && ClientChannel.isOpen()) {
|
||||
try {
|
||||
ClientChannel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
ClientChannel = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
CompletableFuture<Void> CliEvenClose = CliChanClose.thenRunAsync(() -> {
|
||||
if (ClientEventLoopGroup != null) {
|
||||
try {
|
||||
ClientEventLoopGroup.shutdownGracefully().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
ClientEventLoopGroup = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
CliEvenClose.thenCombineAsync(stop0(), (a, b) -> {
|
||||
ClientStatus = Status.STOPPED;
|
||||
ClientBootstrap = null;
|
||||
stopped();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
case STOPPING -> {
|
||||
stoppingError();
|
||||
logger.info("Client is already stopping");
|
||||
}
|
||||
case STOPPED -> {
|
||||
stoppingError();
|
||||
logger.info("Client has stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所连接的服务器发送消息
|
||||
*
|
||||
* @param message 消息
|
||||
*/
|
||||
public abstract void send(Message message);
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket;
|
||||
|
||||
|
||||
import com.r3944realms.dg_lab.DgLab;
|
||||
import com.r3944realms.dg_lab.api.manager.Status;
|
||||
import com.r3944realms.dg_lab.utils.stringUtils.UrlValidator;
|
||||
import com.r3944realms.dg_lab.websocket.handler.server.HttpRequestHandler;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 主要是处理netty逻辑以建立服务端
|
||||
*/
|
||||
@SuppressWarnings({"DuplicatedCode", "LoggingSimilarMessage"})
|
||||
public abstract class AbstractWebSocketServer {
|
||||
|
||||
/**
|
||||
* The constant logger.
|
||||
*/
|
||||
protected static final Logger logger = LoggerFactory.getLogger(AbstractWebSocketServer.class);
|
||||
|
||||
private int Port;
|
||||
|
||||
/**
|
||||
* The Server bootstrap.
|
||||
*/
|
||||
protected ServerBootstrap ServerBootstrap;
|
||||
/**
|
||||
* The Boss group.
|
||||
*/
|
||||
protected EventLoopGroup BossGroup,
|
||||
/**
|
||||
* The Worker group.
|
||||
*/
|
||||
WorkerGroup;
|
||||
/**
|
||||
* The Server channel.
|
||||
*/
|
||||
protected Channel ServerChannel;
|
||||
|
||||
/**
|
||||
* The Websocket server thread.
|
||||
*/
|
||||
protected Thread WebsocketServerThread;
|
||||
/**
|
||||
* The Server status.
|
||||
*/
|
||||
protected volatile Status ServerStatus = Status.WAITING_FOR_INIT;
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract web socket server.
|
||||
*/
|
||||
protected AbstractWebSocketServer() {
|
||||
Port = 9000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract web socket server.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
protected AbstractWebSocketServer(int port) {
|
||||
Port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return this.ServerStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets status.
|
||||
*
|
||||
* @param status the status
|
||||
*/
|
||||
public void setStatus(Status status) {
|
||||
this.ServerStatus = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets port.
|
||||
*
|
||||
* @param port the port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
if(this.ServerStatus != Status.WAITING_FOR_INIT && this.ServerStatus != Status.STOPPED) {
|
||||
logger.error("Unable to Change Port to {}", port);
|
||||
return;
|
||||
}
|
||||
this.Port = UrlValidator.isValidPort(port) ? port : 9000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets port.
|
||||
*
|
||||
* @return the port
|
||||
*/
|
||||
public int getPort() {
|
||||
return Port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供实现此处添加对Message处理Handler
|
||||
*
|
||||
* @param pipeline 管道
|
||||
*/
|
||||
protected abstract void MessagePipeLineHandler(ChannelPipeline pipeline);
|
||||
|
||||
/**
|
||||
* Init thread 0.
|
||||
*/
|
||||
protected void initThread0() {}
|
||||
|
||||
/**
|
||||
* Init thread.
|
||||
*/
|
||||
protected final void initThread() {
|
||||
BossGroup = new NioEventLoopGroup();
|
||||
WorkerGroup = new NioEventLoopGroup();
|
||||
WebsocketServerThread = new Thread(() ->{
|
||||
try {
|
||||
ServerBootstrap = new ServerBootstrap();
|
||||
ServerStatus = Status.STARTING;
|
||||
ServerBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true);
|
||||
ServerBootstrap.group(BossGroup, WorkerGroup);
|
||||
ServerBootstrap.channel(NioServerSocketChannel.class);
|
||||
ServerBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(DgLab.LOGGING_HANDLER);
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new HttpRequestHandler());//去除路径请求,APP会发送带路径请求的HTTP升级请求,目前用不到
|
||||
pipeline.addLast("WSP",new WebSocketServerProtocolHandler("/"));
|
||||
pipeline.addLast(new WebSocketFrameAggregator(65536));
|
||||
MessagePipeLineHandler(pipeline);
|
||||
}
|
||||
});
|
||||
logger.debug("WebSocketServer try binding port ... ");
|
||||
ChannelFuture channelFuture = ServerBootstrap.bind("0.0.0.0",Port);
|
||||
channelFuture.sync();
|
||||
ServerChannel = channelFuture.channel();
|
||||
ServerStatus = Status.RUNNING;
|
||||
started();
|
||||
logger.info("WebSocketServer start on the port of {}", Port);
|
||||
logger.debug("WebSocketServer listening on port {}", Port);
|
||||
channelFuture.channel().closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
ServerStatus = Status.ERROR;
|
||||
stoppingError();
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
if(ServerStatus == Status.ERROR || ServerStatus == Status.RUNNING) stop();
|
||||
}
|
||||
}, "WebSocketServer");
|
||||
initThread0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting.
|
||||
*/
|
||||
protected abstract void starting();
|
||||
|
||||
/**
|
||||
* Starting error.
|
||||
*/
|
||||
protected abstract void startingError();
|
||||
|
||||
/**
|
||||
* Started.
|
||||
*/
|
||||
protected abstract void started();
|
||||
|
||||
/**
|
||||
* Stopping.
|
||||
*/
|
||||
protected abstract void stopping();
|
||||
|
||||
/**
|
||||
* Stopping error.
|
||||
*/
|
||||
protected abstract void stoppingError();
|
||||
|
||||
/**
|
||||
* Stopped.
|
||||
*/
|
||||
protected abstract void stopped();
|
||||
|
||||
/**
|
||||
* 自定义Start后续逻辑
|
||||
*/
|
||||
protected void start0() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Start.
|
||||
*/
|
||||
public final void start() {
|
||||
starting();
|
||||
switch (ServerStatus) {
|
||||
case STARTING -> {
|
||||
startingError();
|
||||
logger.info("Server is already starting.");
|
||||
}
|
||||
case RUNNING -> {
|
||||
startingError();
|
||||
logger.info("Server is already running.");
|
||||
}
|
||||
case STOPPING -> {
|
||||
startingError();
|
||||
logger.info("Server is stopping");
|
||||
}
|
||||
case STOPPED, WAITING_FOR_INIT -> {
|
||||
initThread();
|
||||
WebsocketServerThread.start();
|
||||
start0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义Stop后续逻辑
|
||||
*
|
||||
* @return the completable future
|
||||
*/
|
||||
protected CompletableFuture<Void> stop0() {
|
||||
return CompletableFuture.runAsync(() -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop.
|
||||
*/
|
||||
public final void stop() {
|
||||
stopping();
|
||||
switch (ServerStatus) {
|
||||
case WAITING_FOR_INIT -> {
|
||||
stoppingError();
|
||||
logger.warn("Not Init. (It shouldn't be happened)");
|
||||
}
|
||||
case STARTING -> {
|
||||
stoppingError();
|
||||
logger.info("Server is starting, please waiting.");
|
||||
}
|
||||
case RUNNING -> {
|
||||
ServerStatus = Status.STOPPING;
|
||||
// Close the server channel if it's open
|
||||
CompletableFuture<Void> SerChanClose = CompletableFuture.runAsync(() -> {
|
||||
if (ServerChannel != null && ServerChannel.isOpen()) {
|
||||
try {
|
||||
ServerChannel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
ServerChannel = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Shutdown the event loop groups
|
||||
CompletableFuture<Void> BosGroClose = SerChanClose.thenRunAsync(() -> {
|
||||
if (BossGroup != null) {
|
||||
try {
|
||||
BossGroup.shutdownGracefully().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
BossGroup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
CompletableFuture<Void> WorGroClose = SerChanClose.thenRunAsync(() -> {
|
||||
if (WorkerGroup != null) {
|
||||
try {
|
||||
WorkerGroup.shutdownGracefully().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
WorkerGroup = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
BosGroClose.thenCombineAsync(WorGroClose, (a,b) -> {
|
||||
stop0().thenRunAsync(() -> {
|
||||
ServerStatus = Status.STOPPED;
|
||||
ServerBootstrap = null;
|
||||
stopped();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
case STOPPING -> {
|
||||
stoppingError();
|
||||
logger.info("Server is already stopping");
|
||||
}
|
||||
case STOPPED -> {
|
||||
stoppingError();
|
||||
logger.info("Server has stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定的客户端发送消息
|
||||
*
|
||||
* @param clientId 客户端UUID
|
||||
* @param message 消息
|
||||
*/
|
||||
public abstract void send(String clientId, Message message);
|
||||
}
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.ClientOperation;
|
||||
import com.r3944realms.dg_lab.websocket.handler.client.ClientDLPBHandler;
|
||||
import com.r3944realms.dg_lab.websocket.handler.client.ClientDLPBHandlerContextWrapper;
|
||||
import com.r3944realms.dg_lab.websocket.handler.client.DefaultClientOperation;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketClientRole;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ClientPowerBoxSharedData;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
|
||||
/**
|
||||
* 处理PowerBox的Websocket消息的客户端实现
|
||||
*/
|
||||
public class PowerBoxWSClient extends AbstractWebSocketClient {
|
||||
/**
|
||||
* The Shared data.
|
||||
*/
|
||||
protected final ClientPowerBoxSharedData sharedData;
|
||||
/**
|
||||
* The Role.
|
||||
*/
|
||||
protected final WebSocketClientRole role;
|
||||
/**
|
||||
* The Operation.
|
||||
*/
|
||||
protected final ClientOperation operation;
|
||||
/**
|
||||
* The Use role msg mode.
|
||||
*/
|
||||
protected final boolean useRoleMsgMode;
|
||||
private final ClientDLPBHandlerContextWrapper contextWrapper;
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, ClientOperation operation) {
|
||||
super();
|
||||
this.useRoleMsgMode = false;
|
||||
this.sharedData = sharedData;
|
||||
this.operation = operation;
|
||||
this.role = role;
|
||||
sharedData.address = this.Address;
|
||||
sharedData.port = this.Port;
|
||||
contextWrapper = new ClientDLPBHandlerContextWrapper(sharedData, role, operation, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, ClientOperation operation, String address, int port) {
|
||||
super(address, port);
|
||||
this.useRoleMsgMode = false;
|
||||
this.sharedData = sharedData;
|
||||
this.operation = operation;
|
||||
this.role = role;
|
||||
sharedData.address = this.Address;
|
||||
sharedData.port = this.Port;
|
||||
contextWrapper = new ClientDLPBHandlerContextWrapper(sharedData, role, operation, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role) {
|
||||
this(sharedData, role, new DefaultClientOperation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, String address, int port) {
|
||||
this(sharedData, role, new DefaultClientOperation(), address, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
* @param useRoleMsgMode the use role msg mode
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, ClientOperation operation, boolean useRoleMsgMode) {
|
||||
super();
|
||||
this.useRoleMsgMode = useRoleMsgMode;
|
||||
this.sharedData = sharedData;
|
||||
this.operation = operation;
|
||||
this.role = role;
|
||||
sharedData.address = this.Address;
|
||||
sharedData.port = this.Port;
|
||||
contextWrapper = new ClientDLPBHandlerContextWrapper(sharedData, role, operation, useRoleMsgMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
* @param useRoleMsgMode the use role msg mode
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, ClientOperation operation, boolean useRoleMsgMode,String address, int port) {
|
||||
super(address, port);
|
||||
this.useRoleMsgMode = useRoleMsgMode;
|
||||
this.sharedData = sharedData;
|
||||
this.operation = operation;
|
||||
this.role = role;
|
||||
sharedData.address = this.Address;
|
||||
sharedData.port = this.Port;
|
||||
contextWrapper = new ClientDLPBHandlerContextWrapper(sharedData, role, operation, useRoleMsgMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param useRoleMsgMode the use role msg mode
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, boolean useRoleMsgMode) {
|
||||
this(sharedData, role, new DefaultClientOperation(), useRoleMsgMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws client.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param useRoleMsgMode the use role msg mode
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSClient(ClientPowerBoxSharedData sharedData, WebSocketClientRole role, boolean useRoleMsgMode, String address, int port) {
|
||||
this(sharedData, role, new DefaultClientOperation(), useRoleMsgMode, address, port);
|
||||
}
|
||||
@Override
|
||||
protected void MessagePipeLineHandler(ChannelPipeline pipeline) {
|
||||
pipeline.addLast(new ClientDLPBHandler(contextWrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void starting() {
|
||||
operation.ClientStartingHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startingError() {
|
||||
operation.ClientStartingErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void started() {
|
||||
operation.ClientStartedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopping() {
|
||||
operation.ClientStoppingHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stoppingError() {
|
||||
operation.ClientStoppingErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopped() {
|
||||
operation.ClientStoppedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message message) {
|
||||
if(message instanceof PowerBoxMessage PBMessage) {
|
||||
this.ClientChannel.writeAndFlush(new TextWebSocketFrame(PBMessage.getMsgJson()));
|
||||
} else {
|
||||
logger.error("Message is not a PowerBoxMessage");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
public ClientPowerBoxSharedData getSharedData() {
|
||||
return sharedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 必要要设置{@link ClientPowerBoxSharedData sharedData}和{@link WebSocketClientRole role}正确值
|
||||
*/
|
||||
public final static class Builder {
|
||||
/**
|
||||
* The Shared data.
|
||||
*/
|
||||
ClientPowerBoxSharedData sharedData;
|
||||
/**
|
||||
* The Role.
|
||||
*/
|
||||
WebSocketClientRole role;
|
||||
/**
|
||||
* The Operation.
|
||||
*/
|
||||
ClientOperation operation;
|
||||
/**
|
||||
* The Address.
|
||||
*/
|
||||
String address;
|
||||
/**
|
||||
* The Port.
|
||||
*/
|
||||
int port;
|
||||
/**
|
||||
* The Use role msg mode.
|
||||
*/
|
||||
boolean useRoleMsgMode;
|
||||
private Builder() {
|
||||
role = null;
|
||||
sharedData = null;
|
||||
operation = null;
|
||||
address = null;
|
||||
port = -1;
|
||||
useRoleMsgMode = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets builder.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
public static Builder getBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared data builder.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder sharedData(ClientPowerBoxSharedData sharedData) {
|
||||
this.sharedData = sharedData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Role builder.
|
||||
*
|
||||
* @param role the role
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder role(WebSocketClientRole role) {
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address and port builder.
|
||||
*
|
||||
* @param address the address
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder addressAndPort(String address, int port) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation builder.
|
||||
*
|
||||
* @param operation the operation
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder operation(ClientOperation operation) {
|
||||
this.operation = operation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address builder.
|
||||
*
|
||||
* @param address the address
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder address(String address) {
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Port builder.
|
||||
*
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use role msg mode builder.
|
||||
*
|
||||
* @param useRoleMsgMode the use role msg mode
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder useRoleMsgMode(boolean useRoleMsgMode) {
|
||||
this.useRoleMsgMode = useRoleMsgMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build power box ws client.
|
||||
*
|
||||
* @return the power box ws client
|
||||
*/
|
||||
public PowerBoxWSClient build() {
|
||||
if (sharedData == null || role == null)
|
||||
throw new UnsupportedOperationException("Lost key value, failed to build");
|
||||
boolean isAddressAndPortDefaultValue = port == -1 || address == null;//使用默认地址与端口
|
||||
boolean isOperationDefaultValue = this.operation == null;//使用默认操作
|
||||
return (useRoleMsgMode) ?
|
||||
isOperationDefaultValue ?
|
||||
isAddressAndPortDefaultValue ?
|
||||
new PowerBoxWSClient(sharedData, role, true) :
|
||||
new PowerBoxWSClient(sharedData, role,true, address, port)
|
||||
:
|
||||
isAddressAndPortDefaultValue ?
|
||||
new PowerBoxWSClient(sharedData, role, operation, true) :
|
||||
new PowerBoxWSClient(sharedData, role, operation, true, address, port)
|
||||
: isOperationDefaultValue ?
|
||||
isAddressAndPortDefaultValue ?
|
||||
new PowerBoxWSClient(sharedData, role) :
|
||||
new PowerBoxWSClient(sharedData, role, address, port)
|
||||
:
|
||||
isAddressAndPortDefaultValue ?
|
||||
new PowerBoxWSClient(sharedData, role, operation) :
|
||||
new PowerBoxWSClient(sharedData, role, operation, address, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.ServerOperation;
|
||||
import com.r3944realms.dg_lab.websocket.handler.server.DefaultServerOperation;
|
||||
import com.r3944realms.dg_lab.websocket.handler.server.ServerDLPBHandler;
|
||||
import com.r3944realms.dg_lab.websocket.handler.server.ServerDLPBHandlerContextWrapper;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketServerRole;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ServerPowerBoxSharedData;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 处理PowerBox的Websocket消息的服务器实现
|
||||
*/
|
||||
public class PowerBoxWSServer extends AbstractWebSocketServer {
|
||||
/**
|
||||
* The Shared data.
|
||||
*/
|
||||
protected final ServerPowerBoxSharedData SharedData;
|
||||
/**
|
||||
* The Role.
|
||||
*/
|
||||
protected final WebSocketServerRole role;
|
||||
/**
|
||||
* The Operation.
|
||||
*/
|
||||
protected final ServerOperation operation;
|
||||
private final ServerDLPBHandlerContextWrapper contextWrapper;
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws server.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
*/
|
||||
public PowerBoxWSServer(ServerPowerBoxSharedData sharedData, WebSocketServerRole role, ServerOperation operation) {
|
||||
this.SharedData = sharedData;
|
||||
this.role = role;
|
||||
this.operation = operation;
|
||||
contextWrapper = new ServerDLPBHandlerContextWrapper(
|
||||
sharedData,
|
||||
role,
|
||||
operation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws server.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSServer(ServerPowerBoxSharedData sharedData, WebSocketServerRole role, ServerOperation operation, int port) {
|
||||
super(port);
|
||||
this.SharedData = sharedData;
|
||||
this.role = role;
|
||||
this.operation = operation;
|
||||
contextWrapper = new ServerDLPBHandlerContextWrapper(
|
||||
sharedData,
|
||||
role,
|
||||
operation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws server.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
*/
|
||||
public PowerBoxWSServer(ServerPowerBoxSharedData sharedData, WebSocketServerRole role) {
|
||||
this(sharedData, role, new DefaultServerOperation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box ws server.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param port the port
|
||||
*/
|
||||
public PowerBoxWSServer(ServerPowerBoxSharedData sharedData, WebSocketServerRole role, int port) {
|
||||
this(sharedData, role, new DefaultServerOperation(), port);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void MessagePipeLineHandler(ChannelPipeline pipeline) {
|
||||
pipeline.addLast(new ServerDLPBHandler(contextWrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void starting() {
|
||||
operation.ServerStartingHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startingError() {
|
||||
operation.ServerStartingErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void started() {
|
||||
operation.ServerStartedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopping() {
|
||||
operation.ServerStoppingHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stoppingError() {
|
||||
operation.ServerStoppingErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopped() {
|
||||
operation.ServerStoppedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String connectId, Message message) {
|
||||
if(message instanceof PowerBoxMessage PBMessage) {
|
||||
ChannelHandlerContext context = SharedData.connections.get(connectId);//获取连接连接的Context
|
||||
if(context != null) {
|
||||
String targetId = SharedData.relations.get(connectId);
|
||||
if(targetId.isEmpty()) { //该上下文为客户端
|
||||
context.writeAndFlush(new TextWebSocketFrame(PBMessage.getMsgJson()));//发送给客户端
|
||||
ChannelHandlerContext appContext = SharedData.connections.get(targetId);
|
||||
appContext.writeAndFlush(new TextWebSocketFrame(PBMessage.getDataJson()));//发送给APP端
|
||||
} else if(SharedData.relations.containsValue(targetId)){ // 绑定状态的APP端
|
||||
//仅对APP发信息
|
||||
context.writeAndFlush(new TextWebSocketFrame(PBMessage.getDataJson()));
|
||||
|
||||
} else { // 未绑定状态(不可发送消息)
|
||||
logger.error("Can't send Msg to no relationship obj.");
|
||||
}
|
||||
} else {
|
||||
logger.error("Find that Target is invalid.");
|
||||
}
|
||||
} else {
|
||||
logger.error("Message is not a PowerBoxMessage");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
public ServerPowerBoxSharedData getSharedData() {
|
||||
return SharedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> stop0() {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> {
|
||||
Timer heartTimer = getSharedData().heartTimer;
|
||||
if (heartTimer != null) {
|
||||
heartTimer.cancel();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 必要要设置{@link ServerPowerBoxSharedData sharedData}和{@link WebSocketServerRole role}正确值
|
||||
*/
|
||||
public static final class Builder {
|
||||
/**
|
||||
* The Shared data.
|
||||
*/
|
||||
ServerPowerBoxSharedData sharedData;
|
||||
/**
|
||||
* The Role.
|
||||
*/
|
||||
WebSocketServerRole role;
|
||||
/**
|
||||
* The Port.
|
||||
*/
|
||||
int port;
|
||||
/**
|
||||
* The Operation.
|
||||
*/
|
||||
ServerOperation operation;
|
||||
private Builder() {
|
||||
role = null;
|
||||
sharedData = null;
|
||||
operation = null;
|
||||
port = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets builder.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
public static Builder getBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared data builder.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder sharedData(ServerPowerBoxSharedData sharedData) {
|
||||
this.sharedData = sharedData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Port builder.
|
||||
*
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation builder.
|
||||
*
|
||||
* @param operation the operation
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder operation(ServerOperation operation) {
|
||||
this.operation = operation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Role builder.
|
||||
*
|
||||
* @param role the role
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder role(WebSocketServerRole role) {
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build power box ws server.
|
||||
*
|
||||
* @return the power box ws server
|
||||
*/
|
||||
public PowerBoxWSServer build() {
|
||||
if (sharedData == null || role == null)
|
||||
throw new UnsupportedOperationException("Lost key value, failed to build");
|
||||
return (operation == null) ? (port == -1) ?
|
||||
new PowerBoxWSServer(sharedData, role) :
|
||||
new PowerBoxWSServer(sharedData, role, port)
|
||||
: (port == -1) ?
|
||||
new PowerBoxWSServer(sharedData, role, operation) :
|
||||
new PowerBoxWSServer(sharedData, role, operation, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
|
||||
/**
|
||||
* The type Abstract dg lab power box handler.
|
||||
*/
|
||||
public abstract class AbstractDgLabPowerBoxHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
||||
|
||||
/**
|
||||
* The Context wrapper.
|
||||
*/
|
||||
protected final AbstractDgLabPowerBoxHandlerContextWrapper contextWrapper;
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract dg lab power box handler.
|
||||
*
|
||||
* @param contextWrapper the context wrapper
|
||||
*/
|
||||
public AbstractDgLabPowerBoxHandler(AbstractDgLabPowerBoxHandlerContextWrapper contextWrapper) {
|
||||
this.contextWrapper = contextWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext session) throws Exception {
|
||||
super.handlerAdded(session);
|
||||
contextWrapper.SessionBuildInHandle(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext session) throws Exception {
|
||||
super.channelActive(session);
|
||||
contextWrapper.ActiveSessionHandle(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext session) throws Exception {
|
||||
super.handlerRemoved(session);
|
||||
contextWrapper.SessionCloseHandle(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext session, Throwable cause) {
|
||||
contextWrapper.ErrorHandler(session, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext session, TextWebSocketFrame msg) {
|
||||
contextWrapper.ReadMsgHandle(session, msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.IOperation;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The type Abstract dg lab power box handler context wrapper.
|
||||
*/
|
||||
public abstract class AbstractDgLabPowerBoxHandlerContextWrapper {
|
||||
/**
|
||||
* The constant logger.
|
||||
*/
|
||||
public static final Logger logger = LoggerFactory.getLogger(AbstractDgLabPowerBoxHandlerContextWrapper.class);
|
||||
|
||||
/**
|
||||
* The Shared data.
|
||||
*/
|
||||
protected final ISharedData sharedData;
|
||||
/**
|
||||
* The Operation.
|
||||
*/
|
||||
protected final IOperation operation;
|
||||
/**
|
||||
* The Role.
|
||||
*/
|
||||
protected final Role role;
|
||||
|
||||
/**
|
||||
* Gets role.
|
||||
*
|
||||
* @return the role
|
||||
*/
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
public ISharedData getSharedData() {
|
||||
return sharedData.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets operation.
|
||||
*
|
||||
* @return the operation
|
||||
*/
|
||||
public IOperation getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract dg lab power box handler context wrapper.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
*/
|
||||
public AbstractDgLabPowerBoxHandlerContextWrapper(
|
||||
ISharedData sharedData,
|
||||
Role role,
|
||||
IOperation operation
|
||||
) {
|
||||
this.sharedData = sharedData;
|
||||
this.operation = operation;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接建立时触发
|
||||
*
|
||||
* @param session {@link ChannelHandlerContext Context}
|
||||
*/
|
||||
public abstract void SessionBuildInHandle(ChannelHandlerContext session);
|
||||
|
||||
/**
|
||||
* 连接存活时触发
|
||||
*
|
||||
* @param session {@link ChannelHandlerContext Context}
|
||||
*/
|
||||
public abstract void ActiveSessionHandle(ChannelHandlerContext session);
|
||||
|
||||
/**
|
||||
* 连接关闭时触发
|
||||
*
|
||||
* @param session {@link ChannelHandlerContext Context}
|
||||
*/
|
||||
public abstract void SessionCloseHandle(ChannelHandlerContext session);
|
||||
|
||||
/**
|
||||
* 连接获取信息时触发
|
||||
*
|
||||
* @param session {@link ChannelHandlerContext Context}
|
||||
* @param frame {@link TextWebSocketFrame frame}
|
||||
*/
|
||||
public abstract void ReadMsgHandle(ChannelHandlerContext session, TextWebSocketFrame frame);
|
||||
|
||||
/**
|
||||
* 连接出现错误时触发
|
||||
*
|
||||
* @param session {@link ChannelHandlerContext Context}
|
||||
* @param cause {@link Throwable cause}
|
||||
*/
|
||||
public abstract void ErrorHandler(ChannelHandlerContext session, Throwable cause);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
|
||||
/**
|
||||
* 携带共享数据的接口
|
||||
*/
|
||||
public interface IAttachSharedData {
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
ISharedData getSharedData();
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.client;
|
||||
|
||||
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandler;
|
||||
import com.r3944realms.dg_lab.websocket.handler.IAttachSharedData;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
/**
|
||||
* The type Client dlpb handler.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class ClientDLPBHandler extends AbstractDgLabPowerBoxHandler implements IAttachSharedData {
|
||||
/**
|
||||
* Instantiates a new Client dlpb handler.
|
||||
*
|
||||
* @param contextWrapper the context wrapper
|
||||
*/
|
||||
public ClientDLPBHandler(ClientDLPBHandlerContextWrapper contextWrapper) {
|
||||
super(contextWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISharedData getSharedData() {
|
||||
return contextWrapper.getSharedData();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.client;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.ClientOperation;
|
||||
import com.r3944realms.dg_lab.api.operation.IOperation;
|
||||
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandlerContextWrapper;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketClientRole;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketServerRole;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ClientPowerBoxSharedData;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The type Client dlpb handler context wrapper.
|
||||
*/
|
||||
public class ClientDLPBHandlerContextWrapper extends AbstractDgLabPowerBoxHandlerContextWrapper {
|
||||
|
||||
/**
|
||||
* The Sr msg.
|
||||
*/
|
||||
public final boolean SRMsg;
|
||||
|
||||
/**
|
||||
* Instantiates a new Client dlpb handler.
|
||||
*
|
||||
* @param SharedData the shared data
|
||||
* @param role the role
|
||||
* @param Co the co
|
||||
*/
|
||||
public ClientDLPBHandlerContextWrapper(ClientPowerBoxSharedData SharedData, WebSocketClientRole role, ClientOperation Co) {
|
||||
this(SharedData, role, Co, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Client dlpb handler.
|
||||
*
|
||||
* @param SharedData the shared data
|
||||
* @param role the role
|
||||
* @param clientOperation the client operation
|
||||
* @param SRMsg the sr msg
|
||||
*/
|
||||
public ClientDLPBHandlerContextWrapper(ClientPowerBoxSharedData SharedData, WebSocketClientRole role, ClientOperation clientOperation, boolean SRMsg) {
|
||||
super(SharedData, role, clientOperation);
|
||||
this.SRMsg = SRMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Client dlpb handler context wrapper.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param role the role
|
||||
* @param operation the operation
|
||||
*/
|
||||
public ClientDLPBHandlerContextWrapper(ISharedData sharedData, Role role, IOperation operation) {
|
||||
super(sharedData, role, operation);
|
||||
this.SRMsg = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPowerBoxSharedData getSharedData() {
|
||||
return (ClientPowerBoxSharedData) sharedData;
|
||||
}
|
||||
@Override
|
||||
public void SessionBuildInHandle(ChannelHandlerContext session) {
|
||||
logger.info("连接已建立");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ActiveSessionHandle(ChannelHandlerContext session) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void SessionCloseHandle(ChannelHandlerContext session) {
|
||||
PowerBoxMessage breakMsg = PowerBoxMessage.createPowerBoxMessage("break", ConnectionId(), TargetWSId(), "200", new WebSocketClientRole("Cl" + ConnectionId()), new WebSocketServerRole("WebSocketServer"));
|
||||
if(session != null && session.channel().isActive() && session.channel().isOpen()) {
|
||||
sendMsgOrData(session, breakMsg);
|
||||
|
||||
}
|
||||
logger.info("连接已断开");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ReadMsgHandle(ChannelHandlerContext session, TextWebSocketFrame msg) {
|
||||
PowerBoxMessage dataMsg;
|
||||
PowerBoxData data;
|
||||
String json = msg.text();
|
||||
if(SRMsg && !TargetWSId().isEmpty()) {
|
||||
//通过构造开启且如果有目标(即连上了APP端)则开启Message对象校验,只有通过校验才能进读取data(雾)
|
||||
dataMsg = PowerBoxMessage.getNullMessage().getMessage(json);
|
||||
//发送对象必须是服务器类型 且 接收者(本客户端)为占位对象类型 或 本客户端对象(名字类型相同),这里取反,只有满足条件才能进入 else读取 data
|
||||
if(
|
||||
dataMsg.direction.sender().type != RoleType.T_SERVER
|
||||
&&
|
||||
!(
|
||||
dataMsg.direction.receiver().type == RoleType.PLACEHOLDER
|
||||
||
|
||||
(dataMsg.direction.receiver().type == RoleType.T_CLIENT && Objects.equals(dataMsg.direction.receiver().name, role.name))
|
||||
)
|
||||
) {
|
||||
logger.info("消息验证者错误:{}", dataMsg.direction.sender().name);
|
||||
data = PowerBoxMessage.getNullMessage().getPayload(dataMsg.getInvalidMessageJson());
|
||||
} else {
|
||||
data = PowerBoxMessage.getNullMessage().getPayload(dataMsg.getDataJson());
|
||||
}
|
||||
} else {
|
||||
if (SRMsg) {
|
||||
dataMsg = PowerBoxMessage.getNullMessage().getMessage(json);
|
||||
data = dataMsg == null ? PowerBoxMessage.getNullMessage().getPayload(json) : PowerBoxMessage.getNullMessage().getPayload(dataMsg.getDataJson());
|
||||
} else {
|
||||
data = PowerBoxMessage.getNullMessage().getPayload(json);
|
||||
}
|
||||
}
|
||||
assert data != null;
|
||||
switch (data.getCommandType()) {
|
||||
case _NC_BIND_ -> {
|
||||
if(data.getTargetId().isEmpty()) {
|
||||
//初次连接客户端获取服务器指定id
|
||||
((ClientPowerBoxSharedData)sharedData).connectionId = data.getClientId();
|
||||
logger.info("收到clientId: {}", ConnectionId());
|
||||
String qrCodeContext = "https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#" + ((ClientPowerBoxSharedData)sharedData).getUrl() + ConnectionId();
|
||||
TryCatch(i -> ((ClientOperation)operation).QrCodeUrlHandler(qrCodeContext));
|
||||
TryCatch(i -> ((ClientOperation)operation).ShowQrCodeHandler());
|
||||
logger.debug("重新生成QrCodeContext: {}",qrCodeContext);
|
||||
} else {
|
||||
if(!Objects.equals(data.getClientId(), ConnectionId())) {
|
||||
logger.debug("接收到不正确的target消息,消息中目标Id{}",data.getClientId());
|
||||
return;
|
||||
}
|
||||
((ClientPowerBoxSharedData)sharedData).targetWSId = data.getTargetId();
|
||||
logger.info("已建立绑定连接,TargetId:{}, msg:{}",TargetWSId(),data.getMessage());
|
||||
TryCatch(
|
||||
i -> ((ClientOperation)operation).ConnectSuccessfulNoticeHandler()
|
||||
);
|
||||
}
|
||||
}
|
||||
case _NC_BREAK_ -> {
|
||||
//对方断开
|
||||
if (!Objects.equals(data.getTargetId(), TargetWSId())) {
|
||||
return;
|
||||
}
|
||||
TryCatch(
|
||||
i -> ((ClientOperation)operation).DisconnectHandler(data)
|
||||
);
|
||||
logger.info("对方已断开,targetId:{}, msg:{}", TargetWSId(), data.getMessage());
|
||||
}
|
||||
case _NC_ERROR_ -> {
|
||||
if (!Objects.equals(data.getTargetId(), TargetWSId())) {
|
||||
return;
|
||||
}
|
||||
TryCatch(
|
||||
i -> ((ClientOperation)operation).ErrorHandler(data)
|
||||
);
|
||||
logger.info("接收到错误码:{}",data.getMessage());
|
||||
}
|
||||
case _NC_HEARTBEAT_ -> {
|
||||
logger.info("收到心跳");
|
||||
TryCatch(
|
||||
i -> ((ClientOperation)operation).HeartBeatHandler(data)
|
||||
);
|
||||
((ClientPowerBoxSharedData)sharedData).connectionId = data.getClientId();
|
||||
role.UpdateName("Cl" + ((ClientPowerBoxSharedData)sharedData).connectionId);
|
||||
}
|
||||
default -> {
|
||||
logger.info("收到其它信息{}",data.getMessage());
|
||||
TryCatch(
|
||||
i -> ((ClientOperation)operation).OtherMessageHandler(data)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private void TryCatch(Consumer<Void> event) {
|
||||
try {
|
||||
event.accept(null);
|
||||
} catch (Exception e) {
|
||||
logger.error("DataTypeHandler Error:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void ErrorHandler(ChannelHandlerContext session, Throwable cause) {
|
||||
logger.error("连接出现错误:{}: {}",cause.getMessage(), cause.getStackTrace());
|
||||
//错误处理逻辑待加入
|
||||
if(cause instanceof SocketException) {
|
||||
logger.info("远程服务器关闭");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send msg or data.
|
||||
*
|
||||
* @param target the target
|
||||
* @param dataMsg the data msg
|
||||
*/
|
||||
protected void sendMsgOrData(ChannelHandlerContext target, PowerBoxMessage dataMsg) {
|
||||
String json = SRMsg ? dataMsg.getMsgJson() : dataMsg.getDataJson();
|
||||
target.channel().writeAndFlush(new TextWebSocketFrame(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection id string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String ConnectionId() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).connectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Target ws id string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String TargetWSId() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).targetWSId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay int.
|
||||
*
|
||||
* @return the int
|
||||
*/
|
||||
public int Delay() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay timer timer.
|
||||
*
|
||||
* @return the timer
|
||||
*/
|
||||
public Timer DelayTimer() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).delayTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow a strength boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean FollowAStrength() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).followAStrength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow b strength boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean FollowBStrength() {
|
||||
return ((ClientPowerBoxSharedData)sharedData).followBStrength;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.client;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.ClientOperation;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
|
||||
/**
|
||||
* The type Default client operation.
|
||||
*/
|
||||
public class DefaultClientOperation implements ClientOperation {
|
||||
@Override
|
||||
public void ClientStartingHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClientStartedHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClientStartingErrorHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClientStoppingHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClientStoppingErrorHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClientStoppedHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void QrCodeUrlHandler(String qrCodeUrl) {
|
||||
//NOOP
|
||||
System.out.println(qrCodeUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ShowQrCodeHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ConnectSuccessfulNoticeHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void DisconnectHandler(final PowerBoxData data) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ErrorHandler(PowerBoxData data) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void HeartBeatHandler(PowerBoxData data) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OtherMessageHandler(PowerBoxData data) {
|
||||
//NOOP
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.server;
|
||||
|
||||
import com.r3944realms.dg_lab.api.operation.ServerOperation;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
|
||||
/**
|
||||
* The type Default server operation.
|
||||
*/
|
||||
public class DefaultServerOperation implements ServerOperation {
|
||||
@Override
|
||||
public void HeartbeatMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ClearMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void FeedbackMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void StrengthMessageNoticeMessage(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void StrengthMessageChangeHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void PulseClientMessageHandler(PowerBoxMessage clearMessage, PowerBoxMessage pulseMessage) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void PulseClientMessageHandler(PowerBoxMessage clearMessage, int delayTime, PowerBoxMessage pulseMessage) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void BreakConnectMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OtherMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStartingHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStartingErrorHandler() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStartedHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStoppingHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStoppingErrorHandler() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ServerStoppedHandler() {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void InactiveConnectionRemoveHandler(String clientId) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ErrorMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void BindSuccessMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void BindFailureMessageHandler(PowerBoxMessage message) {
|
||||
//NOOP
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.server;
|
||||
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The type Http request handler.
|
||||
*/
|
||||
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpRequestHandler.class);
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
|
||||
// 读取原始请求的 URI
|
||||
String requestUri = request.uri();
|
||||
// 修改 URI,去掉路径部分,只保留 "/"
|
||||
String modifiedUri = "/";
|
||||
|
||||
// 检查是否是 WebSocket 升级请求
|
||||
String upgradeHeader = request.headers().get(HttpHeaderNames.UPGRADE);
|
||||
String webSocketVersion = request.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);
|
||||
|
||||
if ("WebSocket".equalsIgnoreCase(upgradeHeader) &&
|
||||
"13".equals(webSocketVersion)) { // 注意:WebSocket 版本通常为 "13"
|
||||
|
||||
// 创建一个新的 FullHttpRequest 实例,修改 URI
|
||||
FullHttpRequest modifiedRequest = new DefaultFullHttpRequest(
|
||||
HttpVersion.HTTP_1_1,
|
||||
request.method(),
|
||||
modifiedUri,
|
||||
request.content().retain(), // 保留内容以避免意外释放
|
||||
request.headers().copy(),
|
||||
request.trailingHeaders().copy()
|
||||
);
|
||||
|
||||
// 将修改后的 URI 设置到上下文中
|
||||
ctx.channel().attr(AttributeKey.valueOf("ws-uri")).set(modifiedUri);
|
||||
|
||||
// 传递修改后的请求到下一个处理器
|
||||
ctx.fireChannelRead(modifiedRequest);
|
||||
} else {
|
||||
// 如果不是 WebSocket 升级请求,返回拒绝
|
||||
sendForbiddenResponse(ctx, request);
|
||||
}
|
||||
}
|
||||
private void sendForbiddenResponse(ChannelHandlerContext ctx, FullHttpRequest request) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN);
|
||||
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.content().writeBytes("Access denied".getBytes());
|
||||
|
||||
// Write the response and close the connection
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
logger.error(cause.getMessage(), cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.server;
|
||||
|
||||
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandler;
|
||||
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandlerContextWrapper;
|
||||
import com.r3944realms.dg_lab.websocket.handler.IAttachSharedData;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
|
||||
/**
|
||||
* The type Server dlpb handler.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class ServerDLPBHandler extends AbstractDgLabPowerBoxHandler implements IAttachSharedData {
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new Server dlpb handler.
|
||||
*
|
||||
* @param contextWrapper the context wrapper
|
||||
*/
|
||||
public ServerDLPBHandler(AbstractDgLabPowerBoxHandlerContextWrapper contextWrapper) {
|
||||
super(contextWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISharedData getSharedData() {
|
||||
return contextWrapper.getSharedData();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,645 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.handler.server;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.r3944realms.dg_lab.api.operation.ServerOperation;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.*;
|
||||
import com.r3944realms.dg_lab.utils.stringUtils.StringHandlerUtil;
|
||||
import com.r3944realms.dg_lab.utils.timeTask.DgLabTimerTask;
|
||||
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandlerContextWrapper;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.Message;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithSingleAttachment;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxStatusCode;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
import com.r3944realms.dg_lab.websocket.sharedData.ServerPowerBoxSharedData;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.util.concurrent.FutureListener;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The type Server dlpb handler context wrapper.
|
||||
*/
|
||||
public class ServerDLPBHandlerContextWrapper extends AbstractDgLabPowerBoxHandlerContextWrapper {
|
||||
/**
|
||||
* Instantiates a new Server dlpb handler.
|
||||
*
|
||||
* @param serverPowerBoxSharedData the server power box shared data
|
||||
* @param role the role
|
||||
* @param serverOperation the server operation
|
||||
*/
|
||||
public ServerDLPBHandlerContextWrapper(ServerPowerBoxSharedData serverPowerBoxSharedData, WebSocketServerRole role, ServerOperation serverOperation) {
|
||||
super(serverPowerBoxSharedData, role, serverOperation);
|
||||
}
|
||||
|
||||
public ServerPowerBoxSharedData getSharedData() {
|
||||
return (ServerPowerBoxSharedData) sharedData;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void SessionBuildInHandle(ChannelHandlerContext session) {
|
||||
String clientId;
|
||||
do {
|
||||
clientId = UUID.randomUUID().toString();
|
||||
} while (ChannelIdMap().containsKey(clientId));
|
||||
ChannelIdMap().put(session.channel().id().asLongText(), clientId);
|
||||
logger.debug("channel added: clientId={}", clientId);
|
||||
Channel().add(session.channel());
|
||||
Connections().put(clientId, session);
|
||||
logger.info("新的 webSocket 连接已建立, 标识符为{}", clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ActiveSessionHandle(ChannelHandlerContext session) {
|
||||
String clientId = ChannelIdMap().get(session.channel().id().asLongText());
|
||||
PowerBoxMessage bindMsg = PowerBoxMessage.createPowerBoxMessage("bind", clientId, "", "targetId",
|
||||
role, new PlaceholderRole("Pl" + clientId)
|
||||
);
|
||||
// 延迟发送消息
|
||||
session.executor().schedule(() -> {
|
||||
if (session.channel().isActive() && session.channel().isOpen()) {
|
||||
session.channel().writeAndFlush(new TextWebSocketFrame(bindMsg.getDataJson())).addListener(sendFuture -> {
|
||||
if (sendFuture.isSuccess()) {
|
||||
logger.info("#1 Message sent successfully to clientId={}", clientId);
|
||||
} else {
|
||||
logger.error("#1 Failed to send message to clientId={}", clientId, sendFuture.cause());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logger.debug("Channel is not active, message not sent to clientId={}", clientId);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS); // 延迟200毫秒发送消息
|
||||
if(HeartTimer() != null) return;
|
||||
synchronized (ServerDLPBHandler.class) {
|
||||
// 启动心跳定时器(如果尚未启动)
|
||||
if (HeartTimer() == null) {
|
||||
((ServerPowerBoxSharedData)sharedData).heartTimer = new Timer();
|
||||
HeartTimer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Connections().isEmpty()) {
|
||||
logger.debug("关系池大小:{}连接池大小:{},发送心跳消息", Relations().size(), Connections().size());
|
||||
}
|
||||
for (String clientId : Connections().keySet()) {
|
||||
ChannelHandlerContext client = Connections().get(clientId);
|
||||
if (client != null && client.channel().isActive() && client.channel().isOpen()) {
|
||||
String targetId = Relations().get(clientId);
|
||||
if (ObjectUtils.isEmpty(targetId)) {
|
||||
targetId = "";
|
||||
}
|
||||
Role receiverRole = getRole(clientId);
|
||||
PowerBoxMessage message =
|
||||
PowerBoxMessage.createPowerBoxMessage("heartbeat", clientId, targetId, PowerBoxStatusCode.SUCCESSFUL,
|
||||
role, receiverRole);
|
||||
TryCatch(n -> ((ServerOperation)operation).HeartbeatMessageHandler(message));
|
||||
sendMessageOrData(ServerDLPBHandlerContextWrapper.this, clientId, message);
|
||||
} else {
|
||||
logger.debug("Channel is not active, skipping heartbeat for clientId={}", clientId);
|
||||
TryCatch(n -> ((ServerOperation)operation).InactiveConnectionRemoveHandler(clientId));
|
||||
Connections().remove(clientId);//不活跃移除对应连接
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 500, 60000/*ms = 1min*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull Role getRole(String clientId) {
|
||||
RoleType roleType = getRoleType(this.Relations(), clientId);
|
||||
Role receiverRole;
|
||||
switch (roleType) {
|
||||
case T_CLIENT -> receiverRole = new WebSocketClientRole("Cl" + clientId);
|
||||
case APPLICATION -> receiverRole = new WebSocketApplicationRole("Ap" + clientId);
|
||||
default -> receiverRole = new PlaceholderRole("Pl" + clientId);
|
||||
}
|
||||
return receiverRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void SessionCloseHandle(ChannelHandlerContext session) {
|
||||
logger.debug("Websocket 连接已关闭");
|
||||
String channelId = session.channel().id().asLongText();
|
||||
String disconnectId = ChannelIdMap().remove(channelId);
|
||||
logger.debug("channel removed: channelId={}", channelId);
|
||||
logger.info("断开的client Id:{}", disconnectId);
|
||||
Channel().remove(session.channel());
|
||||
for (String _clientId_ : Relations().keySet()) {
|
||||
String _targetId_ = Relations().get(_clientId_);
|
||||
if (_clientId_.equals(disconnectId)) {
|
||||
//Client断开 ,通知app
|
||||
ChannelHandlerContext appClient = Connections().get(_targetId_);
|
||||
PowerBoxMessage message =
|
||||
PowerBoxMessage.createPowerBoxMessage("break", disconnectId, _targetId_, PowerBoxStatusCode.OPPOSITE_CLIENT_DISCONNECTED, role, new WebSocketApplicationRole("Ap" + _targetId_));
|
||||
TryCatch(n -> ((ServerOperation)operation).BreakConnectMessageHandler(message));
|
||||
sendMessageData(this, appClient, message);
|
||||
appClient.close(); //关闭当前的 websocket 连接
|
||||
Relations().remove(_clientId_); // 清除关系
|
||||
PowerBoxDataMap().remove(_targetId_);
|
||||
logger.debug("Close Application Connecting{}", _targetId_);
|
||||
Connections().remove(_targetId_);//并移除targetId
|
||||
} else if (_targetId_.equals(disconnectId)) {
|
||||
//app断开,通知Client
|
||||
ChannelHandlerContext client = Connections().get(_clientId_);
|
||||
PowerBoxMessage message =
|
||||
PowerBoxMessage.createPowerBoxMessage("break", _clientId_, _targetId_, PowerBoxStatusCode.OPPOSITE_CLIENT_DISCONNECTED, role, new WebSocketClientRole("Cl" + _clientId_));
|
||||
TryCatch(n -> ((ServerOperation)operation).BreakConnectMessageHandler(message));
|
||||
sendMessageText(this, client, message);
|
||||
client.close();//关闭当前的 websocket 连接
|
||||
Relations().remove(_clientId_);// 清除关系
|
||||
PowerBoxDataMap().remove(_targetId_);
|
||||
logger.debug("Close Client Connecting{}", _targetId_);
|
||||
Connections().remove(_clientId_);//并移除clientId
|
||||
}
|
||||
Connections().remove(disconnectId);//移除断开连接的对方
|
||||
Channel().remove(session.channel());
|
||||
logger.debug("channel removed: channelId={}[Current Size:{}]", channelId, Connections().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ReadMsgHandle(ChannelHandlerContext session, TextWebSocketFrame msg) {
|
||||
logger.debug("收到消息:{}", msg);
|
||||
PowerBoxMessage powerBoxMessage, errorMsg;
|
||||
PowerBoxData data;
|
||||
String text = msg.text();
|
||||
try {
|
||||
powerBoxMessage = PowerBoxMessage.getNullMessage().getMessage(text);
|
||||
data = PowerBoxMessage.getNullMessage().getPayload(powerBoxMessage.getDataJson());
|
||||
} catch (JsonSyntaxException e) {
|
||||
errorMsg = PowerBoxMessage.createPowerBoxMessage("error", "", "", PowerBoxStatusCode.NOT_STANDARD_JSON,
|
||||
role, new WebSocketClientRole("ErrorReceiver"));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(errorMsg));
|
||||
sendMessageText(this, session, errorMsg);
|
||||
return;
|
||||
} catch (Exception e) { //非JSON消息
|
||||
data = PowerBoxMessage.getNullMessage().getPayload(text);
|
||||
}
|
||||
if (!Connections().containsKey(data.getClientId()) && !Connections().containsKey(data.getTargetId())) { //非法信息来源拒绝
|
||||
errorMsg = PowerBoxMessage.createPowerBoxMessage("error", "", "", PowerBoxStatusCode.NOT_FOUND_BECAUSE_OF_OFFLINE,
|
||||
role, new WebSocketClientRole("ErrorReceiver"));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(errorMsg));
|
||||
sendMessageText(this, session, errorMsg);
|
||||
return;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(data.getType()) && !ObjectUtils.isEmpty(data.getClientId()) && !ObjectUtils.isEmpty(data.getTargetId()) && !ObjectUtils.isEmpty(data.getMessage())) {
|
||||
String type = data.getType();
|
||||
String clientId = data.getClientId();
|
||||
String targetId = data.getTargetId();
|
||||
String message = data.getMessage();
|
||||
PowerBoxDataType dataType = PowerBoxDataType.getType(type, message);
|
||||
DataTypeHandler(session, dataType, clientId, targetId, message, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataTypeHandler(ChannelHandlerContext session, PowerBoxDataType dataType, String clientId, String targetId, String message, PowerBoxData data) {
|
||||
switch (dataType) {
|
||||
case _NC_BIND_ -> {
|
||||
//服务器下发绑定关系
|
||||
if (Connections().containsKey(clientId) && Connections().containsKey(targetId)){
|
||||
//relations的双方都不存在这对id 且 信息为DGLAB
|
||||
if (!(Relations().containsKey(clientId) || Relations().containsKey(targetId) || Relations().containsValue(clientId) || Relations().containsValue(targetId)) && message.equals("DGLAB")) {
|
||||
Relations().put(clientId, targetId);
|
||||
ChannelHandlerContext client = Connections().get(clientId);
|
||||
PowerBoxMessage bindMessage = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.SUCCESSFUL, role, new PlaceholderRole("Both: " + clientId + " ^ " + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).BindSuccessMessageHandler(bindMessage));
|
||||
sendMessageData(this, session, bindMessage);
|
||||
sendMessageText(this, client, bindMessage);
|
||||
} else {
|
||||
PowerBoxMessage failure = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.TRYING_BINDING_ALREADY_BOUND_ID, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).BindFailureMessageHandler(failure));
|
||||
sendMessageData(this, session, failure);
|
||||
}
|
||||
} else {
|
||||
PowerBoxMessage failure = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.TARGET_CLIENT_NOT_EXIST, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).BindFailureMessageHandler(failure));
|
||||
sendMessageData(this, session, failure);
|
||||
}
|
||||
}
|
||||
case STRENGTH,PULSE,CLEAR,FEEDBACK -> {
|
||||
if (Relations().containsKey(clientId) && !Relations().get(clientId).equals(targetId)) {//没有绑定关系
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.NOT_BINDING_RELATIONSHIP, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(error));
|
||||
sendMessageData(this, session, error);
|
||||
return;
|
||||
}
|
||||
Object[] argsArray = data.getArgsArray();
|
||||
if (argsArray == null) {
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, PowerBoxStatusCode.INTERNAL_ERROR, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(error));
|
||||
sendMessageData(this, session, error);
|
||||
return;
|
||||
}
|
||||
switch (dataType) {
|
||||
case STRENGTH -> {
|
||||
if(Connections().containsKey(targetId)) {
|
||||
ChannelHandlerContext app = Connections().get(targetId);
|
||||
ChannelHandlerContext client = Connections().get(clientId);
|
||||
if (argsArray.length == 3){
|
||||
int channel = ((Integer[])argsArray)[0];
|
||||
int policyChange = ((Integer[])argsArray)[1];
|
||||
int strengthChangeValue = ((Integer[])argsArray)[2];
|
||||
String messageCommand = "strength-" + channel + "+" + policyChange + "+" + strengthChangeValue;
|
||||
PowerBoxMessage strengthUpdate = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, messageCommand, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).StrengthMessageChangeHandler(strengthUpdate));
|
||||
sendMessageData(this, app, strengthUpdate);
|
||||
} else if(argsArray.length == 4){
|
||||
int AStrength = ((Integer[])argsArray)[0];
|
||||
int BStrength = ((Integer[])argsArray)[1];
|
||||
int ALimit = ((Integer[])argsArray)[2];
|
||||
int BLimit = ((Integer[])argsArray)[3];
|
||||
putDataInMap(targetId, data);
|
||||
String currentStrengthMsg = "strength-" + AStrength + "+" + BStrength + "+" + ALimit + "+" + BLimit;
|
||||
PowerBoxMessage clientMsg = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, currentStrengthMsg, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).StrengthMessageNoticeMessage(clientMsg));
|
||||
sendMessageText(this, client, clientMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
case PULSE -> {
|
||||
//Thrown unsupportedMsg
|
||||
PowerBoxMessage unsupportedMsg = PowerBoxMessage.createPowerBoxMessage("error", clientId, targetId, PowerBoxStatusCode.UNSUPPORTED_OPERATION, role, new WebSocketClientRole("Cl" + clientId));
|
||||
//"unsupported-reason:(please use type named clientMsg to adjust wave in order to get the timers of AB)"
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(unsupportedMsg));
|
||||
sendMessageText(this , session, unsupportedMsg);//发送给客户端
|
||||
}
|
||||
case CLEAR -> {
|
||||
if (Connections().containsKey(targetId)) {
|
||||
ChannelHandlerContext client = Connections().get(targetId);
|
||||
String Channel = ((String[])argsArray)[0];
|
||||
String messageCommand = "clear-" + Channel;
|
||||
PowerBoxMessage strengthUpdate = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, messageCommand, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ClearMessageHandler(strengthUpdate));
|
||||
sendMessageData(this, client, strengthUpdate);
|
||||
}
|
||||
}
|
||||
case FEEDBACK -> {
|
||||
if (Connections().containsKey(targetId)) {
|
||||
int index = ((Integer[])argsArray)[0];
|
||||
String feedBack = "feedback-" + index;
|
||||
PowerBoxMessage feedBackMsg = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, feedBack, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).FeedbackMessageHandler(feedBackMsg));
|
||||
sendMessageOrData(this, clientId, feedBackMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case CLIENT_MESSAGE -> {//接收到客户端消息(客户端的作用是附带Timer)
|
||||
if (!Relations().get(clientId).equals(targetId)) {
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.NOT_BINDING_RELATIONSHIP, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(error));
|
||||
sendMessageText(this, session, error);
|
||||
} else if (ObjectUtils.isEmpty(message)){
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("error", clientId, targetId, PowerBoxStatusCode.INVALID_REQUEST, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(error));
|
||||
sendMessageText(this, session, error);
|
||||
} else if (Relations().containsValue(targetId)) {
|
||||
String[] args = (String[]) data.getArgsArrayByPointing(PowerBoxDataType.PULSE);
|
||||
char channel = StringHandlerUtil.getCharForString(args[0],0);
|
||||
ChannelHandlerContext app = Connections().get(targetId);
|
||||
String[] waveDataList = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, waveDataList, 0, args.length - 1);
|
||||
String waveDataListString = StringHandlerUtil.reformWaveDataList(waveDataList);
|
||||
String messageCommand = "pulse-"+ channel + ":" + waveDataListString;
|
||||
PowerBoxMessage pulseMsg = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, messageCommand, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
if (data instanceof PowerBoxDataWithSingleAttachment dataWithSingleAttachment) {
|
||||
Integer timer = dataWithSingleAttachment.getTimer();
|
||||
int sendTime = dataWithSingleAttachment.isValid() ? (timer != null ? timer : PunishmentTime()) : PunishmentTime();
|
||||
|
||||
Integer totalSends = PunishmentTime() * sendTime;
|
||||
Integer timesSpace = 1000 / PunishmentTime();
|
||||
|
||||
logger.debug("频道{}消息发送中,频道消息数:{},持续时间:{}",channel, totalSends, sendTime);
|
||||
if (ClientTimers().containsKey(clientId)) {
|
||||
//接收消息未工作完毕,清除旧计时器并发送清除App队列消息
|
||||
PowerBoxMessage clearAndUpdateMsg = PowerBoxMessage.createPowerBoxMessage("clientMsg", clientId, targetId, "over-previous-value", role, new WebSocketClientRole("Cl" + clientId));
|
||||
sendMessageOrData(this, clientId, clearAndUpdateMsg);
|
||||
Timer timerId = ClientTimers().get(clientId + "-" + channel);
|
||||
clearInterval(clientId, timerId, channel);
|
||||
ClientTimers().remove(clientId + "-" + channel);
|
||||
//发送 App波形队列清除指令
|
||||
String commandMsg = "clear-" + (channel == 'A' ? "1": "2") ;//非A即B(
|
||||
PowerBoxMessage clear = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, commandMsg, role, new WebSocketApplicationRole("Ap" + targetId));
|
||||
TryCatch(n -> ((ServerOperation)operation).PulseClientMessageHandler(clear, totalSends, pulseMsg));
|
||||
sendMessageData(this, app, clear);
|
||||
Promise<Void> promise = session.newPromise();
|
||||
session.executor().schedule(() -> {
|
||||
promise.setSuccess(null);
|
||||
}, 150, TimeUnit.MILLISECONDS);
|
||||
promise.addListener((FutureListener<Void>) future -> {
|
||||
if (future.isSuccess()) {
|
||||
delaySendMsg(clientId, session, app, pulseMsg, totalSends, timesSpace, channel);
|
||||
} else logger.error("WTF?(╯‵□′)╯︵┻━┻");
|
||||
});
|
||||
} else {
|
||||
TryCatch(n -> ((ServerOperation)operation).PulseClientMessageHandler(null, totalSends, pulseMsg));
|
||||
delaySendMsg(clientId, session, app, pulseMsg, totalSends, timesSpace, channel);
|
||||
}//如果不存在未发送玩的消息 直接发送
|
||||
} else {
|
||||
PowerBoxMessage unsupportedMsg = PowerBoxMessage.createPowerBoxMessage("error", clientId, targetId, PowerBoxStatusCode.UNSUPPORTED_OPERATION, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(unsupportedMsg));
|
||||
sendMessageText(this , session, unsupportedMsg);//发送给客户端
|
||||
}
|
||||
} else {
|
||||
logger.debug("未找到匹配的客户端,clientId={}", clientId);
|
||||
PowerBoxMessage notFound = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, PowerBoxStatusCode.NOT_FOUND_BECAUSE_OF_OFFLINE, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(notFound));
|
||||
sendMessageText(this, session, notFound);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
if (!Relations().get(clientId).equals(targetId)) {
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("bind", clientId, targetId, PowerBoxStatusCode.NOT_BINDING_RELATIONSHIP, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(error));
|
||||
sendMessageText(this, session, error);
|
||||
} else if (Connections().containsKey(clientId)) {
|
||||
ChannelHandlerContext client = Connections().get(clientId);
|
||||
PowerBoxMessage defaultMsg = PowerBoxMessage.createPowerBoxMessage(data.getType(), clientId, targetId, data.getMessage(), role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).OtherMessageHandler(defaultMsg));
|
||||
sendMessageText(this, client, defaultMsg);
|
||||
} else {
|
||||
PowerBoxMessage notFound = PowerBoxMessage.createPowerBoxMessage("msg", clientId, targetId, PowerBoxStatusCode.NOT_FOUND_BECAUSE_OF_OFFLINE, role, new WebSocketClientRole("Cl" + clientId));
|
||||
TryCatch(n -> ((ServerOperation)operation).ErrorMessageHandler(notFound));
|
||||
sendMessageText(this, session, notFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryCatch(Consumer<Void> event) {
|
||||
try {
|
||||
event.accept(null);
|
||||
} catch (Exception e) {
|
||||
logger.error("DataTypeHandler Error:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ErrorHandler(ChannelHandlerContext session, Throwable cause) {
|
||||
// java.io.IOException: 远程主机强迫关闭了一个现有的连接。 这个错误是由于连接直接被关闭 无需处理
|
||||
if (cause instanceof IOException) {
|
||||
return;
|
||||
}
|
||||
logger.error("WebSocket 异常{}:{}", cause.getClass(),cause.getMessage());
|
||||
//在此通知用户异常,提过Websocket 发送消息给对方
|
||||
String _errorSideId_ = ChannelIdMap().get(session.channel().id().asLongText());
|
||||
if(ObjectUtils.isEmpty(_errorSideId_)) {
|
||||
logger.debug("Can't found the client");
|
||||
return;
|
||||
}
|
||||
//构造错误信息
|
||||
String errorMessage = "Websocket异常 " + cause.getMessage();
|
||||
for (String _clientId_ : Relations().keySet()) {
|
||||
String _targetId_ = Relations().get(_clientId_);
|
||||
if (_clientId_.equals(_errorSideId_)) {//clientId = value
|
||||
//通知app
|
||||
ChannelHandlerContext appClient = Connections().get(_targetId_);
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("error", _clientId_, _targetId_, PowerBoxStatusCode.INTERNAL_ERROR, role, new WebSocketApplicationRole("Ap" + _targetId_));
|
||||
sendMessageData(this, appClient, error);
|
||||
} else if (_targetId_.equals(_errorSideId_)) {
|
||||
//通知client
|
||||
ChannelHandlerContext client = Connections().get(_clientId_);
|
||||
PowerBoxMessage error = PowerBoxMessage.createPowerBoxMessage("error", _clientId_ , _targetId_, errorMessage, role, new WebSocketClientRole("Cl" + _targetId_) );
|
||||
sendMessageData(this, client, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void putDataInMap(String targetId, PowerBoxData data) {
|
||||
if (PowerBoxDataMap().containsKey(targetId))PowerBoxDataMap().replace(targetId, data);
|
||||
else PowerBoxDataMap().put(targetId, data);
|
||||
}
|
||||
/**
|
||||
* 根据UUID对应对象的类型来决定发送的数据类型JSON
|
||||
* @param uuid 能处理Message对象的目标UUID
|
||||
* @param msg Message{@link Message}
|
||||
*/
|
||||
private static void sendMessageOrData(ServerDLPBHandlerContextWrapper contextWrapper, String uuid, Message msg) {
|
||||
switch (getRoleType(contextWrapper.Relations(), uuid)) {
|
||||
case T_CLIENT -> sendMessageText(contextWrapper, contextWrapper.Connections().get(uuid), msg);
|
||||
case PLACEHOLDER,APPLICATION -> sendMessageData(contextWrapper, contextWrapper.Connections().get(uuid), msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target 能处理Message对象的目标
|
||||
* @param msg Message{@link Message}
|
||||
*/
|
||||
private static void sendMessageText(ServerDLPBHandlerContextWrapper contextWrapper, ChannelHandlerContext target, Message msg) {
|
||||
if (target == null) throw new NullPointerException("target is null");
|
||||
if (msg == null) throw new NullPointerException("message is null");
|
||||
try {
|
||||
String json = msg.getMsgJson();
|
||||
writeAndFlushAndFlush(contextWrapper, target, json);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error sending message{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送Message的Data数据
|
||||
*
|
||||
* @param contextWrapper ServerDLPBHandlerContextWrapper
|
||||
* @param target 能处理Message对象的目标
|
||||
* @param msg 待转化为Message{@link Message}
|
||||
*/
|
||||
protected static void sendMessageData(ServerDLPBHandlerContextWrapper contextWrapper, ChannelHandlerContext target, Message msg) {
|
||||
if (target == null) throw new NullPointerException("target is null");
|
||||
if (msg == null) throw new NullPointerException("message is null");
|
||||
try {
|
||||
String json = msg.getDataJson();
|
||||
writeAndFlushAndFlush(contextWrapper, target, json);
|
||||
|
||||
} catch (Exception e){
|
||||
logger.error("Error sending message{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAndFlushAndFlush(ServerDLPBHandlerContextWrapper contextWrapper, ChannelHandlerContext target, String json) {
|
||||
target.channel().writeAndFlush(new TextWebSocketFrame(json)).addListener((ChannelFutureListener) future -> {
|
||||
if (future.isSuccess()) {
|
||||
logger.debug("消息已成功发送到客户端 UUID={},消息内容={}", contextWrapper.ChannelIdMap().get(target.channel().id().asLongText()), json);
|
||||
} else {
|
||||
logger.error("消息发送失败,客户端 UUID={},消息内容={}", contextWrapper.ChannelIdMap().get(target.channel().id().asLongText()) , json, future.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delay send msg.
|
||||
*
|
||||
* @param clientId the client id
|
||||
* @param client the client
|
||||
* @param target the target
|
||||
* @param message the message
|
||||
* @param totalSends the total sends
|
||||
* @param timeSpace the time space
|
||||
* @param channel the channel
|
||||
*/
|
||||
public void delaySendMsg(String clientId, ChannelHandlerContext client, ChannelHandlerContext target, Message message, Integer totalSends, Integer timeSpace, char channel) {
|
||||
sendMessageData(this, target, message);
|
||||
totalSends--;
|
||||
if (totalSends> 0 ) {
|
||||
Timer timer = new Timer();
|
||||
DgLabTimerTask task = new DgLabTimerTask(client, target, message, totalSends, k -> clearInterval(clientId, timer, channel), channel);
|
||||
timer.scheduleAtFixedRate(task, 0, timeSpace.longValue());//周期触发
|
||||
ClientTimers().put(clientId + "-" + channel, timer);//存储对应的·clientId与频道
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear interval.
|
||||
*
|
||||
* @param clientId the client id
|
||||
* @param timer the timer
|
||||
* @param channel the channel
|
||||
*/
|
||||
public void clearInterval(String clientId, Timer timer, char channel) {
|
||||
timer.cancel();
|
||||
ClientTimers().remove(clientId + "-" + channel); // 删除对应的定时器
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data.
|
||||
*
|
||||
* @param contextWrapper ServerDLPBHandlerContextWrapper
|
||||
* @param clientId 客户端uuid
|
||||
* @return PowerData 客户端Data消息(存储的一般是对应的pulse数据)
|
||||
* @throws NullPointerException 如果对于clientId未存入对应数据
|
||||
*/
|
||||
public static PowerBoxData getData(ServerDLPBHandlerContextWrapper contextWrapper, String clientId) throws NullPointerException {
|
||||
return contextWrapper.PowerBoxDataMap().get(clientId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Channel channel group.
|
||||
*
|
||||
* @return the channel group
|
||||
*/
|
||||
public ChannelGroup Channel() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel id map map.
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, String> ChannelIdMap() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).channelIdMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connections map.
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, ChannelHandlerContext> Connections() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Power box data map map.
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, PowerBoxData> PowerBoxDataMap() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).powerBoxDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relations map.
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, String> Relations() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client timers map.
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<String, Timer> ClientTimers() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).clientTimers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Punishment duration int.
|
||||
*
|
||||
* @return the int
|
||||
*/
|
||||
public int PunishmentDuration() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).punishmentDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Punishment time int.
|
||||
*
|
||||
* @return the int
|
||||
*/
|
||||
public int PunishmentTime() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).punishmentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Heart timer timer.
|
||||
*
|
||||
* @return the timer
|
||||
*/
|
||||
public Timer HeartTimer() {
|
||||
return ((ServerPowerBoxSharedData)sharedData).heartTimer;
|
||||
}
|
||||
|
||||
|
||||
private static RoleType getRoleType(Map<String, String> relations, String uuid) {
|
||||
if (relations.containsKey(uuid)) return RoleType.T_CLIENT;
|
||||
else if (relations.containsValue(uuid)) return RoleType.APPLICATION;
|
||||
else return RoleType.PLACEHOLDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets role type.
|
||||
*
|
||||
* @param sharedData the shared data
|
||||
* @param uuid the uuid
|
||||
* @return the role type
|
||||
*/
|
||||
public static RoleType getRoleType(ServerPowerBoxSharedData sharedData, String uuid) {
|
||||
return getRoleType(sharedData.relations, uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.sharedData;
|
||||
|
||||
import com.r3944realms.dg_lab.utils.stringUtils.StringHandlerUtil;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
|
||||
import java.util.Timer;
|
||||
|
||||
/**
|
||||
* 客户端PowerBox共享数据
|
||||
*/
|
||||
public class ClientPowerBoxSharedData implements ISharedData {
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@Override
|
||||
public ClientPowerBoxSharedData clone() {
|
||||
ClientPowerBoxSharedData clone = new ClientPowerBoxSharedData();
|
||||
clone.connectionId = connectionId;
|
||||
clone.targetWSId = targetWSId;
|
||||
clone.delay = delay;
|
||||
clone.address = address;
|
||||
clone.port = port;
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Connection id.
|
||||
*/
|
||||
public String connectionId = "",
|
||||
/**
|
||||
* The Target ws id.
|
||||
*/
|
||||
targetWSId = "";
|
||||
/**
|
||||
* The Delay.
|
||||
*/
|
||||
public int delay = 500; //防抖
|
||||
/**
|
||||
* The Delay timer.
|
||||
*/
|
||||
public Timer delayTimer;
|
||||
/**
|
||||
* The Address.
|
||||
*/
|
||||
public String address;
|
||||
/**
|
||||
* The Port.
|
||||
*/
|
||||
public int port;
|
||||
/**
|
||||
* The Follow a strength.
|
||||
*/
|
||||
//跟随AB的软上限
|
||||
public final boolean followAStrength = false;
|
||||
/**
|
||||
* The Follow b strength.
|
||||
*/
|
||||
public final boolean followBStrength = false;
|
||||
|
||||
/**
|
||||
* Instantiates a new Client power box shared data.
|
||||
*
|
||||
* @param delay the delay
|
||||
*/
|
||||
public ClientPowerBoxSharedData(int delay) {
|
||||
this.delay = delay > 0 ? delay : 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Client power box shared data.
|
||||
*/
|
||||
public ClientPowerBoxSharedData() {}
|
||||
|
||||
/**
|
||||
* Gets url.
|
||||
*
|
||||
* @return the url
|
||||
*/
|
||||
public String getUrl() {
|
||||
return StringHandlerUtil.buildWebSocketURL(address, port, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.websocket.sharedData;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
|
||||
/**
|
||||
* 服务器PowerBox共享数据
|
||||
*/
|
||||
public class ServerPowerBoxSharedData implements ISharedData,Cloneable {
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@Override
|
||||
public ServerPowerBoxSharedData clone() {
|
||||
ServerPowerBoxSharedData clone = new ServerPowerBoxSharedData(punishmentDuration, punishmentTime);
|
||||
clone.channels.addAll(channels);
|
||||
clone.channelIdMap.putAll(channelIdMap);
|
||||
clone.powerBoxDataMap.putAll(powerBoxDataMap);
|
||||
clone.connections.putAll(connections);
|
||||
clone.relations.putAll(relations);
|
||||
clone.clientTimers.putAll(clientTimers);
|
||||
//不会给计时器(因为毫无意义)
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Channels.
|
||||
*/
|
||||
public final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||
/**
|
||||
* The Channel id map.
|
||||
*/
|
||||
// 映射表
|
||||
public final Map<String, String> channelIdMap = Maps.newConcurrentMap();
|
||||
/**
|
||||
* The Power box data map.
|
||||
*/
|
||||
// targetId -> PowerData
|
||||
public final Map<String, PowerBoxData> powerBoxDataMap = Maps.newConcurrentMap();
|
||||
|
||||
/**
|
||||
* The Connections.
|
||||
*/
|
||||
// 储存已连接的用户及其标识
|
||||
public final Map<String, ChannelHandlerContext> connections = Maps.newConcurrentMap();
|
||||
/**
|
||||
* The Relations.
|
||||
*/
|
||||
// 存储消息关系
|
||||
public final Map<String, String> relations = Maps.newConcurrentMap();
|
||||
/**
|
||||
* The Client timers.
|
||||
*/
|
||||
// 存储定时器
|
||||
public final Map<String, Timer> clientTimers = Maps.newConcurrentMap();
|
||||
/**
|
||||
* The Punishment duration.
|
||||
*/
|
||||
//默认发送时间1秒
|
||||
public final int punishmentDuration;
|
||||
/**
|
||||
* The Punishment time.
|
||||
*/
|
||||
// 默认一秒发送1次
|
||||
public final int punishmentTime;
|
||||
/**
|
||||
* The Heart timer.
|
||||
*/
|
||||
// 心跳定时器(该为线程安全的类)
|
||||
public Timer heartTimer = null;
|
||||
|
||||
/**
|
||||
* Instantiates a new Server power box shared data.
|
||||
*/
|
||||
public ServerPowerBoxSharedData() {
|
||||
this(5, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Server power box shared data.
|
||||
*
|
||||
* @param punishmentDuration the punishment duration
|
||||
* @param punishmentTime the punishment time
|
||||
*/
|
||||
public ServerPowerBoxSharedData(int punishmentDuration, int punishmentTime) {
|
||||
this.punishmentDuration = punishmentDuration > 0 ? punishmentDuration : 5;
|
||||
this.punishmentTime = punishmentTime> 0 ? punishmentTime : 1;
|
||||
}
|
||||
|
||||
}
|
||||
62
Common/src/main/resources/log4j2.xml
Normal file
62
Common/src/main/resources/log4j2.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration monitorInterval="10" status="WARN">
|
||||
<Properties>
|
||||
<Property name="APP_NAME">dg_lab</Property>
|
||||
<Property name="LOG_HOME">${env:log.dir:-logs}/${APP_NAME}</Property>
|
||||
<Property name="ENCODER_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{80} - %msg%n</Property>
|
||||
</Properties>
|
||||
|
||||
<Appenders>
|
||||
<!-- 控制台输出 -->
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${ENCODER_PATTERN}" />
|
||||
</Console>
|
||||
|
||||
<!-- 普通日志输出 -->
|
||||
<RollingFile name="FILE" fileName="${LOG_HOME}/output.log"
|
||||
filePattern="${LOG_HOME}/output.%d{yyyy-MM-dd}.log">
|
||||
<PatternLayout pattern="${ENCODER_PATTERN}" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="7" />
|
||||
</RollingFile>
|
||||
|
||||
<!-- 错误日志输出(WARN及以上) -->
|
||||
<RollingFile name="ERROR_FILE" fileName="${LOG_HOME}/error.log"
|
||||
filePattern="${LOG_HOME}/error.%d{yyyy-MM-dd}.log">
|
||||
<PatternLayout pattern="${ENCODER_PATTERN}" />
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
|
||||
</Filters>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="7" />
|
||||
</RollingFile>
|
||||
|
||||
<!-- sync日志 -->
|
||||
<RollingFile name="SYNC_FILE" fileName="${LOG_HOME}/sync.log"
|
||||
filePattern="${LOG_HOME}/sync.%d{yyyy-MM-dd}.log">
|
||||
<PatternLayout pattern="${ENCODER_PATTERN}" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="7" />
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<!-- 指定 logger -->
|
||||
<Logger name="log.sync" level="debug" additivity="true">
|
||||
<AppenderRef ref="SYNC_FILE" />
|
||||
</Logger>
|
||||
|
||||
<!-- 根日志记录器 -->
|
||||
<Root level="debug">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
<AppenderRef ref="FILE" />
|
||||
<AppenderRef ref="ERROR_FILE" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
86
CommonApi/build.gradle
Normal file
86
CommonApi/build.gradle
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
}
|
||||
group = api_project_group
|
||||
version = project_version
|
||||
base {
|
||||
archivesName.set("${project_name}-${api_suffix}")
|
||||
}
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ['src/main/java']
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src/main/resources']
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = ['src/test/java']
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src/test/resources']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
|
||||
implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.23.1'
|
||||
implementation group: 'org.realityforge.org.jetbrains.annotations', name: 'org.jetbrains.annotations', version: '1.7.0'
|
||||
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0'
|
||||
implementation group: 'com.google.guava', name: 'guava', version: '33.3.0-jre'
|
||||
implementation group: 'io.netty', name: 'netty-all', version: '4.1.109.Final'
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.16'
|
||||
}
|
||||
tasks.register('sourcesJar', Jar) {
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
tasks.register('javadocJar', Jar) {
|
||||
dependsOn(tasks.named("javadoc"))
|
||||
archiveClassifier.set('javadoc')
|
||||
from(tasks.named("javadoc").get().destinationDir)
|
||||
}
|
||||
// 自定义 jar 输出名称
|
||||
tasks.jar {
|
||||
archiveVersion.set(project.version.toString())
|
||||
archiveClassifier.set("")
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar', Jar) {
|
||||
archiveClassifier.set('sources')
|
||||
}
|
||||
|
||||
tasks.named('javadocJar', Jar) {
|
||||
archiveVersion.set(project.version.toString())
|
||||
archiveClassifier.set('javadoc')
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.encoding = 'UTF-8'
|
||||
options.charSet = 'UTF-8'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact(tasks.named("sourcesJar"))
|
||||
artifact(tasks.named("javadocJar"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.dataType;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
|
||||
/**
|
||||
* The type Power box commands.
|
||||
*/
|
||||
public final class PowerBoxCommands {
|
||||
/**
|
||||
* The constant STRENGTH.
|
||||
*/
|
||||
public static final String STRENGTH = "strength";
|
||||
/**
|
||||
* The constant PULSE.
|
||||
*/
|
||||
public static final String PULSE = "pulse";
|
||||
/**
|
||||
* The constant CLEAR.
|
||||
*/
|
||||
public static final String CLEAR = "clear";
|
||||
/**
|
||||
* The constant FEEDBACK.
|
||||
*/
|
||||
public static final String FEEDBACK = "feedback";
|
||||
|
||||
/**
|
||||
* Gets command type.
|
||||
*
|
||||
* @param commandPrefix the command prefix
|
||||
* @return the command type
|
||||
*/
|
||||
static String getCommandType(PowerBoxDataType commandPrefix) {
|
||||
return switch (commandPrefix) {
|
||||
case STRENGTH -> STRENGTH;
|
||||
case PULSE -> PULSE;
|
||||
case CLEAR -> CLEAR;
|
||||
case FEEDBACK -> FEEDBACK;
|
||||
default -> PowerBoxMsgType.UNKNOWN;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.dataType;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
|
||||
/**
|
||||
* The type Power box msg type.
|
||||
*/
|
||||
public final class PowerBoxMsgType {
|
||||
/**
|
||||
* The constant HEARTBEAT.
|
||||
*/
|
||||
public static final String HEARTBEAT = "heartbeat";
|
||||
/**
|
||||
* The constant BIND.
|
||||
*/
|
||||
public static final String BIND = "bind";
|
||||
/**
|
||||
* The constant BREAK.
|
||||
*/
|
||||
public static final String BREAK = "break";
|
||||
/**
|
||||
* The constant MSG_COMMAND.
|
||||
*/
|
||||
public static final String MSG_COMMAND = "msg";
|
||||
/**
|
||||
* The constant ERROR.
|
||||
*/
|
||||
public static final String ERROR = "error";
|
||||
/**
|
||||
* The constant CLIENT_MSG.
|
||||
*/
|
||||
public static final String CLIENT_MSG = "clientMsg";
|
||||
/**
|
||||
* The constant UNKNOWN.
|
||||
*/
|
||||
public static final String UNKNOWN = "unknown";
|
||||
|
||||
/**
|
||||
* Gets msg type.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the msg type
|
||||
*/
|
||||
public static String getMsgType(PowerBoxDataType type) {
|
||||
return getMsgType(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets msg type.
|
||||
*
|
||||
* @param type the type
|
||||
* @param showMsgCommand the show msg command
|
||||
* @return the msg type
|
||||
*/
|
||||
public static String getMsgType(PowerBoxDataType type, boolean showMsgCommand) {
|
||||
return switch (type) {
|
||||
case _NC_HEARTBEAT_ -> HEARTBEAT;
|
||||
case _NC_BIND_ -> BIND;
|
||||
case _NC_BREAK_ -> BREAK;
|
||||
case _NC_ERROR_ -> ERROR;
|
||||
case CLIENT_MESSAGE -> CLIENT_MSG;
|
||||
case STRENGTH, CLEAR, PULSE, FEEDBACK -> showMsgCommand ? PowerBoxCommands.getCommandType(type) : MSG_COMMAND;
|
||||
default -> UNKNOWN;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.exception;
|
||||
|
||||
/**
|
||||
* The type No match data type exception.
|
||||
*/
|
||||
public class NoMatchDataTypeException extends Exception {
|
||||
/**
|
||||
* Instantiates a new No match data type exception.
|
||||
*
|
||||
* @param message the message
|
||||
*/
|
||||
public NoMatchDataTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new No match data type exception.
|
||||
*/
|
||||
public NoMatchDataTypeException() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.manager;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
|
||||
/**
|
||||
* DGLab管理接口
|
||||
*/
|
||||
public interface IDGLabManager {
|
||||
|
||||
/**
|
||||
* Start.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
ISharedData getSharedData();
|
||||
/**
|
||||
* 获取当前单例实例运行
|
||||
*
|
||||
* @return 运行状态 status
|
||||
*/
|
||||
Status getStatus();
|
||||
|
||||
/**
|
||||
* 设置当前单例实例运行
|
||||
*
|
||||
* @param status 运行状态
|
||||
*/
|
||||
void setStatus(Status status);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.manager;
|
||||
|
||||
/**
|
||||
* The enum Status.
|
||||
*/
|
||||
public enum Status {
|
||||
/**
|
||||
* 等待初始化
|
||||
*/
|
||||
WAITING_FOR_INIT,
|
||||
/**
|
||||
* Starting status.
|
||||
*/
|
||||
STARTING,
|
||||
/**
|
||||
* Running status.
|
||||
*/
|
||||
RUNNING,
|
||||
/**
|
||||
* Stopping status.
|
||||
*/
|
||||
STOPPING,
|
||||
/**
|
||||
* Stopped status.
|
||||
*/
|
||||
STOPPED,
|
||||
/**
|
||||
* Error status.
|
||||
*/
|
||||
ERROR
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message;
|
||||
|
||||
import com.r3944realms.dg_lab.api.dataType.PowerBoxCommands;
|
||||
import com.r3944realms.dg_lab.api.dataType.PowerBoxMsgType;
|
||||
import com.r3944realms.dg_lab.api.exception.NoMatchDataTypeException;
|
||||
import com.r3944realms.dg_lab.api.message.argType.ChangePolicy;
|
||||
import com.r3944realms.dg_lab.api.message.argType.Channel;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWaveList;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWaveListGenerator;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.MessageDirection;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithSingleAttachment;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The interface Power box msg.
|
||||
*/
|
||||
public interface IPowerBoxMsg {
|
||||
|
||||
/**
|
||||
* To power box data power box data.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @return the power box data
|
||||
*/
|
||||
PowerBoxData toPowerBoxData(String clientUUID, String targetUUID);
|
||||
|
||||
/**
|
||||
* To power box message power box message.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @param sender the sender
|
||||
* @param receiver the receiver
|
||||
* @return the power box message
|
||||
*/
|
||||
default PowerBoxMessage toPowerBoxMessage(String clientUUID, String targetUUID, Role sender, Role receiver) {
|
||||
return new PowerBoxMessage(toPowerBoxData(clientUUID, targetUUID), new MessageDirection<>(sender, receiver));
|
||||
}
|
||||
|
||||
/**
|
||||
* To power box message power box message.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @param direction the direction
|
||||
* @return the power box message
|
||||
*/
|
||||
default PowerBoxMessage toPowerBoxMessage(String clientUUID, String targetUUID, MessageDirection<?, ?> direction) {
|
||||
return new PowerBoxMessage(toPowerBoxData(clientUUID, targetUUID), direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* To power box message power box message.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @param type the type
|
||||
* @return the power box message
|
||||
*/
|
||||
default PowerBoxMessage toPowerBoxMessage(String clientUUID, String targetUUID, MessageDirection.DirectType type) {
|
||||
return new PowerBoxMessage(toPowerBoxData(clientUUID, targetUUID), MessageDirection.of(type, clientUUID, targetUUID));
|
||||
}
|
||||
|
||||
/**
|
||||
* To power box message power box message.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @param receiverName the receiver name
|
||||
* @param type the type
|
||||
* @return the power box message
|
||||
*/
|
||||
default PowerBoxMessage toPowerBoxMessage(String clientUUID, String targetUUID, String receiverName, MessageDirection.DirectType type) {
|
||||
return new PowerBoxMessage(toPowerBoxData(clientUUID, targetUUID), MessageDirection.of(type, clientUUID, receiverName));
|
||||
}
|
||||
|
||||
/**
|
||||
* To power box message power box message.
|
||||
*
|
||||
* @param clientUUID the client uuid
|
||||
* @param targetUUID the target uuid
|
||||
* @param senderName the sender name
|
||||
* @param receiverName the receiver name
|
||||
* @param type the type
|
||||
* @return the power box message
|
||||
*/
|
||||
default PowerBoxMessage toPowerBoxMessage(String clientUUID, String targetUUID,String senderName, String receiverName, MessageDirection.DirectType type) {
|
||||
return new PowerBoxMessage(toPowerBoxData(clientUUID, targetUUID), MessageDirection.of(type, senderName, receiverName));
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Strength.
|
||||
*/
|
||||
record StrengthChange(Channel channel, ChangePolicy policy, int value) implements IPowerBoxMsg {
|
||||
@Override
|
||||
public PowerBoxData toPowerBoxData(String clientUUID, String targetUUID) {
|
||||
String msg = PowerBoxCommands.STRENGTH + "-" + channel.index_int + "+" + policy.index + "+" + value;
|
||||
return new PowerBoxData(PowerBoxMsgType.MSG_COMMAND, clientUUID, targetUUID, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read strength change.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the strength change
|
||||
* @throws NoMatchDataTypeException the no match data type exception
|
||||
*/
|
||||
public static StrengthChange read(PowerBoxMessage msg) throws NoMatchDataTypeException {
|
||||
if (msg.commandType != PowerBoxDataType.STRENGTH) throw new NoMatchDataTypeException();
|
||||
PowerBoxData payload = msg.getPayload();
|
||||
Object[] argsArrayByPointing = payload.getArgsArrayByPointing(msg.commandType);
|
||||
if (argsArrayByPointing.length != 3) throw new NoMatchDataTypeException();
|
||||
return new StrengthChange(Channel.getChannel((Integer) argsArrayByPointing[0]), ChangePolicy.getChangePolicy((Integer) argsArrayByPointing[1]), (Integer) argsArrayByPointing[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Strength info.
|
||||
*/
|
||||
record StrengthInfo(int aValue, int bValue, int aMax, int bMax) implements IPowerBoxMsg {
|
||||
@Override
|
||||
public PowerBoxData toPowerBoxData(String clientUUID, String targetUUID) {
|
||||
String msg = PowerBoxCommands.STRENGTH + "-" + aValue + "+" + bValue + "+" + aMax + "+" + bMax;
|
||||
return new PowerBoxData(PowerBoxMsgType.MSG_COMMAND, clientUUID, targetUUID, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read strength info.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the strength info
|
||||
* @throws NoMatchDataTypeException the no match data type exception
|
||||
*/
|
||||
public static StrengthInfo read(PowerBoxMessage msg) throws NoMatchDataTypeException {
|
||||
if (msg.commandType != PowerBoxDataType.STRENGTH) throw new NoMatchDataTypeException();
|
||||
PowerBoxData payload = msg.getPayload();
|
||||
Object[] argsArrayByPointing = payload.getArgsArrayByPointing(msg.commandType);
|
||||
if (argsArrayByPointing.length != 4) throw new NoMatchDataTypeException();
|
||||
return new StrengthInfo((Integer) argsArrayByPointing[0], (Integer) argsArrayByPointing[1], (Integer) argsArrayByPointing[2], (Integer) argsArrayByPointing[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Clear.
|
||||
*/
|
||||
record Clear(Channel channel) implements IPowerBoxMsg {
|
||||
@Override
|
||||
public PowerBoxData toPowerBoxData(String clientUUID, String targetUUID) {
|
||||
String msg = PowerBoxCommands.CLEAR + "-" + channel.index_int;
|
||||
return new PowerBoxData(PowerBoxMsgType.MSG_COMMAND, clientUUID, targetUUID, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read clear.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the clear
|
||||
* @throws NoMatchDataTypeException the no match data type exception
|
||||
*/
|
||||
public static Clear read(PowerBoxMessage msg) throws NoMatchDataTypeException {
|
||||
if (msg.commandType != PowerBoxDataType.CLEAR) throw new NoMatchDataTypeException();
|
||||
PowerBoxData payload = msg.getPayload();
|
||||
Object[] argsArrayByPointing = payload.getArgsArrayByPointing(msg.commandType);
|
||||
return new Clear(Channel.getChannel((Integer) argsArrayByPointing[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Feedback.
|
||||
*/
|
||||
record Feedback(int feedback) implements IPowerBoxMsg {
|
||||
@Override
|
||||
public PowerBoxData toPowerBoxData(String clientUUID, String targetUUID) {
|
||||
String msg = "feedback" + "-" + feedback;
|
||||
return new PowerBoxData(PowerBoxMsgType.MSG_COMMAND, clientUUID, targetUUID, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read feedback.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the feedback
|
||||
* @throws NoMatchDataTypeException the no match data type exception
|
||||
*/
|
||||
public static Feedback read(PowerBoxMessage msg) throws NoMatchDataTypeException {
|
||||
if (msg.commandType != PowerBoxDataType.FEEDBACK) throw new NoMatchDataTypeException();
|
||||
PowerBoxData payload = msg.getPayload();
|
||||
Object[] argsArrayByPointing = payload.getArgsArrayByPointing(msg.commandType);
|
||||
return new Feedback((Integer) argsArrayByPointing[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Pulse.
|
||||
*/
|
||||
record Pulse(Channel channel, PulseWaveList pulseWaveList, Integer timer) implements IPowerBoxMsg {
|
||||
/**
|
||||
* Instantiates a new Pulse.
|
||||
*
|
||||
* @param channel the channel
|
||||
* @param waveData the wave data
|
||||
* @param timer the timer
|
||||
*/
|
||||
public Pulse(Channel channel, String[] waveData, Integer timer) {
|
||||
this(channel, PulseWaveListGenerator.toPulseWaveListFromStringArray(waveData), timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Pulse.
|
||||
*
|
||||
* @param channel the channel
|
||||
* @param waveData the wave data
|
||||
*/
|
||||
public Pulse(Channel channel, String[] waveData) {
|
||||
this(channel, waveData, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Pulse.
|
||||
*
|
||||
* @param channel the channel
|
||||
* @param waveData the wave data
|
||||
*/
|
||||
public Pulse(Channel channel, PulseWaveList waveData) {
|
||||
this(channel, waveData, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxDataWithSingleAttachment toPowerBoxData(String clientUUID, String targetUUID) {
|
||||
String msg = "pulse-" + channel.index_char + ":" + pulseWaveList.toListString();
|
||||
return new PowerBoxData(PowerBoxMsgType.CLIENT_MSG, clientUUID, targetUUID, msg).withSingleAttachment(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read pulse.
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the pulse
|
||||
* @throws NoMatchDataTypeException the no match data type exception
|
||||
*/
|
||||
public static Pulse read(PowerBoxMessage msg) throws NoMatchDataTypeException {
|
||||
PowerBoxData payload = msg.getPayload();
|
||||
PowerBoxDataType commandType = payload.getCommandType(true);
|
||||
if (commandType != PowerBoxDataType.PULSE) throw new NoMatchDataTypeException();
|
||||
Object[] argsArrayByPointing = payload.getArgsArrayByPointing(commandType);//10 0 1 ~ 9
|
||||
return new Pulse(Channel.getChannel(((String)argsArrayByPointing[0]).charAt(0)), Arrays.copyOfRange((String[])argsArrayByPointing, 1, argsArrayByPointing.length - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.adapter;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWave;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 处理 PulseWave 的 JSON 格式:
|
||||
* - 格式1: "A1B2C3D4E5F6G7H8" (16进制字符串)
|
||||
* - 格式2: {"frequencies":[20,30,40,50], "strengths":[80,90,70,60]}
|
||||
*/
|
||||
public class PulseWaveAdapter implements JsonSerializer<PulseWave>, JsonDeserializer<PulseWave> {
|
||||
@Override
|
||||
public PulseWave deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
try {
|
||||
if (json.isJsonPrimitive()) {
|
||||
String hex = json.getAsString();
|
||||
return parseHexString(hex);
|
||||
} else if (json.isJsonObject()) {
|
||||
return parseObject(json.getAsJsonObject());
|
||||
} else
|
||||
throw new JsonParseException("Invalid PulseWave format: expected String or Object");
|
||||
} catch (Exception e) {
|
||||
throw new JsonParseException("PulseWave validation failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(PulseWave src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
// 默认序列化为16进制字符串(如 "0A1E1422805A4632")
|
||||
return new JsonPrimitive(src.toHexString());
|
||||
}
|
||||
/**
|
||||
* 解析16进制字符串(格式:16字符,如 "0A1E1422805A4632")
|
||||
*/
|
||||
private PulseWave parseHexString(String hex) {
|
||||
hex = hex.toUpperCase().trim();
|
||||
if (!hex.matches("^[0-9A-F]{16}$")) {
|
||||
throw new JsonParseException("Invalid hex format: must be 16 uppercase hex characters");
|
||||
}
|
||||
return PulseWave.fromHex(hex);
|
||||
}
|
||||
/**
|
||||
* 解析对象格式({"frequencies":[...], "strengths":[...]})
|
||||
*/
|
||||
private PulseWave parseObject(JsonObject obj) {
|
||||
// 解析频率数组
|
||||
int[] frequencies = parseJsonArray(obj.get("frequencies"), "frequencies");
|
||||
// 解析强度数组
|
||||
int[] strengths = parseJsonArray(obj.get("strengths"), "strengths");
|
||||
return PulseWave.fromArrays(frequencies, strengths);
|
||||
}
|
||||
/**
|
||||
* 解析JSON数组并校验长度和类型
|
||||
*/
|
||||
private int[] parseJsonArray(JsonElement element, String fieldName) {
|
||||
if (element == null || !element.isJsonArray()) {
|
||||
throw new JsonParseException("Missing required field: " + fieldName);
|
||||
}
|
||||
|
||||
JsonArray array = element.getAsJsonArray();
|
||||
if (array.size() != 4) {
|
||||
throw new JsonParseException(fieldName + " must have exactly 4 elements");
|
||||
}
|
||||
|
||||
int[] values = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
values[i] = array.get(i).getAsInt();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonParseException("Invalid number in " + fieldName + " at index " + i, e);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.adapter;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWave;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWaveList;
|
||||
import com.r3944realms.dg_lab.api.message.data.PulseWaveListGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type Pulse wave list adapter.
|
||||
*/
|
||||
public class PulseWaveListAdapter extends TypeAdapter<PulseWaveList> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, PulseWaveList value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("name").value(value.getName());
|
||||
out.name("list");
|
||||
writeList(out, value.getList());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PulseWaveList read(JsonReader in) throws IOException {
|
||||
PulseWaveList waveList = new PulseWaveList();
|
||||
boolean hasName = false;
|
||||
boolean hasList = false;
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String field = in.nextName();
|
||||
switch (field) {
|
||||
case "name":
|
||||
waveList.setName(in.nextString());
|
||||
hasName = true;
|
||||
break;
|
||||
case "list":
|
||||
readList(in, waveList);
|
||||
hasList = true;
|
||||
break;
|
||||
default:
|
||||
// 严格模式下可抛出异常
|
||||
// throw new JsonParseException("未知字段: " + field);
|
||||
in.skipValue(); // 宽松模式忽略未知字段
|
||||
}
|
||||
|
||||
}
|
||||
in.endObject();
|
||||
if (!hasName) {
|
||||
throw new JsonParseException("Missing required field: name");
|
||||
}
|
||||
if (!hasList) {
|
||||
throw new JsonParseException("Missing required field: list");
|
||||
}
|
||||
return waveList;
|
||||
}
|
||||
private void writeList(JsonWriter out, List<PulseWave> list) throws IOException {
|
||||
out.beginArray();
|
||||
for (PulseWave wave : list) {
|
||||
PulseWaveListGenerator.gson.toJson(wave, PulseWave.class, out);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
private void readList(JsonReader in, PulseWaveList waveList) throws IOException {
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
if (in.peek() == JsonToken.STRING) {
|
||||
// Parse string to PulseWave (adjust based on toHexString() format)
|
||||
waveList.add(parseWaveFromString(in.nextString()));
|
||||
} else {
|
||||
// Existing logic for object parsing
|
||||
waveList.add(PulseWaveListGenerator.gson.getAdapter(PulseWave.class).read(in));
|
||||
}
|
||||
}
|
||||
in.endArray();
|
||||
}
|
||||
private PulseWave parseWaveFromString(String waveString) {
|
||||
// Implement parsing logic based on PulseWave's toHexString() format
|
||||
// Example: If toHexString() returns JSON, use:
|
||||
return PulseWave.fromString(waveString);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.argType;
|
||||
|
||||
/**
|
||||
* 修改策略
|
||||
*/
|
||||
public enum ChangePolicy {
|
||||
/**
|
||||
* 增加
|
||||
*/
|
||||
INCREASE(0),
|
||||
/**
|
||||
* 减少
|
||||
*/
|
||||
DECREASE(1),
|
||||
/**
|
||||
* 转变
|
||||
*/
|
||||
GOTO(2);
|
||||
/**
|
||||
* The Index.
|
||||
*/
|
||||
public final int index;
|
||||
|
||||
ChangePolicy(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets change policy.
|
||||
*
|
||||
* @param index the index
|
||||
* @return the change policy
|
||||
*/
|
||||
public static ChangePolicy getChangePolicy(int index) {
|
||||
return switch (index) {
|
||||
case 0 -> INCREASE;
|
||||
case 1 -> DECREASE;
|
||||
case 2 -> GOTO;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + index);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.argType;
|
||||
|
||||
/**
|
||||
* 通道
|
||||
*/
|
||||
public enum Channel {
|
||||
/**
|
||||
* A频道
|
||||
*/
|
||||
A(1, 'A'),
|
||||
/**
|
||||
* B频道
|
||||
*/
|
||||
B(2, 'B');
|
||||
/**
|
||||
* 通道int值
|
||||
*/
|
||||
public final int index_int;
|
||||
/**
|
||||
* 通道char值
|
||||
*/
|
||||
public final char index_char;
|
||||
|
||||
Channel(int index_int, char index_char) {
|
||||
this.index_int = index_int;
|
||||
this.index_char = index_char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets channel.
|
||||
*
|
||||
* @param index_int the index int
|
||||
* @return the channel
|
||||
*/
|
||||
public static Channel getChannel(int index_int) {
|
||||
return index_int == 1 ? A : B;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets channel.
|
||||
*
|
||||
* @param index_char the index char
|
||||
* @return the channel
|
||||
*/
|
||||
public static Channel getChannel(char index_char) {
|
||||
return index_char == 'A' ? A : B;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.data;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
|
||||
/**
|
||||
* 定义了100ms内部的4端波形数据<br/>
|
||||
* <code>frequency</code> 范围在10~240里<br/>
|
||||
* <code>strength</code> 范围在0~100里
|
||||
*
|
||||
* @param f1 第一个25ms的波频
|
||||
* @param f2 第二个25ms的波频
|
||||
* @param f3 第三个25ms的波频
|
||||
* @param f4 第四个25ms的波频
|
||||
* @param s1 第一个25ms的强度
|
||||
* @param s2 第二个25ms的强度
|
||||
* @param s3 第三个25ms的强度
|
||||
* @param s4 第四个25ms的强度
|
||||
*/
|
||||
public record PulseWave(int f1, int f2, int f3, int f4, int s1, int s2, int s3, int s4) {
|
||||
/**
|
||||
* Instantiates a new Pulse wave.
|
||||
*
|
||||
* @param f1 the f 1
|
||||
* @param f2 the f 2
|
||||
* @param f3 the f 3
|
||||
* @param f4 the f 4
|
||||
* @param s1 the s 1
|
||||
* @param s2 the s 2
|
||||
* @param s3 the s 3
|
||||
* @param s4 the s 4
|
||||
*/
|
||||
public PulseWave(int f1, int f2, int f3, int f4, int s1, int s2, int s3, int s4) {
|
||||
this.f1 = validateFrequency(f1);
|
||||
this.f2 = validateFrequency(f2);
|
||||
this.f3 = validateFrequency(f3);
|
||||
this.f4 = validateFrequency(f4);
|
||||
this.s1 = validateStrength(s1);
|
||||
this.s2 = validateStrength(s2);
|
||||
this.s3 = validateStrength(s3);
|
||||
this.s4 = validateStrength(s4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Pulse wave.
|
||||
*
|
||||
* @param frequencies the frequencies
|
||||
* @param strengths the strengths
|
||||
*/
|
||||
PulseWave(int[] frequencies, int[] strengths) {
|
||||
this(frequencies[0], frequencies[1], frequencies[2], frequencies[3], strengths[0], strengths[1], strengths[2], strengths[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* From default pulse wave.
|
||||
*
|
||||
* @param f1 the f 1
|
||||
* @param f2 the f 2
|
||||
* @param f3 the f 3
|
||||
* @param f4 the f 4
|
||||
* @param s1 the s 1
|
||||
* @param s2 the s 2
|
||||
* @param s3 the s 3
|
||||
* @param s4 the s 4
|
||||
* @return the pulse wave
|
||||
*/
|
||||
public static PulseWave fromDefault(int f1, int f2, int f3, int f4, int s1, int s2, int s3, int s4) {
|
||||
return new PulseWave(f1, f2, f3, f4, s1, s2, s3, s4);
|
||||
}
|
||||
|
||||
/**
|
||||
* From arrays pulse wave.
|
||||
*
|
||||
* @param frequencies the frequencies
|
||||
* @param strengths the strengths
|
||||
* @return the pulse wave
|
||||
*/
|
||||
public static PulseWave fromArrays(int[] frequencies, int[] strengths) {
|
||||
return new PulseWave(frequencies, strengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字符串还原记录
|
||||
*
|
||||
* @param hex 哈希16进制字符串
|
||||
* @return PulseWave pulse wave
|
||||
*/
|
||||
public static PulseWave fromHex(String hex) {
|
||||
if (hex == null || !hex.toUpperCase().matches("^([0-9A-F]{2}){8}$")) {
|
||||
throw new IllegalArgumentException("Invalid hex string");
|
||||
}
|
||||
|
||||
// 解析前8字节为频率(f1-f4)
|
||||
int[] frequencies = parseHexChunk(hex.substring(0, 8), "frequency");
|
||||
int[] strengths = parseHexChunk(hex.substring(8, 16), "strength");
|
||||
|
||||
return PulseWave.fromArrays(frequencies, strengths);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return PulseWaveListGenerator.gson.toJson(this); // Serialize to JSON string
|
||||
}
|
||||
|
||||
/**
|
||||
* From string pulse wave.
|
||||
*
|
||||
* @param str the str
|
||||
* @return the pulse wave
|
||||
*/
|
||||
public static PulseWave fromString(String str) {
|
||||
return PulseWaveListGenerator.gson.fromJson(str, PulseWave.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* To hex string string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String toHexString() {
|
||||
return String.format("%02X%02X%02X%02X%02X%02X%02X%02X", f1, f2, f3, f4, s1, s2, s3, s4);
|
||||
}
|
||||
private static int[] parseHexChunk(String hex, String type) {
|
||||
int[] values = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
String byteStr = hex.substring(i * 2, (i + 1) * 2);
|
||||
int value = Integer.parseInt(byteStr, 16);
|
||||
|
||||
if ("frequency".equals(type)) validateFrequency(value);
|
||||
else if ("strength".equals(type)) validateStrength(value);
|
||||
|
||||
values[i] = value;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
private static int validateFrequency(int frequency) throws InvalidParameterException{
|
||||
if (frequency < 10 || frequency > 240)
|
||||
throw new IllegalArgumentException("Frequency must be between 10 and 240");
|
||||
return frequency;
|
||||
}
|
||||
private static int validateStrength(int strength) throws InvalidParameterException{
|
||||
if (strength < 0 || strength > 100)
|
||||
throw new IllegalArgumentException("Strength must be between 0 and 100");
|
||||
return strength;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.data;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 波形列表
|
||||
*/
|
||||
public class PulseWaveList {
|
||||
private String name;
|
||||
private final List<PulseWave> list;
|
||||
|
||||
/**
|
||||
* Instantiates a new Pulse wave list.
|
||||
*/
|
||||
public PulseWaveList() {
|
||||
this.name = "";
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param wave the wave
|
||||
*/
|
||||
public void add(PulseWave wave) {
|
||||
list.add(wave);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear.
|
||||
*/
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list.
|
||||
*
|
||||
* @return the list
|
||||
*/
|
||||
public List<PulseWave> getList() {
|
||||
return ImmutableList.copyOf(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* To list string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String toListString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
for (int i = 0; i <= list.size() -1; i++) {
|
||||
sb.append("\"").append(list.get(i).toHexString()).append("\"");
|
||||
if(i != list.size() - 1) sb.append(",");
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PulseWaveList{name='" + name + "', list=" + list + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.message.data;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.r3944realms.dg_lab.api.message.adapter.PulseWaveAdapter;
|
||||
import com.r3944realms.dg_lab.api.message.adapter.PulseWaveListAdapter;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* The type Pulse wave list generator.
|
||||
*/
|
||||
public class PulseWaveListGenerator {
|
||||
/**
|
||||
* The constant gson.
|
||||
*/
|
||||
public static final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(PulseWaveList.class, new PulseWaveListAdapter())
|
||||
.registerTypeAdapter(PulseWave.class, new PulseWaveAdapter())
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
private PulseWaveListGenerator() {}
|
||||
|
||||
/**
|
||||
* To pulse wave list from string array pulse wave list.
|
||||
*
|
||||
* @param waveStringList the wave string list
|
||||
* @return the pulse wave list
|
||||
*/
|
||||
public static PulseWaveList toPulseWaveListFromStringArray(String[] waveStringList) {
|
||||
PulseWaveList ret = new PulseWaveList();
|
||||
for (String s : waveStringList) {
|
||||
int v1, v2, v3, v4, v5, v6, v7, v8;
|
||||
int i = 0;
|
||||
v1 = Integer.parseInt(s.substring(i, 2 + i++), 16);
|
||||
v2 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v3 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v4 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v5 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v6 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v7 = Integer.parseInt(s.substring(++i, 2 + i++), 16);
|
||||
v8 = Integer.parseInt(s.substring(++i, 2 + i), 16);
|
||||
ret.add(new PulseWave(v1, v2, v3, v4, v5, v6, v7, v8));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入频率和强度数组,输出波形列表 <br/>
|
||||
* 数组长度必须一致,且为4的倍数。
|
||||
*
|
||||
* @param frequencies 频率数组
|
||||
* @param strengths 强度数组
|
||||
* @return 波形列表 pulse wave list
|
||||
* @throws IllegalArgumentException 不符合条件的输入
|
||||
*/
|
||||
public static PulseWaveList pulseWave(int[] frequencies, int[] strengths) throws IllegalArgumentException {
|
||||
if (frequencies.length != strengths.length)
|
||||
throw new IllegalArgumentException("frequencies and strengths must be the same length");
|
||||
if (frequencies.length % 4 != 0)
|
||||
throw new IllegalArgumentException("frequencies must be a multiple of 4");
|
||||
PulseWaveList ret = new PulseWaveList();
|
||||
for (int i = 0; i < frequencies.length; i += 4) {
|
||||
ret.add(
|
||||
new PulseWave(
|
||||
frequencies[i], frequencies[i + 1], frequencies[i + 2], frequencies[i + 3],
|
||||
strengths[i], strengths[i + 1], strengths[i + 2], strengths[i + 3]
|
||||
)
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入频率和强度混合数组,输出波形列表 <br/>
|
||||
*
|
||||
* @param args 频率和强度混合数组
|
||||
* @return 波形列表 pulse wave list
|
||||
* @throws IllegalArgumentException 不符合条件的输入
|
||||
*/
|
||||
public static PulseWaveList pulseWave(int[] args) throws IllegalArgumentException {
|
||||
if (args.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("the number of arguments must be a multiple of 2");
|
||||
}
|
||||
int[] frequencies = new int[args.length / 2];
|
||||
int[] strengths = new int[args.length / 2];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i % 2 == 0)
|
||||
frequencies[i] = args[i];
|
||||
else
|
||||
strengths[i] = args[i];
|
||||
}
|
||||
return pulseWave(frequencies, strengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成正弦波
|
||||
*
|
||||
* @param frequency 波的频率
|
||||
* @param minStrength 波的最小强度
|
||||
* @param maxStrength 波的最大强度
|
||||
* @param duration 波的时长d (d 是8的倍数,实际时长 = d * 25ms)
|
||||
* @return 生成的正弦的波 pulse wave list
|
||||
* @throws IllegalArgumentException 不符合条件的输入
|
||||
*/
|
||||
public static PulseWaveList sinPulse(
|
||||
int frequency, int minStrength, int maxStrength, int duration) throws IllegalArgumentException {
|
||||
if (duration <= 0)
|
||||
throw new IllegalArgumentException("duration must be greater than 0");
|
||||
int validDataNumber = duration - duration % 4;
|
||||
int[] strengths = new int[validDataNumber];
|
||||
// 振幅
|
||||
double amplitude = maxStrength - minStrength;
|
||||
// 角度增量, 只需要 0 - π 的范围
|
||||
double angleStep = Math.PI / (duration - 1);
|
||||
for (int i = 0; i < validDataNumber; i++) {
|
||||
// 当前角度
|
||||
double angle = i * angleStep;
|
||||
// 计算正弦值,并且平移到给定的最大最小值
|
||||
double sinValue = Math.sin(angle) * amplitude + minStrength;
|
||||
strengths[i] = ((int) Math.round(sinValue));
|
||||
}
|
||||
return pulseWave(IntStream.generate(() -> frequency).limit(validDataNumber).toArray(), strengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成梯度的波
|
||||
*
|
||||
* @param frequency 波的频率
|
||||
* @param startStrength 起始强度
|
||||
* @param endStrength 最终强度
|
||||
* @param duration 波的时长d (d 是8的倍数,实际时长 = d * 25ms)
|
||||
* @return 生成的梯度的波 pulse wave list
|
||||
* @throws IllegalArgumentException 不符合条件的输入
|
||||
*/
|
||||
public static PulseWaveList gradientPulse(
|
||||
int frequency, int startStrength, int endStrength, int duration) throws IllegalArgumentException {
|
||||
if (duration <= 0)
|
||||
throw new IllegalArgumentException("duration must be greater than 0");
|
||||
duration = duration - duration % 4;
|
||||
int[] strengths = new int[duration];
|
||||
double step = (endStrength - startStrength) / (duration - 1.0);
|
||||
for (int i = 0; i < duration; i++) {
|
||||
strengths[i] = ((int) Math.round(startStrength + step * i));
|
||||
}
|
||||
return pulseWave(IntStream.generate(() -> frequency).limit(duration).toArray(), strengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成平滑的波
|
||||
*
|
||||
* @param frequency 波的频率
|
||||
* @param strength 波的强度
|
||||
* @param duration 波的时长d (d 是8的倍数,实际时长 = d * 25ms)
|
||||
* @return 生成的平滑的波 pulse wave list
|
||||
* @throws IllegalArgumentException the illegal argument exception
|
||||
*/
|
||||
public static PulseWaveList smoothPulse(int frequency, int strength, int duration) throws IllegalArgumentException {
|
||||
if (duration <= 0) {
|
||||
throw new IllegalArgumentException("duration must be greater than 0");
|
||||
}
|
||||
duration = duration - duration % 4;
|
||||
return pulseWave(
|
||||
IntStream.generate(() -> frequency).limit(duration).toArray(),
|
||||
IntStream.generate(() -> strength).limit(duration).toArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.operation;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
|
||||
/**
|
||||
* The interface Client operation.
|
||||
*/
|
||||
public interface ClientOperation extends IOperation {
|
||||
/**
|
||||
* 客户端线程开启中处理
|
||||
*/
|
||||
void ClientStartingHandler();
|
||||
|
||||
/**
|
||||
* 客户端线程完全开启后处理
|
||||
*/
|
||||
void ClientStartedHandler();
|
||||
|
||||
/**
|
||||
* 客户端启动遇到错误后处理
|
||||
*/
|
||||
void ClientStartingErrorHandler();
|
||||
|
||||
/**
|
||||
* 客户端线程关闭中处理
|
||||
*/
|
||||
void ClientStoppingHandler();
|
||||
|
||||
/**
|
||||
* 客户端线程关闭中遇到错误后处理
|
||||
*/
|
||||
void ClientStoppingErrorHandler();
|
||||
|
||||
/**
|
||||
* 客户端线程完全关闭后处理
|
||||
*/
|
||||
void ClientStoppedHandler();
|
||||
|
||||
/**
|
||||
* 接收一个参数,实现二维码生成
|
||||
*
|
||||
* @param qrCodeUrl 二维码URL
|
||||
*/
|
||||
void QrCodeUrlHandler(final String qrCodeUrl);
|
||||
|
||||
/**
|
||||
* 将二维码展现出来
|
||||
*/
|
||||
void ShowQrCodeHandler();
|
||||
|
||||
/**
|
||||
* 通过二维码,连接成功后的通知
|
||||
*/
|
||||
void ConnectSuccessfulNoticeHandler();
|
||||
|
||||
/**
|
||||
* 断开连接信息触发处理
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
void DisconnectHandler(final PowerBoxData data);
|
||||
|
||||
/**
|
||||
* 错误信息触发处理
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
void ErrorHandler(final PowerBoxData data);
|
||||
|
||||
/**
|
||||
* 心跳信息触发处理
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
void HeartBeatHandler(final PowerBoxData data);
|
||||
|
||||
/**
|
||||
* 其它信息触发处理
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
void OtherMessageHandler(final PowerBoxData data);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.operation;
|
||||
|
||||
/**
|
||||
* The interface Operation.
|
||||
*/
|
||||
public interface IOperation {
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.operation;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The interface Server operation.
|
||||
*/
|
||||
public interface ServerOperation extends IOperation {
|
||||
|
||||
/**
|
||||
* 服务器线程开启中通知
|
||||
*/
|
||||
void ServerStartingHandler();
|
||||
|
||||
/**
|
||||
* 服务器线程开启中遇到错误后处理
|
||||
*/
|
||||
void ServerStartingErrorHandler();
|
||||
|
||||
/**
|
||||
* 服务器线程开启后处理
|
||||
*/
|
||||
void ServerStartedHandler();
|
||||
|
||||
/**
|
||||
* 服务器线程关闭中处理后通知
|
||||
*/
|
||||
void ServerStoppingHandler();
|
||||
|
||||
/**
|
||||
* 服务器线程关闭中遇到错误后处理
|
||||
*/
|
||||
void ServerStoppingErrorHandler();
|
||||
|
||||
/**
|
||||
* 服务器线程完全关闭后处理
|
||||
*/
|
||||
void ServerStoppedHandler();
|
||||
|
||||
/**
|
||||
* 在定时器里即将被移除的UUID处理
|
||||
*
|
||||
* @param clientId 不活跃的UUID
|
||||
*/
|
||||
void InactiveConnectionRemoveHandler(final String clientId);
|
||||
|
||||
/**
|
||||
* 读取信息错误走这里处理
|
||||
*
|
||||
* @param message 异常消息
|
||||
*/
|
||||
void ErrorMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 绑定成功处理
|
||||
*
|
||||
* @param message 绑定成功消息
|
||||
*/
|
||||
void BindSuccessMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 绑定失败处理
|
||||
*
|
||||
* @param message 绑定失败消息
|
||||
*/
|
||||
void BindFailureMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 心跳定时器触发处理
|
||||
*
|
||||
* @param message 心跳消息
|
||||
*/
|
||||
void HeartbeatMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 清理频道消息处理
|
||||
*
|
||||
* @param message 清理信息
|
||||
*/
|
||||
void ClearMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 反馈消息处理
|
||||
*
|
||||
* @param message 反馈消息
|
||||
*/
|
||||
void FeedbackMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 强度通知消息处理
|
||||
*
|
||||
* @param message 强度通知消息
|
||||
*/
|
||||
void StrengthMessageNoticeMessage(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 强度改变消息处理
|
||||
*
|
||||
* @param message 强度改变消息
|
||||
*/
|
||||
void StrengthMessageChangeHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 波形信息由客户端下发处理
|
||||
*
|
||||
* @param clearMessage 清理信息
|
||||
* @param pulseMessage 波形消息
|
||||
*/
|
||||
default void PulseClientMessageHandler(final PowerBoxMessage clearMessage, final PowerBoxMessage pulseMessage) {
|
||||
PulseClientMessageHandler(clearMessage, 500, pulseMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 波形信息由客户端下发处理
|
||||
*
|
||||
* @param clearMessage 清理信息
|
||||
* @param delayTime 延迟时间
|
||||
* @param pulseMessage 波形消息
|
||||
*/
|
||||
void PulseClientMessageHandler(@Nullable final PowerBoxMessage clearMessage, final int delayTime, final PowerBoxMessage pulseMessage);
|
||||
|
||||
/**
|
||||
* 连接断开消息处理
|
||||
*
|
||||
* @param message 断开消息
|
||||
*/
|
||||
void BreakConnectMessageHandler(final PowerBoxMessage message);
|
||||
|
||||
/**
|
||||
* 其它类型合法消息转发处理
|
||||
*
|
||||
* @param message 其它合法消息
|
||||
*/
|
||||
void OtherMessageHandler(final PowerBoxMessage message);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.handler;
|
||||
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
|
||||
|
||||
/**
|
||||
* 携带共享数据的接口
|
||||
*/
|
||||
public interface IAttachSharedData {
|
||||
/**
|
||||
* Gets shared data.
|
||||
*
|
||||
* @return the shared data
|
||||
*/
|
||||
ISharedData getSharedData();
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.IData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.adapter.IDataTypeAdapterFactory;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.RoleDeserializer;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 消息,带有方向和有效负载消息
|
||||
*/
|
||||
public abstract class Message implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* The constant gson.
|
||||
*/
|
||||
@Expose(deserialize = false, serialize = false)
|
||||
final static Gson gson;
|
||||
/**
|
||||
* The Direction.
|
||||
*/
|
||||
final public MessageDirection<?,?> direction;
|
||||
/**
|
||||
* The Payload.
|
||||
*/
|
||||
final IData payload;
|
||||
static {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(Role.class, new RoleDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(MessageDirection.class, new MessageDirectionDeserializer());
|
||||
gsonBuilder.registerTypeAdapterFactory(new IDataTypeAdapterFactory());
|
||||
gson = gsonBuilder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 额外的信息, 如
|
||||
* <ul>
|
||||
* <li>消息发送者UUID</li>
|
||||
* <li>消息创建时间</li>
|
||||
* <li>信息校对值</li>
|
||||
* <li>...</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return 信息 string
|
||||
*/
|
||||
public abstract String AdditionalInformation();
|
||||
|
||||
/**
|
||||
* Instantiates a new Message.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param direction the direction
|
||||
*/
|
||||
Message(IData payload, MessageDirection<?, ?> direction) {
|
||||
this.payload = payload;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data json.
|
||||
*
|
||||
* @return the data json
|
||||
*/
|
||||
public String getDataJson() {
|
||||
return getDataJson(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无效信息返回
|
||||
*
|
||||
* @return Json invalid message json
|
||||
*/
|
||||
public abstract String getInvalidMessageJson();
|
||||
|
||||
/**
|
||||
* Gets data json.
|
||||
*
|
||||
* @param isFix the is fix
|
||||
* @return the data json
|
||||
*/
|
||||
public String getDataJson(boolean isFix) {
|
||||
if(payload == null) return getInvalidMessageJson();
|
||||
return payload.isValid() ?
|
||||
(isFix ? gson.toJson(payload).replace("\\","") : gson.toJson(payload))
|
||||
: getInvalidMessageJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets msg json.
|
||||
*
|
||||
* @return the msg json
|
||||
*/
|
||||
public String getMsgJson() {
|
||||
return getMsgJson(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets msg json.
|
||||
*
|
||||
* @param isFix the is fix
|
||||
* @return the msg json
|
||||
*/
|
||||
public String getMsgJson(boolean isFix) {
|
||||
if(payload == null && direction == null) return getInvalidMessageJson();
|
||||
return isFix ? gson.toJson(this).replace("\\","") : gson.toJson(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets payload.
|
||||
*
|
||||
* @return the payload
|
||||
*/
|
||||
public IData getPayload() {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read json return message message.
|
||||
*
|
||||
* @param dataJson the data json
|
||||
* @param messageDirection the message direction
|
||||
* @return the message
|
||||
*/
|
||||
public abstract Message readJsonReturnMessage(String dataJson, MessageDirection<?, ?> messageDirection);
|
||||
|
||||
/**
|
||||
* Gets payload.
|
||||
*
|
||||
* @param json the json
|
||||
* @return the payload
|
||||
*/
|
||||
public abstract IData getPayload(String json);
|
||||
|
||||
/**
|
||||
* Gets message.
|
||||
*
|
||||
* @param json the json
|
||||
* @return the message
|
||||
*/
|
||||
public abstract Message getMessage(String json);
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.*;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* {@link Message}的参数之一
|
||||
*
|
||||
* @param <T> 接收者类型
|
||||
* @param <K> 发送者类型
|
||||
*/
|
||||
public record MessageDirection<T extends Role, K extends Role>(T sender, K receiver) implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"MessageDirection:[ " + sender.type + " -> " + receiver.type + " ] {" + sender.name + " -> " + receiver.name + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Of message direction.
|
||||
*
|
||||
* @param type the type
|
||||
* @param SenderUUID the sender uuid
|
||||
* @param ReceiverUUID the receiver uuid
|
||||
* @return the message direction
|
||||
*/
|
||||
public static MessageDirection<? extends Role, ? extends Role> of(final DirectType type, final String SenderUUID, final String ReceiverUUID) {
|
||||
return switch (type) {
|
||||
case PLACEHOLDER_TO_PLACEHOLDER -> new MessageDirection<>(new PlaceholderRole(SenderUUID), new PlaceholderRole(ReceiverUUID));
|
||||
case PLACEHOLDER_TO_CLIENT -> new MessageDirection<>(new PlaceholderRole(SenderUUID), new WebSocketClientRole(ReceiverUUID));
|
||||
case PLACEHOLDER_TO_SERVER -> new MessageDirection<>(new PlaceholderRole(SenderUUID), new WebSocketServerRole(ReceiverUUID));
|
||||
case PLACEHOLDER_TO_APPLICATION -> new MessageDirection<>(new PlaceholderRole(SenderUUID), new WebSocketApplicationRole(ReceiverUUID));
|
||||
case CLIENT_TO_PLACEHOLDER -> new MessageDirection<>(new WebSocketClientRole(SenderUUID), new PlaceholderRole(ReceiverUUID));
|
||||
case CLIENT_TO_CLIENT -> new MessageDirection<>(new WebSocketClientRole(SenderUUID), new WebSocketClientRole(ReceiverUUID));
|
||||
case CLIENT_TO_SERVER -> new MessageDirection<>(new WebSocketClientRole(SenderUUID), new WebSocketServerRole(ReceiverUUID));
|
||||
case CLIENT_TO_APPLICATION -> new MessageDirection<>(new WebSocketClientRole(SenderUUID), new WebSocketApplicationRole(ReceiverUUID));
|
||||
case SERVER_TO_PLACEHOLDER -> new MessageDirection<>(new WebSocketServerRole(SenderUUID), new PlaceholderRole(ReceiverUUID));
|
||||
case SERVER_TO_CLIENT -> new MessageDirection<>(new WebSocketServerRole(SenderUUID), new WebSocketClientRole(ReceiverUUID));
|
||||
case SERVER_TO_APPLICATION -> new MessageDirection<>(new WebSocketServerRole(SenderUUID), new WebSocketApplicationRole(ReceiverUUID));
|
||||
case SERVER_TO_SERVER -> new MessageDirection<>(new WebSocketServerRole(SenderUUID), new WebSocketServerRole(ReceiverUUID));
|
||||
case APPLICATION_TO_PLACEHOLDER -> new MessageDirection<>(new WebSocketApplicationRole(SenderUUID), new PlaceholderRole(ReceiverUUID));
|
||||
case APPLICATION_TO_CLIENT -> new MessageDirection<>(new WebSocketApplicationRole(SenderUUID), new WebSocketClientRole(ReceiverUUID));
|
||||
case APPLICATION_TO_APPLICATION -> new MessageDirection<>(new WebSocketApplicationRole(SenderUUID), new WebSocketApplicationRole(ReceiverUUID));
|
||||
case APPLICATION_TO_SERVER -> new MessageDirection<>(new WebSocketApplicationRole(SenderUUID), new WebSocketServerRole(ReceiverUUID));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The enum Direct type.
|
||||
*/
|
||||
public enum DirectType {
|
||||
/**
|
||||
* Placeholder to placeholder direct type.
|
||||
*/
|
||||
PLACEHOLDER_TO_PLACEHOLDER,
|
||||
/**
|
||||
* Placeholder to client direct type.
|
||||
*/
|
||||
PLACEHOLDER_TO_CLIENT,
|
||||
/**
|
||||
* Placeholder to server direct type.
|
||||
*/
|
||||
PLACEHOLDER_TO_SERVER,
|
||||
/**
|
||||
* Placeholder to application direct type.
|
||||
*/
|
||||
PLACEHOLDER_TO_APPLICATION,
|
||||
/**
|
||||
* Client to placeholder direct type.
|
||||
*/
|
||||
CLIENT_TO_PLACEHOLDER,
|
||||
/**
|
||||
* Client to client direct type.
|
||||
*/
|
||||
CLIENT_TO_CLIENT,
|
||||
/**
|
||||
* Client to server direct type.
|
||||
*/
|
||||
CLIENT_TO_SERVER,
|
||||
/**
|
||||
* Client to application direct type.
|
||||
*/
|
||||
CLIENT_TO_APPLICATION,
|
||||
/**
|
||||
* Server to placeholder direct type.
|
||||
*/
|
||||
SERVER_TO_PLACEHOLDER,
|
||||
/**
|
||||
* Server to client direct type.
|
||||
*/
|
||||
SERVER_TO_CLIENT,
|
||||
/**
|
||||
* Server to application direct type.
|
||||
*/
|
||||
SERVER_TO_APPLICATION,
|
||||
/**
|
||||
* Server to server direct type.
|
||||
*/
|
||||
SERVER_TO_SERVER,
|
||||
/**
|
||||
* Application to placeholder direct type.
|
||||
*/
|
||||
APPLICATION_TO_PLACEHOLDER,
|
||||
/**
|
||||
* Application to client direct type.
|
||||
*/
|
||||
APPLICATION_TO_CLIENT,
|
||||
/**
|
||||
* Application to application direct type.
|
||||
*/
|
||||
APPLICATION_TO_APPLICATION,
|
||||
/**
|
||||
* Application to server direct type.
|
||||
*/
|
||||
APPLICATION_TO_SERVER;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* The type Message direction deserializer.
|
||||
*/
|
||||
public class MessageDirectionDeserializer implements JsonDeserializer<MessageDirection<?, ?>> {
|
||||
@Override
|
||||
public MessageDirection<?, ?> deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
|
||||
JsonObject obj = jsonElement.getAsJsonObject();
|
||||
JsonElement senderJson = obj.get("sender");
|
||||
JsonElement receiverJson = obj.get("receiver");
|
||||
|
||||
Role sender = jsonDeserializationContext.deserialize(senderJson, Role.class);
|
||||
Role receiver = jsonDeserializationContext.deserialize(receiverJson, Role.class);
|
||||
return new MessageDirection<>(sender, receiver);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithAttachment;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithSingleAttachment;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxStatusCode;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.PlaceholderRole;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
|
||||
|
||||
/**
|
||||
* The type Power box message.
|
||||
*/
|
||||
public class PowerBoxMessage extends Message {
|
||||
|
||||
/**
|
||||
* The Command type.
|
||||
*/
|
||||
public final PowerBoxDataType commandType;
|
||||
private static final PowerBoxMessage Null = PowerBoxMessage.createPowerBoxMessage("null","null","null","null", new PlaceholderRole("null"), new PlaceholderRole("null"));
|
||||
/**
|
||||
* The Invalid message json.
|
||||
*/
|
||||
static final String INVALID_MESSAGE_JSON = gson.toJson(PowerBoxData.createPowerBoxData("error","","",""));
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box message.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param direction the direction
|
||||
*/
|
||||
public PowerBoxMessage(PowerBoxData payload, MessageDirection<?, ?> direction) {
|
||||
super(payload ,direction);
|
||||
commandType = payload.getCommandType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box message power box message.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param message the message
|
||||
* @param timer the timer
|
||||
* @param sender the sender
|
||||
* @param receiver the receiver
|
||||
* @return the power box message
|
||||
*/
|
||||
public static PowerBoxMessage createPowerBoxMessage(
|
||||
String type, String clientId, String targetId, String message, Integer timer,
|
||||
Role sender, Role receiver
|
||||
) {
|
||||
PowerBoxDataWithSingleAttachment data = new PowerBoxDataWithSingleAttachment(PowerBoxData.createPowerBoxData(type, clientId, targetId, message), timer);
|
||||
MessageDirection<?,?> direction = new MessageDirection<>(
|
||||
sender,
|
||||
receiver
|
||||
);
|
||||
return new PowerBoxMessage(data, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box message power box message.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param message the message
|
||||
* @param timerA the timer a
|
||||
* @param timerB the timer b
|
||||
* @param sender the sender
|
||||
* @param receiver the receiver
|
||||
* @return the power box message
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static PowerBoxMessage createPowerBoxMessage(
|
||||
String type, String clientId, String targetId, String message, Integer timerA, Integer timerB,
|
||||
Role sender, Role receiver
|
||||
) {
|
||||
PowerBoxDataWithAttachment data = new PowerBoxDataWithAttachment(PowerBoxData.createPowerBoxData(type, clientId, targetId, message), timerA, timerB);
|
||||
MessageDirection<?,?> direction = new MessageDirection<>(
|
||||
sender,
|
||||
receiver
|
||||
);
|
||||
return new PowerBoxMessage(data, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box message power box message.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param direction the direction
|
||||
* @return the power box message
|
||||
*/
|
||||
public static PowerBoxMessage createPowerBoxMessage(PowerBoxData payload, MessageDirection<?, ?> direction ) {
|
||||
return new PowerBoxMessage(payload, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box message power box message.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param message the message
|
||||
* @param sender the sender
|
||||
* @param receiver the receiver
|
||||
* @return the power box message
|
||||
*/
|
||||
public static PowerBoxMessage createPowerBoxMessage(
|
||||
String type, String clientId, String targetId, String message,
|
||||
Role sender, Role receiver
|
||||
) {
|
||||
PowerBoxData data = PowerBoxData.createPowerBoxData(type, clientId, targetId, message);
|
||||
MessageDirection<?,?> direction = new MessageDirection<>(
|
||||
sender,
|
||||
receiver
|
||||
);
|
||||
return new PowerBoxMessage(data, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box message power box message.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param statusCode the status code
|
||||
* @param sender the sender
|
||||
* @param receiver the receiver
|
||||
* @return the power box message
|
||||
*/
|
||||
public static PowerBoxMessage createPowerBoxMessage(
|
||||
String type, String clientId, String targetId, PowerBoxStatusCode statusCode,
|
||||
Role sender, Role receiver
|
||||
) {
|
||||
return createPowerBoxMessage(type, clientId, targetId, statusCode.getCode(), sender, receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅供转化用,不可使用与传递
|
||||
*
|
||||
* @return Null_Message null message
|
||||
*/
|
||||
public static PowerBoxMessage getNullMessage() {
|
||||
return Null;
|
||||
}
|
||||
@Override
|
||||
public String AdditionalInformation() {
|
||||
return "IPowerBoxMessage : " + direction.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInvalidMessageJson() {
|
||||
return INVALID_MESSAGE_JSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxMessage readJsonReturnMessage(String dataJson, MessageDirection<?, ?> messageDirection) throws JsonSyntaxException {
|
||||
return new PowerBoxMessage(getPayload(dataJson), messageDirection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxData getPayload() {
|
||||
return (PowerBoxData)payload;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param json PowerBoxDataJSON
|
||||
* @return PowerBoxData (如果Data字段存在空则会返回null值)
|
||||
*/
|
||||
@Override
|
||||
public PowerBoxData getPayload(String json) throws JsonSyntaxException {
|
||||
PowerBoxData powerBoxData = gson.fromJson(json, PowerBoxData.class);
|
||||
return (powerBoxData.Type() != null && powerBoxData.getClientId() != null && powerBoxData.getTargetId() != null && powerBoxData.getMessage() != null) ? powerBoxData : null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param json PowerBoxMessageJSON
|
||||
* @return PowerBoxMessage (如果message字段存在空则会返回null值)
|
||||
*/
|
||||
@Override
|
||||
public PowerBoxMessage getMessage(String json) {
|
||||
PowerBoxMessage message = gson.fromJson(json, PowerBoxMessage.class);
|
||||
return (message.direction != null && message.payload != null && message.commandType != null) ? message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets payload with attachment.
|
||||
*
|
||||
* @param json the json
|
||||
* @return the payload with attachment
|
||||
* @throws JsonSyntaxException the json syntax exception
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public PowerBoxDataWithAttachment getPayloadWithAttachment(String json) throws JsonSyntaxException {
|
||||
return gson.fromJson(json, PowerBoxDataWithAttachment.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets payload with single attachment.
|
||||
*
|
||||
* @param json the json
|
||||
* @return the payload with single attachment
|
||||
* @throws JsonSyntaxException the json syntax exception
|
||||
*/
|
||||
public PowerBoxDataWithSingleAttachment getPayloadWithSingleAttachment(String json) throws JsonSyntaxException {
|
||||
return gson.fromJson(json, PowerBoxDataWithSingleAttachment.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data;
|
||||
|
||||
public enum DataType {
|
||||
/**
|
||||
* Power box att data type.
|
||||
*/
|
||||
POWER_BOX_ATT,
|
||||
/**
|
||||
* Default data type.
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* Power box data type.
|
||||
*/
|
||||
POWER_BOX;
|
||||
/**
|
||||
* Gets type from string.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the type from string
|
||||
*/
|
||||
public static DataType getTypeFromString(String type) {
|
||||
try {
|
||||
return valueOf(type.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data;
|
||||
|
||||
public interface IData {
|
||||
|
||||
/**
|
||||
* 获取 无效原因
|
||||
*
|
||||
* @return inValidReason 无效原因
|
||||
*/
|
||||
default String getInvalidReason() {
|
||||
return "Invalid arguments [Default Reason]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前数据是否有效
|
||||
*
|
||||
* @return isValid 有效与否
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* 获取当前数据类型
|
||||
*
|
||||
* @return DateType 数据类型
|
||||
*/
|
||||
DataType Type();
|
||||
}
|
||||
|
|
@ -0,0 +1,379 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxDataType;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.type.PowerBoxStatusCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* PowerBox 负载消息数据
|
||||
*/
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class PowerBoxData implements IData {
|
||||
private final String type;
|
||||
private final String clientId;
|
||||
private final String targetId;
|
||||
private final String message;
|
||||
AtomicReference<String> inValidReason = new AtomicReference<>(getInvalidReason());
|
||||
/**
|
||||
* Instantiates a new Power box data.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param message the message
|
||||
*/
|
||||
public PowerBoxData(String type, String clientId, String targetId, String message) {
|
||||
this.type = type;
|
||||
this.clientId = clientId;
|
||||
this.targetId = targetId;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create power box data power box data.
|
||||
*
|
||||
* @param type the type
|
||||
* @param clientId the client id
|
||||
* @param targetId the target id
|
||||
* @param message the message
|
||||
* @return the power box data
|
||||
*/
|
||||
public static PowerBoxData createPowerBoxData(String type, String clientId, String targetId, String message) {
|
||||
return new PowerBoxData(type, clientId, targetId, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets type.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets client id.
|
||||
*
|
||||
* @return the client id
|
||||
*/
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets target id.
|
||||
*
|
||||
* @return the target id
|
||||
*/
|
||||
public String getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets message.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* With single attachment power box data with single attachment.
|
||||
*
|
||||
* @param timer the timer
|
||||
* @return the power box data with single attachment
|
||||
*/
|
||||
public PowerBoxDataWithSingleAttachment withSingleAttachment(Integer timer) {
|
||||
return new PowerBoxDataWithSingleAttachment(this, timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* With attachment power box data with attachment.
|
||||
*
|
||||
* @param timerA the timer a
|
||||
* @param timerB the timer b
|
||||
* @return the power box data with attachment
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public PowerBoxDataWithAttachment withAttachment(Integer timerA, Integer timerB) {
|
||||
return new PowerBoxDataWithAttachment(this, timerA, timerB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
if(type == null || type.isEmpty() || clientId == null || targetId == null || message == null) {
|
||||
inValidReason.set("Invalid PowerBox Data");
|
||||
return false;
|
||||
}
|
||||
final boolean commonValidCheck = !clientId.isEmpty() && !targetId.isEmpty() && !message.isEmpty();
|
||||
return switch (type) {
|
||||
case "heartbeat" -> !clientId.isEmpty() && PowerBoxStatusCode.isValidStatusCode(message);
|
||||
case "bind" -> Objects.equals(message, "targetId") ? (targetId.isEmpty() && !clientId.isEmpty()) : commonValidCheck;
|
||||
case "msg" -> !clientId.isEmpty() && !targetId.isEmpty() && isCommandValid(message);
|
||||
case "break","clientMsg" -> commonValidCheck;
|
||||
case "error" -> !message.isEmpty();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType Type() {
|
||||
return DataType.POWER_BOX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets command type.
|
||||
*
|
||||
* @return the command type
|
||||
*/
|
||||
public PowerBoxDataType getCommandType() {
|
||||
return PowerBoxDataType.getType(type, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets command type.
|
||||
*
|
||||
* @param mayPulse the may pulse
|
||||
* @return the command type
|
||||
*/
|
||||
public PowerBoxDataType getCommandType(boolean mayPulse) {
|
||||
return PowerBoxDataType.getType(type, message, mayPulse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get args array by pointing object [ ].
|
||||
*
|
||||
* @param dataType the data type
|
||||
* @return the object [ ]
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public Object[] getArgsArrayByPointing(PowerBoxDataType dataType) {
|
||||
if(message == null || message.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String[] args = message.split("-");
|
||||
switch(dataType) {
|
||||
case STRENGTH: {
|
||||
String[] arguments = args[1].split("\\+");
|
||||
int argumentsLength = arguments.length;
|
||||
switch (argumentsLength) {
|
||||
case 3:{
|
||||
int channel = Integer.parseInt(arguments[0]);
|
||||
int strengthChangePolicy = Integer.parseInt(arguments[1]);
|
||||
int value = Integer.parseInt(arguments[2]);
|
||||
return new Integer[]{channel, strengthChangePolicy, value};
|
||||
}
|
||||
case 4:{
|
||||
int AStrength = Integer.parseInt(arguments[0]);
|
||||
int BStrength = Integer.parseInt(arguments[1]);
|
||||
int ALimit = Integer.parseInt(arguments[2]);
|
||||
int BLimit = Integer.parseInt(arguments[3]);
|
||||
return new Integer[]{AStrength, BStrength, ALimit, BLimit};
|
||||
}
|
||||
}
|
||||
}
|
||||
case PULSE: {
|
||||
String channel = args[1].substring(0,1);
|
||||
String[] DataList = getWaveformDataList(args[1]);
|
||||
String[] dataList = new String[DataList.length + 1];
|
||||
int i = 0;
|
||||
dataList[i] = channel;
|
||||
for(String str : DataList) {
|
||||
i++;
|
||||
dataList[i] = str;
|
||||
}
|
||||
return dataList;
|
||||
|
||||
}
|
||||
case CLEAR: {
|
||||
return new Integer[]{ Integer.parseInt(args[1]) };
|
||||
}
|
||||
case FEEDBACK:{
|
||||
int arg = Integer.parseInt(args[1]);
|
||||
return new Integer[]{ arg };
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get args array object [ ].
|
||||
*
|
||||
* @return the object [ ]
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public Object[] getArgsArray() {
|
||||
if(message == null || message.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String[] args = message.split("-");
|
||||
switch(args[0]) {
|
||||
/* 强度 */
|
||||
case "strength": {
|
||||
String[] arguments = args[1].split("\\+");
|
||||
int argumentsLength = arguments.length;
|
||||
switch (argumentsLength) {
|
||||
/* 这个是客户端->指令中转服务器->App->PowerBox主机 */
|
||||
case 3:{
|
||||
int channel = Integer.parseInt(arguments[0]);
|
||||
int strengthChangePolicy = Integer.parseInt(arguments[1]);
|
||||
int value = Integer.parseInt(arguments[2]);
|
||||
/*=== 通道{1->A, 2->B} + 策略模式{0-减小, 1-增加 ,2-指定} + 数值 ===*/
|
||||
return new Integer[]{channel, strengthChangePolicy, value};
|
||||
}
|
||||
/* 这个是PowerBox主机->App->指令中转服务器->客户端 */
|
||||
case 4:{
|
||||
int AStrength = Integer.parseInt(arguments[0]);
|
||||
int BStrength = Integer.parseInt(arguments[1]);
|
||||
int ALimit = Integer.parseInt(arguments[2]);
|
||||
int BLimit = Integer.parseInt(arguments[3]);
|
||||
/*=== A通道目前强度 + B通道目前强度 + A通道强度上限 + B通道强度上限 ===*/
|
||||
return new Integer[]{AStrength, BStrength, ALimit, BLimit};
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 波形 */
|
||||
case "pulse": {
|
||||
String channel = args[1].substring(0,1);
|
||||
String[] DataList = getWaveformDataList(args[1]);
|
||||
String[] dataList = new String[DataList.length + 1];
|
||||
int i = 0;
|
||||
/* 频道 {A->A, B->B} */
|
||||
dataList[i] = channel;
|
||||
for(String str : DataList) {
|
||||
i++;
|
||||
/* 一段波形数据如 1122334455667788 */
|
||||
/* 解释为:
|
||||
* 第0~25ms频率,第25~50ms频率, 第50~75ms频率, 第75~100ms频率: 0x11, 0x22, 0x33 0x44
|
||||
* 第0~25ms强度,第25~50ms强度, 第50~75ms强度, 第75~100ms强度: 0x55, 0x66, 0x77 0x88
|
||||
* */
|
||||
dataList[i] = str;
|
||||
}
|
||||
return dataList;
|
||||
|
||||
}
|
||||
/* 清空波形 */
|
||||
case "clear": {
|
||||
/* 通道{1->A, 2->B} */
|
||||
return new Integer[]{ Integer.parseInt(args[1]) };
|
||||
}
|
||||
/* 反馈 */
|
||||
case "feedback":{
|
||||
/* 拟定不同形状图标代表的感受状态 */
|
||||
return new Integer[]{ Integer.parseInt(args[1]) };
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is command valid boolean.
|
||||
*
|
||||
* @param command the command
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isCommandValid(String command) {
|
||||
if(command == null || command.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String[] args = command.split("-");
|
||||
try {
|
||||
switch(args[0]) {
|
||||
case "strength": {
|
||||
String[] arguments = args[1].split("\\+");
|
||||
int argumentsLength = arguments.length;
|
||||
switch (argumentsLength) {
|
||||
case 3:{
|
||||
int channel = Integer.parseInt(arguments[0]);
|
||||
if(channel != 1 && channel != 2) throw new IllegalArgumentException("Channel must be 1 or 2");
|
||||
int strengthChangePolicy = Integer.parseInt(arguments[1]);
|
||||
if(2 < strengthChangePolicy || strengthChangePolicy < 0) throw new IllegalArgumentException("Strength change policy must in the range of [0,2]");
|
||||
int value = Integer.parseInt(arguments[2]);
|
||||
if (value < 0 || value > 200) throw new IllegalArgumentException("Value must be between 0 and 200");
|
||||
return true;
|
||||
}
|
||||
case 4:{
|
||||
return true;//App发来的数据应该不会有问题(如果有也不是我的锅())
|
||||
}
|
||||
default: throw new IllegalArgumentException("Invalid number of arguments");
|
||||
}
|
||||
}
|
||||
case "pulse": {
|
||||
String channel = args[1].substring(0,1);
|
||||
if(!(channel.equals("A") || channel.equals("B"))) throw new IllegalArgumentException("Channel is incorrect or lacked.");
|
||||
String[] DataList = getWaveformDataList(args[1]);
|
||||
Pattern pattern = Pattern.compile("^[a-zA-Z0-9]{16}$");//检查是否为16进制数字(大小写都可以)
|
||||
if (DataList.length > 100) throw new IllegalArgumentException("The list of Waveform data is too long.");
|
||||
for(String str : DataList) {
|
||||
if(str.length() != 16) {
|
||||
throw new IllegalArgumentException("Find list has a the invalid length of waveform data.");
|
||||
}
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
if (!matcher.matches()) {
|
||||
throw new NumberFormatException("Find list has a incorrect syntax of waveform data.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "clear": {
|
||||
String arg = args[1];
|
||||
if(args.length != 2) throw new IllegalArgumentException("Invalid number of arguments");
|
||||
if(!Objects.equals(arg, "1") && !Objects.equals(arg, "2")) throw new IllegalArgumentException("The argument must be 1 or 2");
|
||||
return true;
|
||||
}
|
||||
case "feedback":{
|
||||
int arg = Integer.parseInt(args[1]);
|
||||
if(args.length != 2) throw new IllegalArgumentException("Invalid number of arguments");
|
||||
if(0 > arg || arg > 10) throw new IllegalArgumentException("args must be between 0 and 10");
|
||||
return true;
|
||||
}
|
||||
default: throw new IllegalArgumentException("Invalid command");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
inValidReason.set(e.getMessage());
|
||||
return false;//指令不正确,直接否
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get waveform data list string [ ].
|
||||
*
|
||||
* @param msg the msg
|
||||
* @return the string [ ]
|
||||
*/
|
||||
String[] getWaveformDataList(String msg) {
|
||||
String dataList = msg.substring(msg.indexOf('[') + 1, msg.indexOf(']'));
|
||||
String[] rawStringList = dataList.split(",");
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
Arrays.stream(rawStringList).forEach(rawString -> {
|
||||
list.add(rawString.replaceAll("\"", ""));
|
||||
});
|
||||
String[] result = new String[list.size()];
|
||||
list.toArray(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* PowerBox双附加的额外数据
|
||||
*
|
||||
* @deprecated 建议使用单附加的
|
||||
*/
|
||||
@Deprecated
|
||||
public class PowerBoxDataWithAttachment extends PowerBoxData {
|
||||
@Nullable
|
||||
private final Integer timer_A;
|
||||
@Nullable
|
||||
private final Integer timer_B;
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box data with attachment.
|
||||
*
|
||||
* @param parent the parent
|
||||
* @param timer_A the timer a
|
||||
* @param timer_B the timer b
|
||||
*/
|
||||
public PowerBoxDataWithAttachment(PowerBoxData parent,@Nullable Integer timer_A,@Nullable Integer timer_B) {
|
||||
super(parent.getType(), parent.getClientId(), parent.getTargetId(), parent.getMessage());
|
||||
this.timer_A = timer_A;
|
||||
this.timer_B = timer_B;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach power box data with attachment.
|
||||
*
|
||||
* @param parent the parent
|
||||
* @param timer_A the timer a
|
||||
* @param timer_B the timer b
|
||||
* @return the power box data with attachment
|
||||
*/
|
||||
public static PowerBoxDataWithAttachment attach(PowerBoxData parent, Integer timer_A, Integer timer_B) {
|
||||
if(parent == null)
|
||||
throw new NullPointerException("parent is null");
|
||||
return new PowerBoxDataWithAttachment(parent, timer_A, timer_B);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets timer a.
|
||||
*
|
||||
* @return the timer a
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getTimerA() {
|
||||
return timer_A;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets timer b.
|
||||
*
|
||||
* @return the timer b
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getTimerB() {
|
||||
return timer_B;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return super.isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType Type() {
|
||||
return DataType.POWER_BOX_ATT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* PowerBox单附加的额外数据
|
||||
*/
|
||||
public class PowerBoxDataWithSingleAttachment extends PowerBoxData {
|
||||
@Nullable
|
||||
private final Integer timer;
|
||||
|
||||
/**
|
||||
* Instantiates a new Power box data with single attachment.
|
||||
*
|
||||
* @param parent the parent
|
||||
* @param timer the timer
|
||||
*/
|
||||
public PowerBoxDataWithSingleAttachment(PowerBoxData parent,@Nullable Integer timer) {
|
||||
super(parent.getType(), parent.getClientId(), parent.getTargetId(), parent.getMessage());
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets timer.
|
||||
*
|
||||
* @return the timer
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getTimer() {
|
||||
return timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach power box data with single attachment.
|
||||
*
|
||||
* @param parent the parent
|
||||
* @param timer the timer
|
||||
* @return the power box data with single attachment
|
||||
*/
|
||||
public static PowerBoxDataWithSingleAttachment attach(PowerBoxData parent, Integer timer){
|
||||
if(parent == null)
|
||||
throw new NullPointerException("parent is null");
|
||||
return new PowerBoxDataWithSingleAttachment(parent, timer);
|
||||
}
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return super.isValid();
|
||||
}
|
||||
@Override
|
||||
public DataType Type() {
|
||||
return DataType.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.adapter;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.IData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithAttachment;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithSingleAttachment;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The type Data type adapter factory.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IDataTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if(!IData.class.isAssignableFrom(typeToken.getRawType())) {
|
||||
return null;
|
||||
}
|
||||
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
|
||||
final TypeAdapter<PowerBoxData> powerBoxDataAdapter = gson.getDelegateAdapter(this, TypeToken.get(PowerBoxData.class));
|
||||
final TypeAdapter<PowerBoxDataWithAttachment> powerBoxDataWithAttachmentAdapter = gson.getDelegateAdapter(this, TypeToken.get(PowerBoxDataWithAttachment.class));
|
||||
final TypeAdapter<PowerBoxDataWithSingleAttachment> powerBoxDataWithSingleAttachmentTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(PowerBoxDataWithSingleAttachment.class));
|
||||
return (TypeAdapter<T>) new TypeAdapter<IData>() {
|
||||
@Override
|
||||
public void write(JsonWriter jsonWriter, IData iData) throws IOException {
|
||||
JsonElement jsonElement = switch (iData.getClass().getSimpleName()) {
|
||||
case "PowerBoxDataWithAttachment" ->
|
||||
powerBoxDataWithAttachmentAdapter.toJsonTree((PowerBoxDataWithAttachment) iData);
|
||||
case "PowerBoxDataWithSingleAttachment" ->
|
||||
powerBoxDataWithSingleAttachmentTypeAdapter.toJsonTree((PowerBoxDataWithSingleAttachment) iData);
|
||||
case "PowerBoxData" -> powerBoxDataAdapter.toJsonTree((PowerBoxData) iData);
|
||||
case "null" ->
|
||||
throw new NullPointerException("IDataTypeAdapterFactory#create(Gson gson, TypeToken iData): null");
|
||||
default -> throw new JsonSyntaxException("Unsupported data type: " + iData.getClass().getName());
|
||||
};
|
||||
elementAdapter.write(jsonWriter, jsonElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IData read(JsonReader jsonReader) throws IOException {
|
||||
JsonElement element = elementAdapter.read(jsonReader);
|
||||
JsonObject jsonObject = element.getAsJsonObject();
|
||||
|
||||
JsonElement type = jsonObject.get("type");
|
||||
|
||||
String Eigenvalues_A = type.getAsString();
|
||||
switch (Eigenvalues_A) {
|
||||
case "heartbeat", "error", "msg", "break", "bind" -> {
|
||||
return powerBoxDataAdapter.fromJsonTree(element);
|
||||
}
|
||||
case "clientMsg" -> {
|
||||
return powerBoxDataWithSingleAttachmentTypeAdapter.fromJsonTree(element);
|
||||
}
|
||||
default -> throw new JsonParseException("Unknown type");
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.adapter;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The type Power box data adapter.
|
||||
*/
|
||||
public class PowerBoxDataAdapter extends TypeAdapter<PowerBoxData> {
|
||||
@Override
|
||||
public void write(JsonWriter out, PowerBoxData value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("type").value(value.getType());
|
||||
out.name("clientId").value(value.getClientId());
|
||||
out.name("targetId").value(value.getTargetId());
|
||||
out.name("message").value(value.getMessage());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxData read(JsonReader in) throws IOException {
|
||||
String type = "";
|
||||
String clientId = "";
|
||||
String targetId = "";
|
||||
String message = "";
|
||||
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
switch (in.nextName()) {
|
||||
case "type":
|
||||
type = in.nextString();
|
||||
break;
|
||||
case "clientId":
|
||||
clientId = in.nextString();
|
||||
break;
|
||||
case "targetId":
|
||||
targetId = in.nextString();
|
||||
break;
|
||||
case "message":
|
||||
message = in.nextString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
if ("POWER_BOX".equals(type)) {
|
||||
return new PowerBoxData(type, clientId, targetId, message);
|
||||
}
|
||||
// Handle other types
|
||||
throw new JsonParseException("Unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.adapter;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithAttachment;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The type Power box data wa adapter.
|
||||
*/
|
||||
@Deprecated
|
||||
public class PowerBoxDataWAAdapter extends PowerBoxDataAdapter {
|
||||
@Override
|
||||
public void write(JsonWriter out, PowerBoxData value) throws IOException {
|
||||
out.beginObject();
|
||||
PowerBoxDataWithAttachment newValue = (PowerBoxDataWithAttachment) value;
|
||||
out.name("type").value(newValue.getType());
|
||||
out.name("clientId").value(newValue.getClientId());
|
||||
out.name("targetId").value(newValue.getTargetId());
|
||||
out.name("message").value(newValue.getMessage());
|
||||
out.name("timer_A").value(newValue.getTimerA());
|
||||
out.name("timer_B").value(newValue.getTimerB());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxData read(JsonReader in) throws IOException {
|
||||
String type = "";
|
||||
String clientId = "";
|
||||
String targetId = "";
|
||||
String message = "";
|
||||
Integer timer_A = null;
|
||||
Integer timer_B = null;
|
||||
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
switch (in.nextName()) {
|
||||
case "type" -> type = in.nextString();
|
||||
case "clientId" -> clientId = in.nextString();
|
||||
case "targetId" -> targetId = in.nextString();
|
||||
case "message" -> message = in.nextString();
|
||||
case "timerA" -> timer_A = in.nextInt();
|
||||
case "timerB" -> timer_B = in.nextInt();
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
if ("POWER_BOX".equals(type)) {
|
||||
return new PowerBoxDataWithAttachment(new PowerBoxData(type, clientId, targetId, message), timer_A, timer_B);
|
||||
}
|
||||
// Handle other types
|
||||
throw new JsonParseException("Unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.adapter;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxDataWithSingleAttachment;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The type Power box data wsa adapter.
|
||||
*/
|
||||
public class PowerBoxDataWSAAdapter extends PowerBoxDataAdapter {
|
||||
@Override
|
||||
public void write(JsonWriter out, PowerBoxData value) throws IOException {
|
||||
out.beginObject();
|
||||
PowerBoxDataWithSingleAttachment newValue = (PowerBoxDataWithSingleAttachment) value;
|
||||
out.name("type").value(newValue.getType());
|
||||
out.name("clientId").value(newValue.getClientId());
|
||||
out.name("targetId").value(newValue.getTargetId());
|
||||
out.name("message").value(newValue.getMessage());
|
||||
out.name("timer").value(newValue.getTimer());
|
||||
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerBoxData read(JsonReader in) throws IOException {
|
||||
String type = "";
|
||||
String clientId = "";
|
||||
String targetId = "";
|
||||
String message = "";
|
||||
Integer timer = null;
|
||||
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
switch (in.nextName()) {
|
||||
case "type" -> type = in.nextString();
|
||||
case "clientId" -> clientId = in.nextString();
|
||||
case "targetId" -> targetId = in.nextString();
|
||||
case "message" -> message = in.nextString();
|
||||
case "timer" -> timer = in.nextInt();
|
||||
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
if ("POWER_BOX".equals(type)) {
|
||||
return new PowerBoxDataWithSingleAttachment(new PowerBoxData(type, clientId, targetId, message), timer);
|
||||
}
|
||||
// Handle other types
|
||||
throw new JsonParseException("Unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.type;
|
||||
|
||||
/**
|
||||
* PowerBox数据类型
|
||||
*/
|
||||
public enum PowerBoxDataType {
|
||||
/**
|
||||
* 心跳类型(无参数)
|
||||
*/
|
||||
_NC_HEARTBEAT_,
|
||||
/**
|
||||
* 绑定类型(无参数)
|
||||
*/
|
||||
_NC_BIND_,
|
||||
/**
|
||||
* 断开类型(无参数)
|
||||
*/
|
||||
_NC_BREAK_,
|
||||
/**
|
||||
* 错误类型(无参数)
|
||||
*/
|
||||
_NC_ERROR_,
|
||||
/**
|
||||
* 强度类型 (参数数量:3~4)
|
||||
*/
|
||||
STRENGTH(3, 4),
|
||||
/**
|
||||
* 波形类型 (参数数量:1~101)
|
||||
*/
|
||||
PULSE(1, 101),
|
||||
/**
|
||||
* 清空类型 (参数数量:1)
|
||||
*/
|
||||
CLEAR(1),
|
||||
/**
|
||||
* 反馈类型 (参数数量:1)
|
||||
*/
|
||||
FEEDBACK(1),
|
||||
/**
|
||||
* 客户端消息类型 (无参数)
|
||||
*/
|
||||
CLIENT_MESSAGE,
|
||||
/**
|
||||
* 未知类型 (无参数)
|
||||
*/
|
||||
UNKNOWN;
|
||||
/**
|
||||
* The Nop.
|
||||
*/
|
||||
public final int NOP;
|
||||
/**
|
||||
* The Max nop.
|
||||
*/
|
||||
public final int MaxNOP;
|
||||
PowerBoxDataType() {
|
||||
this(0, 0);
|
||||
}
|
||||
PowerBoxDataType(int NumberOfParameters) {
|
||||
this(NumberOfParameters,-1);
|
||||
}
|
||||
PowerBoxDataType(int minNumberOfParameters, int maxNumberOfParameters) {
|
||||
this.NOP = minNumberOfParameters;
|
||||
this.MaxNOP = maxNumberOfParameters;
|
||||
}
|
||||
private static PowerBoxDataType getCommandType(String commandPrefix) {
|
||||
return switch (commandPrefix) {
|
||||
case "strength" -> STRENGTH;
|
||||
case "pulse" -> PULSE;
|
||||
case "clear" -> CLEAR;
|
||||
case "feedback" -> FEEDBACK;
|
||||
default -> UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets type.
|
||||
*
|
||||
* @param type the type
|
||||
* @param msg the msg
|
||||
* @return the type
|
||||
*/
|
||||
public static PowerBoxDataType getType(String type, String msg) {
|
||||
return getType(type, msg, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets type.
|
||||
*
|
||||
* @param type the type
|
||||
* @param msg the msg
|
||||
* @param mayPulse the may pulse
|
||||
* @return the type
|
||||
*/
|
||||
public static PowerBoxDataType getType(String type, String msg, boolean mayPulse) {
|
||||
return switch (type) {
|
||||
case "heartbeat" -> _NC_HEARTBEAT_;
|
||||
case "bind" -> _NC_BIND_;
|
||||
case "msg" -> getCommandType(msg.split("-")[0]);
|
||||
case "break" -> _NC_BREAK_;
|
||||
case "error" -> _NC_ERROR_;
|
||||
case "clientMsg" -> {
|
||||
PowerBoxDataType commandType = getCommandType(msg.split("-")[0]);
|
||||
yield mayPulse && commandType == PULSE ? PULSE : CLIENT_MESSAGE;
|
||||
}
|
||||
default -> UNKNOWN;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.data.type;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
public enum PowerBoxStatusCode {
|
||||
/**
|
||||
* 200 - 成功
|
||||
*/
|
||||
SUCCESSFUL("200"),
|
||||
/**
|
||||
* 209 - 对方客户端已断开
|
||||
*/
|
||||
OPPOSITE_CLIENT_DISCONNECTED("209"),
|
||||
/**
|
||||
* 210 - 二维码中没有有效的clientID
|
||||
*/
|
||||
QR_CODE_HAS_A_INVALID_CLIENT_ID("210"),
|
||||
/**
|
||||
* 211 - socket连接上了,但服务器迟迟不下发app端的id来绑定
|
||||
*/
|
||||
WAITING_FOR_SERVER_BINDING_MESSAGE_TOO_LONG("211"),
|
||||
/**
|
||||
* 400 - 此id已被其他客户端绑定关系
|
||||
*/
|
||||
TRYING_BINDING_ALREADY_BOUND_ID("400"),
|
||||
/**
|
||||
* 401 - 要绑定的目标客户端不存在
|
||||
*/
|
||||
TARGET_CLIENT_NOT_EXIST("401"),
|
||||
/**
|
||||
* 402 - 收信方和寄信方不是绑定关系
|
||||
*/
|
||||
NOT_BINDING_RELATIONSHIP("402"),
|
||||
/**
|
||||
* 403 - 发送的内容不是标准json对象
|
||||
*/
|
||||
NOT_STANDARD_JSON("403"),
|
||||
/**
|
||||
* 404 - 未找到收信人(离线)
|
||||
*/
|
||||
NOT_FOUND_BECAUSE_OF_OFFLINE("404"),
|
||||
/**
|
||||
* 405 - 下发的message长度大于1950
|
||||
*/
|
||||
MESSAGE_TOO_LONG("405"),
|
||||
/**
|
||||
* 406 - 未指定通道
|
||||
*/
|
||||
NO_CHOOSE_CHANNEL("406"),
|
||||
/**
|
||||
* 500 - 服务器内部异常
|
||||
*/
|
||||
INTERNAL_ERROR("500"),
|
||||
/**
|
||||
* 501 - 客户端发送了无效信息(Message为无效内容)给服务器
|
||||
*/
|
||||
INVALID_REQUEST("501"),
|
||||
/**
|
||||
* 502 - 不支持的操作
|
||||
*/
|
||||
UNSUPPORTED_OPERATION("502"),
|
||||
/**
|
||||
* -1 - 无效状态码
|
||||
*/
|
||||
INVALID_STATUS_CODE("-1");
|
||||
/**
|
||||
* The Code.
|
||||
*/
|
||||
final String code;
|
||||
PowerBoxStatusCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status code.
|
||||
*
|
||||
* @param code the code
|
||||
* @return the status code
|
||||
*/
|
||||
public static PowerBoxStatusCode getStatusCode(String code) {
|
||||
return switch(code) {
|
||||
case "200" -> PowerBoxStatusCode.SUCCESSFUL;
|
||||
case "209" -> PowerBoxStatusCode.OPPOSITE_CLIENT_DISCONNECTED;
|
||||
case "210" -> PowerBoxStatusCode.QR_CODE_HAS_A_INVALID_CLIENT_ID;
|
||||
case "211" -> PowerBoxStatusCode.WAITING_FOR_SERVER_BINDING_MESSAGE_TOO_LONG;
|
||||
case "400" -> PowerBoxStatusCode.TRYING_BINDING_ALREADY_BOUND_ID;
|
||||
case "401" -> PowerBoxStatusCode.TARGET_CLIENT_NOT_EXIST;
|
||||
case "402" -> PowerBoxStatusCode.NOT_BINDING_RELATIONSHIP;
|
||||
case "403" -> PowerBoxStatusCode.NOT_STANDARD_JSON;
|
||||
case "404" -> PowerBoxStatusCode.NOT_FOUND_BECAUSE_OF_OFFLINE;
|
||||
case "405" -> PowerBoxStatusCode.MESSAGE_TOO_LONG;
|
||||
case "406" -> PowerBoxStatusCode.NO_CHOOSE_CHANNEL;
|
||||
case "500" -> PowerBoxStatusCode.INTERNAL_ERROR;
|
||||
case "501" -> PowerBoxStatusCode.INVALID_REQUEST;
|
||||
case "502" -> PowerBoxStatusCode.UNSUPPORTED_OPERATION;
|
||||
default -> PowerBoxStatusCode.INVALID_STATUS_CODE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid status code boolean.
|
||||
*
|
||||
* @param code the code
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isValidStatusCode(String code) {
|
||||
return getStatusCode(code) != INVALID_STATUS_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets code.
|
||||
*
|
||||
* @return the code
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
/**
|
||||
* WS占位角色
|
||||
*/
|
||||
public final class PlaceholderRole extends Role {
|
||||
/**
|
||||
* Instantiates a new Placeholder role.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public PlaceholderRole(String name) {
|
||||
super(name, RoleType.PLACEHOLDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Of placeholder role.
|
||||
*
|
||||
* @param name the name
|
||||
* @return the placeholder role
|
||||
*/
|
||||
public static PlaceholderRole of(String name) {
|
||||
return new PlaceholderRole("Pl" + name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.MessageDirection;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 角色,是{@link MessageDirection messageDirection}组成部分
|
||||
*/
|
||||
public sealed abstract class Role implements Serializable
|
||||
permits PlaceholderRole, WebSocketApplicationRole, WebSocketClientRole, WebSocketServerRole {
|
||||
/**
|
||||
* The Name.
|
||||
*/
|
||||
public String name;
|
||||
/**
|
||||
* The Type.
|
||||
*/
|
||||
public final RoleType type;
|
||||
|
||||
/**
|
||||
* Instantiates a new Role.
|
||||
*
|
||||
* @param name the name
|
||||
* @param type the type
|
||||
*/
|
||||
Role(String name, final RoleType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色名字,主要在每次心跳时更新
|
||||
*
|
||||
* @param name 角色
|
||||
*/
|
||||
public void UpdateName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Role Json反序列化适配器
|
||||
*/
|
||||
public class RoleDeserializer implements JsonDeserializer<Role> {
|
||||
|
||||
@Override
|
||||
public Role deserialize(JsonElement json, Type typeOFT, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
String name = jsonObject.get("name").getAsString();
|
||||
RoleType type = RoleType.getTypeFromString(jsonObject.get("type").getAsString());
|
||||
if (type != null) {
|
||||
return switch (type){
|
||||
case T_CLIENT -> new WebSocketClientRole(name);
|
||||
case T_SERVER -> new WebSocketServerRole(name);
|
||||
case APPLICATION -> new WebSocketApplicationRole(name);
|
||||
case PLACEHOLDER -> new PlaceholderRole(name);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
/**
|
||||
* WS应用角色
|
||||
*/
|
||||
public final class WebSocketApplicationRole extends Role{
|
||||
/**
|
||||
* Instantiates a new Web socket application role.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public WebSocketApplicationRole(String name) {
|
||||
super(name, RoleType.APPLICATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Of web socket application role.
|
||||
*
|
||||
* @param uuid the uuid
|
||||
* @return the web socket application role
|
||||
*/
|
||||
public static WebSocketApplicationRole of(String uuid) {
|
||||
return new WebSocketApplicationRole("Ap" + uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
/**
|
||||
* WS客户端角色
|
||||
*/
|
||||
public final class WebSocketClientRole extends Role {
|
||||
/**
|
||||
* Instantiates a new Web socket client role.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public WebSocketClientRole(String name) {
|
||||
super(name, RoleType.T_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Of web socket client role.
|
||||
*
|
||||
* @param uuid the uuid
|
||||
* @return the web socket client role
|
||||
*/
|
||||
public static WebSocketClientRole of(String uuid) {
|
||||
return new WebSocketClientRole("Cl" + uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role;
|
||||
|
||||
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
|
||||
|
||||
/**
|
||||
* WS服务器角色
|
||||
*/
|
||||
public final class WebSocketServerRole extends Role {
|
||||
/**
|
||||
* Instantiates a new Web socket server role.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public WebSocketServerRole(String name) {
|
||||
super(name, RoleType.T_SERVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Of web socket server role.
|
||||
*
|
||||
* @param uuid the uuid
|
||||
* @return the web socket server role
|
||||
*/
|
||||
public static WebSocketServerRole of(String uuid) {
|
||||
return new WebSocketServerRole("Sr" + uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.message.role.type;
|
||||
|
||||
/**
|
||||
* 角色枚举类型
|
||||
*/
|
||||
public enum RoleType {
|
||||
/**
|
||||
* Tradition Server 传统意义上的服务器
|
||||
*/
|
||||
T_SERVER,
|
||||
/**
|
||||
* Tradition Client 传统意义上的客户端
|
||||
*/
|
||||
T_CLIENT,
|
||||
/**
|
||||
* App 应用
|
||||
*/
|
||||
APPLICATION,
|
||||
/**
|
||||
* 占位符 即任意端
|
||||
*/
|
||||
PLACEHOLDER;
|
||||
|
||||
/**
|
||||
* Gets type from string.
|
||||
*
|
||||
* @param string the string
|
||||
* @return the type from string
|
||||
*/
|
||||
public static RoleType getTypeFromString(String string) {
|
||||
return switch (string) {
|
||||
case "T_SERVER" -> T_SERVER;
|
||||
case "T_CLIENT" -> T_CLIENT;
|
||||
case "APPLICATION" -> APPLICATION;
|
||||
case "PLACEHOLDER" -> PLACEHOLDER;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2024-2025 R3944Realms. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package com.r3944realms.dg_lab.api.websocket.sharedData;
|
||||
|
||||
/**
|
||||
* C/S ‘s Shared Data that exist in handler
|
||||
*/
|
||||
public interface ISharedData extends Cloneable{
|
||||
ISharedData clone();
|
||||
}
|
||||
201
LICENSE.txt
Normal file
201
LICENSE.txt
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
35
build.gradle
Normal file
35
build.gradle
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
plugins {
|
||||
id("idea")
|
||||
id("eclipse")
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
group = "top.r3944realms.superleadrope"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply {
|
||||
plugin('java')
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
18
gradle.properties
Normal file
18
gradle.properties
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Gradle settings
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.configuration-cache.problems=warn
|
||||
# ROOT
|
||||
project_name=DgLab
|
||||
project_version=4.2.8.16
|
||||
project_group=top.r3944realms.dg_lab
|
||||
|
||||
# API
|
||||
api_project_group=top.r3944realms.dg_lab.api
|
||||
api_suffix=api
|
||||
|
||||
# COMMON
|
||||
common_suffix=common
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#Sun Sep 22 17:25:46 CST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.12-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
16
node_modules/.bin/commitizen
generated
vendored
Normal file
16
node_modules/.bin/commitizen
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../commitizen/bin/commitizen" "$@"
|
||||
else
|
||||
exec node "$basedir/../commitizen/bin/commitizen" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/commitizen.cmd
generated
vendored
Normal file
17
node_modules/.bin/commitizen.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\commitizen\bin\commitizen" %*
|
||||
28
node_modules/.bin/commitizen.ps1
generated
vendored
Normal file
28
node_modules/.bin/commitizen.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../commitizen/bin/commitizen" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../commitizen/bin/commitizen" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../commitizen/bin/commitizen" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../commitizen/bin/commitizen" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/cz
generated
vendored
Normal file
16
node_modules/.bin/cz
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../commitizen/bin/git-cz" "$@"
|
||||
else
|
||||
exec node "$basedir/../commitizen/bin/git-cz" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/cz.cmd
generated
vendored
Normal file
17
node_modules/.bin/cz.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\commitizen\bin\git-cz" %*
|
||||
28
node_modules/.bin/cz.ps1
generated
vendored
Normal file
28
node_modules/.bin/cz.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/git-cz
generated
vendored
Normal file
16
node_modules/.bin/git-cz
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../commitizen/bin/git-cz" "$@"
|
||||
else
|
||||
exec node "$basedir/../commitizen/bin/git-cz" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/git-cz.cmd
generated
vendored
Normal file
17
node_modules/.bin/git-cz.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\commitizen\bin\git-cz" %*
|
||||
28
node_modules/.bin/git-cz.ps1
generated
vendored
Normal file
28
node_modules/.bin/git-cz.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../commitizen/bin/git-cz" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/jiti
generated
vendored
Normal file
16
node_modules/.bin/jiti
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../jiti/lib/jiti-cli.mjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../jiti/lib/jiti-cli.mjs" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/jiti.cmd
generated
vendored
Normal file
17
node_modules/.bin/jiti.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jiti\lib\jiti-cli.mjs" %*
|
||||
28
node_modules/.bin/jiti.ps1
generated
vendored
Normal file
28
node_modules/.bin/jiti.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../jiti/lib/jiti-cli.mjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../jiti/lib/jiti-cli.mjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../jiti/lib/jiti-cli.mjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../jiti/lib/jiti-cli.mjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/js-yaml
generated
vendored
Normal file
16
node_modules/.bin/js-yaml
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../js-yaml/bin/js-yaml.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../js-yaml/bin/js-yaml.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/js-yaml.cmd
generated
vendored
Normal file
17
node_modules/.bin/js-yaml.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\js-yaml\bin\js-yaml.js" %*
|
||||
28
node_modules/.bin/js-yaml.ps1
generated
vendored
Normal file
28
node_modules/.bin/js-yaml.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/tsc
generated
vendored
Normal file
16
node_modules/.bin/tsc
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/tsc.cmd
generated
vendored
Normal file
17
node_modules/.bin/tsc.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
||||
28
node_modules/.bin/tsc.ps1
generated
vendored
Normal file
28
node_modules/.bin/tsc.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/tsserver
generated
vendored
Normal file
16
node_modules/.bin/tsserver
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/tsserver.cmd
generated
vendored
Normal file
17
node_modules/.bin/tsserver.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user